【问题标题】:How to modify a prefix of a Kotlin sequence but retain the tail?如何修改 Kotlin 序列的前缀但保留尾部?
【发布时间】:2016-01-31 14:05:05
【问题描述】:

Kotlin 提供了taketakeWhile 方法,它们可以让一个Sequence<T> 的第一个n 项并将它们作为另一个序列单独处理,例如,drop 其中一些,map 到其他值等。

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

现在,给定一个 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

【问题讨论】:

    标签: iterator sequence kotlin tail


    【解决方案1】:

    当一个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()
    }
    

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

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

    fold 从迭代器i 支持的序列中生成operators 提供的串联序列链,然后将未修改的尾部附加到fold 结果。

    这个实现的限制是当操作符包含takeWhille时,被拒绝的项目将被丢弃并且不会被发射到下一个序列中。

    【讨论】:

      猜你喜欢
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多