Sie sind auf Seite 1von 50

Funktionale Programmierung mit Haskell

Jannis Harder

Funktionale Programmierung mit Haskell Jannis Harder MetaNook 25. November 2011

MetaNook 25. November 2011

Warum?

Funktionale

Programmierung

Funktionen sind Funktionen

Und keine Prozeduren, Methoden, Befehle oder Anweisungen

Funktionen sind Werte

Genauso wie Zahlen, Listen, Texte, Bäume, Bilder…

Außer Werten

und damit auch Funktionen

gibt es nichts

Haskell

Erste Eindrücke

quicksort []

quicksort (p:xs) = quicksort low ++ [p] ++ quicksort high where (low, high) = partition (< p) xs

= []

primes = nubBy (\n p > n `mod` p == 0) [2

]

Anwenden von Funktionen

ghci> sqrt 25

5.0

ghci> min 42 5

5

ghci> 3 * 7 + 2

23

ghci> (+) ((*) 3 7) 2

23

ghci> 345 `mod` 10

5

ghci> min 5 4 + 2

6

ghci> min 5 (4 + 2)

5

Booleans und Strings

Prelude> not True False Prelude> True && False False Prelude> True || False

True Prelude> length "Foobar"

6

Prelude> reverse "!dlrow,olleH"

"Hello,world!"

Typen

Prelude> not "True" Couldn't match expected type `Bool' with actual … … type `[Char]' In the first argument of `not', namely `"True"' In the expression: not "True" Prelude> :type True True :: Bool Prelude> :t "True" "True" :: [Char] Prelude> :t not not :: Bool > Bool Prelude> :t not True not True :: Bool

Eigene Definitionen

definitions.hs

doubleMe x = x + x

Prelude> :load definitions.hs [1 of 1] Compiling Main Ok, modules loaded: Main. *Main> doubleMe 5

10

Alles neu definieren

tripleMe x = 3 * x

*Main> :reload Ok, modules loaded: Main. *Main> tripleMe 5

15

*Main> doubleMe 5 Not in scope: `doubleMe'

Mehr Argumente und Currying

implies a b = not a || b

*Main> :r *Main> True `implies` False False *Main> implies False False True *Main> (implies False) False True *Main> :t implies implies :: Bool > (Bool > Bool) *Main> :t implies False implies False :: Bool > Bool

Listen

*Main> [1,2,3,4]

[1,2,3,4]

*Main> 1:(2:(3:(4:[])))

[1,2,3,4]

*Main> 1:2:3:4:[]

[1,2,3,4]

*Main> head [1,2,3,4]

1

*Main> tail [1,2,3,4]

[2,3,4]

*Main> ['F','o','o'] "Foo" *Main> [True, 'o'] Couldn't match expected type `Bool' with actual … … type `Char'

Maybe?

*Main> [Just 1, Nothing] [Just 1,Nothing] *Main> find (> 9000) [1,2,3] Nothing *Main> find (== 2) [1,2,3] Just 2 *Main> :t Just "something" Just "something" :: Maybe [Char]

Tupel

*Main> (True, 'o') (True,'o') *Main> :t (True, 'o') (True, 'o') :: (Bool, Char) *Main> fst (True, 'o') True *Main> snd (True, 'o') 'o' *Main> (False, '5', 23) (False, '5', 23)

Polymorphie

*Main> :t "Foo" "Foo" :: [Char] *Main> :t [True, True, False] [True, True, False] :: [Bool] *Main> :t [] [] :: [a] *Main> :t head head :: [a] > a *Main> :t (:) (:) :: a > [a] > [a] *Main> :t (,) (,) :: a > b > (a, b) *Main> :t fst fst :: (a, b) > a

Beschränkte Polymorphie

*Main> 42 == 42 True *Main> "Foo" == "Bar" False *Main> '0' == "0" expected type `Char' actual type `[Char]' *Main> reverse == tail No instance for (Eq ([a0] > [a0]))

Typklassen

