Табличная форма ветвлений (when)

Каскадную запись if..else if..else часто можно представить более изящно в табличной форме, используя конструкцию when (когда). Для примера quadraticRootNumber это делается так:

fun quadraticRootNumber(a: Double, b: Double, c: Double): Int {
    // Применяем готовую функцию из первой части
    val d = discriminant(a, b, c)
    // Для сравнения на равенство применяем ==
    return when {
        d > 0.0  -> 2
        d == 0.0 -> 1
        else     -> 0
    }
}

Конструкция when состоит из последовательности записей вида условие -> результат. В последней записи условие заменяется на ключевое слово else (иначе).

Частый случай применения when — ситуация, когда одно и то же выражение необходимо последовательно сравнить на равенство с несколькими другими. Для примера, рассмотрим задачу формирования словесной нотации для оценки. Согласно принятым сейчас стандартам, оценка «5» записывается как «отлично», «4» как «хорошо», «3» как «удовлетворительно» и «2» как «неудовлетворительно». Представим подобное преобразование в виде функции на Котлине, используя when:

fun gradeNotation(grade: Int): String = when (grade) {
    5 -> "отлично"
    4 -> "хорошо"
    3 -> "удовлетворительно"
    2 -> "неудовлетворительно"
    else -> "несуществующая оценка $grade"
}

Эта функция принимает на вход целочисленную оценку (grade) и формирует на выходе соответствующую ей строку. Напомним, что строкам в Котлине соответствует тип String и записываются они в двойных кавычках.

Для проверки возможного значения grade мы используем конструкцию when (grade), в которой оно последовательно сравнивается с 5, 4, 3 и 2. Обратите внимание, что в нашей записи when имеется и пятый случай (else). Его присутствие необходимо, так как функция должна знать, какой результат ей следует вернуть на выход, для любого допустимого значения входа (в данном случае это тип Int с его диапазоном допустимых значений). Строго говоря, ветка else здесь соответствует ошибочной ситуации, которая может предусматривать специальную обработку — но об этом позже. В функции gradeNotation в этой ситуации мы формируем строку «несуществующая оценка», дописывая к ней значение переданной оценки, например: «несуществующая оценка 0».

Логические функции и операции

Условие в операторе if часто в свою очередь вычисляется с помощью функции с результатом типа Boolean. Пусть, например, имеется круг на плоскости с центром в точке (x0, y0) и радиусом r, а также точка на плоскости с координатами (x, y). Необходимо определить, лежит ли точка внутри круга. Особенность данной задачи в том, что у неё есть только два ответа: ДА или НЕТ, либо, более формально, ИСТИННО (true) или ЛОЖНО (false).

Для решения данной задачи необходимо воспользоваться неравенством круга: (x-x0)2 + (y-y0)2 ≤ r2. Если точка (x, y) удовлетворяет этому неравенству, то она лежит внутри круга, если же нет, то она находится снаружи. Функция очень проста и записывается так:

fun pointInsideCircle(x: Double, y: Double, x0: Double, y0: Double, r: Double) =
        sqr(x - x0) + sqr(y - y0) <= sqr(r)

Здесь вновь используется функция sqr из урока 1 для вычисления квадратов чисел. Тип результата функции pointInsideCircle — Boolean. При написании тестовых функций для неё удобно использовать готовые функции assertTrue и assertFalse, например:

@Test
fun pointInsideCircle() {
    // (1, 1) inside circle: center = (0, 0), r = 2
    assertTrue(pointInsideCircle(1.0, 1.0, 0.0, 0.0, 2.0))
    // (2, 2) NOT inside circle: center = (0, 0), r = 2
    assertFalse(pointInsideCircle(2.0, 2.0, 0.0, 0.0, 2.0))
}

Обе функции имеют один параметр типа BooleanassertTrue (проверить на истину) приводит к неудачному исходу теста, если её аргумент равен false, и продолжает выполнение теста, если он равен trueassertFalse (проверить на ложь) работает с точностью до наоборот.

Функцию pointInsideCircle в свою очередь можно использовать для решения более сложных задач. Например, условие принадлежности точки пересечению или объединению двух кругов может выглядеть так:

// Фрагмент программы...
val x = 0.5
val y = 0.5
// Пересечение: логическое И
if (pointInsideCircle(x, y, 0.0, 0.0, 1.0) && pointInsideCircle(x, y, 1.0, 1.0, 1.0)) { ... }
// Объединение: логическое ИЛИ
if (pointInsideCircle(x, y, 0.0, 0.0, 1.0) || pointInsideCircle(x, y, 1.0, 1.0, 1.0)) { ... }
// Не принадлежит
if (!pointInsideCircle(x, y, 0.0, 0.0, 1.0)) { ... }

В этом примере используются логические операции:

  • && — логическое И, результат равен true, если ОБА аргумента true
  • || — логическое ИЛИ, результат равен true, если ХОТЯ БЫ ОДИН из аргументов равен true
  • ! — логическое НЕ, результат равен true, если аргумент false

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.