Clojure懒惰序列:Kotlin中的等价物

s1m*_*nw1 4 sequences clojure lazy-evaluation lazy-sequences kotlin

Clojure提供了对(无限)序列中的值进行惰性求值的方法.这样,只有在实际消耗时才会计算值.

一个重复元素的无限序列的示例:

(take 3 (repeat "Hello StackOverflow")) 
//=> ("Hello StackOverflow" "Hello StackOverflow" "Hello StackOverflow")
Run Code Online (Sandbox Code Playgroud)

使用take帮助只消耗我们想要的序列中的任意数量的元素.没有它,一个人OutOfMemoryError会迅速杀死这个过程.

无限序列的另一个例子如下:

(take 5 (iterate inc 1)) 
//(1 2 3 4 5)
Run Code Online (Sandbox Code Playgroud)

或者是提供阶乘函数的更高级序列:

((defn factorial [n]
   (apply * (take n (iterate inc 1)))) 5)
Run Code Online (Sandbox Code Playgroud)

Kotlin是否提供类似的序列?他们看起来怎么样?

我自己回答了这个问题,以便记录这里的知识.这很好根据我可以回答我自己的问题吗?

s1m*_*nw1 5

在Kotlin中,我们也可以使用Sequences来进行延迟评估.为了创建一个序列,我们可以使用generateSequence(有或没有提供seed.

fun <T : Any> generateSequence(
    seed: T?,
    nextFunction: (T) -> T?
): Sequence<T> (source)
Run Code Online (Sandbox Code Playgroud)

返回由起始值seed和函数定义的序列nextFunction,调用该序列以在每次迭代时基于前一个值计算下一个值.

以下将显示比较Clojure与Kotlin序列的一些实例.

1.一个简单take的无限序列的一个静态值

Clojure的

(take 3 (repeat "Hello StackOverflow")) 
Run Code Online (Sandbox Code Playgroud)

科特林

generateSequence { "Hello StackOverflow" }.take(3).toList()
Run Code Online (Sandbox Code Playgroud)

这些非常相似.在Clojure中我们可以使用repeat和在Kotlin中它只是generateSequence一个静态值,将永远屈服.在这两种情况下,take都用于定义我们想要计算的元素数量.

注意:在Kotlin中,我们将结果序列转换为带有的列表 toList()


2. take来自动态值的无限序列的简单

Clojure的

(take 5 (iterate inc 1))
Run Code Online (Sandbox Code Playgroud)

科特林

generateSequence(1) { it.inc() }.take(5).toList()
Run Code Online (Sandbox Code Playgroud)

这个例子有点不同,因为序列无限地产生前一个值的增量.generateSequence可以使用种子(此处为:) 1nextFunction(增加之前的值)来调用Kotlin .


3.循环重复列表中的值

Clojure的

(take 5 (drop 2 (cycle [:first :second :third ])))
// (:third :first :second :third :first)
Run Code Online (Sandbox Code Playgroud)

科特林

listOf("first", "second", "third").let { elements ->
    generateSequence(0) {
        (it + 1) % elements.size
    }.map(elements::get)
}.drop(2).take(5).toList()
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我们循环地重复列表的值,删除前两个元素然后取5.它在Kotlin中恰好相当冗长,因为列表中的重复元素并不简单.为了解决这个问题,一个简单的扩展函数使相关代码更具可读性:

fun <T> List<T>.cyclicSequence() = generateSequence(0) {
    (it + 1) % this.size
}.map(::get)

listOf("first", "second", "third").cyclicSequence().drop(2).take(5).toList()
Run Code Online (Sandbox Code Playgroud)

4. Factorial

最后但并非最不重要的,让我们看看如何用Kotlin序列解决因子问题.首先,让我们回顾一下Clojure版本:

Clojure的

(defn factorial [n]
   (apply * (take n (iterate inc 1)))) 
Run Code Online (Sandbox Code Playgroud)

我们从序列中取n个值,得到一个从1开始的递增数,并在...的帮助下累加它们apply.

科特林

fun factorial(n: Int) = generateSequence(1) { it.inc() }.take(n).fold(1) { v1, v2 ->
    v1 * v2
}
Run Code Online (Sandbox Code Playgroud)

Kotlin提供fold让我们轻松积累价值的东西.