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 None
Aktive 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.44
Merke
- 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
Aufgabe
Modelliere einen Ausdrucksbaum mit Division und gib bei Division durch null ein Error zurück.