在 Clojure 中将文件内容读入集合的最佳方法
Posted
技术标签:
【中文标题】在 Clojure 中将文件内容读入集合的最佳方法【英文标题】:Best way to read contents of file into a set in Clojure 【发布时间】:2011-11-04 17:41:44 【问题描述】:我正在学习 Clojure,作为练习,我想编写类似 unix“comm”命令的东西。
为此,我将每个文件的内容读入一个集合,然后使用差异/交集来显示独占/公共文件。
经过大量的repl-time,我想出了这样的设置创建部分:
(def contents (ref #))
(doseq [line (read-lines "/tmp/a.txt")]
(dosync (ref-set contents (conj @contents line))))
(我正在使用鸭流/读取行来对文件的内容进行排序)。
这是我第一次尝试任何类型的函数式编程或 lisp/Clojure。例如,我不明白为什么当我在片场上做一个 conj 时,片场仍然是空的。这使我了解了 refs。
-
是否有更好的 Clojure/函数式方法来执行此操作?通过使用 ref-set,我只是将代码扭曲为非功能性思维方式,还是我的代码遵循了应该如何完成的思路?
是否有图书馆已经这样做了?这似乎是一件相对普通的事情,但我找不到类似的事情。
【问题讨论】:
Brian Carper 的回答很好。避免使用鸭流。它已被弃用,其大部分功能都折叠到clojure.core
和 clojure.java.io
。
关于您对集合“仍然为空”的评论;听起来您正在期待可变的行为。请记住,在 clojure 中,数据类型是不可变的。建立集合是递归完成的,因此 reduce
的使用如 Brian Carper 所示(into
在内部使用 reduce
)。
@DaveRay 我不知道鸭流的状态。感谢您的信息。
@AlexStoddard 是的,这正是我的错误。阅读关于不变性是一回事,真正理解它完全是另一回事。
【参考方案1】:
Clojure 1.3:
user> (require '[clojure.java [io :as io]])
nil
user> (line-seq (io/reader "foo.txt"))
("foo" "bar" "baz")
user> (into # (line-seq (io/reader "foo.txt")))
#"foo" "bar" "baz"
line-seq
为您提供了一个惰性序列,其中序列中的每个项目都是文件中的一行。
into
将其全部转储到一个集合中。要执行您想做的事情(将每个项目逐个添加到集合中),而不是 doseq
和 refs,您可以这样做:
user> (reduce conj # (line-seq (io/reader "foo.txt")))
#"foo" "bar" "baz"
请注意,Unix comm
比较两个已排序的文件,这可能是比较文件比设置交集更有效的方法。
编辑:Dave Ray 是对的,为了避免泄漏打开的文件句柄,最好这样做:
user> (with-open [f (io/reader "foo.txt")]
(into # (line-seq f)))
#"foo" "bar" "baz"
【讨论】:
@BrianCarper 谢谢,这真的很有帮助。 line-seq 的使用似乎更自然。我只看到 reduce 被用来对序列中的值进行求和,或者类似的东西,所以看到它被用来创建另一个集合很有趣。 你可以使用函数set
代替(into # ...
【参考方案2】:
我总是使用slurp
阅读,然后根据我的需要与re-seq
分开。
【讨论】:
以上是关于在 Clojure 中将文件内容读入集合的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章