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