代码之家  ›  专栏  ›  技术社区  ›  Carcigenicate

对lazy seq的调用应该有什么作用域?

  •  3
  • Carcigenicate  · 技术社区  · 7 年前

    Recamán's Sequence ,并遇到了一些关于 lazy-seq 应该发生的。

    今天早上我想到的第一个版本是:

    (defn lazy-recamans-sequence []
      (let [f (fn rec [n seen last-s]
                (let [back (- last-s n)
                      new-s (if (and (pos? back) (not (seen back)))
                              back
                              (+ last-s n))]
                  (lazy-seq ; Here
                    (cons new-s (rec (inc n) (conj seen new-s) new-s)))))]
        (f 0 #{} 0)))
    

    后来我意识到 有点武断,它可以放得更高来包装更多的计算:

    (defn lazy-recamans-sequence2 []
      (let [f (fn rec [n seen last-s]
                (lazy-seq ; Here
                  (let [back (- last-s n)
                        new-s (if (and (pos? back) (not (seen back)))
                                back
                                (+ last-s n))]
                      (cons new-s (rec (inc n) (conj seen new-s) new-s)))))]
        (f 0 #{} 0)))
    

    然后我回头看了看 review that someone gave me last night :

    (defn recaman []
      (letfn [(tail [previous n seen]
                (let [nx (if (and (> previous n) (not (seen (- previous n))))
                           (- previous n)
                           (+ previous n))]
                  ; Here, inside "cons"
                  (cons nx (lazy-seq (tail nx (inc n) (conj seen nx))))))]
        (tail 0 0 #{})))
    

    cons !

    仔细想想,似乎没什么区别。随着范围的扩大(如第二个版本),更多的代码在传递给 LazySeq . 但是,如果范围更窄,函数本身可能更小,但是由于传递的函数涉及递归调用,它无论如何都将执行相同的代码。

    他们似乎表现得几乎相同,并给出了相同的答案。有什么理由更喜欢 惰性序列 在一个地方而不是另一个地方?这仅仅是一种文体选择,还是会产生实际影响?

    2 回复  |  直到 7 年前
        1
  •  3
  •   erdos    7 年前

    在前两个例子中 lazy-seq cons 打电话来。这意味着,当生成调用函数时,将立即返回一个延迟序列,而不计算序列的第一项。

    在第一个例子中 let 表达仍然不在 所以第一个项的值会立即计算,但是返回的序列仍然是惰性的,并且 not realized .

    第二个例子与第一个相似。这个 惰性序列 欺骗 细胞和 封锁。这意味着函数将立即返回,并且仅当调用方开始使用惰性序列时才计算第一个项的值。

    在第三个示例中,立即计算列表中第一个项的值,只有返回序列的尾部是惰性的。

    有没有理由更喜欢把懒惰的seq放在一个地方而不是另一个地方?

    这取决于你想要实现什么。是否要立即返回序列而不计算任何值?在这种情况下,使 尽可能广泛。否则,尝试限制lazy seq的范围以仅计算序列的尾部部分。

        2
  •  0
  •   Alan Thompson    7 年前

    当我第一次学习Clojure的时候,我有点困惑于 lazy-seq 结构,在选择哪种结构方面缺乏明确性,以及对如何选择的解释有些模糊 首先创建惰性(它被实现为大约240行的Java类)。

    为了减少重复并使事情尽可能简单,我创建了 the lazy-cons macro

    (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 ))
    

    这个版本确实实现了初始值 n 立刻。