在Kotlin中,我们也可以利用
Sequences进行惰性求值。为了创建一个序列,我们可以使用
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 StackOverflow"))
Kotlin
generateSequence { "Hello StackOverflow" }.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
,可以让我们轻松地累加值。
buildSequence { val items = listOf("first", "second", "third"); var i = 0; while (true) { yield(items[i++]); i %= items.size } }
- Marko Topolnik