Рекурсия - непростая тема для понимания, и я не думаю, что смогу в полной мере передать ее здесь. Вместо этого я попытаюсь сосредоточиться на конкретном фрагменте кода, который у вас есть, и попытаться описать как интуитивное понимание того, почему решение работает, так и механизм вычисления результата кодом.
Приведенный здесь код решает следующую проблему: вы хотите узнать сумму всех целых чисел от a до b включительно. В вашем примере вам нужна сумма чисел от 2 до 5 включительно, т.е.
2 + 3 + 4 + 5
При попытке рекурсивного решения проблемы одним из первых шагов должно быть выяснение того, как разбить проблему на меньшую проблему с той же структурой. Предположим, вы хотите просуммировать числа от 2 до 5 включительно. Один из способов упростить это - заметить, что указанную выше сумму можно переписать как
2 + (3 + 4 + 5)
Здесь (3 + 4 + 5) оказывается суммой всех целых чисел от 3 до 5 включительно. Другими словами, если вы хотите узнать сумму всех целых чисел от 2 до 5, начните с вычисления суммы всех целых чисел от 3 до 5, а затем добавьте 2.
Так как же вычислить сумму всех целых чисел от 3 до 5 включительно? Что ж, эта сумма
3 + 4 + 5
который можно рассматривать как
3 + (4 + 5)
Здесь (4 + 5) - это сумма всех целых чисел от 4 до 5 включительно. Итак, если вы хотите вычислить сумму всех чисел от 3 до 5 включительно, вы должны вычислить сумму всех целых чисел от 4 до 5, а затем добавить 3.
Здесь есть шаблон! Если вы хотите вычислить сумму целых чисел от a до b включительно, вы можете сделать следующее. Сначала вычислите сумму целых чисел от a + 1 до b включительно. Затем добавьте к этой сумме. Вы заметите, что «вычислить сумму целых чисел от a + 1 до b включительно» - это примерно та же проблема, которую мы уже пытаемся решить, но с немного другими параметрами. Вместо того, чтобы вычислять от a до b включительно, мы вычисляем от a + 1 до b включительно. Это рекурсивный шаг - чтобы решить большую проблему («сумма от a до b, включительно»), мы уменьшаем проблему до меньшей версии самой себя («сумма от a + 1 до b, включительно»).
Если вы посмотрите на приведенный выше код, вы заметите, что в нем есть следующий шаг:
return a + sumInts(a + 1, b: b)
Этот код является просто переводом вышеупомянутой логики - если вы хотите суммировать от a до b включительно, начните с суммирования a + 1 до b включительно (это рекурсивный вызов sumInts), а затем добавьте a.
Конечно, сам по себе такой подход не работает. Например, как бы вы вычислили сумму всех целых чисел от 5 до 5 включительно? Итак, используя нашу текущую логику, вы бы вычислили сумму всех целых чисел от 6 до 5 включительно, а затем прибавили бы 5. Итак, как вы вычислите сумму всех целых чисел от 6 до 5 включительно? Итак, используя нашу текущую логику, вы должны вычислить сумму всех целых чисел от 7 до 5 включительно, а затем добавить 6. Вы заметите здесь проблему - это просто продолжается и продолжается!
При рекурсивном решении проблемы должен быть какой-то способ перестать упрощать проблему и вместо этого просто решить ее напрямую. Как правило, вы найдете простой случай, когда ответ может быть определен немедленно, а затем структурируете свое решение для решения простых случаев сразу же, когда они возникают. Обычно это называется базовым случаем или рекурсивным базисом .
Итак, каков базовый случай в этой конкретной проблеме? Когда вы суммируете целые числа от a до b включительно, если a оказывается больше b, то ответ равен 0 - в этом диапазоне нет чисел! Поэтому мы структурируем наше решение следующим образом:
- Если a> b, то ответ - 0.
- В противном случае (a ≤ b) получите ответ следующим образом:
- Вычислите сумму целых чисел от a + 1 до b.
- Добавьте, чтобы получить ответ.
Теперь сравните этот псевдокод с вашим реальным кодом:
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a + 1, b: b)
}
}
Обратите внимание, что между решением, описанным в псевдокоде, и настоящим кодом существует почти однозначное соответствие. Первый шаг - это базовый случай - в случае, если вы запрашиваете сумму для пустого диапазона чисел, вы получаете 0. В противном случае вычислите сумму между a + 1 и b, затем добавьте a.
Пока что я дал лишь общее представление о коде. Но у вас было два других очень хороших вопроса. Во-первых, почему это не всегда возвращает 0, учитывая, что функция требует вернуть 0, если a> b? Во-вторых, откуда на самом деле 14? Давайте посмотрим на них по очереди.
Давайте попробуем очень и очень простой случай. Что будет, если вы позвоните sumInts(6, 5)? В этом случае, прослеживая код, вы видите, что функция просто возвращает 0. Это правильно, чтобы - в диапазоне не было никаких чисел. А теперь попробуйте что-нибудь посложнее. Что происходит, когда вы звоните sumInts(5, 5)? Что ж, вот что происходит:
- Вы звоните
sumInts(5, 5). Мы попадаем вelseПопадаем ветку, которая возвращает значение `a + sumInts (6, 5).
- Чтобы
sumInts(5, 5)определить, что sumInts(6, 5)есть, нам нужно приостановить то, что мы делаем, и позвонить sumInts(6, 5).
sumInts(6, 5)называется. Он входит в ifфилиал и возвращается 0. Однако этот экземпляр sumIntsбыл вызван пользователем sumInts(5, 5), поэтому возвращаемое значение передается обратно sumInts(5, 5), а не вызывающей стороне верхнего уровня.
sumInts(5, 5)теперь можно вычислить, 5 + sumInts(6, 5)чтобы вернуться 5. Затем он возвращает его вызывающей стороне верхнего уровня.
Обратите внимание, как здесь сформировалось значение 5. Мы начали с одного активного звонка в sumInts. Это запустило еще один рекурсивный вызов, и значение, возвращенное этим вызовом, передало информацию обратно sumInts(5, 5). Вызов to, sumInts(5, 5)в свою очередь, произвел некоторые вычисления и вернул значение вызывающей стороне.
Если вы попробуете это сделать sumInts(4, 5), вот что произойдет:
sumInts(4, 5)пытается вернуться 4 + sumInts(5, 5). Для этого он звонит sumInts(5, 5).
sumInts(5, 5)пытается вернуться 5 + sumInts(6, 5). Для этого он звонит sumInts(6, 5).
sumInts(6, 5)возвращает 0 обратно в sumInts(5, 5).</li>
<li>sumInts (5, 5) now has a value forsumInts (6, 5) , namely 0. It then returns5 + 0 = 5`.
sumInts(4, 5)теперь имеет значение sumInts(5, 5), а именно 5. Затем он возвращается 4 + 5 = 9.
Другими словами, возвращаемое значение формируется путем суммирования значений по одному, каждый раз беря одно значение, возвращаемое конкретным рекурсивным вызовом, sumIntsи добавляя текущее значение a. Когда рекурсия достигает дна, самый глубокий вызов возвращает 0. Однако это значение не сразу выходит из цепочки рекурсивных вызовов; вместо этого он просто возвращает значение рекурсивному вызову на один уровень выше. Таким образом, каждый рекурсивный вызов просто добавляет еще одно число и возвращает его выше в цепочке, что завершается общим суммированием. В качестве упражнения попробуйте отследить это дляsumInts(2, 5) , с чего вы и хотели начать.
Надеюсь это поможет!