如何从拉姆达打破传递给递归函数时提供的条件满足(how to break from lambda p

2019-10-30 02:37发布

我正在写一个自定义的loop DSL,我想它的使用看起来像下方


    var counter1 = 0
    var counter2 = 0
    loop {
            counter1 += 1
            println(counter1)
            stopIf(counter1 == 5) // loop should terminate here and not execute rest of the code if condition matches

            counter2 += 2
            println(counter2)
            stopIf(counter2 == 8) // loop should terminate here and not execute rest of the code if condition matches

        }

我有以下代码,确实让我写stopIf任意次数,并在任何地方loop体,但是当条件匹配它不会立即终止执行,但循环体的其余部分,然后终止。


    @UseExperimental(ExperimentalTime::class)
    open class Loop {
        var stop = false

        val loopInterval = 1.seconds

        suspend fun loop(block: suspend () -> Unit): Unit = loop(loopInterval, block)

        suspend fun loop(minimumInterval: Duration, block: suspend () -> Unit): Unit =
            loopWithoutDelay { delayedResult(maxOf(minimumInterval, loopInterval), block) }

        private suspend fun loopWithoutDelay(block: suspend () -> Unit) {
            block()
            if (stop) return else loopWithoutDelay(block)
        }

        suspend fun <T> delayedResult(minDelay: Duration, f: suspend () -> T): T = coroutineScope {
            val futureValue = async { f() }
            delay(minDelay.toJavaDuration())
            futureValue.await()
        }

        fun stopIf(condition: Boolean) {
            if (condition) {
                stop = condition // once stop condition matches, then do not override it with following false condtions
            }
        }
    }

    @ExperimentalTime
    suspend fun loop(block: suspend Loop.() -> Unit) =
        Loop().run { loop { block(this) } }

我曾尝试使用returnlabel ,但没有奏效。 有没有什么办法可以做到这一点?

Answer 1:

例如,它可以做到与抛出一个轻量级的异常。 您必须声明自定义异常:

class LoopStopException : Throwable("Stop look", null, false, false) // lightweight throwable without the stack trace

并抓住它在loopWithoutDelay

private suspend fun loopWithoutDelay(block: suspend () -> Unit) {
    try {
        while (true) {
            block()
        }
    } catch (e: LoopStopException) {
        //do nothing
    }
}



Answer 2:

我没太了解功能delayedResult ,因为没有任何的DSL的公共函数返回一个结果。 不过,我想出了取消该循环的解决方案。

据我了解,我们必须有一个循环,不会阻止当前线程。 因此,它必须在协同程序运行,但为了能够取消循环中,DSL必须运行其自己的协同程序。 这个内部协同程序正在使用运行coroutineScope ,所以暂停父协程,直到它完成或取消。

@ExperimentalTime
class Loop {
    private val loopInterval = 1.seconds

    suspend fun loop(block: suspend () -> Unit) = loop(loopInterval, block)

    suspend fun loop(minimumInterval: Duration, block: suspend () -> Unit):Job  = coroutineScope {
        launch {
            while (true) {
                block()
                delay(minOf(minimumInterval, loopInterval).toLongMilliseconds())
            }
        }
    }

    suspend fun stopIf(condition: Boolean) = coroutineScope {
        suspendCancellableCoroutine<Unit> {
            if (condition) it.cancel() else it.resumeWith(Result.success(Unit))
        }
    }
}

@ExperimentalTime
suspend fun loop(block: suspend Loop.() -> Unit):Job {
    return Loop().run {
        this.loop {
            block(this)
        }
    }
}


文章来源: how to break from lambda passed to recursive function when provided condition met