当满足条件时,如何从传递给递归函数的 lambda 中断



我正在编写一个自定义loopdsl,我希望它的用法如下所示


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
}

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


@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一起使用,但它不起作用。有什么方法可以实现吗?

例如,可以通过抛出轻量级异常来完成。您必须声明自定义异常:

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
}
}

我对函数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)
}
}
}

最新更新