lazy-seq 是如何累加结果的?
How does lazy-seq accumulate the result?
这里是 clojurescript 中 partition
函数的 implementation。为简单起见,删除了其他方法。
我很难理解 lazy-seq
是如何累积结果的。最后有一个when
,如果我理解正确的话,如果测试为假,它将return 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))))))))
;; ...
nil
的特别解读
cons
是一种特殊形式(即它不是函数,而是内置的编译器)。 cons
知道一个 nil
表示 "no more data is coming"。
(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
是第一个 arg,它会照常添加到列表的开头。如果 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
P.S.
请注意与 seq
有一点矛盾,因为我们有:
(seq nil) => nil
P.P.S 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 ))
这里是 clojurescript 中 partition
函数的 implementation。为简单起见,删除了其他方法。
我很难理解 lazy-seq
是如何累积结果的。最后有一个when
,如果我理解正确的话,如果测试为假,它将return 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))))))))
;; ...
nil
的特别解读
cons
是一种特殊形式(即它不是函数,而是内置的编译器)。 cons
知道一个 nil
表示 "no more data is coming"。
(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
是第一个 arg,它会照常添加到列表的开头。如果 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
P.S.
请注意与 seq
有一点矛盾,因为我们有:
(seq nil) => nil
P.P.S 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 ))