Kurz: Records sind unveränderliche Datensätze mit benannten Feldern und eingebauter Gleichheit:
Teil des Kurses F#
Records
Records sind unveränderliche Datensätze mit benannten Feldern und eingebauter Gleichheit:
type Person = { Name: string; Alter: int }
let p = { Name = "Mia"; Alter = 17 }
let p2 = { p with Alter = 18 }
printfn "%A" p
printfn "%A" p2
printfn "%b" (p = { Name = "Mia"; Alter = 17 })
printfn "%s ist %d" p.Name p.Alter
let { Name = n; Alter = a } = p2
printfn "%s %d" n a
type Punkt = { X: float; Y: float }
let abstand (a: Punkt) (b: Punkt) = sqrt ((a.X - b.X) ** 2.0 + (a.Y - b.Y) ** 2.0)
printfn "%.1f" (abstand { X = 0.0; Y = 0.0 } { X = 3.0; Y = 4.0 })
type Konto =
{ Inhaber: string
mutable Stand: decimal }
member this.Einzahlen(betrag) = this.Stand <- this.Stand + betrag
override this.ToString() = sprintf "%s: %M" this.Inhaber this.Stand
let k = { Inhaber = "Mia"; Stand = 100m }
k.Einzahlen 25.5m
printfn "%O" kAusgabe:
{ Name = "Mia"
Alter = 17 }
{ Name = "Mia"
Alter = 18 }
true
Mia ist 17
Mia 18
5.0
Mia: 125.5Discriminated Unions
Unions beschreiben „entweder-oder“-Daten. Der Compiler prüft, dass match alle Fälle abdeckt:
type Form =
| Kreis of radius: float
| Rechteck of breite: float * hoehe: float
| Dreieck of float * float * float
let flaeche form =
match form with
| Kreis r -> System.Math.PI * r * r
| Rechteck(b, h) -> b * h
| Dreieck(a, b, c) ->
let s = (a + b + c) / 2.0
sqrt (s * (s - a) * (s - b) * (s - c))
for f in [ Kreis 1.0; Rechteck(2.0, 3.0); Dreieck(3.0, 4.0, 5.0) ] do
printfn "%A -> %.2f" f (flaeche f)
type Farbe = Rot | Gruen | Blau
let name = function Rot -> "rot" | Gruen -> "grün" | Blau -> "blau"
printfn "%s" (name Gruen)
type Ausdruck =
| Zahl of int
| Plus of Ausdruck * Ausdruck
| Mal of Ausdruck * Ausdruck
| Neg of Ausdruck
let rec auswerten = function
| Zahl n -> n
| Plus(a, b) -> auswerten a + auswerten b
| Mal(a, b) -> auswerten a * auswerten b
| Neg a -> -(auswerten a)
let e = Mal(Plus(Zahl 1, Zahl 2), Neg(Zahl 4))
printfn "%A = %d" e (auswerten e)
type Baum<'a> =
| Blatt
| Knoten of Baum<'a> * 'a * Baum<'a>
let rec einfuegen x = function
| Blatt -> Knoten(Blatt, x, Blatt)
| Knoten(l, w, r) as k ->
if x < w then Knoten(einfuegen x l, w, r)
elif x > w then Knoten(l, w, einfuegen x r)
else k
let rec inOrdnung = function
| Blatt -> []
| Knoten(l, w, r) -> inOrdnung l @ [ w ] @ inOrdnung r
let baum = List.fold (fun b x -> einfuegen x b) Blatt [ 5; 3; 8; 1; 4; 7; 9; 3 ]
printfn "%A" (inOrdnung baum)Ausgabe:
Kreis 1.0 -> 3.14
Rechteck (2.0, 3.0) -> 6.00
Dreieck (3.0, 4.0, 5.0) -> 6.00
grün
Mal (Plus (Zahl 1, Zahl 2), Neg (Zahl 4)) = -12
[1; 3; 4; 5; 7; 8; 9]Option und Result: Fehler ohne Ausnahmen
let teile a b = if b = 0 then None else Some(a / b)
printfn "%A %A" (teile 10 2) (teile 1 0)
printfn "%d %d" (teile 8 2 |> Option.defaultValue -1) (teile 8 0 |> Option.defaultValue -1)
printfn "%A" (teile 8 2 |> Option.map (fun x -> x + 1))
printfn "%A" (teile 8 2 |> Option.bind (fun x -> teile 100 x))
match teile 7 0 with
| Some v -> printfn "Ergebnis %d" v
| None -> printfn "Division durch null"
type Fehler = DurchNull | Negativ of int
let wurzel n = if n < 0 then Error(Negativ n) else Ok(int (sqrt (float n)))
let sicherTeile a b = if b = 0 then Error DurchNull else Ok(a / b)
let berechne a b =
match sicherTeile a b with
| Error e -> Error e
| Ok q -> wurzel q
printfn "%A" (berechne 100 4)
printfn "%A" (berechne 1 0)
printfn "%A" (berechne -16 2)
let ergebnis = sicherTeile 100 5 |> Result.bind wurzel |> Result.map (fun x -> x * 10)
printfn "%A" ergebnis
printfn "%A" (Option.ofObj (null: string))
printfn "%A" (Some 5 |> Option.filter (fun x -> x > 10))Ausgabe:
Some 5 None
4 -1
Some 5
Some 25
Division durch null
Ok 5
Error DurchNull
Error (Negativ -8)
Ok 40
None
NoneAktive Muster und Einheiten
let (|Gerade|Ungerade|) n = if n % 2 = 0 then Gerade else Ungerade
let beschreibe = function
| Gerade -> "gerade"
| Ungerade -> "ungerade"
printfn "%s %s" (beschreibe 4) (beschreibe 7)
let (|Zahl|_|) (s: string) =
match System.Int32.TryParse s with
| true, n -> Some n
| _ -> None
for s in [ "42"; "abc"; "7" ] do
match s with
| Zahl n -> printfn "%s ist die Zahl %d" s n
| _ -> printfn "%s ist keine Zahl" s
[<Measure>] type m
[<Measure>] type s
let strecke = 100.0<m>
let zeit = 9.58<s>
printfn "%.2f" (float (strecke / zeit))Ausgabe:
gerade ungerade
42 ist die Zahl 42
abc ist keine Zahl
7 ist die Zahl 7
10.44Merke
- Records (
{ Name = ...}) sind unveränderlich;{ r with ... }kopiert mit Änderung - Discriminated Unions beschreiben Alternativen;
matchwird vollständig geprüft optionundResultersetzennullund Ausnahmen bei erwartbaren Fehlern- Aktive Muster und Einheiten (
<m>) erweitern die Ausdruckskraft
Übungsaufgabe
Modelliere einen Ausdrucksbaum mit Division und gib bei Division durch null ein Error zurück.
Quiz zur Selbstkontrolle
Was liefert { p with Alter = 18 }?
- Eine Kopie mit geändertem Feld (richtig)
- Verändert p direkt
- Einen Fehler
- Ein Tupel
Wofür sind Discriminated Unions?
- Für Daten, die genau eine von mehreren Varianten sind (richtig)
- Für Schleifen
- Für Dateien
- Für Threads
Was ersetzt option?
- null für fehlende Werte (richtig)
- Schleifen
- Klassen
- Module
Weiter im Kurs
Zurück: Listen, Arrays, Maps und Sequenzen
Weiter: Klassen, Interfaces und Module
Alle Kapitel: F# im Überblick