Typen
Jeder Wert hat einen Typ. Mit :: schreibst du ihn hin. Haskell erschließt Typen meist selbst (Typinferenz); Signaturen schreibt man trotzdem für Funktionen auf Top-Level.
| Typ | Beispiele |
|---|---|
Int | 42 (feste Größe, 64 Bit) |
Integer | 2^100 (beliebig groß) |
Double | 3.14 |
Bool | True, False |
Char | 'a' |
String | "Text" (= [Char], Liste von Zeichen) |
[a] | [1, 2, 3] Liste |
(a, b) | (1, "x") Tupel |
() | Unit |
main :: IO ()
main = do
let n = 42 :: Int
g = 2 ^ 100 :: Integer
d = 3.5 :: Double
c = 'x'
s = "Haskell"
t = (1 :: Int, "eins", True)
print n
print g
print d
print (c, s)
print t
print (fst (1, 'a'), snd (1, 'a'))
print (fromIntegral n * d)
print (truncate d, round 2.5, round 3.5, ceiling 2.1, floor 2.9)
print (maxBound :: Int)
print (show 3.0, show (-2), show [1, 2])42
1267650600228229401496703205376
3.5
('x',"Haskell")
(1,"eins",True)
(1,'a')
147.0
(3,2,4,3,2)
9223372036854775807
("3.0","-2","[1,2]")Zahlen werden nicht automatisch ineinander umgewandelt: fromIntegral wandelt Int/Integer in andere Zahlentypen um.
Funktionen definieren
quadrat :: Int -> Int
quadrat x = x * x
addiere :: Int -> Int -> Int
addiere a b = a + b
gruss :: String -> String
gruss name = "Hallo, " ++ name ++ "!"
fakultaet :: Integer -> Integer
fakultaet 0 = 1
fakultaet n = n * fakultaet (n - 1)
istGerade :: Int -> Bool
istGerade n = n `mod` 2 == 0
main :: IO ()
main = do
print (quadrat 7)
print (addiere 2 3)
putStrLn (gruss "Mia")
print (fakultaet 20)
print (map istGerade [1 .. 5])49 5 Hallo, Mia! 2432902008176640000 [False,True,False,True,False]
Die Signatur addiere :: Int -> Int -> Int liest sich: "nimmt einen Int, dann einen Int, liefert einen Int". Das ist kein Zufall:
Currying und partielle Anwendung
Jede Funktion in Haskell hat genau ein Argument. Mehrere Argumente sind verschachtelte Funktionen: addiere 2 ist selbst eine Funktion (Int -> Int).
addiere :: Int -> Int -> Int
addiere a b = a + b
main :: IO ()
main = do
let plus5 = addiere 5 -- partielle Anwendung
print (plus5 10)
print (map (addiere 100) [1, 2, 3])
print (map (* 2) [1, 2, 3]) -- Operator-Abschnitt (section)
print (map (2 ^) [1, 2, 3, 4])
print (map (^ 2) [1, 2, 3, 4])
print (filter (> 2) [1, 2, 3, 4])
print (map (subtract 1) [5, 6])
print ((.) (+ 1) (* 2) 5) -- Komposition: (+1) nach (*2)
print (((+ 1) . (* 2)) 5)
print ((\x y -> x * 10 + y) 4 2) -- Lambda15 [101,102,103] [2,4,6] [2,4,8,16] [1,4,9,16] [3,4] [4,5] 11 11 42
Der Punkt . verkettet Funktionen ((f . g) x = f (g x)), $ spart Klammern (f $ g x = f (g x)):
main :: IO ()
main = do
print $ sum $ map (* 2) $ filter even [1 .. 10]
print . sum . map (* 2) . filter even $ [1 .. 10]
putStrLn $ "Ergebnis: " ++ show (length [1, 2, 3])60 60 Ergebnis: 3
Typvariablen und Polymorphie
Kleingeschriebene Typnamen sind Typvariablen: Die Funktion gilt für jeden Typ.
erstes :: (a, b) -> a
erstes (x, _) = x
tausche :: (a, b) -> (b, a)
tausche (a, b) = (b, a)
zweimal :: (a -> a) -> a -> a
zweimal f = f . f
groesser :: Ord a => a -> a -> a -- a muss vergleichbar sein
groesser x y = if x > y then x else y
main :: IO ()
main = do
print (erstes (1, "x"))
print (tausche ('a', True))
print (zweimal (+ 3) 10)
print (zweimal ('x' :) "y")
print (groesser 3 7, groesser "apfel" "birne", groesser 2.5 1.5)
print (id 5, const 1 2, flip (-) 1 10)1 (True,'a') 16 "xxy" (7,"birne",2.5) (5,1,9)
Ord a => ist eine Typklasse-Einschränkung ("a muss Ord können"), dazu später mehr.
Merke
- Typen:
Int,Integer,Double,Bool,Char,String, Listen[a], Tupel(a, b) - Signatur
f :: A -> B -> C; Funktionen haben genau ein Argument (Currying) - Partielle Anwendung und Operator-Abschnitte:
(+ 1),(2 ^) .verkettet Funktionen,$spart Klammern,\x -> ...ist ein Lambda- Kleine Typbuchstaben (
a,b) sind Typvariablen (Polymorphie)
Aufgabe
Schreibe hoch3 :: Int -> Int und wende es mit map auf [1..5] an.