Tutoraufgabe 1 (Auswertungsstrategie): Lösung:

Werbung
Programmierung WS16/17
Lösung - Übung 10
F. Frohn, J. Hensel, D. Korzeniewski
Prof.aa
Dr. J. Giesl
Tutoraufgabe 1 (Auswertungsstrategie):
Gegeben sei das folgende Haskell-Programm:
listProd :: [ Int ] -> Int
listProd [] = 1
listProd ( x : xs ) = x * listProd xs
everySecond
everySecond
everySecond
everySecond
:: [ Int ] -> [ Int ]
[] = []
[x] = [x]
( x : _ : xs ) = x : everySecond xs
minus10 :: [ Int ] -> [ Int ]
minus10 [] = []
minus10 ( x : xs ) = x - 10 : minus10 xs
double :: Int -> Int
double x = if x > 0 then x + x else 0
Die Funktion listProd multipliziert die Elemente einer Liste. Beispielsweise ergibt listProd [3,5,2,1] die
Zahl 30. Die Funktion everySecond bekommt eine Liste als Eingabe und gibt die gleiche Liste zurück, wobei
jedes zweite Element gelöscht wurde. So ergibt everySecond [1,2,3] die Liste [1,3]. Die Funktion minus10
gibt seine Eingabeliste zurück, wobei von jedem Element 10 subtrahiert wurde.
Geben Sie alle Zwischenschritte bei der Auswertung der Ausdrücke
listProd (everySecond (minus10 [3,2,1])) und double (listProd [])
an. Schreiben Sie hierbei (um Platz zu sparen) p, s, m und d statt listProd, everySecond, minus10 und
double.
Hinweise:
• Beachten Sie, dass Haskell eine Leftmost-Outermost Lazy Auswertungsstrategie besitzt. Allerdings sind
Operatoren wie *, - und +, die auf eingebauten Zahlen arbeiten, strikt, d. h. hier müssen vor Anwendung
des Operators seine Argumente vollständig ausgewertet worden sein (wobei zunächst das linke Argument
ausgewertet wird).
Lösung:
p (s (m [3,2,1]))
→ p (s (3 - 10 : m [2,1]))
→ p (s (3 - 10 : 2 - 10 : m [1]))
→ p (3 - 10 : s (m [1]))
→ (3 - 10) * p (s (m [1]))
→ (-7) * p (s (m [1]))
→ (-7) * p (s (1 - 10 : m []))
→ (-7) * p (s (1 - 10 : []))
→ (-7) * p (1 - 10 : [])
→ (-7) * ((1 - 10) * p [])
→ (-7) * ((-9) * p [])
→ (-7) * ((-9) * 1)
→ (-7) * (-9)
→ 63
d (p [])
→ if (p []) > 0 then (p []) + (p []) else 0
1
Programmierung WS16/17
Lösung - Übung 10
→
→
→
→
if 1 > 0 then 1 + 1 else 0
if True then 1 + 1 else 0
1 + 1
2
Aufgabe 2 (Auswertungsstrategie):
(6 Punkte)
Gegeben sei das folgende Haskell-Programm:
second
second
second
second
:: [ Int ] -> Int
[]
= 0
( _ :[])
= 0
( _ : x : xs ) = x
doubleEach :: [ Int ] -> [ Int ]
doubleEach []
= []
doubleEach ( x : xs ) = x * 2 : ( doubleEach xs )
repeat :: Int -> Int -> [ Int ]
repeat x n = if n > 0 then x : ( repeat x (n -1)) else []
Die Funktion second gibt zu jeder Eingabeliste mit mindestens zwei Elementen den zweiten Eintrag der Liste
zurück. Für Listen mit weniger als zwei Elementen wird 0 zurückgegeben. Die Funktion doubleEach gibt die
Liste zurück, die durch Verdoppeln jedes Elements aus der Eingabeliste entsteht. Der Aufruf doubleEach [1,
2, 3, 1] würde also [2, 4, 6, 2] ergeben. Die Funktion repeat erzeugt eine Liste, die das erste Argument
so oft enthält, wie das zweite Argument angibt. Beispielsweise erhält man beim Aufruf repeat 5 3 die Liste
[5, 5, 5] als Rückgabe.
Geben Sie alle Zwischenschritte bei der Auswertung des Ausdrucks
repeat ( second ( doubleEach [2, 3, 5] )) ( second [3, 1, 4] )
an. Schreiben Sie hierbei (um Platz zu sparen) s, d und r statt second, doubleEach und repeat.
Hinweise:
• Beachten Sie, dass Haskell eine Leftmost-Outermost Lazy Auswertungsstrategie besitzt. Allerdings sind
Operatoren wie *, > und -, die auf eingebauten Zahlen arbeiten, strikt, d. h. hier müssen vor Anwendung
des Operators seine Argumente vollständig ausgewertet worden sein (wobei zunächst das linke Argument
ausgewertet wird).
2
Programmierung WS16/17
Lösung - Übung 10
Lösung:
r ( s ( d [2, 3, 5] ) ) ( s [3, 1, 4] )
→ if s [3, 1, 4] > 0 then s ( d [2, 3, 5]) : r (s ( d [2, 3, 5] ) ) (s [3, 1, 4] - 1) else []
→ if 1 > 0 then s ( d [2, 3, 5]) : r (s ( d [2, 3, 5] ) ) (1 - 1) else []
→ if True then s ( d [2, 3, 5]) : r (s ( d [2, 3, 5] ) ) (1 - 1) else []
→ s ( d [2, 3, 5] ) : r ( s ( d [2, 3, 5] ) ) (1-1)
→ s ( 2*2 : d [3, 5] ) : r ( s ( 2*2 : d [3, 5] ) ) (1-1)
→ s ( 2*2 : 3*2 : d [5] ) : r ( s ( 2*2 : 3*2 : d [5] ) ) (1-1)
→ 3*2 : r (3*2) (1-1)
→ 6 : r 6 (1-1)
→ 6 : if (1 - 1) > 0 then 6 : r 6 ((1-1) - 1) else []
→ 6 : if 0 > 0 then 6 : r 6 (0 - 1) else []
→ 6 : if False then 6 : r 6 (0 - 1) else []
→ 6 : [] = [6]
Tutoraufgabe 3 (Listen):
Seien x, y, z ganze Zahlen vom Typ Int und seien xs und ys Listen der Längen n und m vom Typ [Int].
Welche der folgenden Gleichungen zwischen Listen sind richtig und welche nicht? Begründen Sie Ihre Antwort.
Falls es sich um syntaktisch korrekte Ausdrücke handelt, geben Sie für jede linke und rechte Seite auch an,
wieviele Elemente in der jeweiligen Liste enthalten sind und welchen Typ sie hat.
Beispiel : Die Liste [[1,2,3],[4,5]] hat den Typ [[Int]] und enthält 2 Elemente.
a) [] ++ [xs] = [] : [xs]
b) [[]] ++ [x] = [] : [x]
c) [x] ++ [y] = x : [y]
d) x:y:z:(xs ++ ys) = [x,y,z] ++ xs ++ ys
e) [(x:xs):[ys],[[]]] = (([]:[]):[]) ++ ([([x] ++ xs),ys]:[])
Hinweise:
• Hierbei steht ++ für den Verkettungsoperator für Listen. Das Resultat von xs ++ ys ist die Liste,
die entsteht, wenn die Elemente aus ys — in der Reihenfolge wie sie in ys stehen — an das Ende
von xs angefügt werden.
Beispiel : [1,2] ++ [1,2,3] = [1,2,1,2,3]
• Falls linke und rechte Seite gleich sind, genügt eine Angabe des Typs und der Elementzahl.
Lösung:
a) Beide Ausdrücke haben den Typ [[Int]]. Jedoch hat die erste Liste ein Element, während die zweite
Liste zwei Elemente besitzt. Die Ausdrücke sind also nicht gleich.
b) Beide Ausdrücke sind nicht typkorrekt. Daher würde die Gleichung in Haskell nicht gelten. Allerdings
würden beide Ausdrücke die gleiche (ungültige) Liste darstellen, nämlich [[],x] (somit sind beide Antworten, ob die Gleichung gilt oder nicht, zulässig). Diese Liste ist nicht typkorrekt, da sie sowohl eine
Liste als auch einen Int Wert enthält.
c) Die Gleichung gilt, denn beide Ausdrücke repräsentieren die Liste [x,y] vom Typ [Int], welche zwei
Elemente enthält.
3
Programmierung WS16/17
Lösung - Übung 10
d) Die Gleichung gilt, denn beide Ausdrücke repräsentieren die gleiche Liste, welche zuerst die Elemente x,
y, z, anschließend die n Elemente der Liste xs und schließlich die m Elemente der Liste ys enthält. Diese
Liste enthält also insgesamt 3 + n + m Elemente und ist vom Typ [Int].
e) Beide Ausdrücke sind vom gleichen Typ [[[Int]]] und sind Listen mit zwei Elementen. Diese Elemente
sind auch noch gleich (einerseits die Liste [[]] und andererseits die Liste [x:xs,ys]), allerdings ist ihre
Reihenfolge in den beiden Ausdrücken unterschiedlich, sodass die Gleichung nicht gilt.
Aufgabe 4 (Listen):
(8 Punkte)
Seien x, y und z ganze Zahlen vom Typ Int und xs und ys Listen der Längen n und m vom Typ [Int].
Welche der folgenden Gleichungen zwischen Listen sind richtig und welche nicht? Begründen Sie Ihre Antwort.
Falls es sich um syntaktisch korrekte Ausdrücke handelt, geben Sie für jede linke und rechte Seite auch an,
wieviele Elemente in der jeweiligen Liste enthalten sind und welchen Typ sie hat.
a) [x : [y] : []] = [[x] ++ [y]]
b) [x, y] ++ xs = [x] ++ [y] ++ xs
c) [x, y, z] = ([x] ++ [y]) : [[z]]
d) (x:[]):[] = [x:[]]++[[]]
e) x:y:z:xs = (x:[y]) ++ (z:xs)
Hinweise:
• Falls linke und rechte Seite gleich sind, genügt wiederum eine Angabe des Typs und der Elementzahl.
Lösung:
a) Der linke Ausdruck ist nicht typkorrekt, da x und [y] nicht den gleichen Typ haben und daher nicht in
einer Liste liegen können.
Der rechte Ausdruck repräsentiert eine Liste der Länge 1 vom Typ [[Int]]
b) Beide Ausdrücke repräsentieren die selbe Liste der Länge n + 2.
c) Beide Ausdrücke sind korrekt, aber repräsentieren unterschiedliche Listen. Der linke Ausdruck ist eine
Liste der Länge 3 vom Typ [Int], die rechte Liste hat die Länge 2 und ist vom Typ [[Int]].
d) Beide Ausdrücke sind korrekt und haben den gleichen Typ [[Int]], aber die Länge unterscheidet sich:
Links 1 und rechts 2.
e) Beide Ausdrücke repräsentieren die gleiche Liste der Länge 3 + n. Die linke Seite ist einfach x, y und
z vorne in xs eingefügt. Auf der rechten Seite wird die Liste [x, y] mit der Liste xs, in die z vorne
eingefügt wurde, verkettet.
Tutoraufgabe 5 (Haskell-Programmierung):
Implementieren Sie alle der im Folgenden beschriebenen Funktionen in Haskell. Geben Sie jeweils auch die
Typdeklarationen an. Verwenden Sie außer Listenkonstruktoren [] und : (und deren Kurzschreibweise) und
Vergleichsoperatoren wie <=, ==, . . . keine vordefinierten Funktionen (dies schließt auch arithmetische Operatoren ein), außer denen, die in den jeweiligen Teilaufgaben explizit erlaubt werden.
4
Programmierung WS16/17
Lösung - Übung 10
a) mult x y
Berechnet x · y. Sie dürfen dazu + und - verwenden. Die Funktion darf sich auf negativen Eingaben
beliebig verhalten.
b) bLog x
Berechnet den aufgerundeten Logarithmus zur Basis 2 von x. Somit liefert bLog 1 den Wert 0, bLog 5
den Wert 3 und bLog 32 den Wert 5. Sie dürfen hier + und * verwenden. Die Funktion darf sich auf
negativen Eingaben oder der Eingabe 0 beliebig verhalten.
c) getLastTwo xs
Berechnet die Teilliste der letzten zwei Elemente der Int-Liste xs. Beispielsweise berechnet getLastTwo
[12, 7, 23] den Wert [7, 23]. Die Funktion darf sich auf Listen der Länge 0 und 1 beliebig verhalten.
d) singletons x
Berechnet eine Liste mit x Elementen, wobei jedes Listenelement eine einelementige Liste ist. Die Elemente der einelementigen Listen sind die Zahlen x, x-1, . . . , 1. Beispielsweise berechnet singletons 3
die Liste [[3],[2],[1]]. Die Funktion darf sich auf negativen Eingaben beliebig verhalten. Sie dürfen
hier - verwenden.
e) packing xs ys
Das erste Argument xs ist eine Liste von Listen von Zahlen (vom Typ [[Int]]), das zweite Argument
ist eine einfache Liste von Zahlen (vom Typ [Int]). Die Funktion ersetzt leere Listen in der ersten
Eingabeliste durch einelementige Listen. Der Inhalt dieser einelementigen Listen ist die jeweils nächste
Zahl aus der zweiten Eingabeliste. Beispielsweise berechnet packing [[5,6],[],[8],[]] [1,2] die
Liste [[5,6],[1],[8],[2]]. Wenn im zweiten Argument nicht genug Zahlen zur Verfügung stehen,
werden zusätzliche leere Listen im ersten Argument leer gelassen. Wenn im zweiten Argument zu viele
Zahlen zur Verfügung stehen, werden diese ignoriert.
f ) listAdd x xs
Addiert jeweils das n-te Listenelement auf das (n+1)-te Listenelement, wobei auf das erste Listenelement
x addiert wird. Beispielsweise berechnet listAdd 5 [1,9,3] die Liste [6,10,12]. Sie dürfen hier +
verwenden.
Lösung:
-- a
mult :: Int -> Int -> Int
mult _ 0 = 0
mult x y = x + mult x (y -1)
-- b
bLog :: Int -> Int
bLog 1 = 0
bLog x = bLogH 2 1
where
bLogH :: Int -> Int -> Int
bLogH y r | x > y
= bLogH ( y *2) ( r +1)
| otherwise = r
-- c
getLastTwo :: [ Int ] -> [ Int ]
getLastTwo [x , y ]
= [x , y ]
getLastTwo ( _ : x : xs ) = getLastTwo ( x : xs )
-- d
singletons :: Int -> [[ Int ]]
5
Programmierung WS16/17
Lösung - Übung 10
singletons 0 = []
singletons n = [ n ] : singletons (n -1)
-- e
packing
packing
packing
packing
packing
:: [[ Int ]] -> [ Int ] -> [[ Int ]]
[]
_
= []
xs
[]
= xs
([]: xs ) ( y : ys ) = [ y ] : packing xs ys
( x : xs ) ( y : ys ) = x
: packing xs ( y : ys )
-- f
listAdd :: Int -> [ Int ] -> [ Int ]
listAdd _ []
= []
listAdd z ( x : xs ) = ( z + x ) : listAdd x xs
Aufgabe 6 (Haskell-Programmierung):
(1.5 + 1.5 + 2 + 1.5 + 5.5 = 12 Punkte)
Implementieren Sie alle der im Folgenden beschriebenen Funktionen in Haskell. Geben Sie jeweils auch die
Typdeklarationen an. Verwenden Sie außer Listenkonstruktoren [] und : (und deren Kurzschreibweise), der
Listenkonkatenation ++, Vergleichsoperatoren wie <=, ==, . . . und arithmetischen Operatoren wie +, *, . . .
keine vordefinierten Funktionen außer denen, die in den jeweiligen Teilaufgaben explizit erlaubt werden.
a) isEven x
Berechnet ob x gerade ist. Die Funktion darf sich auf negativen Eingaben beliebig verhalten.
b) arithSeries x d
Berechnet die Summe aller positiven Zahlen x, x-d, x-2d, x-3d, . . . Für arithSeries 6 2 berechnet die
Funktion also 6 + 4 + 2 und für arithSeries 4 1 wird 4 + 3 + 2 + 1 berechnet.
Die Funktion darf sich beliebig verhalten, falls x oder d nicht positiv sind.
Verwenden Sie nicht die geschlossene Form der arithmetischen Reihe, sondern lösen Sie diese Aufgabe
mittels einer rekursiven Funktion!
c) isSorted xs
Berechnet, ob die Int-Liste xs aufsteigend sortiert ist. Falls ja wird True zurückgegeben, andernfalls
False. Beispielsweise liefert isSorted [-5, 0, 1, 1, 17] das Ergebnis True.
d) interval a b
Gibt eine Liste zurück, die alle ganzen Zahlen zwischen a und b (jeweils einschließlich) enthält, d.h alle
Zahlen x, mit a ≤ x ≤ b.
e) selectKsmallest k xs
Gibt das Element zurück, das in der Int-Liste xs an der Stelle k stehen würde, wenn man xs aufsteigend
sortiert. Wenn k kleiner als 1 oder größer als die Länge von xs ist, darf sich die Funktion beliebig verhalten.
Der Aufruf selectKsmallest 3 [4, 2, 15, -3, 5] würde also 4 zurück geben und selectKsmallest
1 [5, 17, 1, 3, 9] würde 1 zurück geben.
Hinweise:
• Sie können die Liste an einem geeigneten Element x in zwei Listen teilen, sodass eine der beiden
Teillisten nur Elemente enthält, die kleiner oder gleich x sind, und die andere Teilliste nur größere
Elemente als x enthält. Dann können Sie selectKsmallest mit geeigneten Parametern rekursiv
aufrufen.
• Sie dürfen die vordefinierte Funktion length ys verwenden, die zu einer Liste ys die Anzahl enthaltener Elemente zurückgibt.
• Die Funktion isSorted hilft Ihnen in dieser Aufgabe nicht.
6
Programmierung WS16/17
Lösung - Übung 10
Lösung:
isEven
isEven
isEven
isEven
:: Int -> Bool
0 = True
1 = False
x = isEven (x -2)
arithSeries :: Int -> Int -> Int
arithSeries x d | x < 0 = 0
| otherwise = x + ( arithSeries (x - d ) d )
isSorted
isSorted
isSorted
isSorted
:: [ Int ] -> Bool
[] = True
( x :[]) = True
( x : y : ys ) = if ( x <= y ) then isSorted ( y : ys ) else False
interval :: Int -> Int -> [ Int ]
interval x y | x > y = []
| otherwise = x : ( interval ( x +1) y )
selectKsmallest :: Int -> [ Int ] -> Int
selectKsmallest _ [] = 0
selectKsmallest k ( pivot : rest ) =
let
split :: [ Int ] -> ([ Int ] , [ Int ])
split [] = ([] , [])
split ( y : ys ) = if y <= pivot then ( y : left , right ) else ( left , y : right )
where ( left , right ) = split ys
left , right :: [ Int ]
( left , right ) = split rest
left_len :: Int
left_len = length left
in
if ( left_len ) == (k -1) then pivot else
if ( left_len ) > (k -1) then selectKsmallest k left else
selectKsmallest (k -1 - left_len ) right
7
Herunterladen