Почти все реальные задачи требуют для своего решения не только ветвлений, но и многократного повторения однотипных действий. Простым примером такой задачи является вычисление факториала 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. Аналогично ему работают операторы +=, -=, /=, %= и некоторые другие.
fun factorial(n: Int): Double = if (n < 2) 1.0 else n * factorial(n — 1)
"Обратите внимание, что тип результата функции указан как Double. Попробуйте понять, что произойдёт, если Double заменить на Int, а литерал 1.0 на 1."
И всё таки, объясните дураку, почему функция перестает работать при Int?
Просто используйте числа целые не 1.0 , а 1
функция при обретет вид :
fun factorial(n: Int): Int = if (n < 2) 1 else n * factorial(n — 1)