我正在学习Clojure,作为练习,我想写一些像unix"comm"命令.
为此,我将每个文件的内容读入一个集合,然后使用差异/交集来显示独占/公共文件.
经过大量的repl-time,我为set set部分想出了类似的东西:
(def contents (ref #{}))
(doseq [line (read-lines "/tmp/a.txt")]
(dosync (ref-set contents (conj @contents line))))
Run Code Online (Sandbox Code Playgroud)
(我正在使用duck-streams/read-lines来查找文件的内容).
这是我对任何函数式编程或lisp/Clojure的第一次尝试.例如,我无法理解为什么,当我在集合上做了一个结合时,该集合仍然是空的.这让我了解了参考资料.
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"}
Run Code Online (Sandbox Code Playgroud)
line-seq
为您提供一个惰性序列,其中序列中的每个项目都是文件中的一行.
into
将它全部转储到一个集合中.要做你想做的事情(将每个项目逐个添加到一个集合中),而不是doseq
和refs,你可以这样做:
user> (reduce conj #{} (line-seq (io/reader "foo.txt")))
#{"foo" "bar" "baz"}
Run Code Online (Sandbox Code Playgroud)
请注意,Unix comm
比较了两个已排序的文件,这可能是比文件交集更有效的比较文件的方法.
编辑:Dave Ray是对的,为了避免泄漏打开文件句柄,最好这样做:
user> (with-open [f (io/reader "foo.txt")]
(into #{} (line-seq f)))
#{"foo" "bar" "baz"}
Run Code Online (Sandbox Code Playgroud)