sind natürlich die Seele vom Geschäft und auf verschiedene Weise möglich, wobei man immer auf bereits vorhandenes Material aufbaut;
formell besteht eine Funktionsdefinition aus dem Definitionszeichen ``='',
einer linken Seite, die den Namen der Funktion und
(wenn zur Definition nötig) formalen Parametern besteht,
einer rechten Seite, die (evtl. unter Benutzung der formalen Parameter)
ein Ausdruck von entsprechendem Typ ist.
inc :: Int -> Int - Typdeklaration
inc x = x + 1 - Funktionsdefinition
--- - Ausdruck, der benutzt:
- formalen Parameter x,
- vordefinierte Funktionen x und 1
k_aus_n :: Int -> Int -> Int
k_aus_n k n = fac n `div` (fac k * fac (n-k))
negative :: Num(a) => a -> Bool
negative x = x < 0
is_zero :: Num(a) => a -> Bool
is_zero x = x==0
Keine formalen Parameter benötigt man, wenn die neue Funktion eigentlich eine alte ist ...
circle_area :: Float -> Float
circle_area = area
coarse_diff :: (Float -> Float) -> (Float -> Float)
coarse_diff = multi_diff 10.0 - partielle Anwendung, vgl. vorher
Im allgemeinen enthält die rechte Seite einer Definition eine
= die einzelnen Definitionsfälle werden von "`Wächtern"' (guards,
Boolesche Ausdrücke) gestützt;
es wird der (im Sinne der Aufschreibungsreihenfolge) erste Fall genommen,
dessen Wächter sich zu True errechnet.
max2 :: Int -> Int -> Int
max2 x y | x > y = x
| x <= y = y
dafür geht auch:
max2 x y | x > y = x
| otherwise = y
max3 :: Int -> Int -> Int -> Int
max3 x y z | x>=y && x>=z = x
| y>=x && y>=z = y
| otherwise = z
usw.
Beim Aufruf einer Funktion werden aktuelle Parameter (=Ausdrücke, Werte) an die Stelle der formalen Parameter (bloße Bezeichner) gesetzt:
max2 | 17 | (2*3) |
![]() |
![]() |
|
x | y |
Man kann dieses übliche "`matchen"' (auf einander beziehen) von formalen und aktuellen Parametern auch noch verallgemeinern:
Lasse zu, daß ein formaler Parameter ein Muster (pattern) ist, d.h. etwas Strukturiertes.
Typische Beispiele bieten Listen-Datentyp:
eine Liste ist entweder die leere Liste [], oder sie
hat ein Listenelement x gefolgt von einer Restliste
xs, d.h. hat den Aufbau x:xs;
das nimmt man jetzt zur Definition;
sum_the_list :: [Int] -> Int
sum_the_list [] = 0
sum_the_list (x:xs) = x + sum_the_list xs
Beachte:
[ | 1 | , |
![]() |
] | aktuelle Parameter |
![]() |
![]() |
||||
x | xs | pattern-Teile |
Was ist für patterns erlaubt?
vorgaenger :: Int -> Int
vorgaenger 0 = 0 - naja
vorgaenger (n+1) = n
dann folgt:
> vorgaenger 77
76
> vorgaenger (2-3)
Programm Error: vorgaenger(-1)
denn: (-1) läßt sich nicht in der verlangten Weise als n+1 darstellen, und einen Fall für negative Werte gibt es nicht.
head (x:_) = x - Rest der Liste interessiert nicht
- Keine Definition für leere Liste!!
tail (_:xs) = xs - Kopf interessiert nicht,
- nicht definiert für leere Liste!!
Manchmal will man eine Funktion konstruieren, ohne sie einem Bezeichner zuzuweisen, d.h. die Funktion bleibt anonym.
Beispiel: partielle Anwendung einer Funktion, also sowas wie volume 1 1.
Weitere wichtige Möglichkeiten sind:
dann:
x -> exp ist diejenige Funktion, die einem
Wert v den Wert exp
( d.h. v ersetzt x in exp) zuweist.
gegeben: 2.0 * x2 - 1
dann:
x -> (2.0 * x^2 - 1.0)
Das ist die Funktion mit Werten:
x | ... | -1 | 0 | 1 | ... |
Wert | ... | 1 | -1 | 1 | ... |
also graphisch:
Natürlich kann man einen Bezeichner einführen wie z.B.
f = \x -> 2.0 * x^2 - 1.0
aber es ist oft bequem, die unbenannte Funktion als Argument in "`higher order functions"' zu benutzen:
> diff (\x -> 2.0 * x^2 - 1.0) 0
0.00202656
> diff (\x -> 2.0 * x^2 - 1.0) 1
4.00209
> diff (\x -> 2.0 * x^2 - 1.0) (-1)
-3.99792
usw.
Mathematik: gf heißt: erst f, dann darauf g anwenden,
also sowas wie "`2 Verarbeitungsschritte"' hintereinander:
Ergebnis
Input
Die Haskell-Schreibweise für dieselbe Sache ist g.f
Wertebereich von f muß zu Argumentbereich von g passen!
> (diff.diff) cos 0
-1.07288
denn: (-cos)
(-sin)
cos
und - cos 0 = -1
diff.diff cos 0 wird aufgefaßt als
diff.((diff cos) 0) wegen höchster Priorität der Funktionsapplikation!
Funktionsapplikation (bei higher order functions) und Funktionskomposition nicht verwechseln!
(diff.diff) cos 0
| |
| |-- Applikation: diff hat cos als Argument transformiert cos
|
|-- Komposition: zweites diff wird nach dem ersten diff, d.h.
auf Ergebnis vom ersten diff angewendet.
Natürlich kann man dem Kind einen Namen geben, z.B.:
> let diff2 = diff.diff in diff2 cos 0
-1.07288
sqrt . (\x -> x^2) = id in R
| |
| |-- Quadratfunktion
|
|-- vordefinierte Wurzelfunktion
> (sqrt.(\x -> x^2)) 5
5.0
> (sqrt.(\x -> x^2)) (-5) - stimmt nur für x>0
5.0
> diff (sqrt.(\x -> x^2)) (-5)
-0.999927