Лучший способ прочитать содержимое файла в набор в Clojure - PullRequest
4 голосов
/ 04 ноября 2011

Я изучаю Clojure, и в качестве упражнения я хотел написать что-то вроде команды unix "comm".

Чтобы сделать это, я прочитал содержимое каждого файла в набор, а затем использовал разность / пересечение, чтобы показать исключительные / общие файлы.

После большого количества повторов я придумал что-то вроде этого для части создания набора:

(def contents (ref #{}))
(doseq [line (read-lines "/tmp/a.txt")]
  (dosync (ref-set contents (conj @contents line))))

(я использую duck-streams / read-lines для секвенции содержимого файла).

Это мой первый удар по любому виду функционального программирования или lisp / Clojure. Например, я не мог понять, почему, когда я делал мошенничество на съемочной площадке, набор все еще был пустым. Это привело меня к изучению рефери.

  1. Есть ли лучший Clojure / функциональный способ сделать это? Используя ref-set, я просто искажаю код для нефункционального мышления или мой код соответствует принципам, как это должно быть сделано?
  2. Есть ли библиотека, которая уже делает это? Кажется, это довольно обычная вещь, которую я хочу делать, но я не мог найти ничего подобного.

Ответы [ 2 ]

8 голосов
/ 04 ноября 2011

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 и ссылок, вы можете сделать:

user> (reduce conj #{} (line-seq (io/reader "foo.txt")))
#{"foo" "bar" "baz"}

Обратите внимание, что Unix comm сравнивает два отсортированных файла, что, вероятно, является более эффективным способом сравнения файлов, чем пересечение множеств.

Редактировать: Дейв Рэй прав, чтобы избежать утечки открытых файловых дескрипторов, лучше сделать это:

user> (with-open [f (io/reader "foo.txt")]
        (into #{} (line-seq f)))
#{"foo" "bar" "baz"}
0 голосов
/ 04 ноября 2011

Я всегда читаю с slurp, а после этого делю с re-seq из-за моих потребностей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...