gpt4 book ai didi

kotlin - 在Kotlin `when`语句(或其他分支构造)中将函数或lambda作为条件包括在内的最简洁方法是什么?

转载 作者:行者123 更新时间:2023-12-02 12:46:32 26 4
gpt4 key购买 nike

我正在处理字符串,遇到了Regex or Wildcard答案:可以使用自定义类覆盖等号将正则表达式放入when语句中。虽然这确实有效地使用了类型系统将语法糖插入when语句中,但我发现以下情况非常丑陋,并且永远不会在我打算与其他开发人员共享的代码中这样做(引用travis):

import kotlin.text.regex

when (RegexWhenArgument(uri)) {
Regex(/* pattern */) -> /* do stuff */
Regex(/* pattern */) -> /* do stuff */
/* etc */
}

RegexWhenArgument的最低定义为:
class RegexWhenArgument (val whenArgument: CharSequence) {
operator fun equals(whenEntry: Regex) = whenEntry.matches(whenArgument)
override operator fun equals(whenEntry: Any?) = (whenArgument == whenEntry)
}

(结束报价)

我认为将arg传递给 when然后引用对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开发人员会做什么?

最佳答案

您的问题有很多解决方案。我将从简单的开始,然后继续讨论更复杂的部分。

没有争论的时候

Kotlin documentation说:

when can also be used as a replacement for an if-else if chain. If no argument is supplied, the branch conditions are simply boolean expressions, and a branch is executed when its condition is true



用例:
when {
startsWithHelp(givenString) -> printHelp()
startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}

优点:
  • 不需要任何其他代码
  • 易于理解的

  • 缺点:
  • 样板(givenString)


  • 覆盖等于的参数包装器

    所需的包装器类似于 RegexWhenArgument,但是不检查 Regex而是调用 (String) -> BooleanString没什么特别的,所以我将使用 <T>

    包装函数:
    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)}!")
    }

    优点:
  • 函数可以在when分支中使用
  • 很容易理解
  • 只需要一个简单的包装函数

  • 缺点:
  • 容易忘记包装参数
  • 分支条件中可能偶然使用了错误类型的函数

  • 注意:此解决方案和下一个解决方案允许使用功能组合,这些组合可以由以下扩展创建:
    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) }

    基于DSL的 when模拟

    在Kotlin中,DSL不仅可以用于构建对象,还可以用于创建自定义程序流结构。

    源代码:
    @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") }
    }

    优点:
  • 可自定义的语法
  • 可以添加新功能
  • 可以添加仅适用于某些参数类型的函数
  • 可以启用嵌套分支
  • 没有样板
  • 在编译时检查函数类型
  • 易于阅读

  • 缺点:
  • 需要一些帮助程序类和函数
  • 实验性@BuilderInference的用法(可以用用于表达式的显式类型声明和用于语句的单独方法代替)
  • 新语法学习可能需要一些时间

  • 扩展示例:
    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)
    }

    关于kotlin - 在Kotlin `when`语句(或其他分支构造)中将函数或lambda作为条件包括在内的最简洁方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58001124/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com