【问题标题】:Kotlin alternative to Python's coroutine yield and sendKotlin 替代 Python 的协程 yield 和 send
【发布时间】:2017-10-28 01:57:08
【问题描述】:

以下 python 协程 sn-p 的 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

所以我需要从协程中获取值(通过通道?),但还要将值发送回协程。我需要两个频道吗?

【问题讨论】:

  • 您尝试过哪些解决方案?您应该指出这是一种尝试过的解决方案,该解决方案有效或无效。否则,您是在说“请为我编写代码”而不是“此代码不应该工作”
  • 您将 op 设置为 c 即 1,因此它永远不会满足您的递增或乘法条件。

标签: python concurrency kotlin channel coroutine


【解决方案1】:

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

但是,Kotlin 语言中的协程支持足够通用,并且可以根据需要实现双向生成器,使其表现得与 Python 和 ES6 中的一样。相应的实现是可用的here 并且只需要几十行代码。

通过上述双向生成器的实现,你的 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
}

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

【讨论】:

    猜你喜欢
    • 2016-03-31
    • 2021-07-31
    • 2015-08-17
    • 2018-04-29
    • 2021-11-26
    • 2019-12-25
    • 2019-08-03
    • 2019-02-14
    • 1970-01-01
    相关资源
    最近更新 更多