Kotlin替代了Python的协程产量和发送

dmz*_*rsk 3 python concurrency channel coroutine kotlin

以下Python协程代码段的Kotlin惯用替代品是什么:

def generator():
  c = 1
  while True:
    op = yield c
    if op == 'inc':
      c += 1
    elif op == 'mult':
      c *= 2

# main
g = generator()
a = g.send(None) # start
b = g.send('inc')
c = g.send('mult')
d = g.send('inc')

print([a, b, c, d]) # 1, 2, 4, 5
Run Code Online (Sandbox Code Playgroud)

因此,我需要从协程中获取值(通过通道?),还需要将这些值发送回协程中。我需要两个渠道吗?

Rom*_*rov 5

Python和ES6中存在的那种双向生成器在Kotlin中并不是真正惯用的,因为Kotlin是一种静态类型化的语言,因此双向生成器使用起来很笨拙。只需看一下g.send(None)上面的代码即可了解为什么会这样。因此,在Kotlin标准库或支持库中都没有提供双向生成器的实现。

但是,Kotlin语言中的协程支持足够通用,并且可以根据需要实现双向生成器,使其行为类似于Python和ES6。此处提供相应的实现,仅需几十行代码。

通过上述双向生成器的实现,可以将您的Python代码逐行直接转换为Kotlin:

fun generator() = generate<Int, String> {
    var c = 1
    while (true) {
        val op = yield(c)
        when (op) {
            "inc" -> c += 1
            "mult" -> c *= 2
        }
    } 
}

fun main(args: Array<String>) {
    val g = generator()
    val a = g.next("") // start
    val b = g.next("inc")
    val c = g.next("mult")
    val d = g.next("inc")
    println("$a $b $c $d") // 1, 2, 4, 5
}
Run Code Online (Sandbox Code Playgroud)

该代码与其Python版本一样好,但是由于多种原因,它并不是惯用的。例如,Kotlin中的协程支持允许定义任意的挂起函数,因此可以以类型安全的方式表达类似的行为,而无需诉诸于任意的开始标记或使用字符串来表示操作。您可以直接将具有incmult作为其一流的挂起操作的对象定义为对象,或者至少将其实现更改为不需要虚拟启动调用的对象。欢迎您学习协程设计文档,该文档解释了Kotlin提供的所有低级原语,并提供了许多示例来帮助您入门。