【发布时间】:2018-08-14 15:55:01
【问题描述】:
我正在编写 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)))
然后我意识到我对lazy-seq 的放置有点随意,它可以放置得更高以包含更多的计算:
(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 的显式函数中。然而,在更窄的范围内,函数本身可能更小,但由于传递的函数涉及递归调用,所以无论如何它都会执行相同的代码。
他们似乎表现得几乎相同,并给出了相同的答案。有什么理由更喜欢将lazy-seq 放在一个地方而不是另一个地方?这仅仅是一种风格选择,还是会产生实际影响?
【问题讨论】:
-
您可以看看我的回答:stackoverflow.com/a/44102122/597473 - 在标题为“这有什么实际区别”的部分中。
-
@PiotrekBzdyl 嗯,最后一点是有道理的。所以,如果每个元素计算起来都很便宜和/或你不在乎一个元素是否不必要地计算,那么真的没有区别吗?但是对于更昂贵的计算,是否应该尽可能多地包裹以延迟计算,直到绝对必要?
-
我会说默认情况下让它完全惰性,除非你需要(我怀疑)这个语义:产生的惰性序列的第一个元素必须在创建惰性序列的同时实现。否则差异将是一个额外的调用来计算应该便宜的第一个元素。
-
@PiotrekBzdyl 好电话,谢谢。如果您想具体回答这个问题,我很乐意接受。如果没有,当我从远足回来时,我可以写点东西,或者等着看是否有人能写出对差异的良好描述。
-
我认为我提到的答案已经涵盖了差异,但请随时提供您自己的答案。