Zum Inhalt springen

cat posts/match-case-pattern-matching.md

match/case: Strukturelles Pattern Matching

match/case ist mehr als ein switch: Mit Literal-, Sequenz-, Mapping- und Class-Patterns zerlegst Du Datenstrukturen, bindest Werte und ergänzt Bedingungen über Guards.

In Teil 3 der Python-Serie haben wir die Befehle des Spielers mit einer längeren if-elif-else-Kette verarbeitet:

if befehl == "norden":
    ...
elif befehl == "osten":
    ...
elif befehl == "status":
    ...
else:
    ...

Seit Python 3.10 gibt es mit match und case eine weitere Möglichkeit, unterschiedliche Fälle zu behandeln.

Wer switch aus anderen Programmiersprachen kennt, könnte match zunächst für Pythons Variante davon halten. Einfache Wertvergleiche sind damit tatsächlich möglich. Der eigentliche Zweck geht aber deutlich weiter: Structural Pattern Matching kann Datenstrukturen untersuchen, ihre Form prüfen und enthaltene Werte direkt an Variablen binden.

Die Grundform

Ein match-Statement besteht aus einem Subject und mehreren Patterns:

match subject:
    case pattern_1:
        ...
    case pattern_2:
        ...
    case _:
        ...

Python wertet das Subject aus und prüft anschließend die case-Blöcke von oben nach unten. Der erste passende Block wird ausgeführt.

Danach endet das gesamte match-Statement. Anders als bei manchen switch-Varianten gibt es kein automatisches Fall-through und deshalb auch kein break nach jedem case.

Ein einfaches Beispiel:

def reagiere(befehl):
    match befehl:
        case "norden":
            return "Du gehst nach Norden."
        case "osten":
            return "Du gehst nach Osten."
        case "ende":
            return "Du verlässt das Verlies."
        case _:
            return "Das verstehe ich nicht."

Die Strings in den ersten drei Cases sind Literal Patterns. Sie passen nur, wenn das Subject denselben Wert besitzt.

Der Unterstrich _ ist ein Wildcard Pattern. Er passt auf jeden Wert und übernimmt damit die Rolle des abschließenden else.

print(reagiere("norden"))
print(reagiere("tanzen"))

Ausgabe:

Du gehst nach Norden.
Das verstehe ich nicht.

Der Wildcard-Case sollte immer zuletzt stehen. Da er auf alles passt, wären nachfolgende Cases nicht mehr erreichbar.

Mehrere Alternativen mit OR-Patterns

Mehrere Patterns lassen sich mit | verbinden:

def reagiere(befehl):
    match befehl:
        case "norden" | "sueden" | "osten" | "westen":
            return "Du gehst los."
        case "ende" | "quit":
            return "Du verlässt das Verlies."
        case _:
            return "Das verstehe ich nicht."

Das | ist innerhalb eines Patterns kein boolesches or, sondern bildet ein OR-Pattern. Es passt, sobald eine der angegebenen Alternativen passt.

Du kannst den passenden Wert gleichzeitig an einen Namen binden:

def bewege(richtung):
    match richtung:
        case ("norden" | "sueden" | "osten" | "westen") as ziel:
            return f"Du gehst nach {ziel}."
        case _:
            return "Dort kannst Du nicht hingehen."

Das as bindet den vollständig passenden Wert an ziel.

print(bewege("westen"))

Ausgabe:

Du gehst nach westen.

Bei OR-Patterns müssen alle Alternativen dieselben Namen binden. Diese Variante ist gültig:

match eingabe:
    case ["geh", richtung] | ["laufe", richtung]:
        print(f"Du gehst nach {richtung}.")

Beide Alternativen binden einen Wert an richtung.

Diese Variante wäre dagegen ungültig:

match eingabe:
    case ["geh", richtung] | ["laufe", ziel]:
        ...

Python könnte nicht garantieren, welcher Name im zugehörigen Block verfügbar ist.

Das erste passende Pattern gewinnt

Wie bei einer if-elif-Kette ist die Reihenfolge entscheidend.

match befehl:
    case _:
        print("Beliebiger Befehl")
    case "ende":
        print("Spiel beenden")

Dieser Code ist ungültig. Das Wildcard Pattern passt bereits auf jeden möglichen Wert. Der zweite Case könnte deshalb niemals erreicht werden.

Auch bei spezielleren Patterns gilt: Prüfe besondere Fälle vor allgemeineren Fällen.

