Разница между DoSq и For в Clojure

106

В чем разница между DoSq и for в Clojure? Каковы примеры того, когда вы бы предпочли использовать одно вместо другого?

Джефф Медведь
источник

Ответы:

168

Разница в том, что forстроит ленивую последовательность и возвращает ее, а doseqпредназначена для выполнения побочных эффектов и возвращает nil.

user=> (for [x [1 2 3]] (+ x 5))
(6 7 8)
user=> (doseq [x [1 2 3]] (+ x 5))
nil
user=> (doseq [x [1 2 3]] (println x))
1
2
3
nil

Если вы хотите создать новую последовательность на основе других последовательностей, используйте for. Если вы хотите создать побочные эффекты (печать, запись в базу данных, запуск ядерной боеголовки и т. Д.) На основе элементов из некоторых последовательностей, используйте дозу.

Rayne
источник
11
теперь это много побочных эффектов ... запуск ядерной боеголовки :)
Marc
6
Спасибо! Я тянул свои (давно пропавшие) волосы «за», чтобы они никогда не запускали мои ядерные боеголовки поверх моего списка предметов. "Doseq", конечно, сделал.
Yu Shen
Это отличный способ провести различие.
jskulski
60

Также обратите внимание, что doseqлюбит, когда forленится. Пример, отсутствующий в ответе Рейна:

(for [x [1 2 3]] (println x))

В REPL это обычно будет делать то, что вы хотите, но это в основном совпадение: REPL заставляет ленивую последовательность for, созданную с помощью , вызывая выполнение printlns. В неинтерактивной среде ничего не будет напечатано. Вы можете увидеть это в действии, сравнив результаты

user> (def lazy (for [x [1 2 3]] (println 'lazy x)))
#'user/lazy

user> (def eager (doseq [x [1 2 3]] (println 'eager x)))
eager 1
eager 2
eager 3
#'user/eager

Поскольку defформа возвращает созданную новую переменную, а не значение, которое к ней привязано, REPL нечего печатать и lazyбудет ссылаться на нереализованный lazy-seq: ни один из его элементов не был вычислен вообще. eagerбудет ссылаться на nil, и вся его печать будет выполнена.

сплав
источник
Как DoSq обрабатывает оценку бесконечной ленивой последовательности? плохая идея? называть это только конечными последовательностями, нетерпеливыми или ленивыми?
johnbakers
@johnbakers Он будет заблокирован навсегда, пока оценка не будет прервана. Clojure никогда не пытается обрабатывать бесконечные последовательности иначе, чем конечные последовательности.
Radon Rosborough