Du schreibst Funktionen, die sich selbst aufrufen: die Fakultät, die Summe einer Liste, Fibonacci, die Türme von Hanoi und alle Reihenfolgen eines Textes. Die Prüfung sieht im Code nach, ob deine Funktion sich wirklich selbst aufruft.
Eine rekursive Funktion löst ein Problem, indem sie dasselbe Problem eine Nummer kleiner löst, mit einem Aufruf von sich selbst, und daraus ihr Ergebnis baut. Damit das aufhört, gibt es einen Fall, in dem sie sofort fertig ist. Diese Funktion zählt, wie oft man eine Zahl halbieren kann, bis 1 übrig ist:
def halvings(n) return 0 if n <= 1 # sofort fertig 1 + halvings(n / 2) # dasselbe, eine Nummer kleiner end puts halvings(20) # 4: 20, 10, 5, 2, 1
def halvings(n): if n <= 1: # sofort fertig return 0 return 1 + halvings(n // 2) # dasselbe, eine Nummer kleiner print(halvings(20)) # 4: 20, 10, 5, 2, 1
function halvings(n) { if (n <= 1) { // sofort fertig return 0; } return 1 + halvings(Math.floor(n / 2)); // dasselbe, eine Nummer kleiner } console.log(halvings(20)); // 4: 20, 10, 5, 2, 1
halvings(20) wartet auf halvings(10), das auf halvings(5), bis
halvings(1) sofort 0 zurückgibt; dann rechnet jeder Aufruf seins dazu,
von innen nach außen. Fehlt der Fall, der aufhört, ruft sich die Funktion
ohne Ende auf, und das Programm bricht ab, weil der Speicher für die
wartenden Aufrufe voll ist.
Die Fakultät kennst du aus den Schleifen: 5! = 5 · 4 · 3 · 2 · 1. Es geht auch so: 5! = 5 · 4!, und 0! = 1. Schreib factorial(n) genau so: Die Funktion ruft sich selbst mit n - 1 auf, bis n 0 ist. factorial(5) ergibt 120. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Zwei Fälle: Wann bist du sofort fertig? Und wie hilft dir factorial(n - 1) sonst?
Ein Gerüst:
def factorial(n) return 1 if n == 0 # n mal die Fakultät von n - 1 end
Die Fakultät kennst du aus den Schleifen: 5! = 5 · 4 · 3 · 2 · 1. Es geht auch so: 5! = 5 · 4!, und 0! = 1. Schreib factorial(n) genau so: Die Funktion ruft sich selbst mit n - 1 auf, bis n 0 ist. factorial(5) ergibt 120. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Zwei Fälle: Wann bist du sofort fertig? Und wie hilft dir factorial(n - 1) sonst?
Ein Gerüst:
def factorial(n): if n == 0: return 1 # n mal die Fakultät von n - 1
Die Fakultät kennst du aus den Schleifen: 5! = 5 · 4 · 3 · 2 · 1. Es geht auch so: 5! = 5 · 4!, und 0! = 1. Schreib factorial(n) genau so: Die Funktion ruft sich selbst mit n - 1 auf, bis n 0 ist. factorial(5) ergibt 120. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Zwei Fälle: Wann bist du sofort fertig? Und wie hilft dir factorial(n - 1) sonst?
Ein Gerüst:
function factorial(n) { if (n === 0) { return 1; } // n mal die Fakultät von n - 1 }
Die Summe einer Liste ist ihre erste Zahl plus die Summe des Rests; die leere Liste hat die Summe 0. Schreib total(numbers) so: total([3, 4, 5]) ergibt 12. numbers[1..] ist der Rest der Liste. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wann bist du sofort fertig? Bei der leeren Liste, numbers.empty?.
Sonst: die erste Zahl, numbers[0], plus total vom Rest.
Die Summe einer Liste ist ihre erste Zahl plus die Summe des Rests; die leere Liste hat die Summe 0. Schreib total(numbers) so: total([3, 4, 5]) ergibt 12. numbers[1:] ist der Rest der Liste. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wann bist du sofort fertig? Bei der leeren Liste, not numbers.
Sonst: die erste Zahl, numbers[0], plus total vom Rest.
Die Summe einer Liste ist ihre erste Zahl plus die Summe des Rests; die leere Liste hat die Summe 0. Schreib total(numbers) so: total([3, 4, 5]) ergibt 12. numbers.slice(1) ist der Rest der Liste. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wann bist du sofort fertig? Bei der leeren Liste, numbers.length === 0.
Sonst: die erste Zahl, numbers[0], plus total vom Rest.
Schreib countdown(n), die die Liste von n herunter bis 1 zurückgibt: countdown(3) ergibt [3, 2, 1], countdown(0) ergibt []. Der Countdown von 3 ist die 3 und dann der Countdown von 2. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Eine Liste aus einer Zahl ist [n], und + hängt zwei Listen aneinander.
Schreib countdown(n), die die Liste von n herunter bis 1 zurückgibt: countdown(3) ergibt [3, 2, 1], countdown(0) ergibt []. Der Countdown von 3 ist die 3 und dann der Countdown von 2. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Eine Liste aus einer Zahl ist [n], und + hängt zwei Listen aneinander.
Schreib countdown(n), die die Liste von n herunter bis 1 zurückgibt: countdown(3) ergibt [3, 2, 1], countdown(0) ergibt []. Der Countdown von 3 ist die 3 und dann der Countdown von 2. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Eine Liste aus einer Zahl ist [n], und concat hängt zwei Listen aneinander.
Schreib text_length(text), die die Zeichen eines Textes zählt, ohne length und size; text[1..] ist der Text ohne sein erstes Zeichen: Ein leerer Text hat die Länge 0, jeder andere ist um eins länger als der Text ohne sein erstes Zeichen. text_length('Hallo') ergibt 5. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wann bist du sofort fertig? Beim leeren Text, ''.
Sonst: 1 plus die Länge des Rests.
Schreib text_length(text), die die Zeichen eines Textes zählt, ohne len; text[1:] ist der Text ohne sein erstes Zeichen: Ein leerer Text hat die Länge 0, jeder andere ist um eins länger als der Text ohne sein erstes Zeichen. text_length('Hallo') ergibt 5. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wann bist du sofort fertig? Beim leeren Text, ''.
Sonst: 1 plus die Länge des Rests.
Schreib textLength(text), die die Zeichen eines Textes zählt, ohne length; text.slice(1) ist der Text ohne sein erstes Zeichen: Ein leerer Text hat die Länge 0, jeder andere ist um eins länger als der Text ohne sein erstes Zeichen. textLength('Hallo') ergibt 5. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wann bist du sofort fertig? Beim leeren Text, ''.
Sonst: 1 plus die Länge des Rests.
Schreib power(base, exponent) für einen Exponenten ab 0: power(2, 10) ergibt 1024. Jede Zahl hoch 0 ist 1, und base hoch n ist base mal base hoch n - 1. Ohne ** und pow. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wie bei der Fakultät: ein Fall, in dem du sofort fertig bist, und einer, der sich selbst mit einer kleineren Zahl aufruft.
Schreib power(base, exponent) für einen Exponenten ab 0: power(2, 10) ergibt 1024. Jede Zahl hoch 0 ist 1, und base hoch n ist base mal base hoch n - 1. Ohne ** und pow. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wie bei der Fakultät: ein Fall, in dem du sofort fertig bist, und einer, der sich selbst mit einer kleineren Zahl aufruft.
Schreib power(base, exponent) für einen Exponenten ab 0: power(2, 10) ergibt 1024. Jede Zahl hoch 0 ist 1, und base hoch n ist base mal base hoch n - 1. Ohne ** und Math.pow. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wie bei der Fakultät: ein Fall, in dem du sofort fertig bist, und einer, der sich selbst mit einer kleineren Zahl aufruft.
Jede Fibonacci-Zahl ist die Summe der beiden davor; die nullte ist 0, die erste 1. Schreib fibonacci(n) genau so, mit zwei Aufrufen von sich selbst: fibonacci(10) ergibt 55. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Zwei Zahlen stehen fest, fibonacci(0) und fibonacci(1). Jede andere ist fibonacci(n - 1) + fibonacci(n - 2).
Jede Fibonacci-Zahl ist die Summe der beiden davor; die nullte ist 0, die erste 1. Schreib fibonacci(n) genau so, mit zwei Aufrufen von sich selbst: fibonacci(10) ergibt 55. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Zwei Zahlen stehen fest, fibonacci(0) und fibonacci(1). Jede andere ist fibonacci(n - 1) + fibonacci(n - 2).
Jede Fibonacci-Zahl ist die Summe der beiden davor; die nullte ist 0, die erste 1. Schreib fibonacci(n) genau so, mit zwei Aufrufen von sich selbst: fibonacci(10) ergibt 55. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Zwei Zahlen stehen fest, fibonacci(0) und fibonacci(1). Jede andere ist fibonacci(n - 1) + fibonacci(n - 2).
fibonacci
mit 25, 30 und 35 auf und schau, wie lange es jeweils dauert. Warum wird es
so schnell langsamer? Zähl einmal, wie oft fibonacci(2)
aufgerufen wird, wenn du fibonacci(6) ausrechnest.
Ein Text ist ein Palindrom, wenn sein erstes und sein letztes Zeichen gleich sind und das, was dazwischen steht, wieder ein Palindrom ist; ein Text mit weniger als zwei Zeichen ist immer eins. Schreib palindrome(text) so, ohne den Text umzudrehen: palindrome('rentner') ergibt true. Die Texte sind klein geschrieben. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
text[0] ist das erste Zeichen, text[-1] das letzte, text[1...-1] das, was dazwischen steht.
Ein Text ist ein Palindrom, wenn sein erstes und sein letztes Zeichen gleich sind und das, was dazwischen steht, wieder ein Palindrom ist; ein Text mit weniger als zwei Zeichen ist immer eins. Schreib palindrome(text) so, ohne den Text umzudrehen: palindrome('rentner') ergibt True. Die Texte sind klein geschrieben. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
text[0] ist das erste Zeichen, text[-1] das letzte, text[1:-1] das, was dazwischen steht.
Ein Text ist ein Palindrom, wenn sein erstes und sein letztes Zeichen gleich sind und das, was dazwischen steht, wieder ein Palindrom ist; ein Text mit weniger als zwei Zeichen ist immer eins. Schreib palindrome(text) so, ohne den Text umzudrehen: palindrome('rentner') ergibt true. Die Texte sind klein geschrieben. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
text[0] ist das erste Zeichen, text[text.length - 1] das letzte, text.slice(1, -1) das, was dazwischen steht.
Schreib count_char(text, char), die zählt, wie oft das Zeichen char im Text steht: count_char('banane', 'n') ergibt 2. Wie bei der Länge, nur zählt das erste Zeichen nur dann, wenn es char ist. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wie text_length, nur zählt das erste Zeichen 1 oder 0.
Schreib count_char(text, char), die zählt, wie oft das Zeichen char im Text steht: count_char('banane', 'n') ergibt 2. Wie bei der Länge, nur zählt das erste Zeichen nur dann, wenn es char ist. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wie text_length, nur zählt das erste Zeichen 1 oder 0.
Schreib countChar(text, char), die zählt, wie oft das Zeichen char im Text steht: countChar('banane', 'n') ergibt 2. Wie bei der Länge, nur zählt das erste Zeichen nur dann, wenn es char ist. Ohne Schleife: Die Funktion ruft sich selbst auf; die Prüfung sieht nach.
Wie textLength, nur zählt das erste Zeichen 1 oder 0.
Auf dem Stab source liegen n Scheiben, die größte unten. Sie sollen auf den Stab target; eine Scheibe nach der anderen, nie eine größere auf eine kleinere, und spare hilft. Schreib hanoi(n, source, target, spare), die die Züge als Liste zurückgibt, jeden Zug als Paar [von, nach]: hanoi(2, 'A', 'C', 'B') ergibt [['A', 'B'], ['A', 'C'], ['B', 'C']]. Der Trick: Die oberen n - 1 Scheiben sind ein kleinerer Turm.
Drei Schritte: den Turm aus n - 1 Scheiben aus dem Weg räumen, die größte Scheibe ziehen, den kleineren Turm wieder darauf.
Wohin kommt der kleinere Turm zuerst, und welcher Stab hilft ihm dabei? Jeder Schritt ist ein Aufruf von hanoi mit anderen Stäben.
Auf dem Stab source liegen n Scheiben, die größte unten. Sie sollen auf den Stab target; eine Scheibe nach der anderen, nie eine größere auf eine kleinere, und spare hilft. Schreib hanoi(n, source, target, spare), die die Züge als Liste zurückgibt, jeden Zug als Paar [von, nach]: hanoi(2, 'A', 'C', 'B') ergibt [['A', 'B'], ['A', 'C'], ['B', 'C']]. Der Trick: Die oberen n - 1 Scheiben sind ein kleinerer Turm.
Drei Schritte: den Turm aus n - 1 Scheiben aus dem Weg räumen, die größte Scheibe ziehen, den kleineren Turm wieder darauf.
Wohin kommt der kleinere Turm zuerst, und welcher Stab hilft ihm dabei? Jeder Schritt ist ein Aufruf von hanoi mit anderen Stäben.
Auf dem Stab source liegen n Scheiben, die größte unten. Sie sollen auf den Stab target; eine Scheibe nach der anderen, nie eine größere auf eine kleinere, und spare hilft. Schreib hanoi(n, source, target, spare), die die Züge als Liste zurückgibt, jeden Zug als Paar [von, nach]: hanoi(2, 'A', 'C', 'B') ergibt [['A', 'B'], ['A', 'C'], ['B', 'C']]. Der Trick: Die oberen n - 1 Scheiben sind ein kleinerer Turm.
Drei Schritte: den Turm aus n - 1 Scheiben aus dem Weg räumen, die größte Scheibe ziehen, den kleineren Turm wieder darauf.
Wohin kommt der kleinere Turm zuerst, und welcher Stab hilft ihm dabei? Jeder Schritt ist ein Aufruf von hanoi mit anderen Stäben.
In einer Liste können wieder Listen stehen, beliebig tief: [1, [2, [3, 4]], 5]. Schreib flatten(items), die alle Zahlen in einer flachen Liste zurückgibt, in ihrer Reihenfolge: [1, 2, 3, 4, 5]. Ohne flatten.
Geh die Elemente durch. item.is_a?(Array) fragt, ob ein Element selbst eine Liste ist.
Ist es eine, machst du sie mit flatten flach, bevor sie ins Ergebnis kommt.
In einer Liste können wieder Listen stehen, beliebig tief: [1, [2, [3, 4]], 5]. Schreib flatten(items), die alle Zahlen in einer flachen Liste zurückgibt, in ihrer Reihenfolge: [1, 2, 3, 4, 5]. Ohne eine fertige Funktion.
Geh die Elemente durch. isinstance(item, list) fragt, ob ein Element selbst eine Liste ist.
Ist es eine, machst du sie mit flatten flach, bevor sie ins Ergebnis kommt.
In einer Liste können wieder Listen stehen, beliebig tief: [1, [2, [3, 4]], 5]. Schreib flatten(items), die alle Zahlen in einer flachen Liste zurückgibt, in ihrer Reihenfolge: [1, 2, 3, 4, 5]. Ohne flat.
Geh die Elemente durch. Array.isArray(item) fragt, ob ein Element selbst eine Liste ist.
Ist es eine, machst du sie mit flatten flach, bevor sie ins Ergebnis kommt.
Schreib permutations(text), die alle Reihenfolgen der Zeichen zurückgibt: permutations('abc') ergibt ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']. Der Reihe nach kommt jedes Zeichen einmal nach vorn, und dahinter stehen alle Reihenfolgen der übrigen. Der leere Text hat eine Reihenfolge, ['']. Ohne permutation.
Für jedes Zeichen: Nimm es heraus, text[0...i] + text[(i + 1)..] ist der Rest, und setz es vor jede Reihenfolge des Rests.
Eine Schleife über die Zeichen darfst du hier benutzen; die Rekursion steckt in permutations(rest).
Schreib permutations(text), die alle Reihenfolgen der Zeichen zurückgibt: permutations('abc') ergibt ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']. Der Reihe nach kommt jedes Zeichen einmal nach vorn, und dahinter stehen alle Reihenfolgen der übrigen. Der leere Text hat eine Reihenfolge, ['']. Ohne itertools.permutations.
Für jedes Zeichen: Nimm es heraus, text[:i] + text[i + 1:] ist der Rest, und setz es vor jede Reihenfolge des Rests.
Eine Schleife über die Zeichen darfst du hier benutzen; die Rekursion steckt in permutations(rest).
Schreib permutations(text), die alle Reihenfolgen der Zeichen zurückgibt: permutations('abc') ergibt ['abc', 'acb', 'bac', 'bca', 'cab', 'cba']. Der Reihe nach kommt jedes Zeichen einmal nach vorn, und dahinter stehen alle Reihenfolgen der übrigen. Der leere Text hat eine Reihenfolge, ['']. Ohne eine fertige Funktion.
Für jedes Zeichen: Nimm es heraus, text.slice(0, i) + text.slice(i + 1) ist der Rest, und setz es vor jede Reihenfolge des Rests.
Eine Schleife über die Zeichen darfst du hier benutzen; die Rekursion steckt in permutations(rest).
Ein Extra für Schnelle: Die Seite ist ohne dieses Kapitel vollständig.
Zwei Katas, die nicht mitzählen. Bei der ersten zählt auch, wie schnell dein Code ist.
Deine rekursive Fibonacci-Funktion rechnet dieselben Zahlen wieder und wieder aus: fibonacci(70) dauert Jahre. Schreib fast_fibonacci(n), rekursiv wie vorher, aber mit einem Gedächtnis: einem Hash, in dem jede Zahl steht, sobald sie einmal ausgerechnet ist. fast_fibonacci(70) ergibt 190392490709135, in weniger als drei Sekunden.
Ein Hash außerhalb der Funktion, MEMO = {}, merkt sich die Zahlen. Steht n schon darin, gibst du sie gleich zurück.
MEMO[n] ||= … rechnet nur, wenn MEMO[n] noch fehlt, und schreibt das Ergebnis hinein.
Deine rekursive Fibonacci-Funktion rechnet dieselben Zahlen wieder und wieder aus: fibonacci(70) dauert Jahre. Schreib fast_fibonacci(n), rekursiv wie vorher, aber mit einem Gedächtnis: einem Dictionary, in dem jede Zahl steht, sobald sie einmal ausgerechnet ist. fast_fibonacci(70) ergibt 190392490709135, in weniger als drei Sekunden.
Ein Dictionary außerhalb der Funktion, memo = {}, merkt sich die Zahlen. Steht n schon darin, gibst du sie gleich zurück.
Erst nachsehen (n in memo), sonst rechnen und hineinschreiben.
Deine rekursive Fibonacci-Funktion rechnet dieselben Zahlen wieder und wieder aus: fibonacci(70) dauert Jahre. Schreib fastFibonacci(n), rekursiv wie vorher, aber mit einem Gedächtnis: einem Objekt, in dem jede Zahl steht, sobald sie einmal ausgerechnet ist. fastFibonacci(70) ergibt 190392490709135, in weniger als drei Sekunden.
Ein Objekt außerhalb der Funktion, const memo = {};, merkt sich die Zahlen. Steht n schon darin, gibst du sie gleich zurück.
Erst nachsehen (n in memo), sonst rechnen und hineinschreiben.
Schreib subset_sum(numbers, target), die true zurückgibt, wenn einige der Zahlen zusammen genau target ergeben, jede höchstens einmal, und sonst false: subset_sum([3, 34, 4, 12, 5, 2], 9) ergibt true (4 + 5). Für die erste Zahl gibt es zwei Möglichkeiten: Sie ist dabei, oder nicht. Die Zahlen sind größer als 0.
Zwei Fälle, in denen du sofort fertig bist: target ist 0 (dann ja), oder es sind keine Zahlen mehr übrig (dann nein).
Sonst: mit der ersten Zahl, subset_sum(rest, target - numbers[0]), oder ohne sie, subset_sum(rest, target). Geht eins davon, geht es.
Schreib subset_sum(numbers, target), die True zurückgibt, wenn einige der Zahlen zusammen genau target ergeben, jede höchstens einmal, und sonst False: subset_sum([3, 34, 4, 12, 5, 2], 9) ergibt True (4 + 5). Für die erste Zahl gibt es zwei Möglichkeiten: Sie ist dabei, oder nicht. Die Zahlen sind größer als 0.
Zwei Fälle, in denen du sofort fertig bist: target ist 0 (dann ja), oder es sind keine Zahlen mehr übrig (dann nein).
Sonst: mit der ersten Zahl, subset_sum(rest, target - numbers[0]), oder ohne sie, subset_sum(rest, target). Geht eins davon, geht es.
Schreib subsetSum(numbers, target), die true zurückgibt, wenn einige der Zahlen zusammen genau target ergeben, jede höchstens einmal, und sonst false: subsetSum([3, 34, 4, 12, 5, 2], 9) ergibt true (4 + 5). Für die erste Zahl gibt es zwei Möglichkeiten: Sie ist dabei, oder nicht. Die Zahlen sind größer als 0.
Zwei Fälle, in denen du sofort fertig bist: target ist 0 (dann ja), oder es sind keine Zahlen mehr übrig (dann nein).
Sonst: mit der ersten Zahl, subsetSum(rest, target - numbers[0]), oder ohne sie, subsetSum(rest, target). Geht eins davon, geht es.