You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Visitor闭包中使用Kotlin Sequence yield报错的问题排查

解决Sequence中调用yield时的“Suspension functions can be called only within coroutine body”错误

问题本质

yield 是 SequenceScope 中的挂起函数,虽然你在 sequence { ... } 协程体内部创建了 Visitor 实例,但 Visitor 的 visit 方法是普通非挂起函数,不属于协程体的挂起上下文。哪怕通过 this@sequence 获取到 SequenceScope 的引用,也无法在非挂起函数中调用挂起函数 yield,这就是编译器报错的核心原因。

解决方案

根据你是否有权修改 MyType.Visitor 接口,提供三种可选方案:

方案1:修改Visitor接口为挂起函数(推荐,保留完全惰性)

如果可以修改 Visitor 接口,将所有 visit 方法声明为挂起函数,同时确保 MyType.accept 方法兼容挂起 Visitor:

// 修改后的Visitor接口
interface Visitor {
    suspend fun visit(a: MyType.SubTypeA)
    suspend fun visit(b: MyType.SubTypeB)
    suspend fun visit(c: MyType.SubTypeC)
}

// 调整后的flatten函数可正常工作
private fun flatten(x: MyType): Sequence<MyType> {
    return sequence {
        x.accept(object : MyType.Visitor {
            override suspend fun visit(a: MyType.SubTypeA) {
                this@sequence.yield(a)
            }

            override suspend fun visit(b: MyType.SubTypeB) {
                this@sequence.yield(b)
            }

            override suspend fun visit(c: MyType.SubTypeC) {
                this@sequence.yield(c)
            }
        })
    }
}

修改后 visit 方法处于挂起上下文,就能直接调用 yield 实现惰性元素发射。

方案2:无法修改Visitor时,先收集再转序列(牺牲部分惰性)

如果不能修改 Visitor 接口,可以先通过 Visitor 一次性收集所有元素到集合,再将集合转为 Sequence:

private fun flatten(x: MyType): Sequence<MyType> {
    val elements = mutableListOf<MyType>()
    x.accept(object : MyType.Visitor {
        override fun visit(a: MyType.SubTypeA) {
            elements.add(a)
        }

        override fun visit(b: MyType.SubTypeB) {
            elements.add(b)
        }

        override fun visit(c: MyType.SubTypeC) {
            elements.add(c)
        }
    })
    return elements.asSequence()
}

这种方式会预先遍历所有元素并收集,适合元素数量较少的场景,后续序列仍保留惰性消费特性。

方案3:绕过Visitor模式,手动实现惰性遍历(无侵入)

如果需要完全惰性且不能修改任何现有接口,可以直接通过类型判断实现遍历逻辑,替代 Visitor 模式:

private fun flatten(x: MyType): Sequence<MyType> = sequence {
    when (x) {
        is MyType.SubTypeA -> yield(x)
        is MyType.SubTypeB -> yield(x)
        is MyType.SubTypeC -> yield(x)
        // 补充其他子类型的处理逻辑
    }
    // 若子类型包含嵌套元素,可递归调用并通过yieldAll发射
    // 示例:if (x is MyType.SubTypeA) yieldAll(x.nestedElements.flatMap { flatten(it) })
}

这种方式无需依赖 Visitor,直接实现惰性发射,适合 MyType 结构相对简单的场景。

内容的提问来源于stack exchange,提问作者Alex Lamaison

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:37:43