这个'doseq'语句和'for'语句有什么区别;在clojure中读取文件?

这个'doseq'语句和'for'语句有什么区别;在clojure中读取文件?,clojure,Clojure,如果你一整天都在关注我的问题 我正在clojure中做一个类项目,在读取文件、解析文件以及根据其内容创建图形时遇到困难。我已经设法打开并读取了一个文件,并根据需要对行进行了解析。我现在面临的问题是从读入的数据创建一个图形结构 首先是一些背景知识。在我在这个项目中实现的其他功能中,我使用了一个for语句来“构建”一个值列表 ... (let [rem-list (remove nil? (for [j (range (count (graph n)))] (cond (< (rand

如果你一整天都在关注我的问题

我正在clojure中做一个类项目,在读取文件、解析文件以及根据其内容创建图形时遇到困难。我已经设法打开并读取了一个文件,并根据需要对行进行了解析。我现在面临的问题是从读入的数据创建一个图形结构

首先是一些背景知识。在我在这个项目中实现的其他功能中,我使用了一个
for
语句来“构建”一个值列表

...
(let [rem-list (remove nil? (for [j (range (count (graph n)))]
    (cond (< (rand) 0.5)
        [n (nth (seq (graph n)) j)])))
...
虽然如果要运行此函数,最终会出现一个空指针异常,就好像从未“添加”任何内容到
边缘列表中一样。那么,做一个懒惰/善良的人?我是程序员,我很快就想到了另一种方法。虽然这在某种程度上取决于我对
for
如何构建列表的思考

在这个函数中,我首先
让[graph
等于一个节点数已知的空图。然后每次读取一行时,我只需将该边(文件中的每一行都是边)添加到图中,实际上是“构建”我的图。函数如下所示

(defn readGraph [filename, numnodes]
  (let [graph (empty-graph numnodes)]
    (with-open [rdr (io/reader filename)]
      (doseq [line (line-seq rdr)]
        (add-edge graph (lineToEdge line))))
    graph))
此处
lineToEdge
返回一对数字(例如
[12]
)。这是
添加边
功能的正确输入

finalproject.core> (add-edge (empty-graph 5) (lineToEdge "e 1 2"))
[#{} #{2} #{1} #{} #{}]
不过,这个函数的问题是,它似乎从来没有向图形中实际添加边

finalproject.core> (readGraph "/home/eccomp/finalproject/resources/11nodes.txt" 11)
[#{} #{} #{} #{} #{} #{} #{} #{} #{} #{} #{}]

因此,我想我的问题在于
doseq
for
有何不同?它是不同的还是我的实现不正确?

doseq
for
的不同之处在于,它是为了在序列上运行函数,只是为了产生副作用

如果您查看
doseq
的文档: ()

重复执行身体(可能是因为副作用)和 由“for”提供的绑定和筛选。不保留 序列头。返回nil

因此,不管您正在进行什么处理,
nil
都将被返回

您可以使用
for
切换
doseq
,它应该可以工作。但是,
line seq
是惰性的,因此您可能需要将其包装在
doall
中,以确保它在文件打开时尝试读取所有行

此外,第二个readGraph函数将只返回一个空图形:

(defn readGraph [filename, numnodes]
  (let [graph (empty-graph numnodes)]
    (with-open [rdr (io/reader filename)]
      (doseq [line (line-seq rdr)]
        (add-edge graph (lineToEdge line))))
    graph))
最后一行就是您用
let
设置的空图,因为Clojure是一种不可变语言,图形引用永远不会更新,因为您有一个函数可以获取现有图形并向其添加一条边,所以在传递正在构建的列表时,您需要单步遍历该列表

我知道一定有更好的方法,但我并不擅长Clojure,而是:

(defn readGraph
  [filename numnodes]
  (with-open [rdr (io/reader filename)]
    (let [edge-seq (line-seq rdr)]
        (loop [cur-line (first edge-seq)
               rem-line (rest edge-seq)
               graph (empty-graph numnodes)]
          (if-not cur-line
            graph
            (recur (first rem-line)
                   (rest rem-line)
                   (add-edge graph (lineToEdge cur-line))))))))
可能会让你更接近你的目标


再考虑一下,您可以尝试使用reduce,因此:

(defn readGraph
  [filename numnodes]
  (with-open [rdr (io/reader filename)]
    (reduce add-edge (cons (empty-graph numnodes)
                           (doall (line-seq rdr))))))

Reduce将经历一个序列,将您传入的函数应用于前两个参数,然后将结果作为第一个参数传入下一个调用。
cons
在那里,因此我们可以确保传入的第一个参数是空图。

您可以在Clojure do中轻松找到问题的答案文件

您可以在网站上找到所有核心功能的完整文档,或者只需在Clojure REPL中运行
(doc)

下面是我的看法:

换句话说,In总是返回
nil
。因此,您可以使用它的唯一方法是造成一些副作用(例如,重复将某些内容打印到您的控制台)

下面是这样说的:


不会打印任何内容,因为
for
没有使用循环结果,因此不会计算循环结果。您可以使用强制计算惰性序列。

谢谢您的回答,这对我现在更有意义。我们最近一直在讨论惰性序列,这正好与此相适应。我喜欢您提供的解决方案,我也喜欢我尝试过几次让它为我工作,尽管与实现无关,但我得到错误“IllegalArgumentException未找到匹配字段:clojure.lang.Cons类关闭”我试过用多种方法重写它,但却一事无成,在这个问题的背景下,这个错误对你有什么意义吗?我会试着看看我以后是否能解决它,手边没有REPL的情况下工作会很困难。:)啊,哇,我在“打开”中有edge seq这是错误的。我回家后会纠正。我已经尝试了更多的方法来解决问题,我基本上已经归结到了最后链接的问题中所概述的内容。似乎只要我尝试添加逻辑来改变循环中的图表,我就得到了一个错误。我可以理解错误,但不能理解它是如何产生的。//我已经更新了我的答案来修复pr您遇到的问题,并在最后添加了另一种可能的方法。
(defn readGraph
  [filename numnodes]
  (with-open [rdr (io/reader filename)]
    (reduce add-edge (cons (empty-graph numnodes)
                           (doall (line-seq rdr))))))
=> (doc doseq)
(doc doseq)
-------------------------
clojure.core/doseq
([seq-exprs & body])
Macro
  Repeatedly executes body (presumably for side-effects) with
  bindings and filtering as provided by "for".  Does not retain
  the head of the sequence. Returns nil.
=> (doc for)
(doc for)
-------------------------
clojure.core/for
([seq-exprs body-expr])
Macro
  List comprehension. Takes a vector of one or more
   binding-form/collection-expr pairs, each followed by zero or more
   modifiers, and yields a lazy sequence of evaluations of expr.
   Collections are iterated in a nested fashion, rightmost fastest,
   and nested coll-exprs can refer to bindings created in prior
   binding-forms.  Supported modifiers are: :let [binding-form expr ...],
   :while test, :when test.

  (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))
(defn noop []
  (for [i (range 10)]
    (println i))
  nil)