如果您一整天都在关注我的问题,
我正在使用clojure进行一个类项目,并且难以读取文件,解析文件并从其内容创建图形.我已经设法打开并读取文件,同时根据需要解析这些行.我现在面临的问题是从读入的数据创建图形结构.
一些背景优先.在我在这个项目中实现的其他函数中,我使用了一个for语句来"建立"一个值列表
...
(let [rem-list (remove nil? (for [j (range (count (graph n)))]
(cond (< (rand) 0.5)
[n (nth (seq (graph n)) j)])))
...
Run Code Online (Sandbox Code Playgroud)
这for将建立一个从图中删除的边的列表,在完成之后,我可以rem-list在a中使用它reduce来从一些图结构中删除所有边.
回到我的问题.我想如果我要逐行读取文件,我可以用同样的方式"建立"一个列表,所以我实现了下面的功能
(defn readGraphFile [filename, numnodes]
(let [edge-list
(with-open [rdr (io/reader filename)]
(doseq [line (line-seq rdr)]
(lineToEdge line)))]
(edge-list)))
Run Code Online (Sandbox Code Playgroud)
虽然如果我要运行这个函数,我最终会得到一个空指针异常,好像没有任何东西被"添加" edge-list.那么懒惰/好吗?程序员我很快就想到了另一种方式.虽然它仍然有点依赖于我对如何for构建列表的思考.
在这个函数中,我首先let [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))
Run Code Online (Sandbox Code Playgroud)
这里lineToEdge返回一对数字(ex [1 2]).哪个是该add-edge功能的正确输入.
finalproject.core> (add-edge (empty-graph 5) (lineToEdge "e 1 2"))
[#{} #{2} #{1} #{} #{}]
Run Code Online (Sandbox Code Playgroud)
但是这个函数的问题在于它似乎从未真正为图形添加边缘
finalproject.core> (readGraph "/home/eccomp/finalproject/resources/11nodes.txt" 11)
[#{} #{} #{} #{} #{} #{} #{} #{} #{} #{} #{}]
Run Code Online (Sandbox Code Playgroud)
所以我想我的问题在于如何doseq与众不同for?是不同的还是我的实施不正确?
doseq不同之处for在于它仅用于为副作用运行序列上的函数.
如果您查看以下文档doseq:(https://clojuredocs.org/clojure.core/doseq)
通过"for"提供的绑定和过滤反复执行主体(可能是副作用).不保留序列的头部.返回零
因此,无论您正在进行任何处理,nil都将被退回.
您可以切换doseq使用for,它应该工作.但是,它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))
Run Code Online (Sandbox Code Playgroud)
最后一行只是你设置的空图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))))))))
Run Code Online (Sandbox Code Playgroud)
可能会给你更接近你所追求的东西.
考虑一下,你可以尝试使用reduce,所以:
(defn readGraph
[filename numnodes]
(with-open [rdr (io/reader filename)]
(reduce add-edge (cons (empty-graph numnodes)
(doall (line-seq rdr))))))
Run Code Online (Sandbox Code Playgroud)
Reduce将遍历一个序列,将您传入的函数应用于前两个参数,然后将其结果作为第一个参数传递给下一个调用.该cons是存在的,所以我们可以肯定的空图是在传递的第一个参数.
| 归档时间: |
|
| 查看次数: |
620 次 |
| 最近记录: |