我正在尝试编写一个惰性seq以为给定的输入int生成Collatz sequence。
我喜欢这个函数,因为它很干净地映射到数学定义:
(defn collatz
"Returns a lazy seq of the Collatz sequence starting at n and ending at 1 (if
ever)."
[n]
(letfn [(next-term [x]
(if (even? x)
(/ x 2)
(inc (* 3 x))))]
(iterate next-term n)))
问题是由于Collatz序列的行为,这会产生无限个序列:
(take 10 (collatz 5))
=> (5 16 8 4 2 1 4 2 1 4)
我可以通过添加(take-while #(not= 1 %) ...)
轻松地删除循环,但是1 是序列的一部分。我想过的其他所有方法都可能使之后的周期变得很丑陋,并且模糊了Collatz序列的数学核心。
(我曾考虑过将可见的值存储在一个原子中,并在take-while
谓词中使用它,或者只是将一个标志存储在一个原子中,以达到类似的效果。但是我觉得有一些更好,更漂亮的方法,在这里做我想做的事情时比较不打扰。)
所以我的问题是:检测和修整无限序列中的周期的干净方法是什么?或者,我是否可以通过某种方式(可能使用for
)来生成我的懒惰序列(当它到达1
(含)时会自动修剪?
答案 0 :(得分:6)
以下内容看起来像是该定义的大致文字转换,并提供了所需的结果:
Require-Bundle
基本上,您可以使用Import-Package
作为停止序列的值,从而保留最后的1。
答案 1 :(得分:1)
您还可以使用另一种方法,即递归惰性序列生成。对于此类任务,这是很常见的,不会破坏惰性序列抽象,并且避免了中间序列的创建:
(defn collatz [n]
(if (== n 1)
(list 1)
(lazy-seq (cons n (collatz (if (even? n)
(/ n 2)
(inc (* 3 n))))))))
user> (collatz 12)
;;=> (12 6 3 10 5 16 8 4 2 1)