Использование предложения Kotlin WHEN для <, <=,> =,> сравнений

Я пытаюсь использовать предложение WHEN с помощью сравнения > или <.

Это не компилируется. Есть ли способ использовать нормальный набор булевых операторов (< < =, >= > ) в сравнении, чтобы включить это?

val foo = 2

// doesn't compile
when (foo) {
    > 0 -> doSomethingWhenPositive()
    0   -> doSomethingWhenZero()
    < 0 -> doSomethingWhenNegative()
}

Я пытался найти неограниченное сравнение диапазона, но не мог сделать эту работу тоже? Можно ли записать это как неограниченный диапазон?

// trying to get an unbounded range - doesn't compile
when (foo) {
    in 1.. -> doSomethingWhenPositive()
    else -> doSomethingElse()
}

Вы можете поместить все выражение во вторую часть, что нормально, но похоже на ненужное дублирование. По крайней мере, он компилируется и работает.

when {
    foo > 0 -> doSomethingWhenPositive()
    foo < 0 -> doSomethingWhenNegative()
    else -> doSomethingWhenZero()
}

Но я не уверен, что это проще, чем альтернатива if-else, которую мы делали годами. Что-то вроде:

if ( foo > 0 ) {
    doSomethingWhenPositive()
}
else if (foo < 0) {
    doSomethingWhenNegative()
}
else {
    doSomethingWhenZero()
}

Конечно, проблемы в реальном мире сложнее, чем выше, а предложение WHEN привлекательно, но не работает, как я ожидаю для такого типа сравнения.

Ответ 1

Даже гибкий язык, такой как Kotlin, не имеет "элегантного" /DRY -решения для каждого случая.

Вы можете написать что-то вроде:

when (foo) {
    in 0 .. Int.MAX_VALUE -> doSomethingWhenPositive()
    0    -> doSomethingWhenZero()
    else -> doSomethingWhenNegative()
}

Но тогда вы зависите от типа переменной.

Я считаю, что следующая форма является самой идиоматической в ​​Котлине:

when {
    foo > 0  -> doSomethingWhenPositive()
    foo == 0 -> doSomethingWhenZero()
    else     -> doSomethingWhenNegative()
}

Да... существует некоторая (минимальная) копия кода.

Некоторые языки (Ruby?!) пытались обеспечить элегантную форму uber для любого случая - но есть компромисс: язык становится более сложным и сложным для программиста, чтобы он знал конец.

Мои 2 цента...

Ответ 2

Грамматика для условия when выглядит следующим образом:

whenCondition (used by whenEntry)
  : expression
  : ("in" | "!in") expression
  : ("is" | "!is") type
  ;

Это означает, что вы можете использовать только is или in качестве особых случаев, которые не должны быть полным выражением; все остальное должно быть нормальным выражением. Поскольку > 0 не является допустимым выражением, оно не скомпилируется.

Кроме того, диапазоны закрыты в Котлине, поэтому вы не можете избежать попыток использовать неограниченный диапазон.

Вместо этого вы должны использовать оператор when с полным выражением, как в вашем примере:

when {
    foo > 0 -> doSomethingWhenPositive()
    foo < 0 -> doSomethingWhenNegative()
    else -> doSomethingWhenZero()
}

Или в качестве альтернативы:

when {
    foo < 0 -> doSomethingWhenNegative()
    foo == 0 -> doSomethingWhenZero()        
    foo > 0 -> doSomethingWhenPositive()        
}

который может быть более читабельным.

Ответ 3

Вы хотите, чтобы ваш код был изящным, поэтому зачем оставаться в выражении when. Kotlin достаточно гибкий, чтобы построить новый, используя расширение.

Сначала мы должны утверждать, что здесь мы можем передать только Comparable<T>, потому что вы должны сравнить значение.

Тогда у нас есть наша структура:

fun <T: Comparable<T>> case(target: T, tester: Tester<T>.() -> Unit) {
    val test = Tester(target)
    test.tester()
    test.funFiltered?.invoke() ?: return
}
class Tester<T : Comparable<T>>(val it: T) {
    var funFiltered: (() -> Unit)? = null
    infix operator fun Boolean.minus(block: () -> Unit) {
        if (this && funFiltered == null) funFiltered = block
    }

    fun lt(arg: T) = it < arg
    fun gt(arg: T) = it > arg
    fun ge(arg: T) = it >= arg
    fun le(arg: T) = it <= arg
    fun eq(arg: T) = it == arg
    fun ne(arg: T) = it != arg
    fun inside(arg: Collection<T>) = it in arg
    fun inside(arg: String) = it as String in arg
    fun outside(arg: Collection<T>) = it !in arg
    fun outside(arg: String) = it as String !in arg
}

После этого у нас может быть элегантный код:

case("g") {
    (it is String) - { println("hello") } // normal comparison, like `is`
    outside("gg") - { println("gg again") } // invoking the contains method
}

case(233) {
    lt(500) - { println("less than 500!") }
    // etc.
}

Если вы счастливы, вы можете переименовать функцию minus в compareTo и вернуть 0. Таким образом, вы можете заменить - на =>, который выглядит как scala.

Ответ 4

Мы можем использовать let для достижения этого поведения.

response.code().let {
    when {
        it == 200 -> handleSuccess()
        it == 401 -> handleUnauthorisedError()
        it >= 500 -> handleInternalServerError()
    }
}

Надеюсь это поможет

Ответ 5

Я нашел немного хакерский способ, который может помочь вам смешивать больше, меньше или любое другое выражение с другими в выражениях. Проще говоря, когда оператор в Kotlin просматривает "case", и, если это диапазон, он видит, находится ли переменная в этом диапазоне, но если это не так, он смотрит, имеет ли case тот же тип переменной, и если это не так, вы получите синтаксическую ошибку. Итак, чтобы обойти это, вы можете сделать что-то вроде этого:

when (foo) {
    if(foo > 0) foo else 5 /*or any other out-of-range value*/ -> doSomethingWhenPositive()
    in -10000..0   -> doSomethingWhenBetweenNegativeTenKAndZero()
    if(foo < -10000) foo else -11000 -> doSomethingWhenNegative()
}

Как вы можете видеть, это использует тот факт, что все в Kotlin является выражением. Итак, IMO, это довольно хорошее решение, пока эта функция не будет добавлена к языку.