访客闭合中的 Kotlin 序列产量

Kotlin sequence yield in a visitor closure

提问人:Alex Lamaison 提问时间:11/2/2023 更新时间:11/2/2023 访问量:32

问:

我正在尝试在序列中使用 yield 来延迟发出来自访问者的项目,但我收到错误“只能在协程体中调用暂停函数”。

我不是想在这里暂停,只是发出一个项目:

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

Kotlin 访客模式

评论


答:

1赞 Sweeper 11/2/2023 #1

我不是想在这里暂停

你是。该方法需要挂起以使序列延迟。如果它没有挂起,当您尝试使用序列的第一个元素时,将访问所有内容并立即产生所有内容。yieldaccept

当您尝试获取序列的元素时,lambda 中的代码将运行,直到被调用。它会暂停 lambda 的执行,并返回所需的元素。lambda 不会继续运行,直到您尝试获取下一个元素。sequenceyield

所以如果你不能暂停,你就不能懒惰地这样做。使用类似的东西来代替。MyType.VisitorbuildList

访问者方法不仅需要挂起,还必须扩展 。这是因为使用限制悬架。这个想法是,当挂起 lambda 时,你必须继续传递,以跟踪你正在生成的序列。SequenceScopesequencesequenceSequenceScope

因此,所有方法以及方法都需要挂起,并且需要是 的扩展。visitacceptSequenceScope<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)

在 中,您需要用 :sequencexwith

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

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