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

2024-03-20

Kotlin 提供take and takeWhile先采取的方法n的项目Sequence<T>并将它们作为另一个序列单独处理,例如,drop他们中有一些,map到其他值等

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

现在,给定一个一度受限序列,如何将其任意前缀转换为保留尾部的另一个序列(如果它仍然存在)?

Example:

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

如何对多个前缀执行相同的操作?

Example:

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

注意:这个问题是故意提出的作者回答 https://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/.


When a Sequence<T>是一次约束的,这意味着不允许创建多个Iterator从中。因此,解决方案是创建一个迭代器并从中生成更改的前缀和剩余的尾部。

The Iterator<T>'s asSequence() https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/kotlin.-iterator/as-sequence.html方法在这里被证明是有用的,因为它创建了一个由迭代器支持的序列。剩下的就是连接序列。

以下是进行一项更改的方法:

val seq = (1..10).asSequence().constrainOnce()
val modified = seq.iterator().let { iter -> 
    iter.asSequence().take(5).map { it * -1 } + iter.asSequence()
}

请注意,两个序列是从同一个迭代器创建的,但没关系,因为

  • Sequences 被懒惰地求值
  • 两个序列一起使用,不会泄露
  • 对串联中的第二个序列的评估将在第一个序列完成后开始

以下是如何将其推广到任意数量的序列运算符:

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()
}

This fold产生由以下提供的串联序列链operators来自迭代器支持的序列i,然后将未修改的尾部附加到fold result.

此实现的局限性在于,当运算符包含takeWhille,被拒绝的项目将被丢弃,并且不会被发送到下一个序列中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何修改 Kotlin 序列的前缀但保留尾部? 的相关文章

随机推荐