match eingabe.split():
    case ["nimm"]:
        print("Was möchtest Du nehmen?")
    case ["nimm", gegenstand]:
        print(f"Du nimmst {gegenstand}.")
    case ["nimm", *gegenstaende]:
        print(f"Du nimmst {', '.join(gegenstaende)}.")

Die drei Patterns behandeln unterschiedliche Formen der Eingabe:

  • ["nimm"]: genau ein Wort
  • ["nimm", gegenstand]: genau zwei Wörter
  • ["nimm", *gegenstaende]: mindestens ein Wort, alle weiteren werden gesammelt

Die spezielleren Varianten stehen zuerst.

Capture Patterns: Werte an Namen binden

Ein Name innerhalb eines Patterns ist normalerweise kein Vergleich, sondern ein Capture Pattern.

match eingabe.split():
    case ["nimm", gegenstand]:
        print(f"Du nimmst {gegenstand}.")

Bei der Eingabe

nimm fackel

liefert split() diese Liste:

["nimm", "fackel"]

Das Pattern

["nimm", gegenstand]

bedeutet:

  1. Das Subject muss eine Sequenz mit genau zwei Elementen sein.
  2. Das erste Element muss dem Literal "nimm" entsprechen.
  3. Das zweite Element wird an den Namen gegenstand gebunden.

Im zugehörigen Block enthält gegenstand deshalb den String "fackel".

Die Bindung bleibt auch nach dem match-Statement erhalten, sofern der entsprechende Case erfolgreich ausgewählt wurde. Meistens ist es trotzdem übersichtlicher, den Namen nur innerhalb des Case-Blocks zu verwenden.

Sequence Patterns

Sequence Patterns zerlegen sequenzartige Werte wie Listen und Tupel.

def verarbeite(eingabe):
    woerter = eingabe.strip().lower().split()

    match woerter:
        case []:
            return "Sag etwas."

        case ["umsehen"] | ["schau"]:
            return "Du schaust Dich um."

        case ["nimm"]:
            return "Was möchtest Du nehmen?"

        case ["nimm", gegenstand]:
            return f"Du nimmst {gegenstand}."

        case ["geh", richtung]:
            return f"Du gehst nach {richtung}."

        case _:
            return "Unbekannter Befehl."

Ein Pattern ohne Stern benötigt genau die angegebene Anzahl von Elementen:

case ["geh", richtung]:

Es passt auf:

["geh", "norden"]

Es passt aber nicht auf:

["geh"]
["geh", "nach", "norden"]

Mehrere Elemente mit * sammeln

Ein Stern sammelt mehrere Elemente in einer Liste:

match eingabe.split():
    case ["sage", *woerter]:
        print(" ".join(woerter))

Bei

sage öffne das alte tor

enthält woerter:

["öffne", "das", "alte", "tor"]

Das Star-Pattern darf auch in der Mitte stehen:

match daten:
    case [erstes, *mitte, letztes]:
        print(f"Erstes: {erstes}")
        print(f"Mitte: {mitte}")
        print(f"Letztes: {letztes}")

Für

[1, 2, 3, 4, 5]

werden folgende Werte gebunden:

erstes = 1
mitte = [2, 3, 4]
letztes = 5

Innerhalb eines Sequence Patterns darf höchstens ein Star-Pattern vorkommen.

Eckige und runde Klammern

Für Sequence Patterns kannst Du eckige oder runde Klammern verwenden:

case [x, y]:
    ...

case (x, y):
    ...

Beide Schreibweisen prüfen grundsätzlich auf eine passende Sequenz und nicht speziell auf eine Liste beziehungsweise ein Tupel.

Dieses Pattern:

case [x, y]:

kann deshalb sowohl auf

[10, 20]

als auch auf

(10, 20)

passen.

Strings, bytes und bytearray werden beim Structural Pattern Matching bewusst nicht als Sequenzen einzelner Elemente behandelt. Ein Pattern wie

case [erstes, zweites]:

zerlegt deshalb nicht automatisch einen zweistelligen String.

Für Textbefehle verwenden wir zunächst .split(), wodurch eine echte Liste entsteht.

Mapping Patterns

Mit einem Mapping Pattern kannst Du Dictionaries und andere Mapping-Typen untersuchen:

spieler = {
    "name": "Karl",
    "hp": 75,
    "max_hp": 100,
    "gold": 50,
}

Ein Pattern kann gezielt einzelne Schlüssel prüfen und ihre Werte binden:

