我可以 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 中根本没有这样的语法或函数类型。
所以我正在为集合制作正则表达式(所有量词都是所有格)。它看起来像这样(请记住,为了便于阅读,该示例过于简化):
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 中根本没有这样的语法或函数类型。