EMZETT.
Login

Kurz: let pfad = Path.Combine(Path.GetTempPath(), “fsharp-demo.txt”) File.WriteAllLines(pfad, [ “eins”; “zwei”; “drei” ]) File.AppendAllText(pfad, “vier\n”) let zeilen = File.ReadAllLines pfad printfn “%d Zeilen.

Teil des Kurses F#

Kapitel 6 von 8 im Kurs F#. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Strings

let s = "Hallo F#-Welt"
printfn "%d %s %s" s.Length (s.ToUpper()) (s.ToLower())
printfn "%c %s %s" s.[0] (s.Substring(6, 2)) s.[6..7]
printfn "%s" (s.Replace("Welt", "Erde"))
printfn "%A" (s.Split([| ' '; '-' |]))
printfn "%b %b" (s.Contains "F#") (s.StartsWith "Hallo")
printfn "%s" (String.concat ", " [ "a"; "b"; "c" ])
printfn "%s" (String.replicate 3 "ab")
printfn "[%s]" ("  trim  ".Trim())
printfn "%d" (s.IndexOf "F#")
printfn "%s" (System.String(Array.rev (s.ToCharArray())))
printfn "%A" (s |> Seq.filter System.Char.IsUpper |> Seq.toList)
printfn "%s" (sprintf "%05d|%-6s|%8.3f|%x|%e" 42 "li" 3.14159 255 12345.678)
printfn "%s" ((42).ToString("D5"))
printfn "%s" (System.String.Join("-", [| 1; 2; 3 |]))
let name = "Mia"
let alter = 17
printfn "%s" $"{name} ist {alter} Jahre alt, nächstes Jahr {alter + 1}"
printfn "%s" $"{3.14159:F2} | {255:X} | {1234567:N0}"
printfn "%d" (int "17" + 1)
printfn "%A" (System.Int32.TryParse "abc")
printfn "%A" (System.Int32.TryParse "42")
printfn "%s" (string (char (int 'A' + 1)))
printfn "%s" ("""Dreifache "Anführungszeichen" erlauben Text mit "Zitaten".""")
printfn "%s" (@"C:\Pfad\ohne\Escapes")

Ausgabe:

13 HALLO F#-WELT hallo f#-welt
H F# F#
Hallo F#-Erde
[|"Hallo"; "F#"; "Welt"|]
true true
a, b, c
ababab
[trim]
6
tleW-#F ollaH
['H'; 'F'; 'W']
00042|li    |   3.142|ff|1.234568e+004
00042
1-2-3
Mia ist 17 Jahre alt, nächstes Jahr 18
3.14 | FF | 1,234,567
18
(false, 0)
(true, 42)
B
Dreifache "Anführungszeichen" erlauben Text mit "Zitaten".
C:\Pfad\ohne\Escapes

Dateien

open System.IO
 
let pfad = Path.Combine(Path.GetTempPath(), "fsharp-demo.txt")
File.WriteAllLines(pfad, [ "eins"; "zwei"; "drei" ])
File.AppendAllText(pfad, "vier\n")
let zeilen = File.ReadAllLines pfad
printfn "%d Zeilen: %A" zeilen.Length zeilen
printfn "%d" (File.ReadAllText(pfad).Length)
 
for zeile in File.ReadLines pfad do
    printfn "> %s" zeile
 
use leser = new StreamReader(pfad)
printfn "%s" (leser.ReadLine())
leser.Close()
 
printfn "%b %d" (File.Exists pfad) (FileInfo(pfad).Length)
File.Delete pfad
printfn "%b" (File.Exists pfad)
 
printfn "%s %s" (Path.GetFileName "/tmp/a/b.txt") (Path.GetExtension "b.txt")
let verz = Path.Combine(Path.GetTempPath(), "fsdir")
Directory.CreateDirectory verz |> ignore
File.WriteAllText(Path.Combine(verz, "a.txt"), "A")
File.WriteAllText(Path.Combine(verz, "b.txt"), "B")
printfn "%A" (Directory.GetFiles(verz) |> Array.map Path.GetFileName |> Array.sort)
Directory.Delete(verz, true)

Ausgabe:

4 Zeilen: [|"eins"; "zwei"; "drei"; "vier"|]
20
> eins
> zwei
> drei
> vier
eins
true 20
false
b.txt .txt
[|"a.txt"; "b.txt"|]

Async und Tasks

Asynchrone Abläufe schreibt man in async { } oder task { }:

let berechne n = async {
    do! Async.Sleep 10
    return n * n
}
 
let ergebnisse =
    [ 1 .. 5 ]
    |> List.map berechne
    |> Async.Parallel
    |> Async.RunSynchronously
printfn "%A" ergebnisse
 
let aufgabe = task {
    do! System.Threading.Tasks.Task.Delay 10
    return "fertig"
}
printfn "%s" aufgabe.Result
 
let ablauf = async {
    let! a = berechne 3
    let! b = berechne 4
    return a + b
}
printfn "%d" (Async.RunSynchronously ablauf)
 
let mit_fehler = async {
    try
        failwith "kaputt"
        return 0
    with e -> return -1
}
printfn "%d" (Async.RunSynchronously mit_fehler)

Ausgabe:

[|1; 4; 9; 16; 25|]
fertig
25
-1

Eigene Computation Expressions

async und seq sind Computation Expressions. Mit einem Builder definiert man eigene:

type MaybeBuilder() =
    member _.Bind(x, f) = Option.bind f x
    member _.Return(x) = Some x
    member _.ReturnFrom(x) = x
 
let maybe = MaybeBuilder()
 
let teile a b = if b = 0 then None else Some(a / b)
 
let rechnung a b c = maybe {
    let! x = teile a b
    let! y = teile x c
    return x + y
}
 
printfn "%A" (rechnung 100 5 2)
printfn "%A" (rechnung 100 0 2)
 
let protokoll = ResizeArray<string>()
type LogBuilder() =
    member _.Bind(x, f) = protokoll.Add(sprintf "Wert %A" x); f x
    member _.Return(x) = x
let log = LogBuilder()
let r = log {
    let! a = 5
    let! b = a * 2
    return a + b
}
printfn "%d %A" r (List.ofSeq protokoll)

Ausgabe:

Some 30
None
15 ["Wert 5"; "Wert 10"]

Merke

  • Strings: .NET-Methoden, sprintf, $"...", Dreifach-Anführungszeichen, @"..."
  • Dateien mit System.IO.File, Path, Directory, use für Aufräumen
  • async { } und task { } mit let!/do!; Async.Parallel für Parallelität
  • Computation Expressions mit eigenem Builder (Bind, Return) bauen eigene „Syntaxen“

Übungsaufgabe

Lade mit Async.Parallel drei Dateien gleichzeitig und summiere ihre Längen.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Klassen, Interfaces und Module

Weiter: Praxis und Ökosystem

Alle Kapitel: F# im Überblick