Особая интерпретация nil
cons
— это особая форма (т.е. это не функция, а встроенный компилятор). cons
знает, что nil
означает, что "данные больше не поступают".
(cons 7 nil) => (7)
(cons 7 '()) => (7)
(cons 7 []) => [7]
Итак, если when-let
или when
терпит неудачу, возвращается nil
, и у нас есть что-то вроде (cons 7 nil)
. Следовательно, ленивая последовательность завершается (отбрасывается nil
), и в этот момент она эквивалентна обычному списку.
Возвращение nil
Ваш вопрос меня удивил! Я не думал, что это сработает, но вот код:
(defn odd->nil [it]
(if (odd? it)
nil
it))
(defn down-from
"Count down from N to 1"
[n]
(lazy-seq
(when (pos? n)
(cons (odd->nil n) (down-from (dec n))))))
(down-from 5) => (nil 4 nil 2 nil)
Итак, мы видим, что существует большая разница между nil
первым и вторым аргументом cons
. Если nil
является первым аргументом, он добавляется в начало списка как обычно. Если nil
является вторым аргументом, он (тихо) преобразуется в пустой список, и результатом является список из 1 элемента:
(cons nil [99]) => (nil 99) ; works like normal
(cons 99 nil) => (99) ; creates a 1-elem list
(cons nil nil) => (nil) ; for completeness
P.S.
Обратите внимание, что есть небольшое противоречие с seq
, так как мы имеем:
(seq nil) => nil
P.P.S rest
vs next
Я никогда не использую next
, так как мне не нравятся тихие преобразования в nil
:
(next [1]) => nil
(next []) => nil
(next nil) => nil
Я предпочитаю использовать rest
, так как это даст мне пустой список, как я и ожидал:
(rest [1]) => ()
(rest []) => ()
(rest nil) => ()
Затем я могу написать тест следующим образом:
(let [remaining (rest some-seq) ]
(when-not (empty remaining) ; says what it means
....more processing.... ))
Мне не нравятся предположения о тихих преобразованиях:
(when (next some-seq) ; silently converts [] => nil
....more processing.... ) ; & relies on nil <=> false
Последняя вещь
Вас может заинтересовать небольшое уточнение под названием lazy-cons
описанное здесь. Я думаю, что это немного проще, чем оригинальный lazy-seq
.
(defn lazy-countdown [n]
(when (<= 0 n)
(lazy-cons n (lazy-countdown (dec n)))))
(deftest t-all
(is= (lazy-countdown 5) [5 4 3 2 1 0] )
(is= (lazy-countdown 1) [1 0] )
(is= (lazy-countdown 0) [0] )
(is= (lazy-countdown -1) nil ))
У него также есть двоюродный брат, эмулирующий стиль Python. функции генератора.
person
Alan Thompson
schedule
26.10.2018