Lyx – Rekursion
Lyx unterstützt die volle rekursive Funktionsaufrufe. Da der Compiler die Standard Linux x86_64 SysV ABI implementiert, nutzt jeder Aufruf den Hardware-Stack, um Rücksprungadressen und lokale Variablen (Stack-Frames) sicher zu verwalten.
1) Grundlagen
Eine Funktion wird als rekursiv bezeichnet, wenn sie sich innerhalb ihres eigenen Körpers selbst aufruft. In Lyx erfordert eine saubere Rekursion zwei Bestandteile:
- Abbruchbedingung (Base Case): Ein Pfad, der keinen weiteren rekursiven Aufruf tätigt.
- Rekursionsschritt: Der Aufruf der Funktion mit veränderten Argumenten, die dem Base Case näher kommen.
Beispiel: Fakultät (Factorial)
Die mathematische Fakultät (n!) ist das klassische Beispiel für Rekursion:
fn Factorial(n: int64): int64 {
// 1. Abbruchbedingung
if (n <= 1) {
return 1;
}
// 2. Rekursiver Schritt
return n * Factorial(n - 1);
}
fn Main(): int64 {
var result: int64 := Factorial(5);
PrintStr("5! ist: ");
PrintInt(result); // Ausgabe: 120
return 0;
}
2) Technische Implementierung
Stack-Frame Management
Bei jedem rekursiven Aufruf erzeugt das Backend einen neuen Stack-Frame. Dies garantiert, dass jede Instanz der Funktion ihre eigenen lokalen Variablen besitzt.
| Schritt | Aktion im Backend | Register / Stack |
|---|---|---|
| Proloque | Sichern des alten Base-Pointers | `push rbp`, `mov rbp, rsp` |
| Arguments | Parameter für den nächsten Aufruf setzen | `mov rdi, [new_val]` |
| Call | Rücksprungadresse speichern & springen | `call Factorial` |
| Epilogue | Stack aufräumen & zurückkehren | `leave`, `ret` |
Status der Optimierung
| Feature | Status | Beschreibung |
|---|---|---|
| Hardware-Stack | ✅ Aktiv | Volle Nutzung des RSP-Registers. |
| Register-Safety | ✅ Aktiv | Caller-saved Register werden vor dem Aufruf gesichert. |
| Tail-Call Opt. | 🚧 Geplant | Aktuell wird für jeden Aufruf ein neuer Frame erzeugt (keine Ersetzung). |
3) Rekursion in Datenstrukturen (v0.1.7)
Mit der Einführung von Klassen können nun rekursive Methoden auf Objekten definiert werden, was ideal für Baumstrukturen oder verkettete Listen ist.
type Node = class {
Value: int64;
Next: Node?; // Nullable Pointer
fn PrintAll() {
PrintInt(self.Value);
PrintStr(" ");
// Rekursiver Aufruf auf das nächste Element
if (self.Next != null) {
self.Next?.PrintAll();
}
}
};
4) Wichtige Hinweise & Limits
- Stack Overflow: Da Lyx keinen unendlichen Stack hat, führt eine zu tiefe Rekursion (oder eine fehlende Abbruchbedingung) zu einem Segmentation Fault. Das Limit liegt unter Linux standardmäßig bei 8 MB.
- Performance: Rekursion ist elegant, aber für extrem einfache Aufgaben (wie bloßes Zählen) ist eine iterative Lösung (while-Schleife) effizienter, da der Overhead des Funktionsaufrufs entfällt.
Verwandte Themen:
