是否可以将参数传递给序列函数?

Is it possible to pass an argument into a sequence function?

我正在寻找一种将参数传递给 Kotlin 序列函数的方法,类似于它在 JS 中的工作方式:

 function *gen () {
     console.log(yield) // prints 1
     console.log(yield) // prints 2
 }
    
 const it = gen()
    
 it.next()  // first iteration will execute the first yield and pause
 it.next(1) // we pass 1 to the first yield which will be printed
 it.next(2) // we pass 2 to the second yield which will be printed

Kotlin 中的类似内容:

fun main() {
    val it = gen().iterator()

    // Iterator#next() doesn't expect an argument
    it.next(1) 
    it.next(2)
}

fun gen() = sequence {
    println(yield(null)) // Would print 1
    println(yield(null)) // Would print 2
}

Kotlin 序列不支持向每个序列传递参数 yield,但您至少有 2 种方法来实现所需的行为:

  1. 使用演员:
class NextQuery<A, T>(val arg: A, val next: CompletableDeferred<T> = CompletableDeferred())

fun test() = runBlocking {
    val actor = GlobalScope.actor<NextQuery<String, Int>> {
        for (nextQuery in channel) {
            nextQuery.next.complete(nextQuery.arg.length)
        }
    }

    val query1 = NextQuery<String, Int>("12345")
    actor.send(query1)
    println(query1.next.await())

    val query2 = NextQuery<String, Int>("1234")
    actor.send(query2)
    println(query2.next.await())

}
  1. 使用频道:
class ArgSequenceScope<out A, in T>(
    private val argChannel: ReceiveChannel<A>,
    private val nextChannel: SendChannel<T>
) {

    suspend fun yield(next: T) {
        nextChannel.send(next)
    }

    suspend fun arg(): A = argChannel.receive()
}

class ArgSequence<in A, out T>(
    private val argChannel: SendChannel<A>,
    private val nextChannel: ReceiveChannel<T>
) {

    suspend fun next(arg: A): T {
        argChannel.send(arg)
        return nextChannel.receive()
    }

}

fun <A, T> sequenceWithArg(block: suspend ArgSequenceScope<A, T>.() -> Unit): ArgSequence<A, T> {
    val argChannel = Channel<A>()
    val nextChannel = Channel<T>()
    val argSequenceScope = ArgSequenceScope(argChannel, nextChannel)
    GlobalScope.launch {
        argSequenceScope.block()
        argChannel.close()
        nextChannel.close()
    }
    return ArgSequence(argChannel, nextChannel)
}

fun test() {
    val sequence = sequenceWithArg<String, Int> {
        yield(arg().length)
        yield(arg().length)
    }
    runBlocking {
        println(sequence.next("12345"))
        println(sequence.next("1234"))
    }
}