#clojure #lazy-evaluation
#clojure #ленивая оценка
Вопрос:
карта не кажется такой ленивой, как хотелось бы, в этом примере map вызывает функцию один раз, как я и ожидал:
(first (map #(do (println "x: " %) %) '(0 1)))
но в этих двух примерах он вызывает функцию два раза:
(first (map #(do (println "x: " %) %) '[0 1]))
(first (map #(do (println "x: " %) %) (doall (range 2))))
Каков основополагающий принцип для принятия решения быть ленивым или нет?
Есть ли способ гарантировать полную лень?
Спасибо за ваше время.
Ответ №1:
Map (и аналогичные HOFS, которые работают с коллекциями) работает над абстракцией последовательности над коллекциями: она создает последовательность из переданной коллекции (seq coll)
и впоследствии работает с возвращенной последовательностью. PersistentList
( '(0 1)
является экземпляром PersistentList
) реализует ISeq
через ASeq
расширение, поэтому seq
функция возвращает сам список. В случае PersistentVector
( [1 2]
) seq
функция возвращает фрагментированную последовательность (из соображений производительности, она оценивает фрагмент (32 элемента) данных за один шаг, но возвращает только запрошенный элемент; следующий фрагмент вычисляется, когда текущий фрагмент исчерпан).
При тестировании этого поведения попробуйте протестировать коллекции с количеством> 32 ( (vec (range 40))
возвращает вектор элементов 0-39):
=> (first (map #(do (println "x: " %) %) (vec (range 40))))
x: 0
x: 1
x: 2
x: 3
...
x: 30
x: 31
0
Как вы можете видеть, весь чанк (элементы 0-31) оценивается при доступе к первому элементу, остальные элементы (32-39) будут оцениваться при запросе первого элемента из следующего чанка.
В случае коллекций, которые являются списком, фрагментированный seq не используется и оценивается только первый элемент ( (apply list (range 40))
возвращает список элементов 0-39):
=> (first (map #(do (println "x: " %) %) (apply list (range 40))))
x: 0
0
Комментарии:
1. Может быть, есть способ отключить оптимизацию 32 блоков при запуске clojure?
2. В общем, нет, но примеры показывают, что вы можете, если используете список, а не вектор. Или, возможно, вы сможете изменить способ построения отложенного списка.