在 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.coreclojure.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 中将文件内容读入集合的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

c++ 中将标准输入内容读入字符串或向量的最快方法

在Java中将文件读入字节[]数组的优雅方法[重复]

在 Clojure 中将嵌套向量减少为另一个向量

如何将文件内容读入 istringstream?

如何在 Clojure 中将 XML 转换为 edn?

如何在 Clojure/Compojure/Ring 中将映射转换为 URL 查询字符串?