如何修改Kotlin序列的前缀,但保留尾巴?
Kotlin提供了takeWhile
方法,让一个Sequence<T>
前n
项目分别作为另一个序列来处理,例如drop
其中的一些, map
到其他值等。
但是当我使用take
和takeWhile
,序列的尾部被丢弃。
现在,给定一个一次受约束的序列,如何保留尾部,如何保留它的任意前缀到另一个序列?
例:
val seq = (1..10).asSequence().constrainOnce() // emits 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 val modified = seq.changePrefix { take(5).map { -1 * it } } // emits -1, -2, -3, -4, -5, 6, 7, 8, 9, 10
我如何做同样的多个前缀?
例:
val seq = (1..10).asSequence().constrainOnce() val modified = seq.changePrefixes( { take(3).map { it * -1 } }, { drop(1).take(3).map { it * 100 } }, { map { 0 } } ) //emits -1, -2, -3, 500, 600, 700, 0, 0, 0
注意:这个问题是由作者故意询问和回答的 。
当一个Sequence<T>
被一次约束时,意味着不允许从它创建多个Iterator
。 因此,解决方案是创建一个迭代器,并从中生成已更改的前缀和剩余的尾部。
Iterator<T>
的asSequence()
方法在这里被证明是有用的,因为它创建了一个由迭代器支持的序列。 剩下的只是连接序列。
以下是你如何做一个改变:
val seq = (1..10).asSequence().constrainOnce() val modified = seq.iterator().let { iter -> iter.asSequence().take(5).map { it * -1 } + iter.asSequence() }
请注意,两个序列是从同一个迭代器创建的,但是这是可以的,因为
-
Sequence
是懒惰的评估 - 两个序列一起使用,不泄漏
- 在第一个完成之后,将开始评估串联中的第二个序列
下面是如何推广到任意数量的序列操作符:
fun <T> Sequence<T>.changePrefixes(vararg operators: Sequence<T>.() -> Sequence<T>) : Sequence<T> { val i = iterator() return operators.fold(emptySequence<T>()) { acc, it -> acc + i.asSequence().it() } + i.asSequence() }
这个fold
产生由迭代器i
支持的序列中的operators
提供的连接序列链,然后将未修改的尾部附加到fold
结果。
这种实施方式的局限在于,当运营商包括takeWhille
,被拒绝的项目将被丢弃,并且不会被发射到下一个序列中。