match spieler:
    case {"hp": hp}:
        print(f"Der Spieler besitzt {hp} Lebenspunkte.")

Das Pattern passt auf jedes Mapping, das den Schlüssel "hp" enthält. Weitere Einträge stören nicht.

Das ist ein wichtiger Unterschied zu einem Sequence Pattern: Ein Mapping muss nicht exakt dieselben Einträge besitzen, die im Pattern stehen.

Mehrere Schlüssel prüfen

match spieler:
    case {"name": name, "hp": hp}:
        print(f"{name} besitzt {hp} Lebenspunkte.")

Beide Schlüssel müssen vorhanden sein. Ihre Werte werden an name und hp gebunden.

Du kannst auch einen konkreten Wert verlangen:

match spieler:
    case {"hp": 0}:
        print("Game Over.")
    case {"hp": hp}:
        print(f"Noch {hp} HP.")

Der erste Case passt nur, wenn "hp" tatsächlich den Wert 0 besitzt.

Übrige Einträge mit ** sammeln

Mit **rest kannst Du alle nicht ausdrücklich genannten Einträge in einem neuen Dictionary sammeln:

match spieler:
    case {"name": name, **rest}:
        print(f"Name: {name}")
        print(f"Übriger State: {rest}")

Für den vorherigen Player State enthält rest:

{
    "hp": 75,
    "max_hp": 100,
    "gold": 50,
}

Anders als beim Erstellen eines Dictionaries ist **_ in einem Mapping Pattern nicht erlaubt. Nicht benötigte zusätzliche Einträge kannst Du einfach ignorieren, indem Du gar kein **rest verwendest.

Guards: Zusätzliche Bedingungen

Ein Pattern prüft in erster Linie Form, Typ und enthaltene Werte. Für weitere Bedingungen kannst Du einen Guard ergänzen:

match spieler:
    case {"hp": hp} if hp <= 0:
        print("Game Over.")

    case {"hp": hp} if hp <= 25:
        print(f"Dein Zustand ist kritisch: {hp} HP.")

    case {"hp": hp}:
        print(f"Du besitzt noch {hp} HP.")

Der Guard beginnt mit if und steht zwischen Pattern und Doppelpunkt.

Python geht dabei in zwei Schritten vor:

  1. Das Pattern muss passen.
  2. Anschließend muss der Guard True ergeben.

Passt das Pattern, aber der Guard ergibt False, prüft Python den nächsten Case.

Die gebundenen Namen stehen im Guard bereits zur Verfügung:

case {"hp": hp} if hp <= 25:

Zuerst wird der Wert des Schlüssels "hp" an hp gebunden. Danach prüft der Guard, ob dieser Wert kleiner oder gleich 25 ist.

Bereiche besser mit if prüfen

Für reine Wertebereiche bleibt ein normales if häufig klarer:

if hp <= 0:
    print("Game Over.")
elif hp <= 25:
    print("Kritisch!")
else:
    print("Alles in Ordnung.")

match bietet keinen eigenen Pattern-Typ für Zahlenbereiche. Du könntest einen Wert zwar über einen Guard prüfen:

match hp:
    case wert if wert <= 0:
        ...
    case wert if wert <= 25:
        ...

Das ist gegenüber der if-Variante aber meist unnötig kompliziert.

Pattern Matching lohnt sich vor allem dann, wenn neben der Bedingung auch die Struktur der Daten geprüft und zerlegt werden soll.

Class Patterns

Class Patterns prüfen, ob das Subject eine Instanz einer bestimmten Klasse ist, und können anschließend Attribute untersuchen.

class Punkt:
    def __init__(self, x, y):
        self.x = x
        self.y = y

Mit Keyword Patterns kannst Du die Attribute direkt ansprechen:

punkt = Punkt(0, 7)

match punkt:
    case Punkt(x=0, y=0):
        print("Der Punkt liegt im Ursprung.")

    case Punkt(x=0, y=y):
        print(f"Der Punkt liegt auf der Y-Achse bei {y}.")

    case Punkt(x=x, y=0):
        print(f"Der Punkt liegt auf der X-Achse bei {x}.")

    case Punkt(x=x, y=y):
        print(f"Der Punkt liegt bei {x}, {y}.")

Der zweite Case prüft:

  1. Ist das Subject eine Instanz von Punkt?
  2. Besitzt das Attribut x den Wert 0?
  3. Falls ja, binde das Attribut y an den lokalen Namen y.

