我可以 return 从 lambda 调用它体内的一些函数吗(非本地 returns)

Can I return from lambda by invoking some function inside its body (non-local returns)

所以我正在为集合制作正则表达式(所有量词都是所有格)。它看起来像这样(请记住,为了便于阅读,该示例过于简化):

val mayBeAPerson: Boolean = "totally not a person"
    .toList()
    .matches { // this: PatternScope
        one { it.isUpperCase() } // execution of lambda could end after this method
        moreThan(0) { it.isLetter() }
        one { it == ' ' }

        lessThan(2) { // this: PatternScope
            one { it.isUpperCase() }
            one { it == '.' }
            one { it == ' ' }
        }
        
        one { it.isUpperCase() }
        moreThan(0) { it.isLetter() }
    }

如您所见,传递给 matches 的 lambda 的执行可能会在第一个 one 之后结束,因为传递给它的谓词与 List 中的第一个字符不匹配。它确实结束了。然而,我的解决方案与优雅相反,因为它使用在 one 中抛出异常并在 matches.

中捕获它
fun List<Char>.matches(build: PatternScope.() -> Unit) = try {
    val scope = PatternScope(iterator()) 
    scope.build() // may throw MatchFailed
    !scope.iterator.hasNext()
} catch (_: MatchFailed) {
    false
}
class PatternScope(private val iterator: Iterator<Char>) {

    inline fun one(predicate: (element: Char) -> Boolean) {
        if (!iterator.hasNext() || !predicate(iterator.next())) {
            throw MatchFailed("match failed")
        }
    }

    .
    .  etc
    .
}

完全可以,但我不禁想知道:有没有更好的方法?我确实知道像这样抛出异常只是一种幻想 GOTO,我可以将 PatternScope 的所有方法包装在 if 中,如下所示:

class PatternScope(private val iterator: Iterator<Char>) {
    private var matchFailed = false

    inline fun one(predicate: (element: Char) -> Boolean) {
        if (!matchFailed) {
            if (!iterator.hasNext() || !predicate(iterator.next())) {
                matchFailed = true
            }
        }
    }

    inline fun moreThan(n: Int, predicate: (element: Char) -> Boolean) {
        if (!matchFailed) {
            // logic
        }
    }

    .
    .  etc
    .
}

它更优雅吗?现在我正在调用传递给 matches 的 lambda 中的所有函数,老实说,我更不喜欢它了。

我真正的问题是:还有更好的方法吗?来自 lambda 的 return 的一些神奇解决方案我什至无法真正访问?一些非局部的 returns,但是从函数 lambda 甚至还没有看到?

我可以 return 从 lambda 中调用它体内的一些函数吗?

编辑

澄清一下,假设我们有一个 lambda:

val lambda: () -> Unit = {
    someMethod() // this should return from lambda (in some cases)

    someOtherMethod() // this shouldn't be invoked
}

someMethod 的主体应该是什么样子,以便 someOtherMethod 在调用 lambda 时甚至不执行?除了让 someMethod 抛出异常并将 lambda 包装在 try-catch 块中,还有其他方法吗:

try {
    lambda() // throws
} catch (_: SomeThrowableIdk) { }

我没有找到更好的方法,但请证明我是错的。

我假设您实际使用的是 @PublishedApi,因为您有一个私有迭代器和访问它的 public 内联函数。

由于 Kotlin 没有已检查的异常,因此针对程序中实际上不是错误的事情(错误)抛出异常是违反 Kotlin 惯例的。由于这个原因,您的第一种方法感觉有点老套。由于您的 API 具有 public 内联函数,因此无法完全封装异常。您可以切换到非内联函数并在内部将集合中的步骤存储为 运行,但这肯定比内联函数或您使用 if 语句的第二种方法更多 运行 时间开销。

您的第二种方法更像是典型的构建器,所以我看不出它有什么问题。由于您的函数是内联的,因此您的编译代码不会有一堆不必要的函数调用。只是 if 语句。但是,您可以添加一个辅助函数来清理所有子函数的代码,尽管我不确定这是否可以扩展到您实际 class:

的复杂性
class PatternScope(@PublishedApi internal val iterator: Iterator<Char>) {
    @PublishedApi internal var matchFailed = false

    @PublishedApi internal inline fun nextRequire(require: () -> Boolean) {
        matchFailed = matchFailed || !require()
    }

    inline fun one(predicate: (element: Char) -> Boolean) = nextRequire {
        iterator.hasNext() && predicate(iterator.next())
    }
}

无法执行您在编辑中描述的操作。非本地 returns 仅适用于 lambda。为了支持您所描述的内容,Kotlin 需要一种特殊函数的语法,该函数能够从调用它的函数中 return 。像这样的函数必须有一种新的签名,它还声明了允许调用它的函数类型的 return 类型。在 Kotlin 中根本没有这样的语法或函数类型。