Почти все реальные задачи требуют для своего решения не только ветвлений, но и многократного повторения однотипных действий. Простым примером такой задачи является вычисление факториала 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)