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

Clojure Lazy Sequences: Equivalents in Kotlin

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

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

(take 3 (repeat "Hello Whosebug")) 
//=> ("Hello Whosebug" "Hello Whosebug" "Hello Whosebug")

使用take 有助于只消耗我们想要的序列中尽可能多的元素。没有它,OutOfMemoryError 会很快终止进程。

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

(take 5 (iterate inc 1)) 
//(1 2 3 4 5)

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

((defn factorial [n]
   (apply * (take n (iterate inc 1)))) 5)

Kotlin 是否提供类似的序列?他们长什么样?

为了记录这里的知识,我自己回答了这个问题。根据 Can I answer my own question?

这很好

在 Kotlin 中,我们还可以使用 Sequences, too. In order to create a sequence, we may use generateSequence 进行惰性求值(提供或不提供 seed

fun <T : Any> generateSequence(
    seed: T?,
    nextFunction: (T) -> T?
): Sequence<T> (source)

Returns a sequence defined by the starting value seed and the function nextFunction, which is invoked to calculate the next value based on the previous one on each iteration.

下面将展示一些比较 Clojure 和 Kotlin 序列的例子。

1。来自一个静态值

的无限序列的简单 take

Clojure

(take 3 (repeat "Hello Whosebug")) 

Kotlin

generateSequence { "Hello Whosebug" }.take(3).toList()

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

注意:在 Kotlin 中,我们将生成的序列转换为具有 toList()

的列表

2。来自动态值

的无限序列的简单 take

Clojure

(take 5 (iterate inc 1))

Kotlin

generateSequence(1) { it.inc() }.take(5).toList()

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


3。循环重复列表中的值

Clojure

(take 5 (drop 2 (cycle [:first :second :third ])))
// (:third :first :second :third :first)

Kotlin

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

在这个例子中,我们循环重复列表的值,删除前两个元素,然后取 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()

4。阶乘

最后但同样重要的是,让我们看看如何使用 Kotlin 序列解决阶乘问题。首先,让我们回顾一下Clojure版本:

Clojure

(defn factorial [n]
   (apply * (take n (iterate inc 1)))) 

我们从产生从 1 开始的递增数字的序列中取 n 个值,并在 apply 的帮助下累加它们。

Kotlin

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

Kotlin 提供 fold 让我们轻松地积累价值。