Kotlin协程压缩三个流

Kotlin协程压缩三个流,kotlin,kotlin-coroutines,kotlin-coroutines-flow,Kotlin,Kotlin Coroutines,Kotlin Coroutines Flow,有一个函数可以压缩两个流。是否有什么东西可以将三个(或更多)的流压缩在一起 如果没有,你能帮我实现它的扩展功能吗?比如: flow.zip(flow2, flow3) { a, b, c -> } 我还没有测试过这个,但是你可以试试。zip有很多底层代码,因此为了利用这些代码,我将前两个流压缩为一个对流,然后将对流压缩为第三个流。但是传递给这个函数的lambda得到了前两个已经分离的lambda,因此它不必知道中间对步骤 fun <T1, T2, T3, R> zip(

有一个函数可以压缩两个
。是否有什么东西可以将三个(或更多)的
压缩在一起

如果没有,你能帮我实现它的扩展功能吗?比如:

flow.zip(flow2, flow3) { a, b, c -> 

}

我还没有测试过这个,但是你可以试试。
zip
有很多底层代码,因此为了利用这些代码,我将前两个流压缩为一个对流,然后将对流压缩为第三个流。但是传递给这个函数的lambda得到了前两个已经分离的lambda,因此它不必知道中间对步骤

fun <T1, T2, T3, R> zip(
    first: Flow<T1>,
    second: Flow<T2>,
    third: Flow<T3>,
    transform: suspend (T1, T2, T3) -> R
): Flow<R> =
    first.zip(second) { a, b -> a to b }
        .zip(third) { (a, b), c ->
            transform(a, b, c)
        }
对于任意数量的流,这里有一个未经测试的版本,但它们必须是相同的类型:

fun <T, R> zip(
    vararg flows: Flow<T>,
    transform: suspend (List<T>) -> R
): Flow<R> = when(flows.size) {
    0 -> error("No flows")
    1 -> flows[0].map{ transform(listOf(it)) }
    2 -> flows[0].zip(flows[1]) { a, b -> transform(listOf(a, b)) }
    else -> {
        var accFlow: Flow<List<T>> = flows[0].zip(flows[1]) { a, b -> listOf(a, b) }
        for (i in 2 until flows.size) {
            accFlow = accFlow.zip(flows[i]) { list, it ->
                list + it
            }
        }
        accFlow.map(transform)
    }
}
fun-zip(
vararg流:流,
转换:挂起(列表)->R
):Flow=when(flows.size){
0->错误(“无流”)
1->流[0]。映射{transform(listOf(it))}
2->flows[0].zip(flows[1]){a,b->transform(listOf(a,b))}
其他->{
var accFlow:Flow=flows[0].zip(flows[1]){a,b->listOf(a,b)}
用于(i/2,直到流量大小){
accFlow=accFlow.zip(flows[i]){list,it->
列表+它
}
}
accFlow.map(转换)
}
}

您可以检查
zip
运营商的实施情况,并尝试复制/模拟它的工作方式,使其适应您的需要

测试它并进行所有需要的更改

fun <T1, T2, T3, R> Flow<T1>.zip(flow2: Flow<T2>, flow3: Flow<T3>, transform: suspend (T1, T2, T3) -> R): Flow<R> = channelFlow {

    val first: ReceiveChannel<T1> = produce {
        this@zip.collect {
            channel.send(it)
        }
    }

    val second: ReceiveChannel<T2> = produce {
        flow2.collect {
            channel.send(it)
        }
    }

    val third: ReceiveChannel<T3> = produce {
        flow3.collect {
            channel.send(it)
        }
    }

    (second as SendChannel<*>).invokeOnClose {
        if (!first.isClosedForReceive) first.cancel(MyFlowException())
        if (!third.isClosedForReceive) third.cancel(MyFlowException())
    }

    (third as SendChannel<*>).invokeOnClose {
        if (!first.isClosedForReceive) first.cancel(MyFlowException())
        if (!second.isClosedForReceive) second.cancel(MyFlowException())
    }

    val otherIterator = second.iterator()
    val anotherIterator = third.iterator()

    try {
        first.consumeEach { value ->
            if (!otherIterator.hasNext() || !anotherIterator.hasNext()) {
                return@consumeEach
            }
            send(transform(value, otherIterator.next(), anotherIterator.next()))
        }
    } catch (e: MyFlowException) {
        // complete
    } finally {
        if (!second.isClosedForReceive) second.cancel(MyFlowException())
        if (!third.isClosedForReceive) third.cancel(MyFlowException())
    }
}

class MyFlowException: CancellationException()

我对流动还不熟悉,但这似乎对我有用

    // This will hold all 3 values
    data class Foo(val i: Int, val j: Int, val k: Int)

    val flow1 = (1..10).asFlow()
    val flow2 = (11..20).asFlow()
    val flow3 = (21..30).asFlow()

    val combinedFlow = flow1.zip(flow2) {i, j ->  
        Pair(i, j)
    }.zip(flow3) {pair, k ->
        Foo(pair.first, pair.second, k)
    }
然后,您将收集它们并按如下方式获取值:

zip(flow1, flow2, flow3) { a, b, c ->
    Triple(a, b, c)
}
        viewModelScope.launch {
            repo.combinedFlow.collect {foo ->
                System.out.println(foo.i)
                System.out.println(foo.j)
                System.out.println(foo.k)
            }
        }

流量具有组合功能

fun <T1, T2, R> Flow<T1>.combine(
    flow: Flow<T2>,
    transform: suspend (a: T1, b: T2) -> R
): Flow<R>
显然,但我想你可以先把前两个拉链拉上,然后再把下一个拉链拉上,再把下一个拉链拉上,但它不会像你想象的那样平行。
fun <T1, T2, R> Flow<T1>.combine(
    flow: Flow<T2>,
    transform: suspend (a: T1, b: T2) -> R
): Flow<R>
 val flow = flowOf(1, 2).onEach { delay(10) }
 val flow2 = flowOf("a", "b", "c").onEach { delay(15) }
 combine(flow, flow2) { i, s -> i.toString() + s }.collect {
     println(it) // Will print "1a 2a 2b 2c"
 }