3. Основы Kotlin. Рекурсии и циклы

Почти все реальные задачи требуют для своего решения не только ветвлений, но и многократного повторения однотипных действий. Простым примером такой задачи является вычисление факториала factorial(n) = n!. Напомним, что факториал натурального числа n равен произведению всех чисел от 1 до n. По принятому соглашению, 0! = 1 и 1! = 1, факториал от отрицательного числа не определён.

Рекурсивная функция

Наиболее простым для построения программной реализации является индуктивное определение факториала, согласно которому n! = n(n-1)!. Базой индуктивного определения служат соглашения о значении 0! и 1!. На Котлине подобное определение реализуется следующим образом:

fun factorial(n: Int): Double = if (n < 2) 1.0 else n * factorial(n - 1)

Здесь мы использовали приём программирования под названием рекурсия. Для вычисления результата функции factorial происходит вызов этой же функции, но с меньшим значением аргумента. Рано или поздно условие if (n < 2) окажется выполненным, и значение функции будет вычислено. Обратите внимание, что тип результата функции указан как Double. Попробуйте понять, что произойдёт, если Double заменить на Int, а литерал 1.0 на 1.

Цикл for, мутирующие переменные, интервалы и прогрессии

Возможна также реализация итеративного определения, а именно n! = 1 * 2 * …​ * (n-1) * n. Для этого необходимо использовать циклы; наиболее распространённым в Котлине является цикл for, который подходит и для этой задачи. Реализация будет выглядеть так:

fun factorial(n: Int): Double {
    // Мутирующая переменная (var)
    var result = 1.0
    for (i in 1..n) {
        result = result * i
    }
    return result
}

Конструкция for (i in 1..n) { …​ } читается как «Для всех i в интервале от 1 до n (выполнить) …​». В данной конструкции объявляется параметр цикла for, которому даётся имя i. Внутри фигурных скобок находится телоцикла for. Тело цикла в данном случае будет выполнено n раз, то есть произойдёт n итераций. На каждой итерации параметр i будет иметь различные значения — от 1 до n.

Строчкой выше объявлена так называемая мутирующая переменная result. Для её объявления мы использовали ключевое слово var (variable) — в отличие от val (value) для обычных переменных. Мутирующая переменная получает значение 1.0 при её объявлении, но в процессе выполнения функции она может изменять своё значение.

Оператор result = result * i выполняет так называемое присваивание мутирующей переменной другого значения. При этом вначале берётся её прежнее значение (например, 2.0 на 3-й итерации цикла), оно умножается на значение параметра i (3 на 3-й итерации цикла) и результат (6.0) присваивается переменной result. Если бы мы объявили переменную result как val result = 1.0, то в этой строчке функции мы получили бы ошибку:

Val cannot be reassigned

В последнем операторе return result определяется окончательный результат вычисления факториала.

Оператор for может использоваться не только для перебора чисел в интервале (от меньшего к большему с шагом 1), но и для перебора чисел в заданной прогрессии. Интервал, как мы уже видели, создаётся оператором вида min..max. Типичные примеры создания прогрессии выглядят так:

  • 10 downTo 1 — прогрессия от большего числа к меньшему, с шагом 1 (10, 9, 8, …​, 1);
  • 1..99 step 2 — прогрессия от меньшего числа к большему, но с шагом 2 (в данном случае, перебор всех нечётных чисел по возрастанию);
  • 100 downTo 2 step 2 — прогрессия от большего числа к меньшему, с шагом 2 (перебор всех чётных чисел по убыванию).

Модифицирующие операторы

Откройте теперь файл srс/lesson3/task1/Loop.kt в проекте KotlinAsFirst в IDE и внимательно посмотрите на определение функции factorial вверху файла. Внимательные читатели обнаружат, что оператор result = result * iподчёркнут серой волнистой чертой. Если навести на него указатель мыши, мы увидим сообщение «Replace with *= operator», то есть «Заменить оператором *=». Нажмите Alt+Enter, вы увидите контекстное меню с символом лампочки и командой «Replace with *= operator». Нажмите Enter, и IDE выполнит замену, которую предлагает. Мы получим следующий текст функции:

fun factorial(n: Int): Double {
    // Мутирующая переменная (var)
    var result = 1.0
    for (i in 1..n) {
        result *= i
    }
    return result
}

Оператор *= относится к большой группе модифицирующих операторов и выполняет домножение текущего значения переменной result на значение параметра i. Аналогично ему работают операторы +=-=/=%= и некоторые другие.

Комментарии: 2
  1. TurboReptiloid

    fun factorial(n: Int): Double = if (n < 2) 1.0 else n * factorial(n — 1)

    "Обратите внимание, что тип результата функции указан как Double. Попробуйте понять, что произойдёт, если Double заменить на Int, а литерал 1.0 на 1."

    И всё таки, объясните дураку, почему функция перестает работать при Int?

    1. DegtjarenkoDW

      Просто используйте числа целые не 1.0 , а 1
      функция при обретет вид :
      fun factorial(n: Int): Int = if (n < 2) 1 else n * factorial(n — 1)

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