====== 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