Praktische Informatik 3: Funktionale Programmierung Vorlesung 6 vom 20.11.2012: Funktionen Höherer Ordnung II
Christoph Lüth Universität Bremen Wintersemester 2012/13
Rev. 1874 1 [33]
Fahrplan
I Teil I: Funktionale Programmierung im Kleinen
IEinführung
IFunktionen und Datentypen
IRekursive Datentypen
ITypvariablen und Polymorphie
IFunktionen höherer Ordnung I
IFunktionen höherer Ordnung II
ITypinferenz
I Teil II: Funktionale Programmierung im Großen I Teil III: Funktionale Programmierung im richtigen Leben
2 [33]
Funktionen höherer Ordnung
I mapundfiltersind durchfoldrdarstellbar:
map :: (α→ β)→ [α]→ [β] map f = f o l d r (λa b→ f a : b ) [ ]
f i l t e r :: (α→ B o o l )→ [α]→ [α]
f i l t e r p= f o l d r (λa a s→ i f p a then a : a s e l s e a s ) [ ]
foldrist diekanonische einfach rekursiveFunktion.
I Alle einfach rekursiven Funktionen sind als Instanz vonfoldr darstellbar.
foldr (:) [ ]=id
3 [33]
map als strukturerhalten Abbildung
mapist die kanonischestrukturerhaltende Abbildung.
I Struktur(Shape) eines DatentypsTαistT().
IFür Listen: [()]∼=Nat.
I Fürmapgelten folgende Aussagen:
map id=id map f◦map g=map (f◦g) length (map f xs)=length xs
4 [33]
Grenzen von foldr
I Andere rekursive Struktur über Listen
I Quicksort:baumartigeRekursion q s o r t :: Ord a⇒ [ a ]→ [ a ] q s o r t [ ] = [ ]
q s o r t x s = q s o r t ( f i l t e r (< he ad x s ) x s ) ++
f i l t e r ( h ead x s ==) x s ++
q s o r t ( f i l t e r ( hea d x s <) x s ) I Rekursion nicht über Listenstruktur:
I take: Rekursion überInt t a k e :: I n t→ [ a ]→ [ a ]
t a k e n _ | n≤ 0 = [ ]
t a k e _ [ ] = [ ]
t a k e n ( x : x s ) = x : t a k e ( n−1) x s
I Version mitfoldrdivergiert für nicht-endliche Listen
5 [33]
fold für andere Datentypen
foldist universell
Jeder algebraische DatentypThat genau einfoldr.
I Kanonische Signatur fürT:
IPro KonstruktorCein FunktionsargumentfC IFreie TypvariableβfürT
I Kanonische Definition:
IPro KonstruktorCeine Gleichung
IGleichung wendet FunktionsparameterfCauf Argumente an
data I L = Cons I n t I L | E r r S t r i n g | Mt
f o l d I L :: ( I n t→ β→ β)→ ( S t r i n g→ β)→ β→ I L→ β f o l d I L f e a ( Cons i i l ) = f i ( f o l d I L f e a i l ) f o l d I L f e a ( E r r s t r ) = e s t r
f o l d I L f e a Mt = a
6 [33]
fold für bekannte Datentypen
I Bool: Fallunterscheidung:
data B o o l = True | F a l s e f o l d B o o l :: β→ β→ B o o l→ β f o l d B o o l a1 a2 True = a1 f o l d B o o l a1 a2 F a l s e = a2 I Maybe a: Auswertung
data Maybe α = N o t h i n g | J u s t α f o l d M a y b e :: β→ (α→ β)→ Maybe α→ β f o l d M a y b e b f N o t h i n g = b
f o l d M a y b e b f ( J u s t a ) = f a
I Alsmaybevordefiniert
fold für bekannte Datentypen
I Tupel: dieuncurry-Funktion
f o l d P a i r :: (α→ β→ γ)→ (α, β)→ γ f o l d P a i r f ( a , b )= f a b
I Natürliche Zahlen: Iterator
type Nat= I n t −−data Nat = Zero | Succ Nat f o l d N a t :: β→ (β→ β)→ Nat→ β f o l d N a t e f x | x == 0 = e
f o l d N a t e f x | x > 0 = f ( f o l d N a t e f ( x−1))
fold für binäre Bäume
I Binäre Bäume:
data T r e e α=Mt | Node α ( T r e e α) ( T r e e α)
I Labelnurin den Knoten I Instanzen von Map und Fold:
mapT :: (α→ β)→ T r e e α→ T r e e β mapT f Mt =Mt
mapT f ( Node a l r )=
Node ( f a ) ( mapT f l ) ( mapT f r ) f o l d T :: (α→ β→ β→ β)→ β→ T r e e α→ β f o l d T f e Mt= e
f o l d T f e ( Node a l r ) =
f a ( f o l d T f e l ) ( f o l d T f e r )
I Kein (offensichtliches) Filter
9 [33]
Funktionen mit fold und map
I Höhe des Baumes berechnen:
h e i g h t :: T r e e α→ I n t
h e i g h t = f o l d T (λ_ l r→ 1+ l+ r ) 0
I Inorder-Traversion der Knoten:
i n o r d e r :: T r e e α→ [α]
i n o r d e r = f o l d T (λa l r→ l++ [ a ]++ r ) [ ]
10 [33]
Kanonische Eigenschaften von foldT und mapT
I Auch hier gilt:
foldTree Node Mt=id mapTree id=id
mapTree f◦mapTree g=mapTree (f◦g) shape (mapTree f xs)=shape xs
I Mitshape:: Treeα→Tree ()
11 [33]
Das Labyrinth
I Das Labyrinth als variadischer Baum:
data Lab a = Node a [ Lab a ]
I Auch hierfürfoldTundmapT:
f o l d T :: ( a→ [ b ]→ b )→ Lab a→ b
f o l d T f ( Node a n s ) = f a ( map ( f o l d T f ) n s )
mapT :: ( a→ b )→ Lab a→ Lab b
mapT f ( Node a n s ) =Node ( f a ) ( map ( mapT f ) n s )
12 [33]
Suche im Labyrinth
I Tiefensuche viafoldT
d f t s ’ :: Lab a→ [ Path a ] d f t s ’ n = f o l d T add n where
add a [ ] = [ [ a ] ]
add a p s = concatMap ( map ( a : ) ) p s
I Problem:
I foldTterminiertnichtfürzyklischeStruturen
I Auch nicht, wennaddprüft obaschon enthalten ist
13 [33]
Tiefensuche direkt
I Deshalbdirekterekursive Lösung
d f t s :: Eq a⇒ ( Lab a→ B o o l )→ Lab a→ [ Path a ] d f t s t r g = d f t s 0 [ ] where
d f t s 0 p n@ ( Node a n s )
| t r g n = [ r e v e r s e ( a : p ) ]
| e l e m a p = [ ]
| o t h e r w i s e = concatMap ( d f t s 0 ( a : p ) ) n s I Funktioniert füralleLabyrinthe
I Flexible Termination durch Prädikat
IBlätter in gerichteten Graphen i s L e a f ( Node a n s ) = n u l l n s
IBlätter in ungerichteten Graphen
i s U L e a f ( Node a n s ) = n u l l n s | | n u l l ( t a i l n s )
14 [33]
Zusammenfassung map und fold
I mapundfoldsindkanonischeFunktionen höherer Ordnung
I Für jeden Datentyp definierbar
I foldlnur für Listen (linearerDatentyp)
I foldkann beizyklischenArgumenten nicht terminieren
I Problem: Termination vonfoldnurlokalentscheidbar
15 [33]
Funktionen Höherer Ordnung als Entwurfsmethodik
I Kombinationvon Basisoperationen zu komplexen Operationen I KombinatorenalsMusterzur Problemlösung:
IEinfacheBasisoperationen
IWenigeKombinationsoperationen
IAlle anderen Operationenabgeleitet I Kompositionalität:
IGesamtproblem läßt sichzerlegen
IGesamtlösung durchZusammensetzender Einzellösungen
16 [33]
Kombinatoren im engeren Sinne
Definition (Kombinator)
EinKombinatorist ein punktfrei definierte Funktion höherer Ordnung.
I Herkunft:Kombinatorlogik(Schönfinkel, 1924)
K x y B x S x y z B x z(y z)
I x B x
S,K,IsindKombinatoren
I Fun fact #1: kann alle berechenbaren Funktionen ausdrücken I Fun fact #2:SundKsind genug:I=S K K
17 [33]
Beispiel: Parser
I Parserbilden Eingabe auf Parsierungen ab
IMehrere Parsierungenmöglich
IBacktrackingmöglich I Kombinatoransatz:
IBasisparsererkennenTerminalsymbole
IParserkombinatorenzur Konstruktion:
ISequenzierung(erstA, dannB)
IAlternierung(entwederAoderB)
IAbgeleiteteKombinatoren (z.B.ListenA∗,nicht-leereListenA+)
18 [33]
Modellierung in Haskell
WelcherTypfür Parser?
typeParse a b = [a]→[(b, [a])]
I Parametrisiert überEingabetyp(Token)aundErgebnisb I Parser übersetztTokeninabstrakte Syntax
I MussRest der Eingabemodellieren I Mussmehrdeutige Ergebnissemodellieren
I Beispiel:"3+4*5" [ (3,"+4*5"), (3+4,"*5"), (3+4*5,"")]
19 [33]
Basisparser
I Erkenntnichts:
none :: P a r s e a b none = c o n s t [ ] I Erkenntalles:
s u c e e d :: b→ P a r s e a b s u c e e d b i n p = [ ( b , i n p ) ] I Erkennteinzelne Token:
s p o t :: ( a→ B o o l )→ P a r s e a a s p o t p [ ] = [ ]
s p o t p ( x : x s ) = i f p x then [ ( x , x s ) ] e l s e [ ] t o k e n :: Eq a⇒ a→ P a r s e a a
t o k e n t = s p o t (λc→ t == c )
IWarum nichtnone,suceeddurchspot? Typ!
20 [33]
Basiskombinatoren: alt, >*>
I Alternierung:
I Erste Alternative wirdbevorzugt i n f i x l 3 ‘ a l t ‘
a l t :: P a r s e a b→ P a r s e a b→ P a r s e a b a l t p1 p2 i = p1 i ++ p2 i
I Sequenzierung:
I Rest des ersten Parsers alsEingabefür den zweiten i n f i x l 5 >∗>
(>∗>) :: P a r s e a b→ P a r s e a c→ P a r s e a ( b , c ) (>∗>) p1 p2 i =
concatMap (λ( b , r )→
map (λ( c , s )→ ( ( b , c ) , s ) ) ( p2 r ) ) ( p1 i )
21 [33]
Basiskombinatoren: use
I mapfür Parser (Rückgabeweiterverarbeiten):
i n f i x 4 ‘ u s e ‘ , ‘ u s e 2 ‘
u s e :: P a r s e a b→ ( b→ c )→ P a r s e a c u s e p f i = map (λ( o , r )→ ( f o , r ) ) ( p i ) u s e 2 :: P a r s e a ( b , c )→ ( b→ c→ d )→ P a r s e a d u s e 2 p f = u s e p ( u n c u r r y f )
I Damit z.B. Sequenzierungrechts/links:
i n f i x l 5 ∗>, >∗
(∗>) :: P a r s e a b→ P a r s e a c→ P a r s e a c (>∗) :: P a r s e a b→ P a r s e a c→ P a r s e a b p1 ∗> p2 = p1 >∗> p2 ‘ u s e ‘ s n d
p1 >∗ p2 = p1 >∗> p2 ‘ u s e ‘ f s t
22 [33]
Abgeleitete Kombinatoren
I Listen: A∗::=AA∗|ε l i s t :: P a r s e a b→ P a r s e a [ b ] l i s t p = p>∗> l i s t p ‘ u s e 2 ‘ ( : )
‘ a l t ‘ s u c e e d [ ]
I Nicht-leereListen: A+::=AA∗ some :: P a r s e a b→ P a r s e a [ b ] some p = p>∗> l i s t p ‘ u s e 2 ‘ ( : )
I NB. Präzedenzen:>*>(5) voruse(4) voralt(3)
Verkapselung
I Hauptfunktion:
IEingabe mußvollständigparsiert werden
IAufMehrdeutigkeitprüfen
p a r s e :: P a r s e a b→ [ a ]→ E i t h e r S t r i n g b p a r s e p i =
c a s e f i l t e r ( n u l l . s n d ) $ p i o f
[ ] → L e f t " I n p u t ␣ d o e s ␣ n o t ␣ p a r s e "
[ ( e , _ ) ] → R i g h t e
_ → L e f t " I n p u t ␣ i s ␣ a m b i g u o u s "
I Schnittstelle:
INach außen nur TypParsesichtbar, plusOperationendarauf
Grammatik für Arithmetische Ausdrücke
Expr ::= Term+Term|Term Term ::= Factor*Factor|Factor Factor ::= Variable|(Expr) Variable ::= Char+
Char ::= a| · · · |z|A| · · · |Z
25 [33]
Abstrakte Syntax für Arithmetische Ausdrücke
I Zur Grammatikabstrakte Syntax data E x p r = P l u s E x p r E x p r
| Times E x p r E x p r
| Var S t r i n g
I Hier UnterscheidungTerm,Factor,Numberunnötig.
26 [33]
Parsierung Arithmetischer Ausdrücke
I Token:Char I Parsierung vonFactor
p F a c t o r :: P a r s e Char E x p r
p F a c t o r =some ( s p o t i s A l p h a ) ‘ u s e ‘ Var
‘ a l t ‘ t o k e n ’ ( ’ ∗> p E x p r >∗ t o k e n ’ ) ’ I Parsierung vonTerm
pTerm :: P a r s e Char E x p r
pTerm =
p F a c t o r >∗ t o k e n ’∗’ >∗> p F a c t o r ‘ u s e 2 ‘ Times
‘ a l t ‘ p F a c t o r I Parsierung vonExpr
p E x p r :: P a r s e Char E x p r
p E x p r =pTerm >∗ t o k e n ’+’ >∗>pTerm ‘ u s e 2 ‘ P l u s
‘ a l t ‘ pTerm
27 [33]
Die Hauptfunktion
I Lexing:Leerzeichenaus der Eingabeentfernen p a r s e E x p r :: S t r i n g→ E x p r
p a r s e E x p r i =
c a s e p a r s e p E x p r ( f i l t e r ( n o t . i s S p a c e ) i ) o f R i g h t e → e
L e f t e r r → e r r o r e r r
28 [33]
Ein kleiner Fehler
I Mangel:a+b+cführt zuSyntaxfehler— Fehler in derGrammatik I Behebung:Änderungder Grammatik
Expr ::= Term+Expr|Term Term ::= Factor*Term|Factor Factor ::= Variable|(Expr) Variable ::= Char+
Char ::= a| · · · |z|A| · · · |Z
I Abstrakte Syntaxbleibt
29 [33]
Änderung des Parsers
I Entsprechende Änderung des Parsers inpTerm pTerm :: P a r s e Char E x p r
pTerm =
p F a c t o r >∗ t o k e n ’∗’ >∗> pTerm ‘ u s e 2 ‘ Times
‘ a l t ‘ p F a c t o r I . . . und inpExpr:
p E x p r :: P a r s e Char E x p r
p E x p r = pTerm >∗ t o k e n ’+’ >∗> p E x p r ‘ u s e 2 ‘ P l u s
‘ a l t ‘ pTerm
I pFactorund Hauptfunktion bleiben.
30 [33]
Erweiterung zu einem Taschenrechner
I Zahlen:
Factor ::= Variable|Number|. . . Number ::= Digit+
Digit ::= 0| · · · |9 I Eine einfacheEingabesprache:
Input ::= !Variable=Expr|$Expr I EineAuswertungsfunktion:
type S t a t e= [ ( S t r i n g , I n t e g e r ) ] e v a l :: S t a t e→ E x p r→ I n t e g e r
r u n :: S t a t e→ S t r i n g→ ( S t a t e , S t r i n g )
31 [33]
Zusammenfassung Parserkombinatoren
I Systematische Konstruktiondes Parsers aus der Grammatik.
I Kompositional:
ILokale Änderung der Grammatik führt zu lokaler Änderung im Parser
IVgl. Parsergeneratoren (yacc/bison, antlr, happy)
I Struktur vonParsezur Benutzung irrelevant
IVorsicht beiMehrdeutigkeitenin der Grammatik (Performance-Falle)
IEinfache Implementierung(wie oben) skaliertnicht
IEffiziente Implementation mitgleicher Schnittstelleauch fürgroße Eingabengeeignet.
32 [33]
Zusammenfassung
I mapundfoldsind kanonische Funktionen höherer Ordnung I . . . und für alle Datentypen definierbar
I Kombinatoren: Funktionen höherer Ordnung alsEntwurfsmethodik
I EinfacheBasisoperationen
I WenigeabermächtigeKombinationsoperationen
I Reiche Bibliothek anabgeleitetenOperationen
I Nächste Woche: wie prüft man den Typ von (>∗>) p1 p2 i =
concatMap (λ( b , r )→
map (λ( c , s )→ ( ( b , c ) , s ) ) ( p2 r ) ) ( p1 i ) Typinferenz!
33 [33]