Ответ 1
Причина, по которой стандартная библиотека clojure имеет только fold-left (уменьшение), на самом деле очень тонкая, и потому, что clojure недостаточно ленив, чтобы получить главное преимущество fold-right.
Основное преимущество fold-right в таких языках, как haskell, состоит в том, что он может на самом деле коротко замыкаться.
Если мы делаем foldr (&&) True [False, True, True, True, True]
, то способ, которым он фактически оценивается, очень просвещен. Единственное, что нужно оценить, это функция and
с 1 аргументом (первая False
). Как только он туда доберется, он знает ответ и не нуждается в оценке ЛЮБОГО из True
s.
Если вы посмотрите очень внимательно на изображение:
вы увидите, что хотя концептуально fold-right начинается и заканчивается список и перемещается в направлении вперед, на самом деле он начинает оценивать FRONT списка.
Это пример того, где функции lazy/curried и tail recursion могут давать преимущества, которые clojure не могут.
Раздел бонусов (для заинтересованных)
Основываясь на рекомендации vemv, я хотел бы упомянуть, что clojure добавила новую функцию в пространство имен ядра, чтобы обойти это ограничение, которое clojure не может иметь ленивую правую сгиб. В основном пространстве имен есть функция reduced
, которая позволяет вам сделать clojure reduce
lazier. Его можно использовать для короткого замыкания reduce
, говоря ему, чтобы он не смотрел остальную часть списка. Например, если вы хотели бы умножить списки чисел, но имели основания подозревать, что список иногда будет содержать нуль и хотел бы обработать это как особый случай, не глядя на оставшуюся часть списка после того, как вы столкнулись с нулем, вы могли бы написать следующую функцию multiply-all
(обратите внимание на использование reduced
, чтобы указать, что окончательный ответ 0
, вне зависимости от того, что остальная часть списка).
(defn multiply-all [coll]
(reduce
(fn [accumulator next-value]
(if (zero? next-value)
(reduced 0)
(* accumulator next-value)))
1
coll))
И затем, чтобы доказать, что это короткое замыкание, вы можете умножить бесконечный список чисел, который содержит нуль, и увидеть, что он действительно заканчивается с ответом 0
(multiply-all
(cycle [1 2 3 4 0]))