访问者闭包中的Kotlin序列产量

sc4hvdpw  于 7个月前  发布在  Kotlin
关注(0)|答案(1)|浏览(65)

我尝试在一个序列中使用yield来从一个访问者那里懒洋洋地发出项目,但是我得到了错误“Suspension functions can be called only within coroutine body”。
我并不是要在这里暂停,只是发出一个项目:

private fun flatten(x: MyType): Sequence<MyType> {
    return sequence {
        x.accept(object : MyType.Visitor {
            override fun visit(a: MyType.SubTypeA) {
                [email protected](a)
            }

            override fun visit(b: MyType.SubTypeB) {
                [email protected](b)
            }

            override fun visit(c: MyType.SubTypeC) {
                [email protected](c)
            }
        })
    }
}

字符串
我怀疑闭包会让编译器感到困惑,所以我添加了this@sequence,但它没有帮助。我做错了什么?

rqqzpn5f

rqqzpn5f1#

我不是要停职
你是 *。yield方法需要挂起以使序列懒惰。如果它没有挂起,当你尝试消费序列的第一个元素时,accept将访问所有内容并立即产生所有内容。
当你尝试获取序列中的一个元素时,sequence lambda中的代码会运行,直到yield被调用。它会 * 挂起 * lambda的执行,并返回你想要的元素。lambda不会继续运行,直到你尝试获取下一个元素。
所以如果你的MyType.Visitor不能挂起,你就不能懒惰地这样做。使用类似buildList的东西代替。
访问者方法不仅需要挂起,它们还必须是SequenceScope上的扩展。这是因为sequence使用了限制挂起。其思想是,当在sequence lambda中挂起时,您必须继续传递SequenceScope沿着,以跟踪您产生的序列。
因此,所有visit方法以及accept方法都需要挂起,并且需要是SequenceScope<MyType>的扩展。

class MyType {
    suspend fun SequenceScope<MyType>.accept(visitor: Visitor) {
        with(visitor) {
            visit(a = something)
            visit(b = somethingElse)
            // etc...
            // note that if you want to write a receiver for these visit calls,
            // you should write this@accept (i.e. the sequence scope) as the receiver, not "visitor"
        }
    }
}

字符串
visit方法的声明如下:

suspend fun SequenceScope<MyType>.visit(a: MyType.SubTypeA)


sequence中,您需要将x Package 为with

with(x) {
    accept(object: MyType.Visitor { ... })
}


如果你真的想走这条路,你可能应该添加一个新的SuspendVisitoracceptSuspend,而不是修改现有的方法,这样它仍然可以以非挂起的方式使用。

相关问题