Grundlagen der Programmierung 2 Unendliche Listen und Ströme(B)

Werbung
Grundlagen der Programmierung 2
Unendliche Listen und Ströme(B)
Prof. Dr. Manfred Schmidt-Schauÿ
Künstliche Intelligenz und Softwaretechnologie
17. Mai 2006
Beispiel: scanl, scanr
scanl berechnet das foldl jeweils der ersten n Elemente:
So sieht man, welche Unterliste verarbeitet wird:
Prelude> scanl (\x y -> y:x) [] [1..10]
[[],[1],[2,1],[3,2,1],[4,3,2,1],[5,4,3,2,1],[6,5,4,3,2,1],[7,6,5,4,3,2,1],
scanl (+) 0 [1..]
[0,1,3,6,10,15,21,28,36,45,55,66,78,91, ...
Grundlagen der Programmierung 2
- 2 -
Beispiel: scanr
scanr wendet foldr jeweils auf die Restlisten an:
Prelude> scanr (:) [] [1..10]
[[1,2,3,4,5,6,7,8,9,10],[2,3,4,5,6,7,8,9,10],[3,4,5,6,7,8,9,10],
[4,5,6,7,8,9,10],[5,6,7,8,9,10],[6,7,8,9,10],[7,8,9,10],
[8,9,10],[9,10],[10],[]]
sinnvoll, wenn der foldr-Anteil nur Anfänge benötigt,
oder wieder einen Strom erzeugt:
scanr (+) 0 [1..]
[^CInterrupted.
Sinnvolle Verarbeitung:
map head (scanr (:) []
Grundlagen der Programmierung 2
[1..])
- 3 -
Sortierte Ströme: Mischen
Mischen von 2 aufsteigend sortierten Strömen:
mische [] ys = ys
mische xs [] = xs
mische (x:xs) (y:ys) =
if x <= y then x: (mische xs (y:ys))
else y: (mische (x:xs) ys)
mische [1,3..] [2,4..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,
24,25,26,27,28,29,30,31,32,33,34,35,36,37,
38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,
53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,....
> mische [2,4..] [3,6..]
[2,3,4,6,6,8,9,10,12,12
Grundlagen der Programmierung 2
- 4 -
Sortierte Ströme: Mischen
Mischen der Vielfachen von 2,3,5:
[2,4,6,8...]
[2,3,4,5,6,6,8,9,10,10,12,12,14,15,15,16,..]
[3,6,9,12...]
mische
[5,10,15,20...]
Main> mische (mische
(map (2*) [1..]) (map (3*) [1..]))
(map (5*) [1..])
[2,3,4,5,6,6,8,9,10,10,12,12,14,15,15,16,18,18,20,20,21,22,24,24,25,26,27,
28,30,30,30,32,33,34,35,36,36,38,39,40,40,42,42,44,45,45,46,48,48,50,50,..
Ausgabe enthält doppelte Elemente.
Entfernen: mit nub
oder mische umprogrammieren
Grundlagen der Programmierung 2
- 5 -
Sortierte Ströme: Mischen
Entfernen doppelter Zahlen:
*Main> nub (mische (mische
(map (5*) [1..]) )
(map (2*) [1..])
(map (3*) [1..]))
[2,3,4,5,6,8,9,10,12,14,15,16,18,20,21,22,24,25,26,27,28,30,32,33,34,...
Grundlagen der Programmierung 2
- 6 -
Sortierte Ströme: Mischen ohne Doppelte
[2,4,6,8...]
[2,3,4,5,6,8,9,10,12,14,15,16,..]
[3,6,9,12...]
mische
[5,10,15,20...]
mischeNub [] ys = ys
mischeNub xs [] = xs
mischeNub (x:xs) (y:ys) =
if x == y then
(mischeNub xs (y:ys))
else
if x <= y then x: (mischeNub xs (y:ys))
else y: (mischeNub (x:xs) ys)
*Main> mischeNub (mischeNub (map (2*) [1..]) (map (3*) [1..]))
(map (5*) [1..])
[2,3,4,5,6,8,9,10,12,14,15,16,18,20,21,22,24,25,26,27,28,30,32,33,34,35,
Grundlagen der Programmierung 2
- 7 -
Beispiel
Differenz von zwei Strömen, wenn die Eingabeströme aufsteigend sortierte Zahlen enthalten
strom_minus xs [] = xs
strom_minus [] ys = []
strom_minus (x:xs) (y:ys) = if x == y then strom_minus xs (y:ys)
else if x > y then
strom_minus (x:xs) ys
else x: (strom_minus xs (y:ys))
> strom_minus [1..]
(nub (mische (map (2*) [1..])
(mische (map (3*) [1..]) (map (5*) [1..]) )))
[1,7,11,13,17,19,23,29,31,37,41,43,
Grundlagen der Programmierung 2
- 8 -
Beispiel
Schnitt zweier Strömen aus aufsteigend sortierte Zahlen:
strom_schnitt xs [] = []
strom_schnitt [] ys = []
strom_schnitt (x:xs) (y:ys) = if x == y then x: strom_schnitt xs
else if x > y then
strom_schnitt (x:xs)
ys
else strom_schnitt xs (y:ys)
*Main> strom_schnitt [2,4..] [3,6..]
[6,12,18,24,30,36,42,48,54,60,66,72,78,84,...
Grundlagen der Programmierung 2
- 9 -
ys
Verallgemeinerung der Ordnung
auf den Stromelementen
Erfordert Verallgemeinerung der Funktionen:
mischeNubGen ord [] ys = ys
mischeNubGen ord xs [] = xs
mischeNubGen ord (x:xs) (y:ys) =
if x ‘ord‘ y && y ‘ord‘x then
(mischeNubGen ord xs (y:ys))
else
if x ‘ord‘ y then x: (mischeNubGen ord xs (y:ys))
else y: (mischeNubGen ord (x:xs) ys)
Für aufsteigende Ströme von Zahlen: mischeNubGen (<=)
Für absteigende Ströme von Zahlen: mischeNubGen (>=)
Grundlagen der Programmierung 2
- 10 -
Primzahlen als Strom
primes = 2: [x | x <- [3,5..],
and (map (\t-> x ‘mod‘ t /= 0)
(takeWhile (\y -> y^2 <= x) primes))]
Der Vergleich mit einer durch den Fermatschen Primzahltest erzeugten
Liste ergibt:
primesDifference = strom_minus (2:[x | x <- [3,5..], fermat_test_naiv x])
primes
*Main> primesDifference
[561,1105,1729,2465,2821,6601^C
Grundlagen der Programmierung 2
- 11 -
foldr, foldl auf Strömen
foldl ungeeignet, da es für unendliche Ströme nicht terminiert.
*Main> foldl (\y x -> x:x:y) [] [1..10]
[10,10,9,9,8,8,7,7,6,6,5,5,4,4,3,3,2,2,1,1]
*Main> foldl (\y x -> x:x:y) [] [1..]
^CInterrupted.
foldr ist gut einsetzbar:
*Main> foldr (\x y -> x:x:y) [] [1..10]
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]
foldr (\x y -> x:x:y) [] [1..]
[1,1,2,2,3,3,4,4,5,5,6,6.......
*Main>
Grundlagen der Programmierung 2
- 12 -
Verarbeitung von Files mittels Strömen
Funktionen zur Bearbeitung langer Strings:
Vordefinierte Funktionen in Haskell:
words
unwords
lines
unlines
::
::
::
::
String -> [String]
[String] -> String
String -> [String]
[String] -> String
Grundlagen der Programmierung 2
- 13 -
Beispiele: Files als Ströme
*Main> words "abcd efgh eirof kdjn Dcnsajn"
["abcd","efgh","eirof","kdjn","Djcnsajn"]
*Main> concat (words "abcd efgh eirof kdjn djcnsajn")
"abcdefgheirofkdjndjcnsajn"
*Main> unwords (words "abcd efgh eirof kdjn djcnsajn")
"abcd efgh eirof kdjn djcnsajn"
Grundlagen der Programmierung 2
- 14 -
Beispiele: Files als Ströme. lines und unlines:
*Main> lines "Habe nun, ach! Philosophie,\n Juristerei und
Medizin,\n und leider auch Theologie"
["Habe nun, ach! Philosophie,"," Juristerei und Medizin,",
" und leider auch Theologie"]
*Main> unlines ["Habe nun, ach! Philosophie,"," Juristerei und Medizin,",
" und leider auch Theologie"]
"Habe nun, ach! Philosophie,\n Juristerei und Medizin,\n
und leider auch Theologie\n"
*Main> (map words (lines "Habe nun, ach! Philosophie,\n Juristerei
und Medizin,\n und leider auch Theologie"))
[["Habe","nun,","ach!","Philosophie,"],["Juristerei","und","Medizin,"],
["und","leider","auch","Theologie"]]
Grundlagen der Programmierung 2
- 15 -
Beispiele: Files als Ströme. lines und unlines:
*Main> unlines (map unwords [["Habe","nun,","ach!","Philosophie,"],
["Juristerei","und","Medizin,"],["und","leider","auch","Theologie"]])
"Habe nun, ach! Philosophie,\nJuristerei und Medizin,
\nund leider auch Theologie\n"
Grundlagen der Programmierung 2
- 16 -
Beispiele: Files
Kommt Wort w in einem langen String vor?
wordEq w strom = w == take (length w) strom
wordIn w [] = False
wordIn w (x:strom) = wordEq w (x:strom) || wordIn w strom
wordCount w strom =
sum (map (\str -> if wordEq w str then 1 else 0) (tails strom))
Grundlagen der Programmierung 2
- 17 -
Beispiele: Files; Verwendungsbeispiele:
*Main> wordIn "Theo" "Habe nun, ach! Philosophie,\n Juristerei
und Medizin,\n und leider auch Theologie"
True
*Main> wordIn "Theorem" "Habe nun, ach! Philosophie,\n Juristerei
und Medizin,\n und leider auch Theologie"
False
*Main> wordCount "und" "Habe nun, ach! Philosophie,\n Juristerei
und Medizin,\n und leider auch Theologie"
2
Grundlagen der Programmierung 2
- 18 -
Beispiele: Files
Kommt Wort w in einem langen String vor?
Wenn sich Worte überlappen dürfen,
muss man spezifizieren, was genau gezählt werden soll:
*Main> wordCount "aa" "aaaa"
3
Grundlagen der Programmierung 2
- 19 -
IO und Files als Ströme
fileLesen fu = do
putStr "File-Name:?"
fname <-getLine
contents <-(readFile fname)
putStr (show (fu contents))
Grundlagen der Programmierung 2
- 20 -
Bemerkungen zur Stromverarbeitung
verzögerte Auswertung unterstützt
Programmierung kombinierter Anfragen,
die den Strom nur einmal lesen.
nub (filter p (map f xs))
map f .
Grundlagen der Programmierung 2
filter
nub
- 21 -
Bemerkungen zur Stromverarbeitung: Fallen
Speicher-Blockaden
z.B. durch Definition eines Stromes als Konstante im Programm
primes,
Grundlagen der Programmierung 2
- 22 -
Bemerkungen zur Stromverarbeitung
Falle:
findeDoppelte strom = findeDoppelteR strom strom 0
findeDoppelteR strom1 [] n =
False
findeDoppelteR strom1 (x:strom2) n =
(take n strom1 == take n (x:strom2))
|| findeDoppelteR strom1 strom2 (n+1)
*Main> findeDoppelte ([1..10] ++ [1..20])
True
*Main> findeDoppelte ([1..10] ++ [1..])
True
findeDoppelte ([1..])
In 1 min wird ca. 1 GB interner Speicher blockiert.
Grundlagen der Programmierung 2
- 23 -
Stromverarbeitung für endliche Mengen
Repräsentation von Mengen
als aufsteigend sortierte Listen:
Operationen:
Schnitt, Vereinigung, und Differenz sind
einfach und sehr effizient
das Ergebnis ist ebenfalls aufsteigend sortierte Liste
Grundlagen der Programmierung 2
- 24 -
Ströme: Weitere Funktionalitäten
Potenzmenge: nicht sinnvoll bei Strömen, aber bei Mengen
die Ausgabe ist exponentiell
Bei geordneter Ausgabe kann man verschiedene Ordnungen wählen:
Nach Kardinalität,
lexikographischen Ordnung auf Folgen.
Grundlagen der Programmierung 2
- 25 -
Ströme: Potenzmenge
---Potenzmenge, lexikographisch
potenzmenge xs = []:potenzmengeR xs
potenzmengeR [x] = [[x]]
potenzmengeR (x:xs) = let pm = (potenzmengeR xs) in
: (map (x:) pm) ++
Grundlagen der Programmierung 2
[x]
pm
- 26 -
Ströme: Potenzmenge
---Potenzmenge, Kardinalitaet, dann lexikographisch
potenzmengeKL xs = [] :
(concatMap (\n -> potenzmengeKLR xs n) [1.. length xs])
potenzmengeKLR [] _ = []
potenzmengeKLR xs 0 = [[]]
potenzmengeKLR xs 1 = map (\u-> [u]) xs
potenzmengeKLR (x:xs) (n+1) =
let pmox = potenzmengeKLR xs (n+1)
pmwx = potenzmengeKLR xs n
in (map (x:) pmwx) ++ pmox
Grundlagen der Programmierung 2
- 27 -
Ströme: Kreuzprodukt von Mengen
Ausgabe kann nicht die lexikographische Ordnung einhalten,
wenn die Ausgabe fair sein soll.
Kreuzprodukt in lexikographischer Reihenfolge,
wenn die Eingabe sortierte endliche Mengen sind:
kreuzprodukt xs ys = [(x,y) | x <- xs, y <- ys]
Grundlagen der Programmierung 2
- 28 -
Herunterladen