Курс по 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-подпрограми →
💪 Или преминете направо към упражненията в този
раздел.