Будет ли последний из ленивых seq оценить все элементы в clojure? - PullRequest
4 голосов
/ 26 ноября 2011

Давайте предположим, что у нас дорогие вычисления expensive.Если мы считаем, что map создает ленивый seq, то выполняет ли следующее вычисление функция expensive для всех элементов сопоставленной коллекции или только для последнего?

(last
  (map expensive '(1 2 3 4 5)))

Т.е. это оценивает expensive для всех значений 1..5 или оценивается только (expensive 5)?

Ответы [ 3 ]

7 голосов
/ 26 ноября 2011

Вся коллекция будет оценена.Простой тест отвечает на ваш вопрос.

=> (defn exp [x]
     (println "ran")
     x)
=> (last
     (map exp '(1 2 3 4 5)))
ran
ran
ran
ran
ran
5
2 голосов
/ 28 ноября 2011

last всегда заставляет вычислять ленивую последовательность - это, безусловно, необходимо, так как необходимо найти конец последовательности, и, следовательно, необходимо оценивать ленивый seq в каждой позиции.

Если вы хотителень во всех отдельных элементах, один из способов состоит в том, чтобы создать последовательность ленивых последовательностей следующим образом:

(defn expensive [n]
  (do 
    (println "Called expensive function on " n)
    (* n n)))

(def lzy (map #(lazy-seq [(expensive %)]) '(1 2 3 4 5)))

(last lzy)
=> Called expensive function on  5
=> (25)

Обратите внимание, что last в этом случае все еще заставляет вычислять ленивую последовательность верхнего уровня, но не '• принудительно вычислять содержащиеся в нем ленивые последовательности, кроме последней, которую мы извлекаем (потому что она печатается в REPL).

2 голосов
/ 26 ноября 2011

В Clojure нет произвольного доступа для ленивых последовательностей.

В некотором смысле вы можете считать их эквивалентными односвязным спискам - у вас всегда есть текущий элемент и функция для получения следующего.

Так что, даже если вы просто вызываете (last some-seq) он будет оценивать все элементы последовательности, даже если последовательность ленивая. Если последовательность конечна и достаточно мала (и если вы не держите начало последовательности в ссылке), то это нормально, когда дело доходит до памяти.Как вы заметили, существует проблема со временем выполнения, которая может возникнуть, если функция, используемая для получения следующего элемента, является дорогой.

В этом случае вы можете пойти на компромисс, чтобы использовать дешевую функцию для ходьбывплоть до последнего элемента:

(last some-seq)

и затем примените функцию только к этому результату:

(expensive (last some-seq))

...