在Clojure中将文件内容读入集合的最佳方法

rif*_*boy 4 clojure

我正在学习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的第一次尝试.例如,我无法理解为什么,当我在集合上做了一个结合时,该集合仍然是空的.这让我了解了参考资料.

  1. 有没有更好的Clojure /功能方式来做到这一点?通过使用ref-set,我只是将代码扭曲到一个非功能性的思维模式,或者我的代码是如何完成的?
  2. 有没有图书馆已经这样做了?这似乎是一个相对普通的事情,但我找不到类似的东西.

Bri*_*per 8

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)

  • 不要忘记将该阅读器包装在`with-open` :)中 (2认同)