In der Funktionalen Programmierung (FP) dreht sich alles um Funktionen.
Jede Funktion hat Parameter und Rückgabewert.
Davon kann man jeweils 0 bis (fast-)beliebig haben.
Es geht im folgenden um: Funktionen, Parameter, Rückgabewert, Tupel, Signaturen
Ein Parameter und ein Rückgabewert ist am einfachsten zu verstehen.
1:
|
let sqr (x) = x * x // x wird quadriert |
Zwei Parameter und ein Rückgabewert
1:
|
let add (x,y) = x + y // x und y wird addiert |
Hier ist (x,y)
ein sogenanntes Tupel, das zwei Werte verbindet.
So ist es möglich mehr als einen Rückgabewert zu verwenden.
Jeder Teilwert eines Tupels kann einen beliebigen Type haben.
Einige nette Wortspielereien zur Auflockerung. Man muss das nicht auswendig wissen. Und nicht verwechseln mit Tribbles 😉
“Ein 2-Tupel nennt man auch geordnetes Paar,
ein 3-Tupel auch Tripel,
ein 4-Tupel auch Quadrupel,
ein 5-Tupel auch Quintupel und so fort.
Das 0-Tupel heißt leeres Tupel und wird durch () notiert.”
Und das ist jetzt ganz wichtig und kann sonst ein Stolperstein sein beim Erlernen von F#. Darum aufgepasst!
Ja und da ist das Verständnis etwas anders als in C# oder C++, aber vollkommen logisch.
Die Klammer gehört zum Tupel und nicht etwa zum “Funktions-Parameter-Rupf”. Man kennt es ja eher so von C# oder C++, was genau den selben Code ergibt wie oben.
1:
|
let add(x,y) = x + y // kein space zwischen "add" und "(" |
Aber beim obigen ist es klarer als Tupel zu erkennen.
Wichtig ist den Unterschied zwischen Tupel und dem “Funktionsrumpf” zu erkennen und es NICHT als “Funktionsrumpf” zu deuten!
Zwei Parameter und zwei Rückgabewerte
1:
|
let swap (x,y) = (y,x) // Die Werte im Tupel werden vertauscht |
Weil der Rückgabewert hier ein Tupel ist, können zwei Werte zurückgegeben werden.
Drei Parameter und drei Rückgabewerte
1:
|
let shift (x,y,z) = (y,z,x) // Die Werte im Tupel werden nach links geschoben |
Kein Parameter und kein Rückgabewert
Jetzt wird es interessant und wird doch vertraut vorkommen.
1:
|
let nop () = () // keine Operation |
Tupel Zusammenfassung
Ein leeres Tupel wird so ()
geschrieben.
Man nennt es Unit
oder seltener auch 0-Tupel und damit ist Unit
ganz logisch.
Ein 1-Tupel wäre dann z.B. ("X")
was ausgewertet val it : string = "X"
entspricht, also wie "X"
.
D.h. bei 1-Tupel kann man auch die Klammer weglassen.
1: 2: 3: |
let sqr (x) = (x * x) // val sqr : x:int -> int let sqr (x) = x * x // val sqr : x:int -> int let sqr x = x * x // val sqr : x:int -> int |
Diese Parameter sind Tupel
1: 2: |
let add (x,y) = x + y // val add : x:int * y:int -> int let add(x,y) = x + y // val add : x:int * y:int -> int // kein space zwischen "add" und "(" |
Und Achtung, diese Parameter sind KEINE Tupel
1:
|
let add x y = x + y // val add : x:int -> y:int -> int |
Ein n-Tupel hat dann die Anzahl max 0 (n-1)
von Kommas (x , y)
in der Schreibweise und Sternchen (x * y)
in der Signatur.
Funktions Signatur
Funktionen und deren Signatur.
Die Signatur muss man nicht selbst deklarieren, sie entsteht automatisch durch die Komposition der Parameter und des Rückgabewertes.
1: 2: |
let add x y = x + y // val add : x:int -> y:int -> int let add (x,y) = x + y // val add : x:int * y:int -> int |
Bedeutung der Symbole der Signatur
x:int
: Der Doppelpunkt sagt der Wert x ist vom Typeint
->
: Der Pfeil trennt die aneinander Reihung von Parametern und Rückgabewert (immer als Letztes)*
: Der Stern steht quasi als Ersatz für das Komma im Tupel. Das kommt daher, dass ein Tupel ein Produkt Type ist. Produkt im Sinne von Multiplikation. Mehr dazu in einem späteren Kapitel.
Wichtig: Der Letzte ist immer der Rückgabewert. Und es gibt IMMER einen Rückgabewert, er kann auch Unit () sein.
Hier noch ein Signatur Beispiel mit unit
1:
|
let nop () = () // val nop : unit -> unit |
Wichtig: Kommt kein
->
in der Signatur vor, dann ist es keine Funktion, sondern ein Wert.
Unit kann auch im Tupel vorkommen:
1:
|
let nop2 () = ((),()) // val nop2 : unit -> unit * unit |
Und weil das Wesentliche eines Tupels das Komma ist, kann man die Klammerung in bestimmen Fällen weglassen, ausser bei () und links von =
einer let
Anweisung.
1: 2: 3: 4: |
let nop2 () = ((),()) // val nop2 : unit -> unit * unit let nop2 () = (),() // val nop2 : unit -> unit * unit let nop4 ((),()) = (),() // val nop4 : unit * unit -> unit * unit let nop4 (),() = (),() // error FS0039: The pattern discriminator 'nop4' is not defined |
Tupels sind praktisch und ohne grosses Zeremoniell definiert, man braucht nur Komma und Klammer.
Generell können beliebige Tupels in Tupeln vorkommen.
Die weiteren Vorteile zu Tupeln werden dann im Kapitel Pattern Matching behandelt.
Ausstehende Kapitel
Themen die hier angeschnitten wurden.
- Pattern Matching mit Tupel
- Produkt Type
Full name: FunktioneninF.sqr
Full name: FunktioneninF.add
Full name: FunktioneninF.swap
Full name: FunktioneninF.shift
Full name: FunktioneninF.nop
Full name: FunktioneninF.add
Full name: FunktioneninF.nop2
Full name: FunktioneninF.nop4