Die Ausgabe lautet:

Der Punkt liegt auf der Y-Achse bei 7.

Ein Class Pattern erzeugt dabei kein neues Objekt. Die Schreibweise

case Punkt(x=0, y=y):

sieht zwar wie ein Constructor Call aus, beschreibt aber ein Pattern für ein bereits vorhandenes Objekt.

Positionale Class Patterns mit __match_args__

Sollen Attribute positional angegeben werden, benötigt die Klasse __match_args__:

class Punkt:
    __match_args__ = ("x", "y")

    def __init__(self, x, y):
        self.x = x
        self.y = y

Nun sind kürzere Patterns möglich:

match punkt:
    case Punkt(0, 0):
        print("Ursprung")

    case Punkt(0, y):
        print(f"Y-Achse bei {y}")

    case Punkt(x, 0):
        print(f"X-Achse bei {x}")

    case Punkt(x, y):
        print(f"Punkt bei {x}, {y}")

Die Definition

__match_args__ = ("x", "y")

legt fest, dass

Punkt(0, y)

sinngemäß diesem Keyword Pattern entspricht:

Punkt(x=0, y=y)

Bei selbst geschriebenen Klassen sind Keyword Patterns oft expliziter und leichter verständlich. Positionale Patterns eignen sich besonders für Klassen, deren Attribute eine klare und stabile Reihenfolge besitzen.

Patterns kombinieren

Patterns lassen sich ineinander verschachteln.

Betrachten wir ein Event aus einem Spiel:

event = {
    "typ": "schaden",
    "quelle": {
        "name": "Goblin",
        "art": "monster",
    },
    "wert": 25,
}

Dieses Mapping können wir in einem einzigen Pattern untersuchen:

match event:
    case {
        "typ": "schaden",
        "quelle": {"name": name, "art": "monster"},
        "wert": schaden,
    }:
        print(f"{name} verursacht {schaden} Schaden.")

Das Pattern prüft gleichzeitig:

  • Der Event-Typ muss "schaden" sein.
  • quelle muss ein Mapping sein.
  • Die Art der Quelle muss "monster" sein.
  • Der Name wird an name gebunden.
  • Der Schadenswert wird an schaden gebunden.

Ausgabe:

Goblin verursacht 25 Schaden.

Mit einer klassischen if-Abfrage müssten wir die einzelnen Ebenen getrennt ansprechen:

if (
    event["typ"] == "schaden"
    and event["quelle"]["art"] == "monster"
):
    name = event["quelle"]["name"]
    schaden = event["wert"]

Pattern Matching verbindet die Strukturprüfung und das Herauslösen der benötigten Werte.

Die Falle mit nackten Namen

Einer der wichtigsten Unterschiede zu einem klassischen switch betrifft Namen in Patterns.

Betrachten wir eine vermeintliche Konstante:

ENDE = "ende"

Dieser Code sieht nach einem Vergleich aus:

def reagiere(befehl):
    match befehl:
        case ENDE:
            return "Das Spiel endet."

case ENDE vergleicht aber nicht mit dem vorher definierten Wert "ende".

Ein nackter Name ist ein Capture Pattern. Er bindet jeden beliebigen Wert des Subjects an den Namen ENDE. Der Case passt deshalb immer.

print(reagiere("norden"))

würde ebenfalls

Das Spiel endet.

zurückgeben.

Fügst Du danach einen weiteren ungeschützten Case ein, meldet Python bereits beim Parsen einen Fehler, weil nach dem irrefutable Capture Pattern nichts mehr erreichbar wäre:

match befehl:
    case ENDE:
        ...
    case _:
        ...

Warum Literale funktionieren

Diese Patterns sind direkte Wertvergleiche:

case "ende":
case 404:
case True:
case False:
case None:

Strings, Zahlen und die besonderen Literale True, False und None werden nicht als Capture Patterns behandelt.

Benannte Werte müssen gepunktet sein

Ein benannter Wert kann als Value Pattern verwendet werden, wenn der Name mindestens einen Punkt enthält:

class Befehle:
    ENDE = "ende"
    STATUS = "status"

Nun funktionieren echte Vergleiche:

def reagiere(befehl):
    match befehl:
        case Befehle.ENDE:
            return "Das Spiel endet."

        case Befehle.STATUS:
            return "Der Status wird angezeigt."

        case _:
            return "Unbekannter Befehl."

Befehle.ENDE ist ein gepunkteter Name. Python schlägt den zugehörigen Wert nach und vergleicht ihn mit dem Subject.

