карта не совсем ленивая?

#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. В общем, нет, но примеры показывают, что вы можете, если используете список, а не вектор. Или, возможно, вы сможете изменить способ построения отложенного списка.