Табличная форма ветвлений (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)) }
Обе функции имеют один параметр типа Boolean
. assertTrue
(проверить на истину) приводит к неудачному исходу теста, если её аргумент равен false, и продолжает выполнение теста, если он равен true. assertFalse
(проверить на ложь) работает с точностью до наоборот.
Функцию 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
не много подумал над последней задачей, не понимал условие, пока не нарисовал наглядно на системе координат