Auch Enum Members werden typischerweise so verwendet:

from enum import Enum


class Status(Enum):
    AKTIV = 1
    BESIEGT = 2
match status:
    case Status.AKTIV:
        print("Das Spiel läuft.")

    case Status.BESIEGT:
        print("Game Over.")

Hier ist status allerdings ein Enum Member und nicht lediglich dessen numerischer Wert.

Ein Guard als Alternative

Du kannst den Vergleich auch über einen Guard ausdrücken:

ENDE = "ende"

match befehl:
    case wert if wert == ENDE:
        print("Das Spiel endet.")
    case _:
        print("Unbekannter Befehl.")

Das funktioniert, ist für einen einfachen Wertvergleich aber meist umständlicher als:

if befehl == ENDE:
    ...

Ein Command Parser für den Dungeon

Im Dungeon können wir match verwenden, um den in Wörter zerlegten Input zu verarbeiten.

def verarbeite_befehl(eingabe, raum, spieler):
    """Verarbeitet einen Textbefehl und gibt das Ergebnis zurück."""
    woerter = eingabe.strip().lower().split()

    match woerter:
        case []:
            return "Bitte gib einen Befehl ein."

        case ["ende"] | ["quit"]:
            return "ende"

        case ["umsehen"] | ["schau"]:
            gegenstaende = raum["gegenstaende"]

            if gegenstaende:
                return "Hier liegt: " + ", ".join(gegenstaende)

            return "Hier liegt nichts Brauchbares."

        case ["inventar"]:
            inventar = spieler["inventar"]

            if inventar:
                return "Inventar: " + ", ".join(inventar)

            return "Dein Inventar ist leer."

        case ["status"]:
            return (
                f"{spieler['name']} | "
                f"HP: {spieler['hp']}/{spieler['max_hp']} | "
                f"Gold: {spieler['gold']}"
            )

        case ["nimm"]:
            return "Was möchtest Du nehmen?"

        case ["nimm", *namen] if namen:
            gesucht = " ".join(namen)

            for gegenstand in raum["gegenstaende"]:
                if gegenstand.lower() == gesucht:
                    raum["gegenstaende"].remove(gegenstand)
                    spieler["inventar"].append(gegenstand)
                    return f"Du nimmst {gegenstand}."

            return f"Hier liegt kein Gegenstand namens {gesucht}."

        case ["geh", richtung] | [richtung] if richtung in raum["ausgaenge"]:
            return raum["ausgaenge"][richtung]

        case _:
            return "Das verstehe ich nicht."

Der Input

nimm alte fackel

wird durch .split() zu:

["nimm", "alte", "fackel"]

Das Pattern

case ["nimm", *namen] if namen:

bindet

["alte", "fackel"]

an namen.

Anschließend setzt

gesucht = " ".join(namen)

die Wörter wieder zu

alte fackel

zusammen.

Das Bewegungs-Pattern akzeptiert zwei Formen:

case ["geh", richtung] | [richtung] if richtung in raum["ausgaenge"]:

Damit funktionieren sowohl

geh norden

als auch

norden

Der Guard stellt sicher, dass richtung tatsächlich ein Ausgang des aktuellen Raums ist.

Wichtig: Die Funktion verwendet unterschiedliche Return Values für unterschiedliche Ergebnisse. In einem größeren Programm wäre es sinnvoll, diese Ergebnisse eindeutiger zu strukturieren, beispielsweise über Tupel, Dictionaries oder eigene Klassen. Für das Beispiel reicht die vereinfachte Form.

Wann ist match sinnvoll?

match eignet sich besonders, wenn Du:

  • die Form einer Liste oder eines Tupels prüfen möchtest,
  • Dictionaries anhand bestimmter Schlüssel untersuchst,
  • Werte während der Prüfung an Namen binden möchtest,
  • unterschiedliche Klassen oder Objektformen behandelst,
  • mehrere verschachtelte Strukturen gleichzeitig prüfen möchtest,
  • einen kleinen Parser für strukturierte Befehle oder Events baust.

Ein typischer Anwendungsfall ist:

match nachricht:
    case {"typ": "chat", "text": text}:
        ...
    case {"typ": "bewegung", "x": x, "y": y}:
        ...
    case {"typ": "schaden", "wert": wert, "quelle": quelle}:
        ...

Hier unterscheiden sich die Fälle nicht nur durch einen einzelnen Wert, sondern durch ihre gesamte Struktur.

