Musterlösung

Werbung
Algorithmen und Programmieren 1
Funktionale Programmierung
- Musterlösung zu Übung 2 Dozent: Prof. Dr. G. Rote
Tutoren: J. Fleischer, T. Haimberger,
N. Lehmann, C. Pockrandt, A. Steen
25.10.2011
Ziele dieser Übung
Mit Tupeln, Listen und Zeichenketten sicher in Haskell umgehen und die Idee der Polymorphie verstehen.
In dieser Übung sollte man folgende Konzepte geübt und verstanden haben:
• Guards
• Patternmatching
• if-then-else
• Tupel
• Listen
Listengeneratoren
Die Funktion: sum
• Polymorphie
• Strings in Haskell
Die Funktion: putStr
1
Aufgabe 7: Uhrzeiten
Uhrzeiten wie 10:30 Uhr oder 23:59 Uhr sollen als geordnete Paare (10,30) beziehungsweise (23,59)
dargestellt werden.
Schreiben Sie eine Funktion, die die Dauer zwischen zwei Uhrzeiten in Stunden und Minuten berechnet
(negativ, falls die zweite Uhrzeit vor der ersten liegt).
Sie sollten das Problem in Teilprobleme zerlegen und geeignete Hilfsfunktionen denieren.
Lösung zu Aufgabe 7:
-- Gibt den Abstand zwischen 2 Uhrzeiten an.
timeDist::(Int,Int) -> (Int,Int) -> (Int,Int)
timeDist (a,b) (c,d)
| (chkTimeFrmt a b) && (chkTimeFrmt c d)
= min2tupel(tupel2min (c,d) - tupel2min (a,b))
| otherwise = error "Keine gültiges Uhrzeit-Format (hh,mm)!"
-- wandelt Zeittupel in Minuten ab 0 Uhr um.
tupel2min::(Int,Int) -> Int
tupel2min (x,y) = x*60+y
-- wandelt Minuten ab 0 Uhr in Zeittupel um.
min2tupel::Int->(Int,Int)
min2tupel z
| z >= 0
= (div z 60, mod z 60)
| otherwise = (-(div (-z) 60), -(mod (-z) 60))
-- Prüft, ob die Eingabe ein gültiges Uhrzeitformat ist.
chkTimeFrmt::Int->Int->Bool
chkTimeFrmt h m
| h >= 0 && h <24 && m >= 0 && m <60 = True
| otherwise = False
Aufgabe 8: Uhrzeiten
Im angelsächsischen Raum werden Uhrzeiten in einem 12-Stunden-Bereich angegeben und mit dem
Zusatz a.m. (Vormittag), p.m. (Nachmittag), noon oder midnight versehen:
0:00 = 12:00 midnight, 0:13 = 12:13 a.m., 10:30 = 10:30 a.m., 12:00 = 12:00 noon, 12:55 = 12:55 p.m.,
13:20 = 1:20 p.m., 23:59 = 11:59 p.m..
Die Stundenzahl ist also immer zwischen 1 und 12.
Schreiben Sie eine Funktion, die dieses Format (als Zeichenkette) berechnet. Wenn die Eingabe keine
gültige Uhrzeit darstellt, soll der Text ungültig ausgegeben werden.
Lösung zu Aufgabe 8:
angelsaechsisch
angelsaechsisch
angelsaechsisch
angelsaechsisch
:: (Int,Int) -> String
(0,0) = "12:00 midnight"
(12,0) = "12:00 noon"
(a,b)
| a < 0 || a >= 24 || b < 0 ||
| a == 0
= "12:" ++ minuten
| a < 12 = show a ++ ":" ++
| a == 12
= "12:" ++ minuten
| otherwise = show a ++ ":" ++
where minuten = if
b < 10
then "0" ++ show b
else show b
2
b >= 60 = "ungültig"
++ " a.m."
minuten ++ " a.m"
++ " p.m."
minuten ++ " p.m."
Aufgabe 9: Multiplikationstabelle
Erstellen Sie eine Tabelle für das kleine Einmaleins (die Produkte aller Zahlenpaare zwischen 1 und 10)
als Zeichenkette. Die einzelnen Zeilen sind mit \ n abgeschlossen. Das reduzierte Beispiel rechts zeigt,
wie so eine Tabelle aussehen könnte, wenn man sie mit putStr ausgibt. Die Spalten ihrer Tabelle müssen vertikal ausgerichtet sein. (Hinweis: die Funktion show wandelt eine Zahl in eine Zeichenkette um.)
Lösung zu Aufgabe 9:
kleines1x1 :: String
kleines1x1 = kopfzeile ++ trennzeile ++ aneinander [ zeile i | i <- [1..10]]
-- fügt eine Liste von Strings zu einem einzigen String zusammen
aneinander :: [String] -> String
aneinander l = [ c | s<-l, c<-s ]
-- Formatiert eine Zahl mit führenden Leerzeichen zu Gesamtlänge 4
-- Die Zahl hat höchstens 3 Zeichen.
pad :: Int -> String
pad y
| length ys == 1 = "
" ++ ys
| length ys == 2 = " " ++ ys
| length ys == 3 = " "
++ ys
where ys = show y
kopfzeile :: String
kopfzeile = "
|" ++ aneinander [ pad j | j <- [1..10]] ++ "\n"
trennzeile :: String
trennzeile = "-----+" ++ aneinander [ "----" | j <- [1..10]] ++ "\n"
zeile :: Int -> String
zeile i = pad i ++ " |" ++ aneinander [ pad (i*j) | j <- [1..10]] ++ "\n"
Die Ausgabe erfolgt mit:
putStr kleines1x1
Aufgabe 10: Bedingte Ausdrücke
Schreiben Sie die folgende Funktion als einen einzigen (nicht geschachtelten) if-then-else-Ausdruck:
test x
|
|
|
y z
x <= y
= True
y <= z
= False
otherwise = x < z
Lösung zu Aufgabe 10:
Betrachten wir die Funktion test :
1) Die Funktion test erhält drei Eingabeparameter.
2) Die Funktion test prüft den ersten Guard:
2+) Falls x kleiner oder gleich y ist, wird True ausgegeben.
2−) Falls x gröÿer ist als y, so ist die Bedingung für den 1. Guard nicht erfüllt und der nächste Guard
wird überprüft.
3) Die Funktion test überprüft den zweiten Guard:
3+) Falls y kleiner oder gleich z ist, wird False ausgegeben.
3−) Falls y gröÿer ist als z, so ist die Bedingung für den 1. Guard nicht erfüllt und der nächste Guard
3
wird überprüft.
4) Die Funktion test überprüft den dritten Guard:
4+) (otherwise = True), dieser Fall tritt also immer ein! Die Funktion überprüft nun ob x kleiner als
z ist und gibt ein booleschen Wert als Ergebnis zurück.
Betrachten wir erneut die Funktion test :
Der dritte Guard wird nur dann überprüft, wenn die ersten beiden Guards nicht ausgeführt worden
sind.
Wenn die ersten beiden Guards allerdings nicht zutrafen ist x > z .
(Folgt transitiv aus: (x > y) ∧ (y > z) =⇒ (x > z))
Der Ausdruck x < z im dritten Guard ist in unserer Funktion also eine Kontradiktion, die Codezeile
| otherwise = x < z
wird daher immer False ergeben.
test::Ord a => a -> a -> a -> Bool
test x y z = if (x<=y) then True else False
Zusatzfrage (0 Punkte): Kommt man auch ganz ohne if-then-else-Ausdruck aus?
Lösung zu Zusatzfrage:
test2::Ord a => a -> a -> a -> Bool
test2 x y z = x<=y
Aufgabe 11: Perfekte Zahlen
a) Schreiben Sie eine Funktion, die zu einer Zahl n die Liste ihrer Teiler berechnet.
Lösung zu Aufgabe 11a:
-- Berechnet eine Liste aller echten Teiler des Eingabeparameters.
teilerVonN::Int->[Int]
teilerVonN n = [x|x <- [1..n], (mod n x) == 0, x /= n]
b) Bestimmen Sie jede Zahl zwischen 1 und 1000, bei der die Summe ihrer Teiler gröÿer ist als die
Zahl selbst.
(Hinweis: Die Funktion sum berechnet die Summe der Elemente einer Liste)
Lösung zu Aufgabe 11b:
-- Berechnet die Summe aller echten Teiler des Eingabeparameters.
sumTeilerVonN::Int->Int
sumTeilerVonN n = sum (teilerVonN n)
-- Erstellt eine Liste aller Zahlen, bei der die Summe
-- ihrer Teiler gröÿer ist als die Zahl selbst.
biggerThanSumTeilerVonN::[Int]
biggerThanSumTeilerVonN = [z | z <- [1..1000], z < sumTeilerVonN z]
Es kommen 246 Zahlen als Lösung heraus.
c) Bestimmen Sie alle perfekten Zahlen zwischen 1 und 1000: die Zahlen, die gleich der Summe Ihrer
Teiler sind.
Lösung zu Aufgabe 11c:
-- Bestimmt alle perfekte Zahlen zwischen 1 und 1000.
perfekteZahlen::[Int]
perfekteZahlen = [z | z <- [1..1000] , z == sumTeilerVonN z]
Das Ergebnis ist [6,28,496]. Es ist unbekannt, ob es ungerade perfekte Zahlen gibt.
4
Herunterladen