如何修改 Kotlin 序列的前缀但保留尾巴?
How to modify a prefix of a Kotlin sequence but retain the tail?
Kotlin 提供了 take
和 takeWhile
方法,可以获取 Sequence<T>
的第一个 n
项并将它们作为另一个序列分别处理,例如,drop
其中一些,map
到其他值等
但是当我使用take
和takeWhile
时,序列的尾部被丢弃了。
现在,给定一个 once-constrained 序列,如何将其任意前缀转换为另一个保留尾部的序列?
示例:
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
注:问题是故意问的 answered by the author.
当一个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
s 被延迟评估
- 两个序列一起使用,不泄露
- 连接中的第二个序列的计算将在第一个序列完成后开始
以下是将其推广到任意数量的序列运算符的方法:
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
时,被拒绝的项目将被丢弃,不会被发送到下一个序列中。
Kotlin 提供了 take
和 takeWhile
方法,可以获取 Sequence<T>
的第一个 n
项并将它们作为另一个序列分别处理,例如,drop
其中一些,map
到其他值等
但是当我使用take
和takeWhile
时,序列的尾部被丢弃了。
现在,给定一个 once-constrained 序列,如何将其任意前缀转换为另一个保留尾部的序列?
示例:
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
注:问题是故意问的 answered by the author.
当一个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
s 被延迟评估- 两个序列一起使用,不泄露
- 连接中的第二个序列的计算将在第一个序列完成后开始
以下是将其推广到任意数量的序列运算符的方法:
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
时,被拒绝的项目将被丢弃,不会被发送到下一个序列中。