Курс Raku / Функційне, конкурентне, реактивне та веб-програмування / Функційне програмування / Рекурсія
Рекурсія з multi-підпрограмами
Досі базовий випадок був рядком усередині підпрограми —
тернарним оператором або раннім return, що перевіряє
аргумент. Raku пропонує виразніший спосіб записати те саме. Оскільки
підпрограма може мати кілька кандидатів multi, ви можете
дати базовому випадку й рекурсивному крокові власні окремі
підпрограми й дозволити множинній диспетчеризації вибирати
потрібного для кожного виклику.
Пригадаймо факторіал. З multi його два випадки стають
двома підпрограмами:
multi fact(0) { 1 }
multi fact($n) { $n * fact($n - 1) }
say fact(5); # 120Перший кандидат підходить лише тоді, коли аргумент дорівнює точно
0 — цей літерал у сигнатурі і є базовим випадком.
Кожен інший виклик іде до другого кандидата, який множить і рекурсує.
Коли fact($n - 1) нарешті досягає 0,
диспетчеризація перемикається на першого кандидата, і ланцюжок викликів
розкручується назад. Базовий випадок більше не є гілкою, похованою в
тілі; це підпрограма, що існує заради одного-єдиного значення.
Чому 0, а не 1? Бо кожен крок віднімає
одиницю, тож будь-яке початкове число зрештою потрапляє точно на
0, а 0! за означенням дорівнює 1
— отже, саме на 0 спуск справді завершується. Літеральний
кандидат підходить для одного точного значення, тож база
multi fact(1) обчислила б fact(1) правильно,
але залишила б fact(0) провалюватися до
multi fact($n) і рекурсувати за нуль без кінця. Зупинка на
0 тримає підпрограму правильною для кожного невід’ємного
цілого числа, включно з fact(0).
Це читається особливо добре, коли базових випадків більше ніж один. Числам Фібоначчі їх потрібно два:
multi fib(0) { 0 }
multi fib(1) { 1 }
multi fib($n) { fib($n - 1) + fib($n - 2) }
say fib(10); # 55Кожен базовий випадок є власним однорядковим кандидатом, а рекурсивний кандидат обробляє все решта — без вкладених умов.
Літерал на кшталт 0 підходить лише для цього точного
значення. Коли базовий випадок покриває діапазон —
«$n дорівнює 1 або менше», — використовуйте
натомість обмеження where:
multi fact($n where * <= 1) { 1 }
multi fact($n) { $n * fact($n - 1) }
say fact(6); # 720Обмежений кандидат є конкретнішим, тож Raku пробує його першим;
кандидат зі звичайним $n ловить усе решта.
Та сама дисципліна, що й раніше, залишається чинною: кожен
рекурсивний шлях має досягти кандидата з базовим випадком. Факторіал із
літеральним 0, наприклад, безпечний лише для невід’ємних
цілих чисел — fact(-1) переступив би 0 і
рекурсував би вічно, бо жоден кандидат ніколи не підійшов би. Рознесення
випадків по multi-підпрограмах не скасовує потреби в
базовому випадку; воно лише дає цьому базовому випадку ім’я та власну
домівку.
Практика
Виконайте 1 тест за змістом цієї теми.
Навігація курсом
← Тест — Базовий випадок | Тест — Рекурсія з multi-підпрограмами →
💪 Або перейдіть одразу до вправ цього розділу.