Clojure为(无限)序列中的延迟评估值提供了方法。这样,只有在实际消耗时才会计算值。
一个重复元素的无限序列的一个例子:
(take 3 (repeat "Hello StackOverflow"))
//=> ("Hello StackOverflow" "Hello StackOverflow" "Hello StackOverflow")
使用take
有助于仅消耗我们想要的序列中的任意数量的元素。没有它,OutOfMemoryError
会很快杀死这个过程。
无限序列的另一个例子如下:
(take 5 (iterate inc 1))
//(1 2 3 4 5)
或提供阶乘函数的更高级序列:
((defn factorial [n]
(apply * (take n (iterate inc 1)))) 5)
Kotlin是否提供类似的序列?它们看起来像什么?
我自己回答了这个问题,以便记录这里的知识。根据{{3}}
,这很好答案 0 :(得分:5)
在Kotlin中,我们也可以使用Sequences来进行惰性评估。为了创建序列,我们可以使用generateSequence
(提供或不提供seed
。
fun <T : Any> generateSequence( seed: T?, nextFunction: (T) -> T? ): Sequence<T> (source)
返回由起始值
seed
和函数nextFunction
定义的序列,该函数被调用以根据每次迭代的前一个值计算下一个值。
以下将展示一些比较Clojure与Kotlin序列的实例。
take
<强> Clojure的强>
(take 3 (repeat "Hello StackOverflow"))
<强>科特林强>
generateSequence { "Hello StackOverflow" }.take(3).toList()
这些非常相似。在Clojure中,我们可以使用repeat
,而在Kotlin中,它只是generateSequence
,其静态值将永远产生。在这两种情况下,都使用take
来定义我们想要计算的元素数量。
注意:在Kotlin中,我们将生成的序列转换为toList()
take
<强> Clojure的强>
(take 5 (iterate inc 1))
<强>科特林强>
generateSequence(1) { it.inc() }.take(5).toList()
这个例子有点不同,因为序列无限地产生前一个值的增量。可以使用种子(此处为:generateSequence
)和1
(增加前一个值)来调用Kotlin nextFunction
。
<强> Clojure的强>
(take 5 (drop 2 (cycle [:first :second :third ])))
// (:third :first :second :third :first)
<强>科特林强>
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()
最后但并非最不重要的是,让我们看看如何用Kotlin序列解决因子问题。首先,让我们回顾一下Clojure版本:
<强> Clojure的强>
(defn factorial [n]
(apply * (take n (iterate inc 1))))
我们从序列中取n个值,得到一个从1开始的递增数,并在apply
的帮助下累积它们。
<强>科特林强>
fun factorial(n: Int) = generateSequence(1) { it.inc() }.take(n).fold(1) { v1, v2 ->
v1 * v2
}
Kotlin提供fold
,让我们轻松积累价值。