lazy-seq如何累加结果?

时间:2018-10-26 16:32:46

标签: clojure clojurescript lazy-sequences

这是clojurescript中partition函数的implementation。为简便起见,其他方法已删除。

我很难理解lazy-seq是如何累积结果的。最后有一个when,如果我理解正确的话,如果测试为假,它将返回nilnil在下一次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))))))))
  ;; ...

1 个答案:

答案 0 :(得分:4)

nil的特殊解释

cons是一种特殊形式(即它不是函数,而是内置的编译器)。 cons知道nil的意思是“没有更多数据要来了”。

(cons 7 nil)   => (7)
(cons 7 '())   => (7)
(cons 7  [])   => [7]

因此,如果when-letwhen失败,将返回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

P.S。

请注意,与seq有点矛盾,因为我们有:

(seq nil) => nil

P.P.S restnext

我从不使用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 ))

它也有a cousin the emulates Python-style generator functions