====== Lyx – Pattern Matching ======
''match'' ist das zentrale Kontrollfluss-Element für Fallunterscheidungen in Lyx. Es ist typsicher, **exhaustiv** — der Compiler prüft, ob alle möglichen Werte abgedeckt sind — und wird vom Backend oft zu einer Jump-Table optimiert, die schneller ist als eine if/else-Kette.
Der Unterschied zu ''switch'' in C: Es gibt kein Fall-through, kein vergessenes ''break'', keine Sprünge zwischen Armen. Jeder Arm ist vollständig isoliert. Der Compiler warnt, wenn Fälle fehlen.
→ [[lyx_-_programmiersprache:exception-handling|Fehlerbehandlung]] · [[lyx_-_programmiersprache:dedingungen|Bedingungen]] · [[lyx_-_programmiersprache:datentypen|Datentypen]]
----
===== 1. Grundsyntax =====
match (ausdruck) {
case muster1 => anweisung;
case muster2 => { anweisung1; anweisung2; }
default => anweisung;
}
* Jeder Arm besteht aus ''case muster =>'' gefolgt von einer Anweisung oder einem Block.
* ''default'' fängt alle Werte ab, die kein explizites ''case'' getroffen haben.
* Kein Fall-through — nur der erste passende Arm wird ausgeführt.
* Blöcke ''{ }'' sind optional bei einer einzelnen Anweisung, aber empfohlen.
fn HttpStatusText(code: int64): pchar {
match (code) {
case 200 => return "OK";
case 201 => return "Created";
case 204 => return "No Content";
case 301 => return "Moved Permanently";
case 400 => return "Bad Request";
case 401 => return "Unauthorized";
case 403 => return "Forbidden";
case 404 => return "Not Found";
case 500 => return "Internal Server Error";
case 503 => return "Service Unavailable";
default => return "Unknown";
}
}
fn main(): int64 {
PrintStr(HttpStatusText(404)); // "Not Found"
PrintStr("\n");
PrintStr(HttpStatusText(200)); // "OK"
PrintStr("\n");
return 0;
}
----
===== 2. OR-Muster ( | ) =====
Mehrere Werte können in einem einzigen Arm zusammengefasst werden:
fn ClassifyChar(c: int64): pchar {
match (c) {
case 65 | 69 | 73 | 79 | 85 => return "Vokal (groß)";
case 97 | 101 | 105 | 111 | 117 => return "Vokal (klein)";
case 48 | 49 | 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 => return "Ziffer";
case 32 | 9 | 10 | 13 => return "Whitespace";
default => return "Sonstiges";
}
}
fn ClassifyHttpMethod(method: int64): int64 {
match (method) {
case HTTP_GET | HTTP_HEAD | HTTP_OPTIONS => return METHOD_SAFE;
case HTTP_POST | HTTP_PUT | HTTP_PATCH => return METHOD_MUTATING;
case HTTP_DELETE => return METHOD_DESTRUCTIVE;
default => return METHOD_UNKNOWN;
}
}
----
===== 3. Bereichsmuster ( .. ) =====
Wertebereiche werden mit ''von..bis'' angegeben — der Bereich ist auf beiden Seiten einschließend:
fn AltitudeMode(altitude_m: int64): pchar {
match (altitude_m) {
case 0..500 => return "Bodennaher Betrieb";
case 501..3000 => return "Platzrunde / Anflug";
case 3001..10000 => return "Steigflug";
case 10001..13000 => return "Reiseflug";
case 13001.. => return "Hochflug";
default => return "Ungültige Höhe";
}
}
fn HttpStatusClass(code: int64): pchar {
match (code) {
case 100..199 => return "Informational";
case 200..299 => return "Success";
case 300..399 => return "Redirection";
case 400..499 => return "Client Error";
case 500..599 => return "Server Error";
default => return "Unknown";
}
}
fn main(): int64 {
PrintStr(AltitudeMode(11500)); // "Reiseflug"
PrintStr("\n");
PrintStr(HttpStatusClass(404)); // "Client Error"
PrintStr("\n");
return 0;
}
OR-Muster und Bereiche können kombiniert werden:
fn IsSpecialPort(port: int64): bool {
match (port) {
case 80 | 443 => return true; // HTTP(S)
case 20 | 21 => return true; // FTP
case 22 => return true; // SSH
case 1..1023 => return true; // Privilegierte Ports
default => return false;
}
}
----
===== 4. Enum-Matching und Exhaustivität =====
Das stärkste Argument für ''match'' in Lyx: Bei Enums prüft der Compiler, ob **alle** Werte abgedeckt sind. Fehlt ein Arm, ist es ein Compiler-Fehler — kein Warning, kein Silent-Fail.
enum FlightPhase {
Preflight,
Taxiing,
Takeoff,
Climbing,
Cruise,
Descending,
Approach,
Landing,
Rollout
}
fn PhaseActions(phase: FlightPhase): void {
match (phase) {
case FlightPhase::Preflight => { RunChecklist(); }
case FlightPhase::Taxiing => { EnableTaxiLights(); CheckFlaps(); }
case FlightPhase::Takeoff => { SetTakeoffPower(); RetractFlaps(); }
case FlightPhase::Climbing => { RetractGear(); SetClimbPower(); }
case FlightPhase::Cruise => { SetCruisePower(); EngageAutopilot(); }
case FlightPhase::Descending => { SetDescentRate(); }
case FlightPhase::Approach => { ExtendFlaps(); ArmAutobrake(); }
case FlightPhase::Landing => { ExtendGear(); SetLandingPower(); }
case FlightPhase::Rollout => { ApplyBrakes(); ReverseThrust(); }
// Kein 'default' nötig — alle 9 Werte sind abgedeckt
// Wird ein neuer Wert zum Enum hinzugefügt → Compiler-Fehler hier
}
}
Wird ''FlightPhase::GoAround'' später hinzugefügt, erzeugt der Compiler sofort:
error: non-exhaustive match — FlightPhase::GoAround not covered
--> flight_ctrl.lyx:42:5
hint: add 'case FlightPhase::GoAround =>' or 'default =>'
Das ist eine Sicherheitsgarantie: Neue Zustände können nicht unbehandelt durchrutschen.
==== Enum mit Werten ====
enum SensorStatus {
Ok = 0,
Timeout = 1,
OutOfRange = 2,
HardwareFault = 3
}
fn HandleSensorStatus(s: SensorStatus): void {
match (s) {
case SensorStatus::Ok => { /* normal */ }
case SensorStatus::Timeout => {
PrintStr("Sensor-Timeout — Wiederholungsversuch\n");
RetrySensor();
}
case SensorStatus::OutOfRange => {
PrintStr("Messwert außerhalb Plausibilitätsbereich\n");
FlagSensorData();
}
case SensorStatus::HardwareFault => {
PrintStr("Hardware-Defekt — Fallback aktivieren\n");
ActivateRedundantSensor();
}
}
}
----
===== 5. Match als Ausdruck =====
''match'' kann direkt als Ausdruck verwendet werden — der Wert des passenden Arms wird zurückgegeben:
fn DayName(day: int64): pchar {
return match (day) {
case 1 => "Montag";
case 2 => "Dienstag";
case 3 => "Mittwoch";
case 4 => "Donnerstag";
case 5 => "Freitag";
case 6 => "Samstag";
case 7 => "Sonntag";
default => "Ungültig";
};
}
fn DaysInMonth(month: int64, year: int64): int64 {
return match (month) {
case 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31;
case 4 | 6 | 9 | 11 => 30;
case 2 => IsLeapYear(year) ? 29 : 28;
default => 0;
};
}
fn main(): int64 {
PrintStr(DayName(3)); // Mittwoch
PrintStr("\n");
PrintInt(DaysInMonth(2, 2024)); // 29
PrintStr("\n");
return 0;
}
----
===== 6. Guard-Bedingungen =====
Mit ''when'' kann einem Muster eine zusätzliche Bedingung hinzugefügt werden. Der Arm passt nur, wenn sowohl das Muster als auch die Guard-Bedingung wahr sind:
fn ClassifyTemperature(sensor_id: int64, temp: f64): pchar {
match (sensor_id) {
case 1 when temp > 100.0 => return "Sensor 1: Überhitzung";
case 1 when temp < -20.0 => return "Sensor 1: Untertemperatur";
case 1 => return "Sensor 1: Normal";
case 2 when temp > 80.0 => return "Sensor 2: Warnung";
case 2 => return "Sensor 2: Normal";
default => return "Unbekannter Sensor";
}
}
fn RoutePacket(port: int64, size: int64): pchar {
match (port) {
case 80 when size > 65535 => return "HTTP: Paket zu groß";
case 80 => return "HTTP-Handler";
case 443 when size > 65535 => return "HTTPS: Paket zu groß";
case 443 => return "HTTPS-Handler";
case 1..1023 => return "Privilegierter Port";
default => return "Unprivilegierter Port";
}
}
----
===== 7. Struct-Deconstruction =====
Match kann direkt in Struct-Felder hineinschauen und auf deren Werte matchen:
type Packet = struct {
type_id: int64;
length: int64;
flags: int64;
checksum: int64;
};
fn ProcessPacket(pkt: Packet): void {
match (pkt) {
case Packet { type_id: 1, flags: 0 } => {
PrintStr("Datenpunkt, keine Flags\n");
ProcessData(pkt);
}
case Packet { type_id: 1 } => {
PrintStr("Datenpunkt mit Flags\n");
ProcessDataWithFlags(pkt);
}
case Packet { type_id: 2, length: 0..8 } => {
PrintStr("Kurzbefehl\n");
ProcessShortCommand(pkt);
}
case Packet { type_id: 2 } => {
PrintStr("Langer Befehl\n");
ProcessCommand(pkt);
}
case Packet { type_id: 255 } => {
PrintStr("Heartbeat\n");
ResetWatchdog();
}
default => {
PrintStr("Unbekanntes Paket — verworfen\n");
}
}
}
Felder, die nicht im Muster auftauchen, werden ignoriert — es ist kein Wildcard nötig.
----
===== 8. Verschachteltes Matching =====
''match'' kann in anderen ''match''-Armen verschachtelt werden — nützlich für mehrstufige Zustandsmaschinen:
enum ProtocolState { Idle, Handshake, Active, Closing }
enum ErrorCode { None, Timeout, AuthFailed, Overflow }
fn HandleProtocolEvent(state: ProtocolState, error: ErrorCode): void {
match (state) {
case ProtocolState::Idle => {
match (error) {
case ErrorCode::None => StartHandshake();
case ErrorCode::Timeout => ResetConnection();
default => LogError(error);
}
}
case ProtocolState::Handshake => {
match (error) {
case ErrorCode::None => TransitionToActive();
case ErrorCode::AuthFailed => RejectConnection();
case ErrorCode::Timeout => RetryHandshake();
default => AbortHandshake();
}
}
case ProtocolState::Active => {
match (error) {
case ErrorCode::None => { /* normal */ }
case ErrorCode::Overflow => HandleOverflow();
default => GracefulClose();
}
}
case ProtocolState::Closing => {
// Im Closing-Zustand alle Fehler ignorieren
}
}
}
----
===== 9. Performance =====
Der Compiler optimiert ''match'' je nach Struktur der Muster:
^ Muster-Struktur ^ Backend-Strategie ^ Eigenschaft ^
| Wenige dichte Einzelwerte (0,1,2,3) | Jump-Table | O(1) — schnellste Variante |
| Viele verstreute Einzelwerte | Binäre Suche | O(log n) |
| Bereiche | Vergleichsfolge | O(n) im Worst Case |
| Struct-Deconstruction | Feldvergleiche | Abhängig von Feldanzahl |
| Enum (vollständig) | Jump-Table oder direkt | O(1) — Enum-Werte sind int64 |
# Assembler-Ausgabe prüfen — zeigt ob Jump-Table erzeugt wurde
lyxc main.lyx --emit-asm | grep -A 20 "HttpStatusText"
Ein ''match'' auf 10 dichte HTTP-Statuscodes erzeugt eine Jump-Table mit einem einzigen indirekten Sprung — ein ''if/else''-Äquivalent würde 10 Vergleiche brauchen.
----
===== 10. match vs. if/else — Wann was =====
^ Kriterium ^ match ^ if/else ^
| Viele Fälle auf denselben Wert | ✓ Klar strukturiert | ✗ Lange Kette |
| Exhaustivitäts-Garantie | ✓ Compiler erzwingt | ✗ Manuell |
| Enum-Abdeckung | ✓ Vollständigkeitsprüfung | ✗ Keine |
| OR-Muster | ✓ ''case 1 | 2 | 3 =>'' | ○ Umständlich |
| Bereichsprüfungen | ✓ ''case 100..199 =>'' | ○ Möglich, aber unübersichtlich |
| Komplexe boolesche Logik | ✗ Ungeeignet | ✓ Natürlicher |
| Struct-Felder prüfen | ✓ Deconstruction | ○ Einzelne if-Abfragen |
| Zwei-Wege-Entscheidung | ○ Overkill | ✓ Natürlicher |
**Faustregel:** Ab drei Fällen auf denselben Ausdruck ist ''match'' klarer. Bei Enums immer ''match''.
----
===== 11. Pattern Matching in Safety-Code =====
In DO-178C-zertifiziertem Code ist ''match'' besonders wertvoll, weil es Exhaustivität erzwingt. Fehlende Zustände können in sicherheitskritischen Systemen fatale Folgen haben.
enum SystemMode {
Initializing,
Standby,
Active,
Degraded, // Eingeschränkter Betrieb wegen Sensorausfall
Emergency,
Shutdown
}
@dal(B)
@flight_crit
@stack_limit(256)
fn ApplySystemMode(mode: SystemMode): void {
match (mode) {
case SystemMode::Initializing => {
DisableAllOutputs();
RunSelfTest();
}
case SystemMode::Standby => {
SetIdleConsumption();
EnableWakeupSensor();
}
case SystemMode::Active => {
EnableFullOperation();
StartControlLoop();
}
case SystemMode::Degraded => {
DisableNonCriticalSystems();
ActivateRedundantSensors();
AlertCrew();
}
case SystemMode::Emergency => {
ActivateSafeMode();
TriggerEmergencyProtocol();
AlertCrew();
}
case SystemMode::Shutdown => {
SaveFlightData();
DisableAllOutputs();
}
// Kein default — jeder neue Modus erzeugt sofort einen Compiler-Fehler
// Das erzwingt, dass neue Zustände immer bewusst behandelt werden
}
}
==== Exhaustivität als Sicherheitsnetz ====
Wird das System um einen neuen Modus erweitert — z.B. ''Maintenance'' — und ''ApplySystemMode'' nicht aktualisiert, bricht der Build sofort ab:
error: non-exhaustive match — SystemMode::Maintenance not covered
--> flight_ctrl.lyx:88:5
in function: ApplySystemMode
hint: add 'case SystemMode::Maintenance =>' or 'default =>'
Dieser Fehler tritt beim Build auf — nicht zur Laufzeit in einem Flugzeug.
----
===== 12. Vollständiges Beispiel: Protokoll-Parser =====
Ein binärer Protokoll-Parser, der ''match'' auf mehreren Ebenen nutzt:
unit FrameParser;
enum FrameType {
Data = 0x01,
Command = 0x02,
Ack = 0x03,
Nack = 0x04,
Heartbeat = 0xFF
}
enum CommandId {
Reset = 0x01,
Configure = 0x02,
Calibrate = 0x03,
Shutdown = 0x0F
}
type Frame = struct {
frame_type: int64;
command_id: int64;
length: int64;
payload: int64;
checksum: int64;
};
fn ParseFrame(raw: int64, len: int64): Frame {
// ... Frame aus Bytes aufbauen ...
return Frame { frame_type: 0, command_id: 0, length: 0, payload: 0, checksum: 0 };
}
fn ValidateChecksum(frame: Frame): bool {
return true; // vereinfacht
}
pub fn ProcessFrame(raw: int64, len: int64): void {
if (len < 5) {
PrintStr("Frame zu kurz\n");
return;
}
var frame: Frame := ParseFrame(raw, len);
if (!ValidateChecksum(frame)) {
PrintStr("Checksummen-Fehler\n");
return;
}
match (frame.frame_type) {
case FrameType::Data => {
PrintStr("Datenframe: ");
PrintInt(frame.length);
PrintStr(" Bytes\n");
StoreData(frame.payload, frame.length);
}
case FrameType::Command => {
match (frame.command_id) {
case CommandId::Reset => {
PrintStr("Reset-Kommando\n");
PerformReset();
}
case CommandId::Configure => {
PrintStr("Konfiguration\n");
ApplyConfig(frame.payload, frame.length);
}
case CommandId::Calibrate => {
PrintStr("Kalibrierung\n");
RunCalibration();
}
case CommandId::Shutdown => {
PrintStr("Shutdown-Kommando\n");
InitiateShutdown();
}
// Alle Kommandos abgedeckt — Compiler schlägt Alarm bei Erweiterung
}
}
case FrameType::Ack => {
PrintStr("ACK empfangen\n");
ClearRetryBuffer();
}
case FrameType::Nack => {
PrintStr("NACK empfangen — Wiederholung\n");
RetransmitLastFrame();
}
case FrameType::Heartbeat => {
ResetWatchdog();
}
// Alle FrameType-Werte abgedeckt
}
}
----
===== Best Practices =====
^ Situation ^ Empfehlung ^
| Enum-Fallunterscheidung | Immer ''match'' — kein ''if/else'' |
| Kein ''default'' bei Enums | Exhaustivität als Sicherheitsnetz nutzen |
| Viele dichte Einzelwerte | ''match'' für Jump-Table-Optimierung |
| Bereiche | ''case lo..hi =>'' statt ''x >= lo && x <= hi'' |
| Mehrere Werte, gleiche Aktion | ''case a | b | c =>'' |
| Komplexe Nebenbedingungen | Guard mit ''when'' |
| Neue Zustände/Kommandos | Build-Fehler durch Exhaustivität — Feature, kein Bug |
| Safety-Code (DAL-A/B) | Kein ''default'' bei zustandsbehafteten Enums |
----
→ [[lyx_-_programmiersprache:exception-handling|Fehlerbehandlung — Result, Tuple-Return, panic()]]\\
→ [[lyx_-_programmiersprache:dedingungen|Bedingungen & Logik — if, while, for]]\\
→ [[lyx_-_programmiersprache:datentypen|Datentypen — Enums, Structs, Skalare]]\\
→ [[lyx_-_programmiersprache:do-178c|DO-178C — Sicherheitskritische Programmierung]]
Letzte Aktualisierung: 2026-05-22