Специальная интерпретация 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
PS
Примечаниеесть небольшое противоречие с seq
, так как у нас есть:
(seq nil) => nil
PPS rest
против 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 .