#functional-programming #clojure #lazy-evaluation
Вопрос:
Я экспериментирую с ленивыми последовательностями clojure. Чтобы увидеть, когда произойдет оценка элемента, я создал функцию square, которая печатает результат перед его возвратом. Затем я применяю эту функцию к вектору, используя map.
(defn square [x]
(let [result (* x x)]
(println "printing " result)
result))
(def s (map square [1 2 3 4 5])) ; outputs nothing
Здесь, в моем объявлении s, REPL ничего не выводит. Это сигнализирует о том, что вычисление еще не началось. Это кажется правильным. Затем я делаю:
(first s)
Функция «first» принимает только первый элемент. Поэтому я ожидаю, что будет вычислен только 1. Я ожидаю, что REPL выведет следующее:
printing 1
1
Однако вместо этого REPL выдал следующее.
printing 1
printing 4
printing 9
printing 16
printing 25
1
Таким образом, вместо того, чтобы оценивать только первый элемент, кажется, что он оценивает все элементы, хотя я обращаюсь только к первому элементу.
Если состояние отложенной последовательности может быть либо вычисленными всеми значениями, либо не вычисленными значениями, то как оно может получить преимущества отложенной оценки? Я пришел из фона схемы, и я ожидал больше похожего на поведение потоков. Похоже, я ошибаюсь. Кто-нибудь может объяснить, что происходит?
Ответ №1:
Лень — это не все или ничего, но некоторые реализации seq работают с «фрагментами» входной последовательности (объяснение см. Здесь). Это относится к vector, который вы можете проверить с помощью chunked-seq?
:
(chunked-seq? (seq [1 2 3 4 5]))
При задании коллекция map
проверяет, разбита ли базовая последовательность на фрагменты, и если да, то оценивает результат для каждого фрагмента, а не для элемента за раз.
Размер блока обычно равен 32, поэтому вы можете увидеть это поведение, сравнив результат
(first (map square (vec (range 35))))
Это должно отображать сообщение только для первых 32 элементов, а не для всей последовательности.
Комментарии:
1. Является ли производительность единственной причиной этого отклонения от принципа «достаточно просто оценить» ленивой оценки?
2. @Lee Мне интересно, почему функция sseq1 в блоге, на который вы ссылались, все еще отсутствует в официальной библиотеке. Было бы неплохо контролировать размер фрагмента последовательности (например, если каждый вызов выполняет http-запрос, было бы неплохо заставить его выполнять 1 за раз, 32 за раз потребляют много ресурсов).