Funktionsdefinitionen
Next:OperatorenUp:Grundlegende SprachelementePrevious:Typen von Funktionen
Funktionsdefinitionen
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.
- Beispiele:
-
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
- Fallunterscheidung
= 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.- Beispiele:
-
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.
- pattern matching
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;- Beispiel:
-
sum_the_list :: [Int] -> Int
sum_the_list [] = 0
sum_the_list (x:xs) = x + sum_the_list xs
Beachte:
- für jeden pattern-Fall eigene Gleichung;
- bei Anfang wird erst passende Gleichung (d.h. passendes Muster) genommen; gibt es keine Übereinstimmung, resultiert ein Laufzeitfehler;
- Witz an der Sache ist, daß in Verallgemeinerung der formal/aktuell-Beziehung ein Zugriff auf Teile (Konstituenten) des aktuellen Parameterwerts ermöglicht wird:
im Beispiel werden Kopf und Schwanz der Liste an x und xs gebunden:[ 1 , ] aktuelle Parameter x xs pattern-Teile
Was ist für patterns erlaubt?
- Variable (=formale Parameter) und Konstante (=numerische Literale, Char- , Bool-Elemente ),
- Listen, von denen die Elemente wieder Patterns sind, also sowas wie [True, b1, b2],
- mit ``:'' gebildete Listenausdrücke (siehe oben),
- mit ``+'' gebildete arithmetische Ausdrücke der Form n+k, wobei k konstant und n-k>=0 ist,
- ähnlich: Ausdrücke der Form k*n,
- das wild-card-pattern ``_'' auf das alles paßt
- Beispiel:
-
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.
- Beispiel:
- für beliebige Listentypen:
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:
- Lambda-Abstraktion
gegeben: ein Ausdruck exp und ein Variablenbezeichner xdann: x -> exp ist diejenige Funktion, die einem Wert v den Wert exp ( d.h. v ersetzt x in exp) zuweist.
- Beispiel:
-
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.
- Komposition von Funktionen
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!
- Beispiel:
- 2. Ableitung:
> (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
- Beispiel:
- Komposition von Funktion und inverser Funktion gibt Identität, z.B.:
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
Next:OperatorenUp:Grundlegende SprachelementePrevious:Typen von Funktionen Ronald Blaschke
1998-04-19