在 Kotlin 中,我们也可以使用 Sequences 的惰性求值。为了创建一个序列,我们可以使用generateSequence(有或没有提供seed。
fun <T : Any> generateSequence(
seed: T?,
nextFunction: (T) -> T?
): Sequence<T> (source)
返回由起始值seed 和函数nextFunction 定义的序列,在每次迭代中调用该函数以根据前一个值计算下一个值。
下面将展示一些比较 Clojure 和 Kotlin 序列的示例。
1。来自一个静态值的无限序列的简单take
Clojure
(take 3 (repeat "Hello StackOverflow"))
科特林
generateSequence { "Hello StackOverflow" }.take(3).toList()
这些非常相似。在 Clojure 中,我们可以使用 repeat,而在 Kotlin 中,它只是 generateSequence,带有一个将永远产生的静态值。在这两种情况下,都使用take 来定义我们要计算的元素数量。
注意:在 Kotlin 中,我们将生成的序列转换为带有 toList() 的列表
2。来自动态值的无限序列的简单take
Clojure
(take 5 (iterate inc 1))
科特林
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)
科特林
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))))
我们从一个序列中获取 n 个值,该序列产生一个从 1 开始的递增数字,并在 apply 的帮助下将它们累加。
科特林
fun factorial(n: Int) = generateSequence(1) { it.inc() }.take(n).fold(1) { v1, v2 ->
v1 * v2
}
Kotlin 提供了fold,让我们轻松积累价值。