如何修改Kotlin序列的前缀,但保留尾巴?

Kotlin提供了takeWhile方法,让一个Sequence<T>n项目分别作为另一个序列来处理,例如drop其中的一些, map到其他值等。

但是当我使用taketakeWhile ,序列的尾部被丢弃。

现在,给定一个一次受约束的序列,如何保留尾部,如何保留它的任意前缀到另一个序列?

例:

 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 ,被拒绝的项目将被丢弃,并且不会被发射到下一个序列中。

Interesting Posts