Kleine Projekte
// Primzahlsieb
let sieb n =
let prim = Array.create (n + 1) true
for i in 2 .. int (sqrt (float n)) do
if prim.[i] then
for j in i * i .. i .. n do prim.[j] <- false
[ for i in 2 .. n do if prim.[i] then yield i ]
printfn "%A" (sieb 50)
printfn "%d" (List.length (sieb 10000))
// Caesar
let verschiebe (text: string) schluessel =
text
|> String.map (fun c ->
if System.Char.IsLower c then char ((int c - int 'a' + schluessel + 26) % 26 + int 'a')
elif System.Char.IsUpper c then char ((int c - int 'A' + schluessel + 26) % 26 + int 'A')
else c)
let geheim = verschiebe "Hallo F#!" 3
printfn "%s" geheim
printfn "%s" (verschiebe geheim -3)Ausgabe
[2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47] 1229 Kdoor I#! Hallo F#!
Datenanalyse mit Pipelines
type Verkauf = { Region: string; Produkt: string; Menge: int; Preis: decimal }
let daten =
[ { Region = "Nord"; Produkt = "Apfel"; Menge = 10; Preis = 1.20m }
{ Region = "Sued"; Produkt = "Birne"; Menge = 5; Preis = 0.80m }
{ Region = "Nord"; Produkt = "Birne"; Menge = 8; Preis = 0.80m }
{ Region = "West"; Produkt = "Apfel"; Menge = 20; Preis = 1.10m }
{ Region = "Sued"; Produkt = "Apfel"; Menge = 7; Preis = 1.20m } ]
let umsatz v = decimal v.Menge * v.Preis
daten
|> List.groupBy (fun v -> v.Region)
|> List.sortBy fst
|> List.iter (fun (region, vs) ->
printfn "%-5s %7.2f" region (vs |> List.sumBy umsatz))
printfn "Gesamt: %M" (daten |> List.sumBy umsatz)
let bester = daten |> List.maxBy umsatz
printfn "Bester Posten: %s in %s" bester.Produkt bester.Region
printfn "Äpfel: %d" (daten |> List.filter (fun v -> v.Produkt = "Apfel") |> List.sumBy (fun v -> v.Menge))Ausgabe
Nord 18.40 Sued 12.40 West 22.00 Gesamt: 52.80 Bester Posten: Apfel in West Äpfel: 37
Tests und Ausnahmen
exception MeinFehler of string
let pruefe n =
if n < 0 then raise (MeinFehler(sprintf "negativ: %d" n))
n
try
printfn "%d" (pruefe 3)
printfn "%d" (pruefe -1)
with
| MeinFehler msg -> printfn "Eigener Fehler: %s" msg
| ex -> printfn "anderer Fehler: %s" ex.Message
let ergebnis =
try Ok(int "abc")
with :? System.FormatException as e -> Error e.Message
printfn "%A" ergebnis
try
try failwith "innen"
finally printfn "finally läuft immer"
with e -> printfn "außen gefangen: %s" e.Message
let mutable bestanden = 0
let mutable fehler = 0
let pruefeGleich name erwartet erhalten =
if erwartet = erhalten then
bestanden <- bestanden + 1
printfn "ok %s" name
else
fehler <- fehler + 1
printfn "FEHLER %s" name
pruefeGleich "Addition" 3 (1 + 2)
pruefeGleich "Liste" [ 2; 4 ] (List.map ((*) 2) [ 1; 2 ])
pruefeGleich "absichtlich" 5 (2 * 2)
printfn "%d bestanden, %d fehlgeschlagen" bestanden fehlerAusgabe
3 Eigener Fehler: negativ: -1 Error "The input string 'abc' was not in a correct format." finally läuft immer außen gefangen: innen ok Addition ok Liste FEHLER absichtlich 2 bestanden, 1 fehlgeschlagen
Ökosystem
| Werkzeug | Zweck |
|---|---|
dotnet CLI, NuGet | Build und Pakete |
F# Interactive (dotnet fsi) | REPL und Skripte (.fsx) |
| Ionide (VS Code), Rider | Editoren |
| FSharp.Data | JSON/CSV/HTML-Typprovider |
| Giraffe, Saturn, Falco | Web-Frameworks |
| Fable | F# nach JavaScript |
| Expecto, xUnit, FsCheck | Tests (inkl. Property-based) |
| Paket, FAKE | Paketmanager und Build-DSL |
| Polyglot Notebooks | Datenanalyse in Notebooks |
Merke
- F# eignet sich hervorragend für Datenverarbeitung, Domänenmodelle und Parser
- Pipelines (
|>) und Records/Unions machen Code kurz und lesbar - Ausnahmen mit
try ... with,exceptionundfailwith - Das .NET-Ökosystem steht vollständig zur Verfügung
Aufgabe
Baue einen kleinen Taschenrechner-Parser mit Discriminated Unions.