Fachrichtung 6.2 — Informatik Universität des Saarlandes Tutorenteam der Vorlesung Programmierung 1 Programmierung 1 (Wintersemester 2015/16) Lösungsblatt: Aufgaben für die Übungsgruppen: 4 (Kapitel 4) Hinweis: Dieses Übungsblatt enthält von den Tutoren für die Übungsgruppe erstellte Aufgaben. Die Aufgaben und die damit abgedeckten Themenbereiche sind für die Klausur weder relevant noch irrelevant. 1 Einstieg in Listen Aufgabe 4.1 (Pattern Matching) Geben Sie an, welche der folgenden Ausdrücke auf die folgenden Muster passen, und welche Bindungen dabei erzeugt werden. Muster → ↓ Wert (2,4) ([2],[4]) [1,2,3] [1,3] [[]] [[3]] [(1,2)] ([],[]) ((),()) nil::nil (nil::nil)::nil nil::nil::nil Lösung 4.1: Muster → ↓ Wert (2,4) ([2],[4]) [1,2,3] [1,3] [[]] [[3]] [(1,2)] ([],[]) ((),()) nil nil::nil (nil::nil)::nil nil::nil::nil (a,_) x [1,_] _::x::_ a::b (a::b)::c (a,_) x [1,_] _::x::_ a::b (a::b)::c 3 3 7 7 7 7 7 3 3 7 7 7 7 3 3 3 3 3 3 3 3 3 3 3 3 3 7 7 7 3 7 7 7 7 7 7 7 7 7 7 7 3 3 7 7 7 7 7 7 7 7 3 7 7 3 3 3 3 3 7 7 7 3 3 3 7 7 7 7 7 3 7 7 7 7 7 3 7 Aufgabe 4.2 Entscheiden Sie, ob die folgenden Ausdrücke wohlgetypt sind und geben Sie die Typen an. (a) nil 1 (b) (2 :: nil) :: nil (c) [(true, 5), (false, 28)] (d) (nil :: nil) :: 1 :: nil :: nil :: nil (e) (nil :: nil :: nil) @ nil :: [[]] Lösung 4.2: (a) α list (b) int list list (c) (bool ∗ int) list (d) type clash E (e) α list list Aufgabe 4.3 Schreiben Sie eine Prozedur count: ’α → ’α list → int, die zählt, wie oft ein Element a in einer Liste enthalten ist. Nutzen Sie keine Hilfsprozeduren. Lösung 4.3: fun count a nil = 0 | count a ( x :: xr ) = ( if x = a then 1 else 0) + count a xr Aufgabe 4.4 Schreiben Sie eine Prozedur body: α list → α list, welche die Liste ohne das letzte Element zurück gibt, erst mit und dann ohne die Hilfe von Listenprozeduren. Wenn die übergebene Liste leer ist, soll Empty geworfen werden. Lösung 4.4: fun body xs = rev ( tl ( rev xs )) fun body ’ nil = raise Empty | body ’ [ x ] = nil | body ’ ( x :: xr ) = x :: body xr Aufgabe 4.5 Schreiben Sie eine Prozedur truesum: (int * bool) list → int, welche eine Liste aus Paaren nimmt, und die Zahlen aus den Paaren, die als zweite Komponente true haben, aufaddiert und die restlichen außer Acht lässt. Verzichten Sie auf Konditionale. Lösung 4.5: fun truesum nil = 0 | truesum (( x , false ) :: xr ) = truesum xr | truesum (( x , true ) :: xr ) = x + truesum xr Aufgabe 4.6 Schreiben Sie eine Prozedur sEven: int list * int list → bool, die für ein Paar von Listen entscheidet, ob die erste Liste an Position 2 eine 7 und die zweite Liste an Position 1 eine gerade Zahl hat. Verallgemeinern Sie sEven, sodass kein Paar, sondern eine Liste von Listen als Eingabe genommen wird, wobei nur die ersten beiden Listen betrachtet werden. Hinweis: sEven’: int list list → bool. 2 Lösung 4.6: fun sEven ( _ :: _ ::7:: _ , _ :: x :: _ ) = x mod 2 = 0 | sEven _ = false fun sEven ’ (( _ :: _ ::7:: _ )::( _ :: x :: _ ):: _ ) = x mod 2 = 0 | sEven ’ _ = false Aufgabe 4.7 Schreiben Sie eine Prozedur kubilist: int → int list, die eine Zahl k als Eingabe nimmt, und die Liste der Kubikzahlen von k bis 1 liefert. Für k ≤ 0 soll nil zurückgegeben werden. Für k = 3 soll das Ergebnis [27, 8, 1] lauten. Lösung 4.7: fun kubilist k = if k<=0 then nil else rev ( tl ( List . tabulate ( k+1 , fn x ⇒ x * x * x ))) Alternative: fun kubilist k = if k<=0 then nil else k * k * k :: kubilist ( k−1) Aufgabe 4.8 Schreiben Sie eine Prozedur doublemap: (α * β → γ) → α list → β list → γ list, die eine übergebene Prozedur f auf ein Tupel aus je einem Element der beiden übergebenen Listen anwendet. Sind die Listen unterschiedlich groß, soll die Ausnahme Subscript geworfen werden. Beispiel: doublemap f [2, 3] [true, false] = [f (2, true), f (3, false)] Lösung 4.8: fun doublemap f nil nil = nil | doublemap f ( x :: xr ) ( y :: yr ) = f (x , y ) :: doublemap f xr yr | doublemap f _ _ = raise Subscript Alternative: fun doublemap f xs ys = f ( hd xs , hd ys ) :: doublemap f ( tl xs ) ( tl ys ) Aufgabe 4.9 Schreiben Sie eine Prozedur confuse: int list → int list, die zunächst prüft, ob eine Liste mindestens eine gerade Zahl enthält. Falls dem so ist, soll die Liste, die alle ungeraden Zahlen der Eingabeliste enthält, zurückgegeben werden. Andernfalls werden sämtliche Werte der Eingabeliste verdoppelt und die so entstandene Liste wird zurückgegeben. Lösung 4.9: fun confuse xs = if List . exists ( fn x ⇒ x mod 2 = 0) xs then List . filter ( fn x ⇒ x mod 2 = 1) xs else map ( fn x ⇒ 2* x ) xs 2 Strings Aufgabe 4.10 Schreiben Sie eine Prozedur abc: string → int, die berechnet, wie oft in einem Eingabestring der String 00 abc00 enthalten ist. 3 Lösung 4.10: fun abc ’ ((# " a " )::(# " b " )::(# " c " ):: xr ) = 1 + abc ’ xr | abc ’ ( x :: xr ) = abc ’ xr | abc ’ nil = 0 fun abc eingabe = abc ’ ( explode eingabe ) Aufgabe 4.11 Schreiben Sie eine Prozedur revi: string → string, die einen String bis auf das erste Zeichen reversiert. Zum Beispiel soll revi(Hallo) = Holla gelten. Handelt es sich um den leeren String oder besteht der String nur aus einem Zeichen, so soll die Prozedur divergieren. Sie dürfen Hilfsprozeduren verwenden. Lösung 4.11: fun revi s = if ( List . length ( explode s )) < 2 then revi s else implode ( hd ( explode s ) :: ( List . rev ( tl ( explode s )))) Aufgabe 4.12 Schreiben Sie eine Prozedur isord: string → bool, die für einen gegebenen String prüft, ob die einzelnen Zeichen innerhalb des Strings der lexikalischen Ordnung entsprechend geordnet sind. Lösung 4.12: fun isord s = if ( List . length ( explode s )) < 2 then true else ( ord ( hd ( explode s ))) < ( ord ( hd ( tl ( explode s )))) andalso isord ( implode ( tl ( explode s ))) Aufgabe 4.13 Schreiben Sie eine Prozedur remove: char → string list → string list, welche alle Elemente einer Liste von Strings entfernt, in denen ein übergebenes Zeichen vorkommt. Überlegen Sie hierfür zunächst, welche Ihnen bekannten Prozeduren hilfreich sein könnten. Schreiben Sie nun mit Hilfe von remove eine Prozedur removea: string list → string list, die alle Elemente einer Liste entfernt, in denen ein #00 a00 vorkommt. Lösung 4.13: fun remove a xs = List . filter ( fn x ⇒ not ( List . exists ( fn y ⇒ y = a ) ( String . explode x ))) xs fun removea xs = remove # " a " xs Aufgabe 4.14 Schreiben Sie eine Prozedur intlist: string → int list, die einen String, der eine Liste von positiven Zahlen nur durch Komma getrennt enthält, in diese Liste von Zahlen umschreibt. Aus 00 345, 786, 3200 wird [345,786,32]. Lösung 4.14: fun intlist ’ (# " ," :: xr ) zahl = zahl ::( intlist ’ xr 0) | intlist ’ nil zahl = [ zahl ] | intlist ’ ( x :: xr ) zahl = intlist ’ ( xr ) (10* zahl + ( ord x − ord # " 0 " )) fun intlist eingabe = intlist ’ ( explode eingabe ) 0 4 3 Faltung Aufgabe 4.15 Unterscheiden sich foldl op:: nil xs und foldr op:: nil xs? Lösung 4.15: foldl op:: nil xs beginnt von links, Elemente an nil anzuhängen, wodurch die Liste reversiert wird. foldr op:: nil xs gibt schlicht xs zurück. Fertigen Sie jeweils ein Ausführungsprotokoll für [1, 2, 3] an, um eventuelle Unklarheiten zu beseitigen. Aufgabe 4.16 Schreiben Sie eine Prozedur double: α list → α list, welche für eine gegebene Liste die Liste zurückgibt, in der jedes Element doppelt vorkommt. Lösung 4.16: fun double xs = foldr (fn (x,s) ⇒ x::x::s) nil xs Aufgabe 4.17 Schreiben Sie eine Prozedur length’: α list list → int list, die eine Liste von Listen nimmt und eine Liste mit den Längen der Listen zurückgibt. Für [nil, [1, 2, 8], [5, 2]] lautet die Ausgabe [0, 3, 2]. Lösung 4.17: fun length ’ xs = foldr ( fn ( ys , akku ) ⇒ ( length ys ):: akku ) nil xs Aufgabe 4.18 Schreiben Sie eine Prozedur minmax: int list → (int*int), die mit einmal Falten (insgesamt nur einem Aufruf von foldl bzw. foldr) die kleinste und die größte Zahl einer Liste zurückgibt. Für [3, 6, 2] lautet das Ergebnis (2, 6). Lösung 4.18: fun minmax nil = raise Empty | minmax ( x :: xr ) = let fun f (z , ( min , max )) = ( if z < min then z else min , if z > max then z else max ) in foldl f (x , x ) xr end Aufgabe 4.19 Schreiben Sie eine Prozedur evengreater: int list → bool, die für eine Liste von Zahlen entscheidet, ob sie mehr gerade als ungerade Zahlen enthält. Lösung 4.19: fun evengreater xs = let val ( gerade , ungerade ) = foldl ( fn (x , (g , u ) ⇒ if x mod 2 = 0 then ( g+1 , u ) else (g , u+1))) (0 , 0) xs in gerade > ungerade end Alternative: fun evengreater xs = #3( foldl ( fn (x ,( a ,b , c )) ⇒ let val ( gerade , ungerade ) = if x mod 2 = 0 then ( a+1 , b ) else (a , b+1) 5 in ( gerade , ungerade , gerade > ungerade ) end ) (0 , 0 , false ) xs ) Aufgabe 4.20 Schreiben Sie eine Prozedur zip : α list → β list → (α * β) list, die gleichlange Listen zu einer Liste wie aus dem Typschema ablesbar zusammenfügt. Es soll zip [1,2] [true,false] = [(1,true),(2,false)] gelten. Lösung 4.20: fun zip xs ys = rev (#2( foldl ( fn (x ,( a , ys )) ⇒ (( x , hd ( ys )):: a , tl ( ys ))) ( nil , ys ) xs )) Aufgabe 4.21 Schreiben Sie eine Prozedur primpro: int list → bool, die für eine Liste von Zahlen entscheidet, ob das Produkt der Primzahlen größer ist als die Summe der Nicht-Primzahlen. Eine Prozedur prim: int → bool, die sagt, ob eine Zahl eine Primzahl ist, sei gegeben. Lösung 4.21: fun primpro xs = let val ( pro , sum ) = foldl ( fn (x , ( pro , sum )) ⇒ if prim x then ( pro *x , sum ) else ( pro , sum+x )) (1 ,0) xs in pro > sum end Aufgabe 4.22 Schreiben Sie eine Prozedur evensmaller: int list → int list list, die eine Liste von Zahlen nimmt und eine Liste zurückgibt, die zu jedem Element eine Liste mit allen geraden Zahlen zwischen 0 und diesem Element (jeweils exklusive) enthält. Für [3, 8, 7, 1] lautet die Ausgabe [[2], [2, 4, 6], [2, 4, 6], []]. Lösung 4.22: fun evensmaller ’ n = if n <=0 then nil else if n mod 2 = 0 then ( evensmaller ’ ( n−2)) @ [ n ] else evensmaller ’ ( n−1) fun evensmaller xs = foldr ( fn (n , akku ) ⇒ ( evensmaller ’ ( n−1)):: akku ) nil xs Aufgabe 4.23 Schreiben Sie die Listenprozedur foldl mithilfe von iter. Sie dürfen dafür die Prozedur List.length: α list → int verwenden, die die Länge einer Liste liefert. Lösung 4.23: fun myFoldl f s xs = #1( iter ( List . length xs ) (s , xs ) ( fn (s ’ , xr ) ⇒ ( f ( hd xr , s ’) , tl xr ))) Aufgabe 4.24 Schreiben Sie eine Prozedur map’: (α → β) → α list → β list, die map mithilfe von Faltung implementiert. Für (fn x ⇒ x div 2) [2, 5, 6, 9] liefert map und somit map’: [1, 2, 3, 4]. Lösung 4.24: fun map ’ f xs = foldr ( fn (x , akku ) ⇒ ( f x ):: akku ) nil xs 6 Aufgabe 4.25 Schreiben Sie eine Prozedur counta: string list → (string * int) list, die eine Liste von Strings nimmt, diejenigen behält, die mindestens drei as enthalten, und ihnen die Anzahl von as zuordnet. Für ["abc","bcd","aaaaaz","b","ababa"] lautet die Ausgabe [("aaaaaz",5),("ababa",3)]. Lösung 4.25: fun counta ’ (# " a " :: xr ) = 1 + counta ’ xr | counta ’ ( _ :: xr ) = counta ’ xr | counta ’ nil = 0 fun counta xs = foldr ( fn ( eingabe , akku ) ⇒ if counta ’ ( explode eingabe ) >= 3 then ( eingabe , counta ’ ( explode eingabe )):: akku else akku ) nil xs Aufgabe 4.26 Schreiben Sie eine Prozedur clean: string list → string, die eine nicht-leere Liste von Strings nimmt, den längsten String bestimmt und ihn um alle Ziffern bereinigt zurückgibt. Für ["abc2","bc1d","a22a3aaaz","b","a1baba"] lautet die Ausgabe "aaaaaz". Lösung 4.26: fun clean ( x :: xr ) = let fun f (y ,( maxstring , maxlaenge )) = if List . length ( explode y ) > maxlaenge then (y , List . length ( explode y )) else ( maxstring , maxlaenge ) fun rem nil = nil | rem ( x :: xr ) = if ord x >= ord # " 0 " andalso ord x <= ord # " 9 " then rem xr else x ::( rem xr ) val lang = #1( foldr f (x , List . length ( explode x )) xr ) in implode ( rem ( explode lang )) end | clean nil = raise Empty Aufgabe 4.27 Schreiben Sie eine Prozedur thegreatest: int list list → int, die für eine Liste von nicht-leeren Listen von positiven Zahlen entscheidet, die wievielte Liste von Zahlen die insgesamt größte Zahl enthält. Für [[2, 3, 0], [5], [3, 5, 3, 1]] soll 1 geliefert werden. Für eine leere Liste von Listen soll die Ausnahme Empty geworfen werden. Hinweis: Int.max : int * int → int liefert die größere zweier Zahlen. Lösung 4.27: load " Int " fun greater ( ys , ( max , curpos , maxpos )) = let val curmax = foldl Int . max 0 ys in if curmax > max then ( curmax , curpos + 1 , curpos ) else ( max , curpos + 1 , maxpos ) end fun thegreatest nil = raise Empty | thegreatest xs = #3( foldl greater (0 , 0 , ∼1) xs ) 7 Aufgabe 4.28 Schreiben Sie eine Prozedur power: α list → α list list, die ähnlich zur Potenzmenge die Liste aller Teillisten einer Eingabeliste liefert. Für [1,2,2] soll die Ausgabeliste folgende Listen enthalten: [], [1], [2], [2], [1,2], [1,2], [2,2], [1,2,2]. Lösung 4.28: fun power nil = [ nil ] | power ( x :: xr ) = map ( fn xs ⇒ x :: xs ) ( power xr ) @ power xr 4 Programmierung – eine Einführung in die Informatik mit Dieter Schlau Aufgabe 4.29 (Einmal Falten, bitte!) Schreiben Sie eine nichtrekursive Prozedur magic : real list → real list mittels einer Benutzung von Faltung, die eine Liste von Werten nimmt und eine Liste der Differenzen dieser Werte zum Durchschnittswert der Liste zurückgibt. Es soll also gelten: magic [1.0,2.0,3.0] = [∼1.0,0.0,1.0]. Benutzen Sie keine der bekannten Listenprozeduren (außer genau einmal Faltung). Sie dürfen Real.fromInt verwenden. Lösung 4.29: load " Real " fun magic xs = let val (b ,s , c ) = foldr ( fn (e , (b ,s , c )) ⇒ ( fn m ⇒ e − m :: b m , s + e , c + 1)) (( fn m ⇒ nil ) , 0.0 , 0) xs in ( b ( s / Real . fromInt c )) end 8