*Main> :t (==) (==) :: Eq a => a > a > Bool *Main> :info Eq class Eq a where (==) :: a > a > Bool (/=) :: a > a > Bool instance Eq Integer instance Eq Char instance Eq a => Eq [a]

.

.

.

Mehr Listen

*Main> [1

5]

[1,2,3,4,5]

*Main> ['a' 'z']

"abcdefghijklmnopqrstuvwxyz" *Main> [ x^2 | x <reverse [1

[25,16,9,4,1]

*Main> [ x | x <[50

[52,59,66,73,80,87,94]

*Main> [(x, y) | x <["Foo", "Bar"], y <[1

[("Foo",1),("Foo",2),("Foo",3),

5]]

100],

x `mod` 7 == 3]

3]]

("Bar",1),("Bar",2),("Bar",3)]

Pattern Matching

isEmptyList [] = True isEmptyList _ = False

orElse (Just a) _ = a

= b

orElse Nothing

b

majority False False _ = False

majority True

True

_ = True

majority _

_

x = x

end l = case reverse l of

h

: _ > h

Pattern Matching II

*Main> isEmpty "" True

*Main> (find (>5) [1,2,3]) `orElse` 0

0

*Main> (find (>5) [9,8,7]) `orElse` 0

9

*Main> end "Haskell!" '!'

If und Guards

sizeOfNumber x = if x > 9000 then "gigantisch" else "winzig"

sizeOfNumber' x | x > 9000 = "gigantisch"

| x < 100

| otherwise = "normal"

= "winzig"

