====== Funktionen (vertieft) ====== Funktionen sind der zentrale Baustein jedes Lyx-Programms. Diese Seite geht über die Grunddeklaration hinaus und behandelt Tupel-Rückgaben, anonyme Funktionen, Funktionstypen, Higher-Order Functions und den Pipe-Operator. → Einführung: [[lyx_-_programmiersprache:erste-schritte|Erste Schritte]] · Syntax: [[lyx_-_programmiersprache:syntax|Sprachsyntax]] · Methoden: [[lyx_-_programmiersprache:oop|OOP & Klassen]] · Generics: [[lyx_-_programmiersprache:generics-traits|Generics & Traits]] ---- ===== 1. Grunddeklaration ===== fn Add(a: int64, b: int64): int64 { return a + b; } * Rückgabetyp steht nach dem Doppelpunkt hinter der Parameterliste. * Kein Rückgabewert: Rückgabetyp weglassen (implizit ''void''); ''return'' ohne Wert oder ganz weglassen. * ''pub'' macht eine Funktion aus anderen Units importierbar. // Kein Rückgabewert fn Log(msg: pchar) { PrintStr(msg); PrintStr("\n"); } // Öffentlich sichtbar (exportiert) pub fn GetVersion(): pchar { return "1.0.0"; } Alle Funktionen ohne ''pub'' sind privat — sie sind nur innerhalb derselben Unit sichtbar. Das ist die strenge Voreinstellung; explizites Exportieren verhindert unkontrollierte Kopplung zwischen Modulen. ---- ===== 2. Tupel-Rückgabe — mehrere Werte ===== Funktionen können mehr als einen Wert zurückgeben, indem der Rückgabetyp als Tupel angegeben wird. Das ersetzt Out-Parameter und verhindert, dass Fehlerinformationen über Ausnahmen transportiert werden müssen. ==== Syntax ==== fn Divide(a: int64, b: int64): (int64, bool) { if (b = 0) { return (0, false); } return (a / b, true); } Rückgabetyp: ''(int64, bool)'' — ein Tupel aus zwei Typen.\\ Rückgabewert: ''(Ausdruck1, Ausdruck2)'' — beide Werte in runden Klammern, kommagetrennt. ==== Destrukturierung beim Aufruf ==== fn main(): int64 { var (result, ok): (int64, bool) := Divide(10, 3); if (ok) { PrintInt(result); // 3 PrintStr("\n"); } // Kurzform ohne explizite Typangabe — Typen werden inferiert var (q, ok2) := Divide(7, 0); if (ok2 = false) { PrintStr("Division durch null\n"); } return 0; } ==== Dreiwertige Ergebnisse ==== Tupel-Rückgaben sind nicht auf zwei Elemente beschränkt: fn ParseCoord(input: pchar): (f64, f64, bool) { // ... Parser-Logik ... return (lat, lon, true); } fn main(): int64 { var (lat, lon, valid) := ParseCoord("48.137,11.576"); if (valid) { PrintF64(lat); PrintStr(", "); PrintF64(lon); PrintStr("\n"); } return 0; } ==== Tupel-Rückgabe vs. struct ==== ^ Situation ^ Empfehlung ^ | Zwei eng zusammengehörige Werte (Ergebnis + Fehlerflag) | Tupel-Rückgabe | | Mehr als drei Werte | Struct definieren, als Wert zurückgeben | | Rückgabe soll benannt und dokumentiert sein | Struct — Feldnamen machen den Code lesbarer | | Temporäres Ergebnis in einer Funktion | Tupel — weniger Overhead als ein Typ zu definieren | ---- ===== 3. Anonyme Funktionen ===== Lyx erlaubt Funktionsliterale — also Funktionen ohne Namen, die direkt als Ausdruck geschrieben werden. Sie folgen der gleichen Syntax wie benannte Funktionen, tragen aber keinen Bezeichner. ==== Syntax ==== fn(param1: Typ1, param2: Typ2): RückgabeTyp { return ...; } ==== Inline als Argument ==== import std.io; fn Apply(value: int64, fn transform: fn(int64): int64): int64 { return transform(value); } fn main(): int64 { var result: int64 := Apply(21, fn(x: int64): int64 { return x * 2; }); PrintInt(result); // 42 PrintStr("\n"); return 0; } ==== Als Variable speichern ==== Eine anonyme Funktion kann mit ''let'' oder ''var'' gebunden werden: let double: fn(int64): int64 := fn(x: int64): int64 { return x * 2; }; let greet: fn(pchar): void := fn(name: pchar): void { PrintStr("Hallo, "); PrintStr(name); PrintStr("\n"); }; fn main(): int64 { PrintInt(double(21)); // 42 PrintStr("\n"); greet("Lyx"); // Hallo, Lyx return 0; } > **Hinweis:** Anonyme Funktionen in Lyx sind **keine Closures** im Sinne von funktionalen Sprachen — sie erfassen keine Variablen aus dem umgebenden Scope. Wenn Zustand übergeben werden muss, wird er explizit als Parameter mitgegeben. Das vermeidet versteckte Abhängigkeiten und ist für Safety-kritischen Code (DO-178C, MISRA) zwingend. ---- ===== 4. Funktionstypen ===== Ein Funktionstyp beschreibt die Signatur einer Funktion — welche Parameter sie nimmt und welchen Typ sie zurückgibt. Mit ''type'' lässt sich ein benannter Alias für einen Funktionstyp anlegen. ==== Syntax ==== type TypName = fn(Param1Typ, Param2Typ): RückgabeTyp; ==== Beispiele ==== // Vergleichsfunktion für zwei int64-Werte — gibt Ordnung zurück type Comparator = fn(a: int64, b: int64): int32; // Transformationsfunktion: int64 → int64 type Transformer = fn(value: int64): int64; // Ereignis-Handler ohne Rückgabewert type EventHandler = fn(event_id: int32, data: int64): void; Benannte Typen verbessern die Lesbarkeit von Parameterlisten erheblich: // Ohne Alias — schwer zu lesen fn Sort(arr: int64, n: int64, cmp: fn(int64, int64): int32): void { ... } // Mit Alias — sofort klar type Comparator = fn(a: int64, b: int64): int32; fn Sort(arr: int64, n: int64, cmp: Comparator): void { ... } Benannte Funktionen können überall dort übergeben werden, wo ein passender Funktionstyp erwartet wird — einfach durch Angabe des Namens ohne ''()'': fn CompareAsc(a: int64, b: int64): int32 { if (a < b) { return -1i32; } if (a > b) { return 1i32; } return 0i32; } fn CompareDesc(a: int64, b: int64): int32 { return CompareAsc(b, a); // Reihenfolge getauscht } fn main(): int64 { var cmp: Comparator := CompareAsc; // Funktion als Wert PrintInt(cmp(3, 7) as int64); // -1 PrintStr("\n"); cmp := CompareDesc; PrintInt(cmp(3, 7) as int64); // 1 PrintStr("\n"); return 0; } ---- ===== 5. Higher-Order Functions ===== Eine Higher-Order Function (HOF) ist eine Funktion, die eine andere Funktion als Parameter entgegennimmt oder eine Funktion als Ergebnis zurückgibt. ==== Funktion als Parameter ==== Der Parameter wird mit dem Präfix ''fn'' und dem Funktionstyp deklariert: fn paramName: fn(ArgTyp): RückgabeTyp Vollständiges Beispiel: import std.io; // Apply: wendet transform auf value an und gibt das Ergebnis zurück fn Apply(value: int64, fn transform: fn(int64): int64): int64 { return transform(value); } // Für jeden Index in [0, n) callback aufrufen fn ForEach(n: int64, fn callback: fn(int64): void): void { var i: int64 := 0; while (i < n) limit(1000000) { callback(i); i := i + 1; } } // Akkumuliert f(i) für alle i in [0, n) fn Accumulate(n: int64, initial: int64, fn fold: fn(int64, int64): int64): int64 { var acc: int64 := initial; var i: int64 := 0; while (i < n) limit(1000000) { acc := fold(acc, i); i := i + 1; } return acc; } fn Square(x: int64): int64 { return x * x; } fn PrintIdx(i: int64): void { PrintInt(i); PrintStr(" "); } fn Sum(acc: int64, val: int64): int64 { return acc + val; } fn main(): int64 { PrintInt(Apply(7, Square)); // 49 PrintStr("\n"); ForEach(5, PrintIdx); // 0 1 2 3 4 PrintStr("\n"); PrintInt(Accumulate(5, 0, Sum)); // 0+1+2+3+4 = 10 PrintStr("\n"); return 0; } ==== HOF mit Generics ==== Funktionsparameter lassen sich mit Generics kombinieren: fn MapApply(value: In, fn transform: fn(In): Out): Out { return transform(value); } fn main(): int64 { var doubled: int64 := MapApply(21, fn(x: int64): int64 { return x * 2; }); PrintInt(doubled); // 42 PrintStr("\n"); return 0; } → Ausführliche Generics-Syntax: [[lyx_-_programmiersprache:generics-traits|Generics & Traits]] ==== Dispatch-Tabelle ==== Funktionstypen lassen sich in Arrays speichern — das ist das Lyx-Äquivalent einer vtable ohne Klassen: type Handler = fn(data: int64): void; fn OnConnect(data: int64): void { PrintStr("connect\n"); } fn OnDisconnect(data: int64): void { PrintStr("disconnect\n"); } fn OnMessage(data: int64): void { PrintStr("message\n"); } con CONNECT: int64 := 0; con DISCONNECT: int64 := 1; con MESSAGE: int64 := 2; fn main(): int64 { var dispatch: [3]Handler := [OnConnect, OnDisconnect, OnMessage]; var event: int64 := MESSAGE; if (event >= 0 & event < 3) { dispatch[event](0); // message } return 0; } ==== Funktionen an Threads übergeben ==== ''std.thread'' erwartet den Funktionszeiger als ''int64''. Das ist die einzige Ausnahme von der typisierten Übergabe — es liegt an der C-POSIX-Schnittstelle unter der Haube: import std.thread; import std.io; fn Worker(arg: int64): int64 { PrintStr("Thread läuft\n"); return 0; } fn main(): int64 { var t: Thread := ThreadCreate(Worker as int64, 0); ThreadJoin(t); return 0; } → Vollständige Thread-Dokumentation: [[lyx_-_programmiersprache:threads-nebenlaeufikeit|Threads & Nebenläufigkeit]] ---- ===== 6. Pipe-Operator ===== Der Pipe-Operator ''|>'' leitet den Wert auf der linken Seite als **erstes Argument** an die Funktion auf der rechten Seite weiter. Er macht Transformationsketten ohne verschachtelte Klammern lesbar. ==== Syntax ==== wert |> Funktion(weitereArgumente) // entspricht: Funktion(wert, weitereArgumente) ==== Beispiel: Wert-Transformation ==== import std.math; import std.io; fn Clamp(val: f64, lo: f64, hi: f64): f64 { if (val < lo) { return lo; } if (val > hi) { return hi; } return val; } fn main(): int64 { // Ohne Pipe — schwer zu lesen bei tiefer Schachtelung var r1: f64 := Round(Clamp(Abs(-3.7), 0.0, 3.0)); // Mit Pipe — Datenfluss von links nach rechts var r2: f64 := -3.7 |> Abs() |> Clamp(0.0, 3.0) |> Round(); PrintF64(r1); // 4.0 → Clamp → 3.0 → Round → 3.0 PrintStr("\n"); PrintF64(r2); // identisch PrintStr("\n"); return 0; } ==== Pipe mit HOF ==== fn Double(x: int64): int64 { return x * 2; } fn Negate(x: int64): int64 { return -x; } fn IsEven(x: int64): bool { return x mod 2 = 0; } fn main(): int64 { var v: int64 := 5 |> Double() |> Negate(); // -10 if (10 |> IsEven()) { PrintStr("gerade\n"); } return 0; } ---- ===== 7. Sichtbarkeit und Modifizierer ===== ^ Modifizierer ^ Position ^ Bedeutung ^ | ''pub'' | Vor ''fn'' | Exportiert die Funktion — aus anderen Units importierbar | | ''@inline'' | Vor ''fn'' | Compiler soll die Funktion inline expandieren | | ''@no_opt'' | Vor ''fn'' | Keine Optimierungen — für Benchmarks, MMIO-Debugging | | ''@extern'' | Vor ''fn'' | Externe C-Funktion ohne Implementierung in Lyx | | ''@variadic'' | Zusätzlich zu ''@extern'' | Variadische Argumentliste im C-Stil | // Exportierte Hilfsfunktion pub fn Clamp(val: int64, lo: int64, hi: int64): int64 { if (val < lo) { return lo; } if (val > hi) { return hi; } return val; } // Immer inlinen — minimiert Aufruf-Overhead @inline fn Abs(x: int64): int64 { if (x < 0) { return -x; } return x; } // Externe C-Funktion @extern fn strlen(s: pchar): int64; // Variadisch (C printf-Schnittstelle) @extern @variadic fn printf(fmt: pchar): void; → Alle Attribute: [[lyx_-_programmiersprache:attributes-pragmas|Attribute & Pragmas]] · FFI: [[lyx_-_programmiersprache:ffi|C-Interoperabilität]] ---- ===== 8. Vollständiges Beispiel: Auswertungs-Pipeline ===== Das folgende Beispiel kombiniert Tupel-Rückgabe, Funktionstypen, Higher-Order Functions und den Pipe-Operator in einem realistischen Muster: import std.io; import std.math; // Funktionstypen type Validator = fn(value: f64): bool; type Transform = fn(value: f64): f64; // Gibt (transformierterWert, erfolgreich) zurück fn Process(value: f64, fn validate: Validator, fn transform: Transform): (f64, bool) { if (validate(value) = false) { return (0.0, false); } return (transform(value), true); } // Konkrete Implementierungen fn IsPositive(v: f64): bool { return v > 0.0; } fn IsFinite(v: f64): bool { return v = v; } // NaN-Prüfung: NaN ≠ NaN fn Normalize(v: f64): f64 { return v / 100.0; } fn Cap(v: f64): f64 { return v |> Abs() |> Clamp(0.0, 1.0); } fn Clamp(v: f64, lo: f64, hi: f64): f64 { if (v < lo) { return lo; } if (v > hi) { return hi; } return v; } fn main(): int64 { var inputs: [4]f64 := [42.0, -5.0, 150.0, 0.0]; var i: int64 := 0; while (i < 4) limit(4) { var (out, ok) := Process(inputs[i], IsPositive, Normalize); if (ok) { PrintStr("OK: "); PrintF64(out); PrintStr("\n"); } else { PrintStr("UNGÜLTIG: "); PrintF64(inputs[i]); PrintStr("\n"); } i := i + 1; } // Ausgabe: // OK: 0.42 // UNGÜLTIG: -5.0 // OK: 1.5 // UNGÜLTIG: 0.0 return 0; } ---- ===== Zusammenfassung ===== ^ Konzept ^ Syntax ^ Beispiel ^ | Einfache Funktion | ''fn Name(p: T): R { return ...; }'' | ''fn Add(a: int64, b: int64): int64'' | | Kein Rückgabewert | Rückgabetyp weglassen | ''fn Log(msg: pchar)'' | | Exportiert | ''pub fn ...'' | ''pub fn GetVersion(): pchar'' | | Tupel-Rückgabe | '': (T1, T2)'' | ''fn Divide(...): (int64, bool)'' | | Destrukturierung | ''var (a, b) := Fn(...)'' | ''var (result, ok) := Divide(10, 3)'' | | Anonyme Funktion | ''fn(p: T): R { return ...; }'' | direkt als Argument | | Funktionstyp-Alias | ''type F = fn(T): R'' | ''type Comparator = fn(int64, int64): int32'' | | HOF-Parameter | ''fn name: fn(T): R'' | ''fn Apply(v: int64, fn f: fn(int64): int64)'' | | Pipe-Operator | ''wert |> Fn(args)'' | ''-3.7 |> Abs() |> Round()'' | | Funktion als Wert | Name ohne ''()'' | ''var f: Comparator := CompareAsc'' | ---- Letzte Aktualisierung: 2026-05-22