====== Lyx – Arrays, Maps & Datensammlungen ====== Lyx unterscheidet vier Container-Formen. Die Wahl zwischen ihnen ist keine Stilfrage, sondern eine Sicherheits- und Architekturentscheidung: Stack-Arrays sind deterministisch und DO-178C-tauglich, Heap-Arrays sind flexibel aber nicht echtzeitfähig, SIMD-Arrays für numerische Hochleistungsberechnungen, Maps für Schlüssel-Wert-Zugriff. → [[lyx_-_programmiersprache:datentypen|Datentypen]] · [[lyx_-_programmiersprache:memory-management|Memory Management]] · [[lyx_-_programmiersprache:do-178c|DO-178C]] ---- ===== 1. Statische Arrays — [N]T ===== Statische Arrays liegen auf dem **Stack**. Größe und Typ stehen zur Compile-Zeit fest und sind unveränderlich. Das ist die bevorzugte Form für sicherheitskritischen und echtzeitfähigen Code. ==== Syntax ==== // [Größe]Typ — Typ-zuerst-Notation (wie Go/Zig, NICHT wie C) var buffer: [256]uint8 := []; // 256 Bytes, nullinitialisiert var coords: [3]f64 := [0.0, 0.0, 0.0]; var ids: [16]int64 := []; // let — unveränderliche Bindung (Werte im Array selbst bleiben änderbar) let defaults: [4]int64 := [0, 1, 2, 3]; > **Wichtig:** Die Syntax ist ''[N]T'' — Größe in eckigen Klammern **vor** dem Typ. ''T[N]'' (C-Stil) ist in Lyx kein gültiger Typ. ==== Zugriff und Zuweisung ==== var coords: [3]f64 := [0.0, 0.0, 0.0]; coords[0] := 10.5; // Schreiben coords[1] := coords[0]; // Lesen und Schreiben var z: f64 := coords[2]; // Lesen // Bounds-Checking: // — Konstanter Index außerhalb der Grenzen → Compile-Fehler // — Variabler Index außerhalb der Grenzen → kontrolliertes panic zur Laufzeit ==== Mehrdimensionale statische Arrays ==== // 8×8-Matrix — flaches Layout im Speicher (64 × 8 Bytes = 512 Bytes) var grid: [8][8]int64 := []; grid[3][4] := 99; var val: int64 := grid[3][4]; // 99 ==== Safety-Eigenschaften ==== * Keine Heap-Allokation — kein Fragmentierungsrisiko, kein GC-Druck * Bounds-Checking bei konstantem Index zur Compile-Zeit * Bounds-Checking bei variablem Index zur Laufzeit (kontrollierbarer ''panic'') * Maximaler Stack-Verbrauch statisch berechenbar → kompatibel mit ''@stack_limit'' * Kein ''dispose'' nötig — Stack-Frame wird automatisch freigegeben // DO-178C DAL-A: Waypoint-Liste als statisches Array @dal(A) @flight_crit @stack_limit(2048) @wcet(100) fn FindNearest(wps: [32]Waypoint, n: int64, pos: GeoPoint): int64 { var best: int64 := 0; var best_d: f64 := 1.0e18; for i := 0 to n - 1 do { var d: f64 := GeoDistance(pos, wps[i].pos); if (d < best_d) { best_d := d; best := i; } } return best; } ---- ===== 2. Dynamische Arrays — array ===== Dynamische Arrays liegen auf dem **Heap** und wachsen automatisch. Intern speichern sie drei Werte (Fat-Pointer): Speicheradresse, aktuelle Länge (''len'') und reservierte Kapazität (''cap''). ==== Syntax und Operationen ==== import std.io; var list: array := [10, 20, 30]; list.push(40); // Anhängen list.push(50); PrintInt(list.len()); // 5 PrintInt(list.cap()); // Reservierte Kapazität (≥ len) PrintInt(list[0]); // 10 list[0] := list[0] + 1; // Lesen und Schreiben (kein ++ Operator in Lyx) list[2] := 999; list.clear(); // len := 0, Speicher bleibt reserviert PrintInt(list.len()); // 0 dispose list; // Zwingend — sonst Memory Leak ==== dispose mit mehreren return-Pfaden ==== Verlässt eine Funktion über mehrere Pfade, muss ''dispose'' **vor jedem** ''return'' stehen: fn LoadConfig(path: pchar): bool { var cfg: Map := {}; if (ParseFile(path, cfg) = false) { dispose cfg; // Auch im Fehlerfall freigeben return false; } ApplyConfig(cfg); dispose cfg; return true; } > Lyx hat kein automatisches RAII/Destruktor-System für Heap-Typen. Bei mehreren ''return''-Pfaden statische Arrays ''[N]T'' bevorzugen — kein ''dispose'', kein Leak-Risiko. ==== Wann array sinnvoll ist ==== * Anzahl der Elemente zur Compile-Zeit unbekannt * Server- und Tool-Code, bei dem Flexibilität wichtiger ist als deterministisches Timing * **Nicht** in DAL-A/B-Regelschleifen — Heap-Allokation hat nicht-deterministische Latenz > In Funktionen mit ''@flight_crit'' warnt der Compiler bei Heap-Allokation. Ausschließlich ''[N]T'' verwenden. ---- ===== 3. Parallele Arrays — parallel Array ===== Parallele Arrays sind Heap-Arrays mit garantiertem **SIMD-Alignment** (16 Byte für SSE/NEON, 32 Byte für AVX). Batch-Operationen auf diesen Arrays übersetzt der Compiler direkt in Vektor-Instruktionen. var data: parallel Array := parallel Array(1024); data[0] := 1.0; data[1] := 2.0; // Batch-Skalierung — compiler übersetzt in SSE/AVX/NEON // data := data * 2.0; dispose data; ==== Einsatzgebiete ==== * Signalverarbeitung (FFT, Filter, Fensterfunktionen) * Lineare Algebra (Matrix-Vektor-Produkt) * Machine-Learning-Inferenz (Dot-Produkte, Aktivierungen) * Physik-Simulationen (Partikelsysteme, Kraftfelder) * Nur für primitive Typen: ''f32'', ''f64'', ''int32'', ''int64'' * Kein Einsatz in DAL-A/B-Regelschleifen — SIMD-Timing nicht vorhersehbar genug ---- ===== 4. Maps — Map ===== Maps erlauben den Zugriff über einen Schlüssel. Lyx bietet native Literal-Syntax seit v0.5.0. ==== Erstellen, Lesen, Schreiben ==== import std.io; var scores: Map := {"Alpha": 100, "Bravo": 250, "Charlie": 175}; // Lesen var s: int64 := scores["Alpha"]; // 100 // Schreiben / Einfügen scores["Delta"] := 300; // Existenzprüfung mit in — immer vor Zugriff auf unbekannte Schlüssel if ("Delta" in scores) { PrintInt(scores["Delta"]); // 300 PrintStr("\n"); } // Löschen eines Eintrags delete scores["Charlie"]; dispose scores; ==== Iteration ==== var config: Map := { "timeout_ms": 5000, "retry_count": 3, "log_level": 2 }; for key, val in config do { PrintStr(key); PrintStr(": "); PrintInt(val); PrintStr("\n"); } // Ausgabe (Reihenfolge implementierungsabhängig): // timeout_ms: 5000 // retry_count: 3 // log_level: 2 dispose config; ==== Map ==== var headers: Map := { "Content-Type": "application/json", "Authorization": "Bearer abc123" }; if ("Content-Type" in headers) { PrintStr(headers["Content-Type"]); // application/json PrintStr("\n"); } dispose headers; ==== Performance-Modell ==== * **< 128 Einträge:** Cache-optimierte lineare Suche — O(n), aber sehr schnell durch Lokalität * **≥ 128 Einträge:** Hash-Verfahren — O(1) amortisiert, automatischer Wechsel ---- ===== 5. Speicherverwaltung — Heap-Container ===== Alle Heap-Container (''array'', ''parallel Array'', ''Map'') müssen explizit freigegeben werden: fn ProcessData(): void { var buf: array := []; var data: parallel Array := parallel Array(256); var meta: Map := {}; // ... Verarbeitung ... dispose buf; dispose data; dispose meta; } Wird ''dispose'' vergessen, entsteht ein Memory Leak — besonders in Langzeit-Systemen (Avionik, Server) kritisch. ---- ===== 6. Zusammenfassung & Safety-Matrix ===== ^ Typ ^ Syntax ^ Ort ^ Timing ^ DO-178C DAL-A ^ dispose ^ | Statisches Array | ''[N]T'' | Stack | Deterministisch | ✅ | Nein | | Dynamisches Array | ''array'' | Heap | Nicht-deterministisch | ✗ (nur Init) | Ja | | Paralleles Array | ''parallel Array'' | Heap (SIMD) | Nicht-deterministisch | ✗ | Ja | | Map | ''Map'' | Heap | Nicht-deterministisch | ✗ | Ja | ^ Eigenschaft ^ ''[N]T'' ^ ''array'' ^ ''parallel Array'' ^ ''Map'' ^ | Größe zur Compile-Zeit | Ja | Nein | Nein | Nein | | Bounds-Checking | Compile + Runtime | Runtime | Runtime | Runtime | | @stack_limit-kompatibel | Ja | Nein | Nein | Nein | | SIMD-Vektorisierung | Nein | Nein | Ja | Nein | | Schlüssel-Zugriff | Nein | Nein | Nein | Ja | | for key, val in | Nein | Nein | Nein | Ja | ---- Letzte Aktualisierung: 2026-05-22