Wann ist if besser?

Ein normales if bleibt häufig besser für:

  • einfache boolesche Bedingungen,
  • Zahlenbereiche,
  • Vergleiche mit wenigen einzelnen Werten,
  • komplexe Bedingungen ohne erkennbare Datenstruktur.

Beispiel:

if hp <= 0:
    print("Game Over.")
elif hp <= 25:
    print("Kritisch!")
else:
    print("Du kannst weiterkämpfen.")

Diese Logik als match mit mehreren Guards zu schreiben, wäre nicht klarer.

Auch dieser einfache Vergleich benötigt kein Pattern Matching:

if befehl == "ende":
    spiel_beenden()

match ist kein Ersatz für jedes if. Es ist ein zusätzliches Werkzeug für Fälle, in denen die Struktur der Daten Teil der Entscheidung ist.

Stolperfallen

  • Einen nackten Namen für eine Konstante halten: case ENDE vergleicht nicht mit einer Variablen namens ENDE, sondern bindet jeden Wert an diesen Namen. Verwende ein Literal, einen gepunkteten Namen oder ein normales if.

  • Den Wildcard-Case zu früh einsetzen: case _ passt auf alles und muss deshalb zuletzt stehen.

  • Die Reihenfolge zu allgemein beginnen: Das erste passende Pattern gewinnt. Spezifische Patterns gehören vor allgemeinere Patterns.

  • Sequence Patterns mit exakten Typprüfungen verwechseln: [x, y] kann auf unterschiedliche Sequenztypen passen. Es prüft nicht ausschließlich auf eine Liste.

  • Strings direkt als Sequence Pattern zerlegen wollen: Strings, bytes und bytearray werden von Sequence Patterns nicht als Folgen einzelner Elemente behandelt. Nutze bei Textbefehlen beispielsweise .split().

  • Bei einem Mapping exakte Gleichheit erwarten: {"hp": hp} passt auch auf Dictionaries mit zusätzlichen Schlüsseln.

  • Bei OR-Patterns unterschiedliche Namen binden: Alle Alternativen müssen dieselbe Menge an Namen bereitstellen.

  • Class Patterns für Constructor Calls halten: case Punkt(x=0, y=y) erzeugt kein Objekt, sondern prüft ein vorhandenes Objekt.

  • break nach einem Case schreiben: Nach dem ersten passenden Case endet das match-Statement automatisch.

  • match für jeden Vergleich verwenden: Einfache Bedingungen und Wertebereiche sind mit if häufig klarer.

Kompakte Übersicht

Pattern Beispiel Bedeutung
Literal Pattern case "ende": passt auf einen konkreten Wert
Wildcard Pattern case _: passt auf jeden Wert
Capture Pattern case wert: bindet das Subject an wert
OR-Pattern case "ja" \| "j": passt auf eine der Alternativen
AS-Pattern case ("n" \| "s") as richtung: bindet das passende Pattern
Sequence Pattern case ["nimm", ding]: prüft und zerlegt eine Sequenz
Star Pattern case [erstes, *rest]: sammelt übrige Elemente
Mapping Pattern case {"hp": hp}: prüft Schlüssel und bindet Werte
Class Pattern case Punkt(x=0, y=y): prüft Klasse und Attribute
Guard case wert if wert > 0: ergänzt eine Bedingung
Value Pattern case Status.AKTIV: vergleicht mit einem gepunkteten Wert

Fazit

match und case sind weit mehr als eine alternative Schreibweise für if-elif-Ketten.

Structural Pattern Matching kann:

  • Literale vergleichen,
  • Sequenzen zerlegen,
  • Dictionaries anhand ihrer Schlüssel untersuchen,
  • Attribute von Objekten prüfen,
  • Werte direkt an Namen binden,
  • Patterns kombinieren,
  • zusätzliche Bedingungen über Guards anwenden.

Seine größte Stärke zeigt match nicht beim Vergleich einzelner Strings, sondern bei strukturierten Daten.

Die wichtigste Regel dabei lautet:

Ein nackter Name in einem Pattern ist eine neue Bindung, kein Zugriff auf eine vorhandene Variable.

Sobald Du diesen Unterschied verstanden hast, wird aus der vermeintlichen switch-Anweisung ein mächtiges Werkzeug zum Analysieren und Zerlegen von Daten.

Weiterlesen

0 Kommentare

Noch keine Kommentare. Sei der/die Erste!