【问题标题】:How does lazy-seq accumulate the result?lazy-seq 是如何累积结果的?
【发布时间】:2019-03-31 11:07:48
【问题描述】:

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

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

【问题讨论】:

    标签: clojure clojurescript lazy-sequences


    【解决方案1】:

    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)
    

    所以我们看到nilcons 的第一个或第二个参数之间存在很大差异。如果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
    

    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

    【讨论】:

    • (cons nil '(1)) ;; =&gt; (nil 1)(cons 1 nil) ;; =&gt; (1)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    • 2012-11-08
    • 2013-07-18
    • 2021-05-26
    相关资源
    最近更新 更多