*Main> [sizeOfNumber x | x <[99, 999, 9999]] ["winzig","winzig","gigantisch"] *Main> [sizeOfNumber' x | x <[99, 999, 9999]] ["winzig","normal","gigantisch"]

Rekursion

selectNth 0 (h : _) = h selectNth n (_ : t) = selectNth (n 1) t

fibonacci = 0 : 1 : [ a + b | (a, b) <zip fibonacci (tail fibonacci)]

Funktionen höherer Ordnung

*Main> succ 1

2

*Main> map succ [0,2,2,6]

[1,3,3,7]

*Main> :t map map :: (a > b) > [a] > [b] *Main> map (reverse . map succ) ["Foo", "Bar"] ["ppG","sbC"] *Main> :t (.) (.) :: (b > c) > (a > b) > a > c

*Main> foldr (++) "!" ["Foo", "Bar", "Baz"] "FooBarBaz!"

Funktionen höherer Ordnung II

collatzStep x | x `mod` 2 == 0 = x `div` 2

x +

collatzSequence = takeWhile (> 1) . iterate collatzStep collatzNumber = length . collatzSequence

1

| otherwise

=

3

*

*Main> :t iterate iterate :: (a > a) > a > [a] *Main> :t takeWhile takeWhile :: (a > Bool) > [a] > [a] *Main> collatzSequence 11

[11,34,17,52,26,13,40,20,10,5,16,8,4,2]

*Main> collatzNumber 27

111

λ-Ausdrücke und lokale Definitionen

*Main> map (\x > x ^ 3 x ^ 2) [1,2,3,4]

[0,4,18,48]

*Main> let f x = x ^ 3 x ^ 2 in map f [1,2,3,4]

[0,4,18,48]

Eigene Datentypen

data Color = Red | Green | Blue | Orange deriving (Show, Read, Eq) data Size = XS | S | M | L | XL | XXL | XXXL deriving (Show, Read, Eq, Ord) data Shape = Circle Float | Rectangle Float Float | Polygon [(Float, Float)] deriving (Show, Eq) data Person = Person { name :: String , age :: Int } deriving (Show, Eq)

Eigene Datentypen II

*Main> [Red, Green, Blue] [Red,Green,Blue] *Main> Red == Green False *Main> [Circle 2.3, Rectangle 5 6] [Circle 2.3, Rectangle 5 6] *Main> name (Person "Jannis" 20) "Jannis"

Eigene polymorphe Datentypen

data OneOrTwo a = One a | Two a a deriving (Show, Eq, Ord)

data List a = Empty | Cons a (List a) deriving (Show, Eq, Ord)

data Tree a = Nil | Branch a (Tree a) (Tree a) deriving (Show, Eq, Ord)

Eigene polymorphe Datentypen

*Main> [One 1, Two 2 3] [One 1,Two 2 3] *Main> Two "Meta" "Meute" Two "Meta" "Meute"

Eigene Typklassen

class Empty a where empty :: a isEmpty :: a > Bool

instance Empty [a] where empty = [] isEmpty [] = True isEmpty _ = False

instance Empty (Tree a) where empty = Nil isEmpty Nil = True isEmpty _ = False

Eigene Typklassen

*Main> isEmpty "" True *Main> isEmpty [1,2,3] False *Main> isEmpty (Branch 1 Nil Nil) False *Main> empty :: Tree Int Nil

Functors

*Main> :info Functor class Functor f where fmap :: (a > b) > f a > f b instance Functor Maybe instance Functor [] *Main> fmap (+1) [1, 2]

[2,3]

*Main> fmap (+1) (Just 1) Just 2 *Main> fmap (+1) Nothing Nothing

Eigene Funktor-Instanzen

instance Functor OneOrTwo where fmap f (One a) = One (f a) fmap f (Two a b) = Two (f a) (f b)

instance Functor Tree where fmap _ Nil = Nil fmap f (Branch a l r) = Branch (f a) (fmap f l) (fmap f r)

Eigene Funktor-Instanzen

*Main> fmap (*2) (One 1) One 2 *Main> fmap (*2) (Two 2 3) One 4 6 *Main> fmap (reverse) (Branch "Meta" (Branch "Meute" Nil Nil) Nil) Branch "ateM" (Branch "etueM" Nil Nil) Nil

Monads

*Main> :info Monad class Monad m where (>>=) :: m a > (a > m b) > m b return :: a > m a instance Monad Maybe instance Monad [] instance Monad IO

join :: Monad m => m (m a) > m a liftM :: Monad m => (a > b) > m a > m b m >>= f = join (liftM f m)

Monads II

foo l = l >>= \x > [2 * x, 4 * x] bar m = m >>= \x > if x > 10 then return x 10 else Nothing

*Main> foo [1,2,3]

[2,4,4,8,6,12]

*Main> foo (return 5)

[10,20]

*Main> bar Nothing Nothing *Main> bar (Just 5) Nothing *Main> bar (Just 20) Just 10

Do-Notation

foo l = do

x <l

[2 * x, 4 * x] bar m = do

x <m

if x > 10 then return x 10 else Nothing

Ein- und Ausgabe

*Main> getLine metameute "metameute" *Main> :t getLine getLine :: IO String *Main> putStrLn "metameute" metameute *Main> :t putStrLn putStrLn :: String > IO () *Main> getLine >>= putStrLn metameute metameute *Main> getLine >>= putStrLn . reverse metanook koonatem

Ein- und Ausgabe mit do-Notation

−− hier die collatz funktionen main = do handleOneInput main

handleOneInput = do n <readLn let sequence = collatzSequence n putStr "collatzsequence:" print sequence putStrLn "length:" print (length sequence)

Module

import Data.Maybe import Data.List import Data.Functor import Control.Monad

Hilfe!

Buch: Learn You a Haskell for Great Good

von Miran Lipovača http://learnyouahaskell.com

Buch: Real World Haskell von Bryan O’Sullivan, Don Stewart und John Goerzen http://book.realworldhaskell.org/

Suchmaschine: Hayoo http://holumbus.fh-wedel.de/hayoo/hayoo.html

Suchmaschine: Hoogle http://www.haskell.org/hoogle/

IRC: #haskell im FreeNode-Netzwerk.

Weitere funktionale Programmiersprachen

Lisp und Scheme

(let ((list '(print "HelloWorld"))) (print list) (eval list))

ML

fun fact (n) = if n=0 then 1 else n*fact(n1);

Scala

List(1,2,3,4,5) map {l => System.out.println(l) }

Erlang

http://goo.gl/ZYj64