在Kotlin`when`语句(或其他分支构造)中,将函数或lambda作为条件包括在内的最简洁的方法是什么?
我在处理字符串,我找到了答案:当语句中有一个覆盖equals的自定义类时,可以将正则表达式放在在Kotlin`when`语句(或其他分支构造)中,将函数或lambda作为条件包括在内的最简洁的方法是什么?,kotlin,lambda,functional-programming,conditional-statements,control-flow,Kotlin,Lambda,Functional Programming,Conditional Statements,Control Flow,我在处理字符串,我找到了答案:当语句中有一个覆盖equals的自定义类时,可以将正则表达式放在语句中。虽然这确实有效地使用了类型系统将语法糖塞进了when语句中,但我发现下面的语句非常难看,而且我永远不会在我打算与其他开发人员共享的代码中这样做(引用): 其中RegexWhenArgument的最低定义为: class RegexWhenArgument (val whenArgument: CharSequence) { operator fun equals(whenEntry: R
语句中。虽然这确实有效地使用了类型系统将语法糖塞进了when
语句中,但我发现下面的语句非常难看,而且我永远不会在我打算与其他开发人员共享的代码中这样做(引用):
其中RegexWhenArgument的最低定义为:
class RegexWhenArgument (val whenArgument: CharSequence) {
operator fun equals(whenEntry: Regex) = whenEntry.matches(whenArgument)
override operator fun equals(whenEntry: Any?) = (whenArgument == whenEntry)
}
(完)
我认为在
时将arg传递给,然后引用在arg类型上操作的函数,可读性会更好。举一个人为的例子:
// local declaration
val startsWithFn: (String) -> Boolean = {s -> s.startsWith("fn:")}
when(givenString) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
// package level function
fun startsWithHelp(s:String) = s.startsWith("help", true)
但当然,这段代码不会编译。有没有一种方法可以做到可读、可维护和简洁?也许使用流?有经验的Kotlin开发人员会怎么做?您的问题有很多解决方案。我将从简单的开始,然后进入更复杂的部分
毫无争议
说:
when
也可用作if
-else if
链的替代品。如果没有
参数,则分支条件仅为布尔值
表达式,并在其条件为true时执行分支
用例:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
专业人士:
不需要任何附加代码
易懂
缺点:
样板文件(givenString)
重写等于的参数包装器
所需的包装器类似于RegexWhenArgument
,但它不是选中Regex
,而是调用(字符串)->Boolean
。String
没有什么特别之处,所以我将使用
包装函数:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
优点:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
当
分支时,可以在中使用函数
很容易理解
只需要一个简单的包装函数
缺点:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
很容易忘记包装一个论点
在分支条件下,可能会意外使用错误类型的函数
注意:此解决方案和下一个解决方案允许使用函数组合,可通过以下扩展创建:
infix fun <T> ((T) -> Boolean).and(other: ((T) -> Boolean)) = { it: T -> invoke(it) && other(it) }
infix fun <T> ((T) -> Boolean).or(other: ((T) -> Boolean)) = { it: T -> invoke(it) || other(it) }
operator fun <T> ((T) -> Boolean).not() = { it: T -> !invoke(it) }
语句用例:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
表达式用例:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
优点:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
可自定义语法
可以添加新功能
可以添加仅适用于某些参数类型的函数
可以启用嵌套分支
没有样板
在编译时检查函数类型
易读
缺点:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
需要几个助手类和函数
实验性@builderReference
的使用(可以用表达式的显式类型声明和语句的单独方法替换)
新语法学习可能需要一些时间
扩展示例:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
override fun equals(other: Any?) =
if (other is Function1<*, *>)
(other as Function1<T, Boolean>)(arg)
else arg == other
}
when (whenArg(givenString)) {
::startsWithHelp -> printHelp()
startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl
// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing.
@WhichDsl
object WhichCase
// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
// R? is not used here because R can be nullable itself
var result: Holder<R>? = null
inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
if (result == null && invoke(arg)) result = Holder(code(WhichCase))
}
// else analog
inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}
data class Holder<out T>(val element: T)
inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
Which<T, R>(arg).code()
which(givenString) {
::startsWithHelp { printHelp() }
startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
::startsWithHelp { 0 }
startsWithFn { 1 }
other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
other { throw NoWhenBranchMatchedException(message()) }
val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
condition(code)
inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
branch({ it == value }, code)
fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
branch({ regex.matches(it) }, code)
fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
branch({ it in range }, code)
inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
if (condition(arg)) code(WhichCase)
}
fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
sideBranch({ it == value }, code)
inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
result?.let { (element) ->
if (WhichCase.condition(element)) result = null
}
}
inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
branch(condition) {
which(this@subBranch.arg, code)
}
inline.orThrow(消息:()->字符串)=
其他{抛出NoWhenBranchMatchedException(message())}
val Which.orThrow get()=orThrow{“没有与$arg匹配的分支”}
inline fun Which.branch(条件:(T)->布尔,代码:WhichCase.(->R)=
条件(代码)
inline fun Which.case(值:T,代码:WhichCase.(->R)=
分支({it==value},代码)
regex(regex:regex,代码:WhichCase.(->R)=
分支({regex.matches(it)},代码)
有趣的。范围(范围:ClosedRange,代码:WhichCase.(->R)=
分支({it in range},代码)
inline fun Which.sideBranch(条件:(T)->布尔,代码:WhichCase.(->单位){
if(条件(参数))代码(哪种情况)
}
有趣的Which.sideCase(值:T,代码:WhichCase.(->单位)=
边分支({it==value},代码)
dropResult(条件:WhichCase.(R)->Boolean={{uu->true}){
结果?让{(元素)->
if(WhichCase.condition(element))结果=null
}
}
内联fun Which.subWhich(条件:(T)->布尔,代码:Which.(->R)=
分支(条件){
哪个(this@subBranch.arg,代码)
}
我真的,真的会做一连串的假设。我真的认为它看起来和读起来都很好。