Действительно ли "for" не ленив в clojure?

(take 2 (for [x (range 10)
              :let [_ (println x)]
              :when (even? x)] x))
>> (* 0
* 1
* 2
* 3
* 4
* 5
* 6
* 7
* 8
* 9
0 2)

Я предположил, что я просто невероятно плотный. Но нет, оказывается, что Clojure фактически оценивает первые 32 элемента любой ленивой последовательности (если доступно). Уч.

У меня был for с рекурсивным вызовом в :let. Мне было очень любопытно, почему вычисление, по-видимому, идет сначала по ширине, а не по глубине первой моды. Кажется, что вычисление (хотя, если честно, а не память) взрывалось, когда я продолжал спускаться по всем верхним ветвям рекурсивного дерева. Clojure 32-chunking заставлял первую оценку, хотя логический смысл кода был глубиной вначале.

Во всяком случае, есть ли какой-либо простой способ заставить 1-chunking, а не 32-chunking ленивых последовательностей?

Ответы

Ответ 1

Michaes Fogus написал запись в блоге отключив это поведение, предоставив пользовательскую реализацию ISeq.

Чтобы бесстыдно украсть измененную версию Колина Джонса:

(defn seq1 [#^clojure.lang.ISeq s]
  (reify clojure.lang.ISeq
    (first [_] (.first s))
    (more [_] (seq1 (.more s)))
    (next [_] (let [sn (.next s)] (and sn (seq1 sn))))
    (seq [_] (let [ss (.seq s)] (and ss (seq1 ss))))
    (count [_] (.count s))
    (cons [_ o] (.cons s o))
    (empty [_] (.empty s))
    (equiv [_ o] (.equiv s o))))

Более простой подход дается в Радости Clojure:

(defn seq1 [s]
  (lazy-seq
    (when-let [[x] (seq s)]
       (cons x (seq1 (rest s))))))

Ответ 2

Чтобы ответить на вопрос в вашем названии, нет, for не ленится. Однако, it:

Принимает вектор одного или нескольких пары привязки-формы/коллекции-expr, за которыми следуют ноль или более модификаторы и дает ленивую последовательность оценок expr.

(акцент мой)

Итак что происходит?

в основном Clojure всегда строго оценивается. Ленивый seqs в основном используют те же самые трюки, что и питон с их генераторами и т.д. Строгие оценки в ленивой одежде.

Другими словами, for нетерпеливо возвращает ленивую последовательность. Который не будет оцениваться до тех пор, пока вы его не попросите, и будет разделяться.