gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 18:30:03 25 4
gpt4 key购买 nike

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

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

首先介绍一些背景知识。在我在这个项目中实现的其他功能中,我使用了 for声明“建立”这样的值列表

...
(let [rem-list (remove nil? (for [j (range (count (graph n)))]
(cond (< (rand) 0.5)
[n (nth (seq (graph n)) j)])))
...

这个for将建立一个要从图中删除的边列表,完成后我可以使用 rem-listreduce从某些图结构中删除所有边。

回到我的问题。我想如果我要逐行读取文件,我可以以相同的方式“构建”一个列表,所以我实现了下面的函数

(defn readGraphFile [filename, numnodes]
(let [edge-list
(with-open [rdr (io/reader filename)]
(doseq [line (line-seq rdr)]
(lineToEdge line)))]
(edge-list)))

尽管如果我要运行这个函数,我最终会遇到空指针异常,就好像没有任何内容“添加”到 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))

这里lineToEdge返回一对数字(例如 [1 2] )。哪个是 add-edge 的正确输入功能。

finalproject.core> (add-edge (empty-graph 5) (lineToEdge "e 1 2"))
[#{} #{2} #{1} #{} #{}]

这个函数的问题是它似乎从未真正向图表添加边

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

所以我想我的问题在于如何 doseqfor 不同?是不同还是我的实现不正确?

最佳答案

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

如果您查看doseq的文档:(https://clojuredocs.org/clojure.core/doseq)

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

因此,无论您正在进行任何处理,都将返回 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 将执行一个序列,将传入的函数应用到前两个参数,然后将其结果作为下一个调用的第一个参数传递。缺点就在那里,所以我们可以确定空图是传入的第一个参数。

关于clojure - 这个 `doseq`语句和 `for`语句有什么区别;在 Clojure 中读取文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29312449/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com