这是clojurescript中partition
函数的implementation。为简便起见,其他方法已删除。
我很难理解lazy-seq
是如何累积结果的。最后有一个when
,如果我理解正确的话,如果测试为假,它将返回nil
。 nil
在下一次lazy-seq
的迭代中会去哪里?
(defn partition
"Returns a lazy sequence of lists of n items each, at offsets step
apart. If step is not supplied, defaults to n, i.e. the partitions
do not overlap. If a pad collection is supplied, use its elements as
necessary to complete last partition up to n items. In case there are
not enough padding elements, return a partition with less than n items."
;; ...
([n step coll]
(lazy-seq
(when-let [s (seq coll)]
(let [p (take n s)]
(when (== n (count p))
(cons p (partition n step (drop step s))))))))
;; ...
答案 0 :(得分:4)
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
是第二个arg,它将(无提示)转换为一个空列表,结果是一个1元素的列表:
(cons nil [99]) => (nil 99) ; works like normal
(cons 99 nil) => (99) ; creates a 1-elem list
(cons nil nil) => (nil) ; for completeness
请注意,与seq
有点矛盾,因为我们有:
(seq nil) => nil
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
described here的小改进感兴趣。我认为它比原始的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 ))