3.1 Datenstrukturen und Typen in Haskell. Bisher haben wir nur die eingebauten Basisdatentypen wie Zahlen und Wahrheitswerte benutzt.

Größe: px
Ab Seite anzeigen:

Download "3.1 Datenstrukturen und Typen in Haskell. Bisher haben wir nur die eingebauten Basisdatentypen wie Zahlen und Wahrheitswerte benutzt."

Transkript

1 Kapitel 3 Daten Abstraktion 3.1 Datenstrukturen und Typen in Haskell Bisher haben wir nur die eingebauten Basisdatentypen wie Zahlen und Wahrheitswerte benutzt Basisdatentypen Ganze Zahlen (Int) umfassen oft nur in einem bestimmten Zahlbereich. Meist wird der Zahlbereich durch die Binärzahl b begrenzt, die in ein Halbwort (2 Byte), Wort (4 Byte), Doppelwort (8 Byte) passen, so dass der Bereich b, b darstellbar ist. Die Darstellung ist somit mathematisch außerhalb dieses Bereichs nicht korrekt. Das bedeutet, man muss damit rechnen, dass ein Überlauf stattfindet. Unbeschränkte ganze Zahlen (Integer). Kann man in Haskell verwenden, gibt es aber nicht in allen Programmiersprachen. Hier ist die Division problematisch, da die Ergebnisse natürlich nicht immer ganze Zahlen sind. Verwendet wird die ganzzahlige Division. Die Division durch 0 ergibt einen Laufzeitfehler. Rationale Zahlen. Werden in einigen Programmiersprachen unterstützt; meist als Paar von Int, in Haskell auch exakt als Paar von Integer. Komplexe Zahlen sind in Python direkt verfügbar. Es ist nicht schwer, diese in Haskell selbst zu definieren. In Haskell gibt es einen Module Complex, der einige Funktionen implementiert, u.a. auch exp und die Winkelfunktionen für Komplexe Eingaben. Gleitkommazahlen (Gleitpunktzahlen) (Float). Das sind Approximationen für reelle Zahlen in der Form Mantisse, Exponent, z.b e 40 Die Bedeutung ist Die interne Darstellung ist getrennt in Mantisse und Exponent. Die arithmetischen Operationen sind 1

2 Praktische Informatik 1, WS 2001/02, Kapitel 3 2 alle definiert, aber man muß immer damit rechnen, dass Fehler durch die Approximation auftreten (Rundungsfehler, Fehler durch Abschneiden), dass ein Überlauf eintritt, wenn der Bereich des Exponenten überschritten wurde, oder dass Division durch 0 einen Laufzeitfehler ergibt. Beachte, dass es genaue IEEE-Standards gibt, wie Rundung, Abschneiden, die Operationen usw. für normale Genauigkeit (4 Byte) oder für doppelte Genauigkeit (8 Byte) funktionieren, damit die Ergebnisse von Berechnungen (insbesondere finanzielle) auf allen Rechnern den gleichen Wert ergeben. Meist sind diese Operationen schon auf der Prozessorebene implementiert, so dass man diese Operationen i.a. über die Programmiersprache aufruft. Zeichen, Character. Sind meist ASCII-Zeichen (1 Byte); andere Standards zu 1 Byte werden verdrängt. In Haskell ist dies Typ Char. Ein weiterer Standard, der 4 Bytes pro Zeichen verwendet und der es erlaubt, viel mehr Zeichen zu kodieren, und der für alle Sprachen Kodierungen bereithält, ist der Unicode-Standard ( Es gibt drei Varianten, die es erlauben, Kompressionen zu verwenden, so dass häufige Zeichen in einem oder 2 Byte kodiert werden. In Unicode gibt es u.a. zusammengesetzte Zeichen, z.b. wird ü bevorzugt mittels zwei Kode-einheiten kodiert. In Python werden ASCII und Unicode Zeichen unterstützt. Funktionen in Haskell: ord liefert die Hex-Codierung eines Zeichens und chr ist die Umkehrfunktion von ord. Wir werden nun größere, zusammengesetzte Datenobjekte als Abstraktion verwenden Einfache Typen Beispiel Rationale Zahlen Eine rationale Zahl kann man als zusammengesetztes Objekt verstehen, das aus zwei Zahlen, dem Zähler und dem Nenner besteht. Normalerweise schreibt man x für die rationale Zahl mit Zähler y x und dem Nenner y. Die einfachste Methode, dies in Haskell darzustellen, ist als Paar von Zahlen (x, y). Beachte, daß in Haskell rationale Zahlen bereits vordefiniert sind, und die entsprechenden Paare (x, y) als x%y dargestellt werden. Z.B.: Prelude> (3%4)*(4%5) 3 % 5 Prelude> 1%2+2%3 7 % 6 Datenkonversionen macht man mit torational bzw. truncate. Es gibt rationale Zahlen mit kurzen und beliebig langen ganzen Zahlen.

3 Praktische Informatik 1, WS 2001/02, Kapitel 3 3 Paare von Objekten kann man verallgemeinern zu n-tupel von Objekten: (t 1,..., t n ) stellt ein n-tupel der Objekte t 1,..., t n dar. Beispiel (1,2,3,True) (1,(2,True),3) ("hallo",false) (fak 100,\x-> x) Um mit Datenobjekten operieren zu können, benötigt man: Datenkonstruktoren: Hiermit wird ein Datenobjekt neu konstruiert, wobei die Teile als Parameter übergeben werden. Datenselektoren: Funktion, die gegeben ein Datenobjekt, bestimmte Teile daraus extrahiert. Zum Beispiel konstruiert man ein Paar (s, t) aus den beiden Ausdrücken s und t. Da man die Ausdrücke wieder extrahieren können muß benötigt man die Selektoren fst und snd, für die gelten muß: fst(s, t) = s und snd(s, t) = t. Für ein n-tupel benötigt man n Datenselektoren, auch wegen der Typisierbarkeit, für jede Stelle des Tupels einen. Die Definition dieser Selektoren wird in Haskell syntaktisch vereinfacht durch sogenannte Muster (pattern). Einfache Beispiele einer solchen Definition sind: fst (x,y) = x snd (x,y) = y selektiere_3_von_5 (x1,x2,x3,x4,x5) = x3 Diese Muster sind syntaktisch überall dort erlaubt, wo formale Parameter (Variablen) neu eingeführt werden, d.h. in Funktionsdefinitionen, in Lambda- Ausdrücken und in let-ausdrücken. Nun können wir auch den Typ von Tupeln hinschreiben: n-tupel haben einen impliziten Konstruktor: (.,...,.). Der Typ wird entsprechend notiert: Der }{{} n Typ von (1, 1) ist z.b. (Integer, Integer), der Typ von selektiere_3_von_5 ist (α 1,..., α 5 ) α 3, und der Typ von (1, 2.0, selektiere_3_von_5) ist (Integer, Float, (α 1,..., α 5 ) α 3 ). Bemerkung In Haskell kann man Typen und Konstruktoren mittels der data-anweisung definieren. Zum Beispiel data Punkt = Punktkonstruktor Double Double data Strecke = Streckenkonstruktor Punkt Punkt data Viertupel a b c d = Viertupelkons a b c d

4 Praktische Informatik 1, WS 2001/02, Kapitel 3 4 Definition Ein Muster ist ein Ausdruck, der nach folgender Syntax erzeugt ist: Muster ::= Variable ( Muster ) Konstruktor (n) Muster... Muster }{{} n ( Muster,..., Muster ) Als Kontextbedingung hat man, dass in einem Muster keine Variable doppelt vorkommen darf. Beachte, dass Zahlen und Character als Konstruktoren zählen. Die erlaubten Transformationen bei Verwendung von Mustern kann man grob umschreiben als: wenn das Datenobjekt wie das Muster aussieht und die Konstruktoren übereinstimmen, dann werden die Variablen an die entsprechenden Ausdrücke (Werte) gebunden. Diese Musteranpassung kann man mit der Funktion anpassen Muster Ausdruck rekursiv beschreiben, wobei das Ergebnis eine Menge von Bindungen ist: anpassen Kon Kon = anpassen x t = {x t}: (passt; aber keine Bindung notwendig.) (x wird an t gebunden.) anpassen (Kon s 1... s n ) (Kon t 1... t n ) = anpassen s 1 t 1... anpassen s n t n anpassen (Kon s 1... s n ) (Kon t 1... t n ) = Fail, wenn Kon kon Dies bedeutet auch, dass die Musteranpassung erzwingt, dass die Datenobjekte, die an das Muster angepaßt werden sollen, zunächst ausgewertet werden müssen. Muster wirken wie ein let-ausdruck mit Selektoren kombiniert. Man kann Zwischenstrukturen in Mustern ebenfalls mit Variablen benennen: Das Muster (x, y@(z 1, z 2 )) wird bei der Musteranpassung auf (1, (2, 3)) folgende Bindungen liefern: x = 1, y = (2, 3), z 1 = 2, z 2 = 3. Typen Jedes Datenobjekt in Haskell muß einen Typ haben. Ein Typ korrespondiert somit zu einer Klasse von Datenobjekten. Syntaktisch wird für jeden Typ ein Name eingeführt. Die eingebauten arithmetischen Datenobjekte haben z.b. die Typen Int, Integer, Float, Char,... Komplexere Typen und zugehörige Datenobjekte werden durch ihre obersten Konstruktoren charakterisiert. Datenkonstruktoren können explizit definiert werden und haben den Status eines Ausdrucks in Haskell. Es gibt eine eigene Anweisung, die Datentypen definiert, wobei man auf die definierten Typnamen zurückgreift und auch rekursive Definitionen machen darf. Es genügt, nur die Datenkonstrukoren zu definieren, da die Selektoren durch Musteranpassung (Musterinstanziierung)

5 Praktische Informatik 1, WS 2001/02, Kapitel 3 5 definierbar sind. In der Typanweisung werden auch evtl. neue Typnamen definiert. Diese Typnamen können mit einem anderen Typ parametrisiert sein (z.b. [a]: Liste mit dem dem a). Beispiel Punkte und Strecken und ein Polygonzug werden in der Zahlenebene dargestellt durch Koordinaten: data Punkt(a) = Punkt a a data Strecke(a) = Strecke (Punkt a) (Punkt a) data Vektor(a) = Vektor a a data Polygon a = Polygon [Punkt a] Hier ist Punkt ein neu definierter (parametrisierter) Typ, Punkt auf der rechten Seite der neu definierte Konstruktor für Punkte der Ebene. Strecke ist ein neuer Typ, der aus zwei Punkten besteht. Es ist unproblematisch, Typ und Konstruktor gleich zu benennen, da keine Verwechslungsgefahr besteht. Der Parameter a kann beliebig belegt werden: z.b. mit Float, Int, aber auch mit [[(Int, Char)]]. Haskell sorgt mit der Typüberprüfung dafür, dass z.b. Funktionen, die für Punkte definiert sind, nicht auf rationale Zahlen angewendet werden, die ebenfalls aus zwei Zahlen bestehen. Einige Funktionen, die man jetzt definieren kann, sind: addierevektoren::num a => Vektor a -> Vektor a -> Vektor a addierevektoren (Vektor a1 a2) (Vektor b1 b2) = Vektor (a1 + b1) (a2 + b2) streckenlaenge (Strecke (Punkt a1 a2) (Punkt b1 b2)) = sqrt (frominteger ((quadrat (a1 - b1)) + (quadrat (a2-b2)))) verschiebestrecke s v = let (Strecke (Punkt a1 a2) (Punkt b1 b2)) = s (Vektor v1 v2) = v in (Strecke (Punkt (a1+v1) (a2+v2)) (Punkt (b1+v1) (b2+v2))) teststrecke = (Strecke (Punkt 0 0) (Punkt 3 4)) test_streckenlaenge = streckenlaenge (verschiebestrecke teststrecke (Vektor 10 (-10))) streckenlaenge teststrecke <CR> > 5.0

6 Praktische Informatik 1, WS 2001/02, Kapitel 3 6 test_streckenlaenge <CR> > 5.0 Wenn wir die Typen der Funktionen überprüfen, erhalten wir, wie erwartet: addierevektoren :: Num a => Vektor a -> Vektor a -> Vektor a streckenlaenge :: Num a => Strecke a -> Float test_streckenlaenge :: Float verschiebestrecke :: Num a => Strecke a -> Vektor a -> Strecke a Summentypen und Fallunterscheidung Bisher können wir noch keine Entsprechung des Booleschen Datentyps selbst definieren. Wir können Klassen von Datenobjekten verschiedener Struktur in einer Klasse vereinigen, indem wir einem Typ mehr als einen Konstruktor zuordnen: Dies ist gleichzeitig die Deklaration (Erklärung) eines syntaktischen Datentyps. data Wahrheitswerte = Wahr Falsch Die zugehörigen Typen nennt man auch Summentypen. Da Funktionen für Objekte unterschiedlicher Struktur eine Fallunterscheidung machen müssen, gibt es eine einfache Möglichkeit dies in Haskell hinzuschreiben: Man schreibt die Funktionsdefinition für jeden Fall der verschiedenen Muster der Argumente hin. und1 Wahr Falsch = Falsch und1 Wahr Wahr = Wahr und1 Falsch Falsch = Falsch und1 Falsch Wahr = Falsch oder und2 Wahr x = x und2 Falsch x = Falsch oder und3 Wahr x = x und3 Falsch _ = Falsch Der Unterstrich ist eine anonyme Mustervariable (wildcard). Beachte, dass und2 und und3 gleiches Verhalten haben, während und1 anderes Verhalten hat bzgl. Terminierung. Definition Es gibt ein weiteres syntaktisches Konstrukt, den case- Ausdruck, der zur Fallunterscheidung verwendet werden kann. Die Syntax ist: case Ausdruck of{ Muster -> Ausdruck ;... ; Muster -> Ausdruck }

7 Praktische Informatik 1, WS 2001/02, Kapitel 3 7 Die Kontextbedingung ist, dass die Muster vom Typ her passen. Die Bindungsbereiche der Variablen in den Mustern sind genau die zugehörigen Ausdrücke hinter dem Pfeil (-> ). Der Gültigkeitsbereich der Variablen in bezug auf das case-konstrukt kann an der Definition der freien Variablen abgelesen werden: F V (case s of (c 1 x x 1n1 ) t 1 );... ; (c k x k1... x knk t k )) = F V (s) F V (t 1 ) \ {x 11,... x 1n1 }... F V (t k ) \ {x k1,... x knk } GV (case s of (c 1 x x 1n1 t1);... ; (c k x k1... x knk t k )) = GV (s) GV (t 1 ) {x 11,... x 1n1 }... F V (t k ) {x k1,... x knk } Beispiel Folgende Definition ist äquivalent zum in Haskell definierten logischen und (und auch zu und2 und und3) : &&. und4 x y = case x of True -> y; False -> False Beispiel Folgende Definition ist äquivalent zum normalen if. then. else. D.h. case-ausdrücke sind eine Verallgemeinerung des if. then. else. mein_if x y z = case x of True -> y; False -> z Die Reduktionsregel zum case ist case (case (c t 1... t n ) of... (c x 1... x n s)...) s[t 1 /x 1,..., t n /x n ] Fallunterscheidungen in Funktionsdefinitionen können als case-ausdrücke geschrieben werden, allerdings muss man vorher analysieren, über welches Argument eine Fallunterscheidung notwendig ist. In Haskell wird bei überlappenden Mustern in der Funktionsdefinition die Strategie Muster von oben nach unten verfolgt, die ebenfalls in geschachtelte case-ausdrücke übersetzt werden kann.

8 Praktische Informatik 1, WS 2001/02, Kapitel Rekursive Datenobjekte: z.b. Listen Listen sind eine Datenstruktur für Folgen von gleichartigen (gleichgetypten) Objekten. Da wir beliebig lange Folgen verarbeiten und definieren wollen, nutzen wir die Möglichkeit, rekursive Datentypen zu definieren. Der Typ der Objekte in der Liste ist nicht festgelegt, sondern wird hier als (Typ-) Variable in der Definition verwendet. D.h. aber, dass trotzdem in einer bestimmten Liste nur Elemente eines Typs sein dürfen. -- eine eigene Definition data Liste a = Leereliste ListenKons a (Liste a) Dies ergibt Datenobjekte, die entweder leer sind: Leereliste, oder deren Folgenelemente alle den gleichen Typ a haben, und die aufgebaut sind als ListenKons b 1 (ListenKons b 2... Leereliste)). Listen sind in Haskell eingebaut und werden syntaktisch bevorzugt behandelt. Aber: man könnte sie völlig funktionsgleich auch selbst definieren. Im folgenden werden wir die Haskell-Notation verwenden. Die Definition würde man so hinschreiben: (allerdings entspricht diese nicht der Syntax) data [a] = [] a : [a] Listenobjekte werden auch dargestellt als Folge in eckigen Klammern: [1,2,3,4,5] ist die Liste der Zahlen von 1 bis 5; die leere Liste wird einfach durch [] dargestellt. Listentypen werden mit eckigen Klammern notiert: [Int] ist der Typ Listen von Int. Eine Liste von Zeichen hat den Typ [Char], der auch mit String abgekürzt wird. Der Typ einer Liste von Listen von Zeichen ist: [[Char]]. der Typ einer Liste von Paaren von langen ganzen Zahlen ist [(Integer, Integer)], Wir können jetzt rekursive Funktionen auf Listenobjekten definieren: -- Laenge einer Liste length [] = 0 length (_:xs) = 1 + length xs -- map wendet eine Funktion f auf alle Elemente einer Liste an. map f [] = [] map f (x:xs) = f x : map f xs Die erste Funktion berechnet die Anzahl der Elemente einer Liste, d.h. deren Länge, die zweite Funktion wendet eine Funktion auf jedes Listenelement an und erzeugt die Liste der Resultate. Strings oder Zeichenketten in Haskell sind Listen von Zeichen und können auch genauso verarbeitet werden. Die folgende Funktion hängt zwei Listen zusammen: append [] ys append (x:xs) ys = ys = x : (append xs ys)

9 Praktische Informatik 1, WS 2001/02, Kapitel 3 9 In Haskell wird diese Funktion als ++ geschrieben und Infix benutzt. Beispiel Main> 10:[7,8,9] [10,7,8,9] Main> length [3,4,5] 3 Main> length [ ] 1000 Main> let a = [1..] in (length a,a) ( ERROR - Garbage collection fails to reclaim sufficient space Main> map quadrat [3,4,5] [9,16,25] Main> [0,1,2] ++ [3,4,5] [0,1,2,3,4,5] Beispiel: einfache geometrische Algorithmen Einige weitere geometrische Algorithmen, die man mit den bisherigen Mitteln strukturieren kann: Berechnung der Fläche eines von einem Polygonzug umschlossenen Areals, falls dieses konvex ist. Der Test auf Konvexität ist ebenfalls angegeben. Typische für geometrische Algorithmen sind Sonderfälle: Die Sonderfälle, die die Implementierung beachtet, sind: i. Der Polygonzug muss echt konvex sein, d.h. es darf keine Null-Strecke dabei sein, und ii. es dürfen keine drei benachbarten Punkte auf einer Geraden liegen. Als Beispiel ist die Fläche eines regelmäßigen n-ecks und ein Vergleich mit der Fläche des Kreises programmiert. -- polgonflaeche data Polygon a = Polygon [Punkt a] deriving Show polyflaeche poly = if ist_konvex_polygon poly then polyflaeche_r poly else error "Polygon ist nicht konvex" polyflaeche_r (Polygon (v1:v2:v3:rest)) = dreiecksflaeche v1 v2 v3 + polyflaeche_r (Polygon (v1:v3:rest)) polyflaeche_r _ = fromint 0 dreiecksflaeche v1 v2 v3 = let a = abstand v1 v2

10 Praktische Informatik 1, WS 2001/02, Kapitel 3 10 b = abstand v2 v3 c = abstand v3 v1 s = 0.5*(a+b+c) in sqrt (s*(s-a)*(s-b)*(s-c)) abstand (Punkt a1 a2 ) (Punkt b1 b2 ) = let d1 = a1-b1 d2 = a2-b2 in sqrt (d1^2 + d2^2) -- testet konvexitaet: aber nur gegen den Uhrzeigersinn. ist_konvex_polygon (Polygon []) = True ist_konvex_polygon (Polygon (p:polygon)) = ist_konvex_polygonr (polygon ++ [p]) testkonvex = ist_konvex_polygon (Polygon [Punkt (fromint 2) (fromint 2), Punkt (fromint (-2)) (fromint 2), Punkt (fromint (1)) (fromint 1), Punkt (fromint 2) (fromint (-2)) ]) ist_konvex_polygonr (p1:rest@(p2:p3:rest2)) = ist_konvex_drehung_positiv p1 p2 p3 && ist_konvex_polygonr rest ist_konvex_polygonr _ = True ist_konvex_drehung_positiv (Punkt a1 a2) (Punkt b1 b2) (Punkt c1 c2) = let ab1 = a1-b1 ab2 = a2-b2 bc1 = b1-c1 bc2 = b2-c2 in ab1*bc2-ab2*bc1 > 0 testpoly = polyflaeche (Polygon [Punkt (fromint 1) (fromint 1), Punkt (fromint (-1)) (fromint 1), Punkt (fromint (-1)) (fromint (-1)), Punkt (fromint 1) (fromint (-1)) ]) vieleck n = Polygon [Punkt (cos (2.0*pi*i/n)) (sin (2.0*pi*i/n)) i <- [1.0..n]] vieleckflaeche n = polyflaeche (vieleck n) vieleck_zu_kreis n = let kreis = pi vieleck = vieleckflaeche n ratio = vieleck / kreis in (n,kreis, vieleck,ratio) -- Elimination von gleichen Punkten --- und kollinearen Tripeln poly_normeq_r [] = [] poly_normeq_r [x] = [x]

11 Praktische Informatik 1, WS 2001/02, Kapitel 3 11 poly_normeq_r (x:rest@(y:_)) = if x == y then poly_normeq_r rest else x: poly_normeq_r rest poly_norm_koll (x:rest1@(y:z:tail)) = if poly_drei_koll x y z then poly_norm_koll (x:z:tail) else x:poly_norm_koll rest1 poly_norm_koll rest = rest --testet x,y,z auf kollinearitaet: poly_drei_koll (Punkt x1 x2) (Punkt y1 y2) (Punkt z1 z2) = (z1-y1)*(y2-x2) == (y1-x1)*(z2-y2) -- (z1-y1)/(z2-y2) == (y1-x1)/(y2-x2) Funktionen auf Listen Zwei allgemeine Funktionen (Methoden), die Listen verarbeiten sind foldl und foldr und z.b. die Summe aller Elemente einer Liste verallgemeinern. Die Argumente sind: eine zweistellige Operation, ein Anfangselement (Einheitselement) und die Liste. foldl foldl f z [] foldl f z (x:xs) :: (a -> b -> a) -> a -> [b] -> a = z = foldl f (f z x) xs foldr foldr f z [] foldr f z (x:xs) :: (a -> b -> b) -> b -> [a] -> b = z = f x (foldr f z xs) Für einen Operator und ein Anfangselement (Einheitselement) e ist der Ausdruck foldl e [a 1,..., a n ] äquivalent zu ((... ((e a 1 ) a 2 )... ) a n ). Analog entspricht foldr e [a 1,..., a n ] der umgekehrten Klammerung: a 1 (a 2 (... (a n e))). Für einen assoziativen Operatoren und wenn e Rechts- und Linkseins zu ist, ergibt sich derselbe Wert. Die Operatoren foldl und foldr unterscheiden sich bzgl. des Ressourcenbedarfs in Abhängigkeit vom Operator und dem Typ des Arguments. Beispiele für die Verwendung, wobei die bessere Variante definiert wurde. sum xs = foldl (+) 0 xs produkt xs = foldl (*) 1 xs -- (foldl (*) 1 xs) concat xs = foldr (++) [] xs

12 Praktische Informatik 1, WS 2001/02, Kapitel 3 12 Weitere wichtige Funktionen auf Listen sind: filter f [] = [] filter f (x:xs) = if (f x) then x : filter f xs else filter f xs -- umdrehen einer Liste reverse xs = foldl (\x y -> y:x) [] xs --- verbindet eine Liste von Listen zu einer einzigen Liste: concat xs = foldr append [] xs -- nimmt die ersten n Elemente der Liste xs. take 0 _ = [] take n [] = [] take n (x:xs) = x : (take (n-1) xs) randomints a b -- liefert eine Liste von (Pseudo-) Zufallszahlen -- wenn import Random im File steht. Weitere Listen-Funktionen sind: -- Restliste nach n-tem Element drop 0 xs = xs drop _ [] = [] drop n (_:xs) n>0 = drop (n-1) xs drop = error "Prelude.drop: negative argument" -- Bildet Liste der Paare zip :: [a] -> [b] -> [(a,b)] zip (a:as) (b:bs) = (a,b) : zip as bs zip = [] --- aus Liste von Paaren ein Paar von Listen unzip :: [(a,b)] -> ([a],[b]) unzip = foldr (\(a,b) (as,bs) -> (a:as, b:bs)) ([], []) Beispielauswertungen sind: drop 10 [1..100] -----> [11,12,... zip "abcde" [1..] -----> [( a,1),( b,2),( c,3),( d,4),( e,5)]

13 Praktische Informatik 1, WS 2001/02, Kapitel 3 13 unzip (zip "abcdefg" [1..]) ----> ("abcdefg",[1,2,3,4,5,6,7]) Es gibt noch weitere brauchbare allgemeine Listenfunktionen, siehe Prelude der Implementierung von Haskell Listenausdrücke, list comprehensions Dies ist eine Spezialität von Haskell und erleichtert die Handhabung von Listen. Die Syntax ist analog zu Mengenausdrücken, nur dass eine Reihenfolge der Listenelemente festgelegt ist. Syntax: [ Ausdruck Generator Filter {, { Generator Filter }} ]. Vor dem senkrechten Strich kommt ein Ausdruck, danach eine mit Komma getrennte Folge von Generatoren der Form v <- liste oder von Prädikaten. Wirkungsweise: die Generatoren liefern nach und nach die Elemente der Listen. Wenn alle Prädikate zutreffen, wird ein Element entsprechend dem Ausdruck links von in die Liste aufgenommen. Hierbei können auch neue lokale Variablen eingeführt werden, deren Geltungsbereich rechts von der Einführung liegt, aber noch in der Klammer [...]. Beispiel [x x <- xs] ergibt die Liste selbst [f x x <- xs] ist dasselbe wie map f xs [x x <- xs, p x] ist dasselbe wie filter p xs [(x,y) x <- xs, y <-ys] kartesisches Produkt der endlichen Listen xs und ys [y x <- xs, y <-x] entspricht der Funktion concat Spezielle Listenausdrücke sind: [n..m] erzeugt eine Liste [n, n+1,..., m] [n,m..e] erzeugt eine Liste [n, m, n+2*(m-n),..., e ] mit e e. [n..] erzeugt die (potentiell unendliche) Liste [n,n+1,n+2,...]. Beispiel [(x,y) x <- [1..10], even x, y <- [2..6], x < y] Resultat: [(2,3),(2,4),(2,5),(2,6),(4,5),(4,6)] Die Erzeugungsreihenfolge tabellarisch aufgelistet ergibt: x y ? N N Y Y Y Y N N N N Y Y N N... Ein weiteres Beispiel, das zeigt, dass die Elementvariable auch in der Listenerzeugung weiter rechts verwendet werden kann:

14 Praktische Informatik 1, WS 2001/02, Kapitel 3 14 [(x,y) x <- [1..10], y <- [1..x]] [(1,1), (2,1),(2,2), (3,1),(3,2),(3,3), (4,1),(4,2),(4,3),(4,4), (5,1),(5,2),(5,3),(5,4),(5,5), (6,1),(6,2),(6,3),(6,4),(6,5),(6,6), (7,1),(7,2),(7,3),(7,4),(7,5),(7,6),(7,7), (8,1),(8,2),(8,3),(8,4),(8,5),(8,6),(8,7),(8,8), (9,1),(9,2),(9,3),(9,4),(9,5),(9,6),(9,7),(9,8),(9,9), (10,1),(10,2),(10,3),(10,4),(10,5),(10,6),(10,7),(10,8),(10,9),(10,10)] 3.3 Listenbehandlung in Python Viele Funktionen auf Listen haben in Python zwar dieselbe Hauptfunktionalität. Der Rückgabewert (return...) ist oft analog zu dem der Haskellfunktionen, aber es scheint einen Unterschied bei Laufzeitfehlern zu geben: Oft wird der Laufzeitfehler selbst als Wert zurückgegeben. Bei Seiteneffekten haben diese Funktionen ein ganz anderes Verhalten als in Haskell. Haskell ist referentiell transparent, was bewirkt, dass die Listenargumente nach einer Funktionsanwendung unverändert sind, während eine Funktionsanwendung in Python oft eine Veränderung der Listenstruktur, und damit der Werte von Argumentvariablen nach sich zieht. Einige Funktionen sind nur dazu gemacht, genau dies zu bewirken. Z.B. reverse dreht die Python- Liste um. Dies kann soweit gehen, dass Variablen, die in einem Aufruf nicht erwähnt werden, trotzdem nach dem Aufruf andere Werte haben (sogenanntes aliasing: der gleiche Speicherbereich hat verschiedene Namen). Wir geben zur Listenverarbeitung in Python Funktionen an und Beispiele: >>> range(20) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] >>> range(5,10) [5, 6, 7, 8, 9] >>> len(range(10)) 10 >>> len(range( )) Traceback (most recent call last): File "<input>", line 1, in? MemoryError >>> a = [ a, b, c ] >>> a [ a, b, c ] >>> b = [3,4,5]

15 Praktische Informatik 1, WS 2001/02, Kapitel 3 15 >>> a.extend(b) >>> a [ a, b, c, 3, 4, 5] >>> b [3, 4, 5] >>> a.append(b) >>> a [ a, b, c, 3, 4, 5, [3, 4, 5]] >>> a.reverse() >>> a [[3, 4, 5], 5, 4, 3, c, b, a ] >>> b [3, 4, 5] Ein Beispiel für aliasing: >>> a = [3,2,1] >>> b = a >>> a [3, 2, 1] >>> b [3, 2, 1] >>> a.sort() >>> a [1, 2, 3] >>> b [1, 2, 3] Um sich ein Bild von verketteten Listen zu machen, ist folgendes Diagramm hilfreich, das die Situation nach der Zuweisung a = [1,2,3]; b = a repräsentiert. Die letzte Box symbolisiert einen Zeiger auf Nil. a b Es ist zu beachten, dass bei a.extend(b) eine Kopie der Liste b an a angehängt wird. Bei append wird nur der Verweis genommen: >>> a = [1,2,3] >>> b = [4,5,6] >>> a.append(b) >>> a [1, 2, 3, [4, 5, 6]]

16 Praktische Informatik 1, WS 2001/02, Kapitel 3 16 >>> b.reverse() >>> a [1, 2, 3, [6, 5, 4]] >>> a Bild des Speichers nach den obigen Befehlen: b Es gibt in Python noch die Funktionen map, filter, und reduce, die vordefiniert sind. Sie haben analoge Wirkung wie die Funktionen map, filter, und foldl in Haskell. Folgende Funktion erzeugt eine Kopie einer Liste: def id(x): return x def listcopy(x): return map(id, x) def geradeq(n): return n%2 == 0 >>> filter(geradeq,range(20)) [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] Die Summe aller Elemente einer Liste kann man ebenfalls analog zu den Haskell-Methoden berechnen: def add(x,y): return x+y >>> reduce(add,range(100)) 4950

17 Praktische Informatik 1, WS 2001/02, Kapitel 3 17 def mal(x,y): return x*y def fak(n): return reduce(mal,range(1,n+1)) Schleifen in Python Schleifen kann man in Python programmieren mittels while oder for: >>> a=10 >>> while a > 0:... print a... a = a >>> for i in range(1,11):... print(i) >>> Stacks und Queues Ein Stack, Keller, Stapel ist eine Datenstruktur. Man kann sie benutzen wie einen Stapel, auf den man viele Dinge drauflegen kann, und auch wieder runternehmen kann, aber man darf nicht mitten im Stapel etwas hinzufügen oder wegnehmen. Man sagt auch last-in first-out (lifo). Die Operationen nennt man push um etwas draufzulegen, und pop um das oberste Element zu entfernen.

18 Praktische Informatik 1, WS 2001/02, Kapitel 3 18 In Haskell kann man das sehr gut mit Listen implementieren. In Python ebenfalls, man kann eine Liste von beiden Seiten als Stack verwenden: von rechts: # von links: a.insert(0,b) a.pop(0) # von rechts: a.append(b) a.pop() Es kann ein Fehler auftreten, wenn man vom leeren Stapel etwas herunternehmen will. Beispiel Zwei Beispiele, die sich eines Stacks bedienen, sind: Umgebung zur Auswertung von (Python-)Programmen. Umdrehen einer Liste in Haskell: reverse xs = foldl (\x y -> y:x) [] xs Eine Schlange, Queue ist eine Datenstruktur, die sich wie eine Warteschlange verhält: Vorne wird bedient (abgearbeitet), und hinten stellen sich Leute an (first-in first out, fifo). Dies kann man mit Haskell-Listen implementieren, allerdings ist dann entweder das Hinzufügen oder das Entfernen nicht effizient. In Python kann man das effizient implementieren mit einer der folgenden Kombination von Operationen, allerdings ist nicht offengelegt, wie es intern gemacht wird 1 : a.insert(0,b), a.pop() oder a.append(b), a.pop(0) Auswertungsregeln für Listenfunktionen in Python Dieser Paragraph soll verdeutlichen, dass man die operationelle Semantik der Listenverarbeitung in einer imperativen Programmiersprache wie Python formal darstellen kann, unabhängig von einem linearen Modell des Speichers mit Adressen. Natürlich wird dies dann letztlich doch auf einen Speicher abgebildet, aber man kann danach konzeptuell trennen zwischen den minimalen Erfordernissen der Auswertung und den zufälligen Eigenschaften der Implementierung. Z.B. ist die Größe der Adressen oder die Lokalität eher eine Eigenschaft der Implementierung auf einem Rechner mit linearem Speicher. Der Heap (Haufen) dient dazu, Daten mit Struktur, die vom Programm modelliert werden, darzustellen, und deren Veränderung korrekt zu beschreiben. Dazu werden Heap-Variablen verwendet, die man sich wie Zeiger (Pointer) in einer Implementierung vorstellen kann. Definition Ein Haufen (Heap, Halde) besteht aus folgenden Komponenten: Einer Menge von Wurzelvariablen. 1 Vermutlich mit Feldern und Indices

19 Praktische Informatik 1, WS 2001/02, Kapitel 3 19 Einer Menge von Heap-Variablen, wobei die Wurzelvariablen ebenfalls Heap-Variablen sind. Einer Menge von Bindungen, wobei eine Bindung sein kann: (x, v): eine Bindung eines Wertes (Zahl oder Character oder Nil) v an die Heap-Variable x. (x, (x 1, x 2 )): eine Bindung eines Paares aus Heap-Variablen (x 1, x 2 ) an die Heap-Variable x. Es darf pro Heap-Variable nur eine Bindung im Heap sein; Außerdem muß jede Heap-Variable, die in einer Paarbindung vorkommt, im Heap gebunden sein. In diesem Fall heißt der Heap gültig. Die beiden Formen der Bindung kann man folgendermaßen darstellen: x x v x 1 x 2 Definition Ein Stack S zu einem Heap H ist eine Folge von Paaren aus Programm-Variablen und Heap-Variablen. Die im Stack vorkommenden Heap-Variablen müssen Wurzelvariablen des Heaps sein. Diese Modellierung erlaubt keine Werte direkt auf dem Stack. In realen Implementierung werden allerdings oft Werte direkt auf dem Stack abgelegt und verarbeitet, da dies effizienter ist. Beispiel Die Darstellung der Liste [1] mit Stack und Heap ist folgendermaßen: Stack (zelle1,zelle2) Zeiger auf Liste Heap {(zelle2, (zelle3, zelle4)) (zelle3,1) (zelle4,nil)} zelle2 zelle3 zelle4 1 Nil

20 Praktische Informatik 1, WS 2001/02, Kapitel 3 20 Beispiel Darstellung einer zyklischen Liste, die man gedruckt nur so darstellen kann: [[[[[...]]]]]] AA AB Der Heap dazu sieht so aus: {(AA, (AA, AB)), (AB, Nil)} Definition Die Erreichbarkeitsrelation er des Heaps H kann man folgendermaßen definieren: Ein Heap-Variable y ist von der Heap-Variablen x aus erreichbar, Notation x er,h y, wenn x = y, oder wenn es eine endliche Folge von Variablen x 1,..., x n gibt, so dass x = x 1 und y = x n und für all i = 1,... n 1 entweder (x i, (x i+1, x i+1 )) H oder (x i, (x i+1, x i+1)) H wobei x i+1 irgendwelche Heap- Variablen sein dürfen. Eine Heap-variable x ist erreichbar (aktiv), wenn es eine Wurzelvariable x 0 gibt mit x 0 er x, andernfalls heißt sie unerreichbar (redundant). Bemerkung Die Erreichbarkeitsrelation er,h des Heaps H kann man auch als die reflexiv-transitive Hülle der folgenden Relation definieren: Wenn (x, (x 1, x 2 )) H, dann gilt x x 1 und x x 2. Die Bindung von unerreichbaren Variablen kann man aus dem Heap entfernen. In einer Implementierung nennt man das garbage collection. Wurzelvariablen sind i.a. die Heap-Variablen, auf die vom Stack aus referenziert wird; aber auch die Heap-Variablen, auf die das Programm referenziert, muss man dazu nehmen. Jede Variable des Heaps stellt einen Wert oder eine komplexere Datenstruktur dar. z.b.: Im Heap H = {(x, (x 1, x 2 )), (x 1, 1), (x 2, Nil)} repräsentiert die Heap-Variable x die Liste [1]; x 2 die leere Liste. x x 1 x 2 Nil 1 Der Aufwand ist etwas höher als bei der ersten Variante der operationellen Semantik von Python, die nur mit Zahlen umgehen konnte, da auch zyklische

21 Praktische Informatik 1, WS 2001/02, Kapitel 3 21 Strukturen beschreibbar sein sollen. Regeln der (erweiterten) operationellen Semantik Der Zustand besteht aus zwei Komponenten: Stack und Heap. Die übliche Umgebung (der Stack) wird so verallgemeinert, dass jeder Programmvariablen eine Heapvariable zugeordnet wird, die ihrerseits auf einen (evtl. komplexeren) Wert im Heap verweist. Man kann sich vorstellen, dass auf dem Stack Zeiger auf Werte im Heap stehen, und im Heap eine Zeigerstruktur einen Wert darstellt. Definition Der Zustand ist ein Paar (S, H), wobei S die Variablenumgebung (der Stack) ist, der eine Folge von Bindungen (x, u) ist, wobei x ein Programmvariable und u eine Heapvariable ist. H ist ein Heap (s.o.). Jetzt können wir die Auswertung von Listenausdrücken und Listenfunktionen in Python mittels dieser Heap-Modellierung beschreiben: Wir verwenden die Klammer [.], um syntaktische Konstrukte einzuklammern, damit diese von den Operationen deutlich getrennt werden, und verwenden die Notation (Z, e) (Z, x) um eine Auswertung des Ausdrucks e im Zustand Z zu bezeichnen, wobei x die Heapvariable ist, die den zurückgegebenen Wert repräsentiert und Z der neue Zustand ist. Definition Eine Auswertung des Listenausdrucks [a 1,..., a n ] in einem gültigen Heap H folgt der Regel (n 1): (Z; [[a 1 ]) (Z ; x 1 ) (Z ; [[[a 2,..., a n ]]) (Z ; x 2 ) Z = (S ; H ) (Z; [[[a 1,..., a n ]]) ((S, H {(x, (x 1, x 2 ))}); x) Am Ende der Listen verwenden wir die spezielle Konstante Nil: ((S, H); [[]]) ((S, H {(x, Nil)}); x) Die Auswertung des Listenausdrucks [a 1,..., a n ] kann man so umschreiben: Zuerst wird a 1 ausgewertet, danach a 2 usw, bis a n. Der Zustand am Ende ist der letzt Zustand nach dieser Auswertung. Im Heap repräsentiert die Heapvariable x die ausgewertete Liste. Das Aliasing passiert jetzt durch Zuweisung einer Listenvariablen: Sei die Folge der Befehle gegeben: a = [1,2] b = a Dann entsteht folgende Struktur im Stack und Heap: Nach a = [1,2]:

22 Praktische Informatik 1, WS 2001/02, Kapitel 3 22 Stack:..., (a, u 1 ),... Heap: {(u 1, (u 2, u 3 )), (u 2, 1), (u 3, (u 4, u 5 )), (u 4, 2), (u 5, Nil),...} Nach der weiteren Zuweisung b = a entsteht der Zustand Stack:..., (a, u 1 ),..., (b, u 1 ),... Heap: {(u 1, (u 2, u 3 )), (u 2, 1), (u 3, (u 4, u 5 )), (u 4, 2), (u 5, Nil),...} Wenn jetzt innerhalb der Liste zu a etwas verändert wird, dann ändert sich automatisch etwas in der Liste, auf die b zeigt. a u 1 b u 2 u 3 u 4 u 5 Nil 1 2 Jetzt können wir auch die Auswertung von Funktionen wie len, append, insert,... angeben. Die Angabe () als Wert soll den leeren Wert bedeuten: D.h., es wird kein Wert erzeugt. Für diese Regel nehmen wir der Einfachheit halber an, dass a, b Programm-variablen sind. ((S, H); [a.append(b)]) ((S, H ); ()) Wobei H = H 0 {(x, Nil)} der Heap vor der Auswertung ist, x sei letzte Heap-Variable der Liste zu a die auf Nil zeigt, (b, x 1 ) ist im Stack S, und H = H 0 {(x, (x 1, x 2 )), (x 2, Nil) mit der neu erzeugten Heap-Variablen x 2. Die Auswertung von a.append(b) kann man sich so veranschaulichen: a u 1 x Nil Vorher: b a u 1 x 1 w x x 1 x 2 Nil b x 1 Nachher: Dadurch wird das Element b als letztes Element an die Liste angehängt. Es kann dadurch auch eine Liste entstehen, die unendlich tiefe Verschachtelung w

23 Praktische Informatik 1, WS 2001/02, Kapitel 3 23 hat, indem man a.append(a) ausführt. Der zugehörige Heap, für die Liste a = [1, 2] sieht so aus: {(u 1, (u 2, u 3 )), (u 2, 1), (u 3, (u 4, u 5 )), (u 4, 2), (u 5, (u 1, u 7 )), (u 7, Nil),...} Dieser Heap ist zyklisch, aber zulässig. a u 1 u 2 u 3 u 4 u 5 u 1 u 7 Nil 1 2 Als weiteres Beispiel die operationelle Semantik der Funktion insert. Für diese Regel nehmen wir ebenfalls an, dass a, b Programm-variablen sind. ((S, H); [a.insert(i, b)]) ((S, H ); ()) wobei H = H 0 {(x i, (y 1, y 2 ))}, (b, z 1 ) S, und H = H 0 {(x i, (z 1, z 2 )), (z 2, (y 1, y 2 ))}, wobei z 2 neue Heap-Variable ist. Im Falle, dass die Liste zu Ende ist, ergibt sich ein kleiner Unterschied: H = H 0 {(x i, Nil)}, (b, z 1 ) S, und H = H 0 {(x i, (z 1, z 2 )), (z 2, Nil)}. Die operationelle Semantik von map kann damit ebenfalls beschrieben werden, allerdings ist diese Funktion aus kleineren Operationen zusammengesetzt, so dass es einfacher wäre, für die Implementierung der Funktion map die operationelle Semantik zu beschreiben. Den Inhalt des Elements mit Index i der Liste a kann man mittels a[i] = b verändern. Wir geben hierfür die operationelle Semantik an, wobei wir, annehmen, dass b eine Programmvariable ist, und bei der Auswertung von Anweisungen rechts nur den veränderten Zustand angeben. ((S, H); [a[i] = b]) (S, H ) wobei H = H 0 {(x i, (y 1, y 2 ))}, (b, z) S, und H = H 0 {(x i, (z, y 2 ))}.

24 Praktische Informatik 1, WS 2001/02, Kapitel 3 24 Indirektionen Diese könnte man auch im Heap erlauben. Es sind Bindungen von der Art (x 1, x 2 ), wobei x 1, x 2 beides Heap-Variablen sind. Deren Bedeutung ist, dass der Wert von x 1 erst über die Referenz x 2 und evtl. weitere Indirektionen gefunden werden kann, wobei die Indirektionen für die Programmiersprache nicht sichtbar sind. Das formale Modell würde dadurch etwas komplizierter, da man den Wert nicht direkt finden kann. Man muss auch Indirektionszyklen beachten, die eine Nichtterminierung des Wertesuchens bewirken könnten. In Implementierungen werden Indirektionen z.t. verwendet, aus verschiedenen Gründen: manche Operationen lassen sich leichter formulieren, und manchmal ist es effizienter, Indirektionen zu verwenden Implementierung der Halde in einem Speichermodell Zunächst das Speichermodell. Definition Einen RAM-Speicher S kann man definieren als endliche Folge von Bytes (1 Byte = 8 Bit), (alternativ: von Zahlen zwischen 0 und = 255), nummeriert mit Indices 0,..., L 1. L ist die Größe des Speichers. Der gespeicherte Wert unter der Adresse i ist das Byte S(i), d.h. das i te Element der Folge, wenn die Zählung mit 0 beginnt. Die benötigte Adresslänge in Bits ist log 2 (L). Die Zugriffsfunktion get(s, i, l) hat als Ergebnis die Subfolge von S der Länge l ab i Eine Implementierung von Adressen eines Speichers mit Adresslänge 32 Bit kann man durchführen, indem man eine Binärzahl, die durch eine Folge von 4 Bytes repräsentiert wird, als Adresse im gleichen Speicher interpretiert. Heapvariablen kann man dann implementieren als Folge von 4 Bytes (auch Wort genannt), die im Speicher direkt hintereinanderliegen, d.h. L(i), L(i + 1), L(i + 2), L(i + 3). Ein Paar (x 1, x 2 ) von zwei Heapvariablen kann man als hintereinanderliegende Folge von 8 Bytes implementieren, d.h. L(i),..., L(i + 7). Eine Zahl kann man implementieren wie eine Adresse, nur wird der Wert als die binäre Zahl selbst interpretiert. In dieser Implementierung geht Information verloren, welche Daten gemeint sind. Das sieht man daran, dass man damit Indirektionen, Paare, Nil, und Zahlen nicht unterscheiden kann. Je nach Implementierung benötigt man daher

25 Praktische Informatik 1, WS 2001/02, Kapitel 3 25 i.a. 2 noch eine Markierung (englisch: Tag), die diese verschiedenen Daten voneinander unterscheidet. D.h. man könnte vereinbaren: Adresse: 1 Byte Markierung (z.b. binär 1), und 4 Byte Adresse. Paar: 1 Byte Markierung (z.b. binär 2), und 8 Byte Adressen. Ganze Zahl: 1 Byte Markierung (z.b. binär 3), und 4 Byte Zahl. Nil: 1 Byte Markierung (z.b. binär 4). kein Wert: 1 Byte Markierung (z.b. binär 5). Jetzt können wir auch unterscheiden, welche Eigenschaften der Implementierung eher zufällig sind und nicht durch die operationelle Semantik erzwungen werden: Z.B. erzwingt die operationelle Semantik keinerlei lokale Zusammenhänge wie: die Adressen eines Adresspaares (x 1, x 2 ) liegen direkt nebeneinander. Adressen sind als aufsteigende Folge von Bytes repräsentiert. Der nächste Eintrag im Array (Feld) hat Adresse des aktuellen Feldes +4. Auch die Länge der Adressen ist nicht festgelegt. 3.4 Felder, Arrays Felder (arrays, Vektoren) sind eine weitverbreitete Datenstruktur in Programmiersprachen, die normalerweise im einfachsten Fall eine Folge a i, i = 0,..., n modellieren. Als Semantik eines Feldes A mit den Grenzen (a, b) und Elementen des Typs α kann man eine Funktion nehmen: f A : [a, b] α. In Haskell sind Felder als Zusatzmodul verfügbar. Die Elemente müssen gleichen Typ haben. In Python sind die Listen gleichzeitig eindimensionale Felder, die auch heterogen (d.h. mit Elementen unterschiedlichen Typs) sein dürfen. Mehrdimensionale Arrays und Matrizen kann man mit einem Array von Arrays modellieren. Beispiel Transposition einer 2-dimensionalen quadratischen Matrix in Python def transpose(a,n): for i in range(n): for j in range(i+1,n): a[i][j], a[j][i] = a[j][i], a[i][j] return a def testtranspose(n): b = range(n) for i in range(n): soll. 2 Diese Markierung wird nicht benötigt, wenn ein Programm vorher weiß, was dort stehen

26 Praktische Informatik 1, WS 2001/02, Kapitel 3 26 b[i] = range(n) for i in range(n): print b[i]; c = transpose(b,n) print " "; print "Nach Transposition:" for i in range(n): print c[i] Die interne Implementierung eines Feldes macht es möglich, eine (praktisch) konstante Zugriffszeit zu realisieren, wenn der Index bekannt ist. Allerdings ist die Handhabung eines Feldes im Programm etwas aufwendiger, da man bei Zugriffen stets den Index berechnen muß und da man im allgemeinen ein Feld nicht so ohne weiteres verkleinern oder erweitern kann Felder in Haskell In Haskell gibt es als eingebaute Datenstruktur (als Modul) ebenfalls Arrays. Dies sind Felder von Elementen des gleichen Typs. Die Implementierung eines Feldes als Liste ist möglich, hätte aber als Nachteil, dass der Zugriff auf ein bestimmtes Element, wenn der Index bekannt ist, die Größe O(länge(liste)) hat. Als Spezialität kann man beim Erzeugen verschiedene Typen des Index wählen, so dass nicht nur Zahlen, sondern auch Tupel (auch geschachtelte Tupel) von ganzen Zahlen möglich sind, womit man (mehrdimensionale) Matrizen modellieren kann. Einige Zugriffsfunktionen sind: array x y : erzeugt ein Feld (array) mit den Grenzen x = (start, ende), initialisiert anhand der Liste y, die eine Liste von Index-Wert Paaren (eine Assoziationsliste) sein soll listarray x y: erzeugt ein array mit den Grenzen x = (start, ende), initialisiert sequentiell anhand der Liste y. (!): Infix funktion: ar!i ergibt das i-te Element des Feldes ar. (//): Infix-funktion a // xs ergibt ein neues Feld, bei dem die Elemente entsprechend der Assoziationsliste xs abgeändert sind. bounds: Erlaubt es, die Indexgrenzen des Feldes zu ermitteln. Beispiel umdrehen_array ar = let (n,m) = bounds ar mplusn = m+n in ar // [(i,ar!(mplusn -i)) i <- [n..m] ]

27 Praktische Informatik 1, WS 2001/02, Kapitel Kontrollstrukturen, Iteration in Haskell Wir können auch Kontrollstrukturen wie while, until und for definieren, wobei das Typsystem bestimmte Einschränkungen für den Rumpf macht. Generell ist Rekursion allgemeiner als Iteration, allerdings sind vom theoretischen Standpunkt aus beide gleichwertig: wenn man den Speicher erweitern kann (d.h. beliebig große Datenobjekte verwenden und aufbauen kann), dann kann Iteration die Rekursion simulieren. Die Definition dieser Kontrollstrukturen benutzt einen Datentyp als Umgebung, so dass jeder Iterationsschritt die alte Umgebung als Eingabe hat, und die neue Umgebung als Ausgabe, die dann wieder als Eingabe für den nächsten Iterationsschritt dient. Diesen Effekt kann man auch beschreiben als: die Umgebung wird in jedem Iterationsschritt geändert. Der Rumpf ist eine Funktion, die genau diesen Schritt beschreibt. Beispiel while:: (a -> Bool) -> (a -> a) -> a -> a -- while test f init -- Typ der Umgebung: a -- test: Test, ob While-bedingung erfuellt -- f:: a -> a Rumpf, der die Umgebung abaendert -- init: Anfangsumgebung while test f init = if test init then while test f (f init) else init untill :: (a -> Bool) -> (a -> a) -> a -> a untill test f init = if test init then init else untill test f (f init) for :: (Ord a, Num a) => a -> a -> a -> (b -> a -> b) -> b -> b -- For laeuft von start bis end in Schritten von schritt -- Umgebung:: b, Zaehler:: a -- f: Umgebung, aktueller Zaehler -> neue Umgebung -- f : init start -> init for start end schritt f init = if start > end then init else let startneu = start + schritt in for startneu end schritt f (f init start) Die Funktion f, die als Argument mit übergeben wird, erzeugt ein neues Datenobjekt.

28 Praktische Informatik 1, WS 2001/02, Kapitel 3 28 Verwendung im Beispiel: dreinwhile n = while (> 1) dreinschritt n dreinschritt x = if x == 1 then 1 else if geradeq x then x div 2 else 3*x+1 -- berechnet n: summebis n = for 1 n 1 (+) 0 -- berechnet fibonacci (n) fib_for n = fst (for 1 n 1 fib_schritt (1,1)) fib_schritt (a,b) _ = (a+b,a) Ein Ausdruck mit foldl läßt sich als while-ausdruck schreiben: foldl f e xs ist äquivalent zu: fst (while (\(res,list) -> list /= []) (\(res,list) -> (f res (head list), tail list)) (e,xs)) Ein Ausdruck mit foldr läßt sich nicht so ohne weiteres als while-ausdruck schreiben. Für endliche Listen ist das (unter Effizienzverlust) möglich: foldr f e xs ist äquivalent (für endliche Listen) zu: fst (while (\(res,list) -> list /= []) (\(res,list) -> (f (head list) res, tail list)) (e,reverse xs)) 3.6 Church-Rosser Sätze in vollem Haskell Nimmt man eine größere Menge der Konstrukte und Möglichkeiten von Haskell, dann hat die Auswertung etwas kompliziertere Eigenschaften als im einfachen Haskell. Folgendes Bild veranschaulicht die Auswertung:

29 Praktische Informatik 1, WS 2001/02, Kapitel 3 29 Haskell- Programm Entzuckerung Programm in Kernsprache Syntaxanalyse Syntaxbaum des Programms transformierter Syntaxbaum Auswertung (operationelle Semantik) Der erste Schritt der Auswertung ist die Transformation einiger Konstrukte in einfachere (Entzuckerung), so dass man sich bei der Definition der operationellen Semantik-Regeln auf eine Untermenge der syntaktischen Möglichkeiten von Haskell beschränken kann. Hierbei werden u.a. die Pattern in Haskell und die Fallunterscheidung bei der Funktionsdefinition in case- Ausdrücke übersetzt. Beispiel Wir zeigen, wie z.b. die Funktionsdefinition in einfacheres Haskell transformiert wird. map f [] = [] map f (x:xs) = f x : map f xs Das kann man transformieren zu: map f l = (case l of [] -> []; (x:xs) -> f x : map f xs) Man beachte, dass diese Transformation vorher eine Analyse der Art der Fallunterscheidung machen muss. Hier ist zu erkennen, dass Fallunterscheidung über das zweite Argument gemacht werden muss. Danach setzen wir die operationale Semantik auf einem Haskell-Fragment auf, das Funktionsdefinitionen, Konstruktoren, Lambda-Ausdrücke und let kennt. Definition Ein verallgemeinerter Wert (WHNF) ist entweder 1. eine Zahl, oder

30 Praktische Informatik 1, WS 2001/02, Kapitel eine Abstraktion, oder 3. eine Applikation (f t 1... t n ), wobei f ein Konstruktor ist mit der Stelligkeit n, oder 4. eine Applikation (f t 1... t n ), f ist eine global definierte Funktion, deren Stelligkeit > n ist. 5. ein let-ausdruck let... int, wobei t von einer der Formen 1 4 ist. Atomare Werte sind Zahlen oder Konstruktorkonstanten (z.b. True. D.h. neben den Zahlen und Funktionen sinf jetzt auch konstruierte Daten als Werte zugelassen. Es ist zu beachten, dass auch Daten noch beliebige (unausgewertete) Unterausdrücke haben können. Die Auswertung in normaler Reihenfolge ist jetzt eine Erweiterung der normalen Reihenfolge der Auswertung, wobei als zusätzlicher Fall nur der case- Ausdruck mit der case-reduktion auftritt. Hier hat man im Falle, dass man ein case t... hat, und t kein Wert ist, rekursiv die normale Reihenfolge auf t anzuwenden. Man kann die Fälle ignorieren, in denen man nicht weiterkommt, da t zu einem nicht brauchbaren Ausdruck reduziert (z.b. eine Abstraktion), da Haskells Typcheck dafür sorgt, dass dies nicht vorkommt. Zu beachten ist, dass die normale Reihenfolge die Argumente eines Konstruktors nicht reduziert. Dies bewirkt, dass unendliche Listen in Haskell verarbeitet werden können. Die Sätze von Church-Rosser gelten immer noch, wenn es keine rekursiven lets gibt, allerdings etwas verallgemeinert: Satz (Church-Rosser) Annahme: Kein letwird rekursiv verwendet. Wenn s zu s und s reduziert, und sowohl s als auch s ist ein verallgemeinerter Wert, dann gibt es Ausdrücke s, s, die sich nur um let-reduktionen und um eine Umbenennung von gebundenen Variablen unterscheiden, und s s, s s. Beachte: die Werte s, s können noch Unterausdrücke enthalten, die man weiter auswerten kann. Satz (Church-Rosser) Annahme: Kein letwird rekursiv verwendet. Wenn s zu s reduziert und s ist ein verallgemeinerter Wert, dann reduziert die normale Reihenfolge s zu s, so dass s ein verallgemeinerter Wert ist, und s reduziert zu einem s, so dass sich s und s nur um let-reduktionen und eine Umbenennung von gebundenen Variablen unterscheiden. Auch in der Erweiterung gilt: Satz Wenn s zu s reduziert, und s ist ein atomarer Wert, dann terminiert auch die normale Reihenfolge der Auswertung mit dem Wert s.

31 Praktische Informatik 1, WS 2001/02, Kapitel 3 31 Damit kann man salopp sagen: Die Normalordnung berechnet den Wert, wenn es einen gibt, und dieser Wert ist (bis auf let-reduktionen und Umbenennung von gebundenen Variablen) eindeutig bestimmt. Beachte: wenn rekursive Verwendung des let erlaubt ist, dann gelten die Church-Rosser Sätze in dieser Form nicht mehr. Man benötigt dann das Konzept der Verhaltensgleichheit.

Grundlagen der Programmierung 2 (2.B)

Grundlagen der Programmierung 2 (2.B) Grundlagen der Programmierung 2 (2.B) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 11. Juni 2008 Reduktionsregel zum case case-reduktion (case (c t 1... t n ) of... (c

Mehr

Grundlagen der Programmierung 2 (2.A)

Grundlagen der Programmierung 2 (2.A) Grundlagen der Programmierung 2 (2.A) Prof. Dr. Manfred Schmidt-Schauß Künstliche Intelligenz und Softwaretechnologie 5. Mai 2011 Listen und Listenfunktionen Listen modellieren Folgen von gleichartigen,

Mehr

Listen und Listenfunktionen. Grundlagen der Programmierung 2 A (Listen) Listen und Listenfunktionen. Listen? Haskell: Listen

Listen und Listenfunktionen. Grundlagen der Programmierung 2 A (Listen) Listen und Listenfunktionen. Listen? Haskell: Listen Listen und Listenfunktionen Grundlagen der Programmierung 2 A (Listen) Haskell: Listen Prof. Dr. Manfred Schmidt-Schauß Listen modellieren Folgen von gleichartigen, gleichgetypten Objekten. Ausdruck im

Mehr

Grundlagen der Programmierung 2 (1.C)

Grundlagen der Programmierung 2 (1.C) Grundlagen der Programmierung 2 (1.C) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 8. Mai 2007 Listen und Listenfunktionen Listen modellieren Folgen von gleichartigen,

Mehr

Auswertungsregeln für Listenfunktionen in Python: operationale Semantik

Auswertungsregeln für Listenfunktionen in Python: operationale Semantik Auswertungsregeln für Listenfunktionen in Python: operationale Semantik Operationale Semantik einer imperativen Programmiersprache mit Listen; hier Python Modellierung der Verzeigerung zwischen Objekten

Mehr

Listenverarbeitung in Python

Listenverarbeitung in Python Listenverarbeitung in Python Datentypen für Sequenzen von Objekten: Tupel, Listen und Strings Tupel und Listen sind analog zu Haskells Tupel und Listen: (1, 2, 3) 3-Tupel aus den Zahlen 1,2,3, [1, 2, 3]

Mehr

Grundlagen der Programmierung 2 A (Listen)

Grundlagen der Programmierung 2 A (Listen) Grundlagen der Programmierung 2 A (Listen) Haskell: Listen Prof. Dr. Manfred Schmidt-Schauß Sommersemester 2017 Listen und Listenfunktionen Listen modellieren Folgen von gleichartigen, gleichgetypten Objekten.

Mehr

Einführung in die funktionale Programmierung

Einführung in die funktionale Programmierung Einführung in die funktionale Programmierung Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 26. Oktober 2006 Haskell - Einführung Syntax Typen Auswertung Programmierung

Mehr

Listen und Funktionen auf Listen

Listen und Funktionen auf Listen Listen und Funktionen auf Listen Achtung Zunächst: Einfache Einführung von Listen Listenfunktionen zugehörige Auswertung Genauere Erklärungen zu Listenfunktionen folgen noch (in ca, 1-2 Wochen) P raktische

Mehr

Grundlagen der Programmierung 2. Operationale Semantik

Grundlagen der Programmierung 2. Operationale Semantik Grundlagen der Programmierung 2 Operationale Semantik Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 29. April 2009 Semantik von Programmiersprachen Semantik = Bedeutung

Mehr

Grundlagen der Programmierung 2 B

Grundlagen der Programmierung 2 B Grundlagen der Programmierung 2 B Haskell: Listen-Komprehensionen Prof. Dr. Manfred Schmidt-Schauß Sommersemester 2017 Listenausdrücke, Listen-Komprehensionen Analog zu Mengenausdrücken, aber Reihenfolge

Mehr

Operationale Semantik: Haskell

Operationale Semantik: Haskell Kapitel 4 Operationale Semantik: Haskell 4.1 Semantik von Programmiersprachen Programme sind zunächst mal nur Text. Programme sollen aber etwas im Rechner bewirken bzw. eine Funktion oder Funktionalität

Mehr

Grundlagen der Programmierung 2 (2.B)

Grundlagen der Programmierung 2 (2.B) Grundlagen der Programmierung 2 (2.B) Prof. Dr. Manfred Schmidt-Schauß Künstliche Intelligenz und Softwaretechnologie 5. Mai 2010 Listenausdrücke, Listen-Komprehensionen; (list comprehensions) Analog zu

Mehr

Stackmaschine; Speicheradressierung

Stackmaschine; Speicheradressierung Stackmaschine; Speicheradressierung Erweiterung um globalen Speicher (Heap, Halde) pro Speicherplatz eine Zahl. Notation ist als Array SP [0..]. Zugriff mittels Adresse (Index): eine Zahl i.a.: Zahlen

Mehr

Grundlagen der Programmierung 2 (1.D)

Grundlagen der Programmierung 2 (1.D) Grundlagen der Programmierung 2 (1.D) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 9. Mai 2006 Fallunterscheidung mit case Syntax: case Ausdruck of { Muster -> Ausdruck

Mehr

Grundlagen der Programmierung 2 (1.C)

Grundlagen der Programmierung 2 (1.C) Grundlagen der Programmierung 2 (1.C) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 3. Mai 2006 Funktionen auf Listen: map map :: (a -> b) -> [a] -> [b] map f [] = []

Mehr

Funktionale Programmierung ALP I. Funktionen höherer Ordnung SS Prof. Dr. Margarita Esponda. Prof. Dr. Margarita Esponda

Funktionale Programmierung ALP I. Funktionen höherer Ordnung SS Prof. Dr. Margarita Esponda. Prof. Dr. Margarita Esponda ALP I SS 2011 Funktionstypen Funktionen haben einen Datentyp, der folgende allgemeine Form hat: functionname :: T 1 -> T 2, wobei T 1, T 2 wiederum beliebige Datentypen sind Beispiel: T 1 T 2 Der Datentyp

Mehr

Grundlegende Datentypen

Grundlegende Datentypen Funktionale Programmierung Grundlegende Datentypen Fakultät für Informatik und Mathematik Hochschule München Letzte Änderung: 14.11.2017 15:37 Inhaltsverzeichnis Typen........................................

Mehr

Funktionale Programmierung Grundlegende Datentypen

Funktionale Programmierung Grundlegende Datentypen Grundlegende Datentypen Prof. Dr. Oliver Braun Fakultät für Informatik und Mathematik Hochschule München Letzte Änderung: 06.11.2017 16:45 Inhaltsverzeichnis Typen........................................

Mehr

Grundlegende Datentypen

Grundlegende Datentypen Grundlegende Datentypen (Funktionale Programmierung) Prof. Dr. Oliver Braun Letzte Änderung: 18.03.2018 21:08 Grundlegende Datentypen 1/16 Typen in Haskell ist alles streng typisiert Haskell verfügt über

Mehr

Grundlegende Datentypen

Grundlegende Datentypen Grundlegende Datentypen Funktionale Programmierung Prof. Dr. Oliver Braun Letzte Änderung: 22.10.2018 10:53 Grundlegende Datentypen 1/21 Typen in Haskell ist alles streng typisiert Haskell verfügt über

Mehr

Paradigmen der Programmierung

Paradigmen der Programmierung SS 11 Prüfungsklausur 25.07.2011 Aufgabe 5 (6+9 = 15 Punkte) a) Bestimmen Sie jeweils den Typ der folgenden Haskell-Ausdrücke: ( 1, 2 :"3", 4 < 5) :: (Char, String, Bool) [(last, tail), (head, take 5)]

Mehr

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 12/13. Kapitel 3. Grunddatentypen, Ausdrücke und Variable

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 12/13. Kapitel 3. Grunddatentypen, Ausdrücke und Variable 1 Kapitel 3 Grunddatentypen, Ausdrücke und Variable 2 Eine Datenstruktur besteht aus Grunddatentypen in Java einer Menge von Daten (Werten) charakteristischen Operationen Datenstrukturen werden mit einem

Mehr

HASKELL KAPITEL 2.1. Notationen: Currying und das Lambda-Kalkül

HASKELL KAPITEL 2.1. Notationen: Currying und das Lambda-Kalkül HASKELL KAPITEL 2.1 Notationen: Currying und das Lambda-Kalkül Bisheriges (Ende VL-Teil 1) weite :: (Float,Float) ->Float weite (v0, phi) = (square(v0)/9.81) * sin(2 * phi) (10, 30 ) smaller ::(Integer,

Mehr

Ströme als unendliche Listen in Haskell

Ströme als unendliche Listen in Haskell Ströme als unendliche Listen in Haskell Strom := Folge oder Liste von Daten, unbegrenzt viele Daten-Elemente. Ströme sind in Haskell als als (potentiell) unendliche Listen darstellbar und programmierbar

Mehr

Einführung in die Informatik I

Einführung in die Informatik I Einführung in die Informatik I Einige wichtige Datenstrukturen: Vektor, Matrix, Liste, Stapelspeicher, Warteschlange Prof. Dr. Nikolaus Wulff Datenstruktur / Datentyp Programme benötigen nicht nur effiziente

Mehr

Kapitel 4. Programmierkurs. Datentypen. Arten von Datentypen. Datentypen und Operatoren Ganzzahlige Numerische Datentypen Logischer Datentyp

Kapitel 4. Programmierkurs. Datentypen. Arten von Datentypen. Datentypen und Operatoren Ganzzahlige Numerische Datentypen Logischer Datentyp Kapitel 4 Programmierkurs Birgit Engels, Anna Schulze Datentypen und Operatoren Ganzzahlige Numerische Datentypen Logischer Datentyp ZAIK Universität zu Köln WS 07/08 1 / 6 Datentypen Arten von Datentypen

Mehr

Kapitel 4. Programmierkurs. Datentypen. Arten von Datentypen. Wiederholung Kapitel 4. Birgit Engels, Anna Schulze WS 07/08

Kapitel 4. Programmierkurs. Datentypen. Arten von Datentypen. Wiederholung Kapitel 4. Birgit Engels, Anna Schulze WS 07/08 Kapitel 4 Programmierkurs Birgit Engels, Anna Schulze Wiederholung Kapitel 4 ZAIK Universität zu Köln WS 07/08 1 / 23 2 Datentypen Arten von Datentypen Bei der Deklaration einer Variablen(=Behälter für

Mehr

WS 2011/2012. RobertGiegerich. November 12, 2013

WS 2011/2012. RobertGiegerich. November 12, 2013 WS 2011/2012 Robert AG Praktische Informatik November 12, 2013 Haskell-Syntax: Ergänzungen Es gibt noch etwas bequeme Notation für Fallunterscheidungen, die wir bisher nicht benutzt haben. Bisher kennen

Mehr

WS 2011/2012. RobertGiegerich. November 12, 2013

WS 2011/2012. RobertGiegerich. November 12, 2013 WS 2011/2012 Robert AG Praktische Informatik November 12, 2013 Haskell-Syntax: Ergänzungen Es gibt noch etwas bequeme Notation für Fallunterscheidungen, die wir bisher nicht benutzt haben. Bisher kennen

Mehr

Algorithmen als systematische Vorgehensweisen zur Lösung eines formal definierten Problems

Algorithmen als systematische Vorgehensweisen zur Lösung eines formal definierten Problems 4. Algorithmen Motivation Algorithmen als systematische Vorgehensweisen zur Lösung eines formal definierten Problems Der Begriff Algorithmus geht auf den Gelehrten Muhammad al-chwarizmi zurück, der um

Mehr

Algorithmen als systematische Vorgehensweisen zur Lösung eines formal definierten Problems

Algorithmen als systematische Vorgehensweisen zur Lösung eines formal definierten Problems 4. Algorithmen Motivation Algorithmen als systematische Vorgehensweisen zur Lösung eines formal definierten Problems Der Begriff Algorithmus geht auf den Gelehrten Muhammad al-chwarizmi zurück, der um

Mehr

WS 2012/2013. Robert Giegerich. 21. November 2012

WS 2012/2013. Robert Giegerich. 21. November 2012 WS 2012/2013 Robert AG Praktische Informatik 21. November 2012 Funktionen als Bürger erster Klasse Funktionen definieren kann man in jeder Programmiersprache. Eine funktionalen Programmiersprache erlaubt

Mehr

Wertebereich und Genauigkeit der Zahlendarstellung

Wertebereich und Genauigkeit der Zahlendarstellung Wertebereich und Genauigkeit der Zahlendarstellung Sowohl F als auch C kennen bei ganzen und Floating Point-Zahlen Datentypen verschiedener Genauigkeit. Bei ganzen Zahlen, die stets exakt dargestellt werden

Mehr

Tag 7. Pattern Matching und eigene Datentypen

Tag 7. Pattern Matching und eigene Datentypen Tag 7 Pattern Matching und eigene Datentypen Heute werden wir eine Technik kennenlernen, die dafür sorgt, daß wir sehr viel übersichtlichere und kürzere Programme schreiben können. Als Überleitung auf

Mehr

Beispiele elementarer Datentypen Ganze Zahlen (integer) Unterbereiche Gleitkommazahlen Festkommazahlen

Beispiele elementarer Datentypen Ganze Zahlen (integer) Unterbereiche Gleitkommazahlen Festkommazahlen Beispiele elementarer Datentypen Ganze Zahlen (integer) - Werte sind ganze Zahlen in vorgegebenen Bereich (z. B. -2 31 bis 2 31-1) - Übliche Operationen: Arithmetik (z. B. +,-,*, Division mit Rest, Rest

Mehr

Grundlagen der Programmierung 3 A

Grundlagen der Programmierung 3 A Grundlagen der Programmierung 3 A Typen, Typberechnung und Typcheck Prof. Dr. Manfred Schmidt-Schauß Sommersemester 2016 Haskell, Typen, und Typberechnung Ziele: Haskells Typisierung Typisierungs-Regeln

Mehr

Beispiele: Funktionsabstraktion (3) Funktionsdeklaration. Funktionsdeklaration (2) Funktionsdeklaration (3) 3. Abstraktion über Funktionsbezeichner:

Beispiele: Funktionsabstraktion (3) Funktionsdeklaration. Funktionsdeklaration (2) Funktionsdeklaration (3) 3. Abstraktion über Funktionsbezeichner: Beispiele: Funktionsabstraktion (3) Funktionsdeklaration 3. Abstraktion über Funktionsbezeichner: Ausdruck: f (f x) Abstraktion: \ f x -> f (f x) Mit Bezeichnervereinbarung: twice = \ f x -> f (f x) erg

Mehr

Kapitel 3. Grunddatentypen, Ausdrücke und Variable

Kapitel 3. Grunddatentypen, Ausdrücke und Variable Kapitel 3 Grunddatentypen, Ausdrücke und Variable Grunddatentypen, Ausdrücke und Variable 1 Eine Datenstruktur besteht aus Grunddatentypen in Java einer Menge von Daten (Werten) charakteristischen Operationen

Mehr

ALP I. Funktionale Programmierung

ALP I. Funktionale Programmierung ALP I Funktionale Programmierung Zusammengesetzte Datentypen in Haskell WS 2012/2013 Zusammengesetzte Datentypen Tupel List String Zusammengesetzte Datentypen Tupel-Datentyp Ein Tupel ist eine Ansammlung

Mehr

Programmieren in Haskell

Programmieren in Haskell Programmieren in Haskell Syntax und Semantik von Haskell Programmieren in Haskell 1 Was wir heute (und nächstes mal) machen Datentypdefinitionen Wertdefinitionen, Variablenbindungen Musterbindungen Funktionsbindungen

Mehr

Aufgabe 1 Basiswissen zur Vorlesung (8 Punkte)

Aufgabe 1 Basiswissen zur Vorlesung (8 Punkte) Matrikelnummer: 1 Aufgabe 1 Basiswissen zur Vorlesung (8 Punkte) Kreuzen Sie an, ob die folgenden Aussagen richtig oder falsch sind. Bewertung: keine Antwort: 0 Punkte richtige Antwort: +0.5 Punkte falsche

Mehr

Einführung in die funktionale Programmierung

Einführung in die funktionale Programmierung Einführung in die funktionale Programmierung Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 17. Oktober 2006 Einführung in Haskell: Syntax, Reduktionen, Kernsprachen Haskell,

Mehr

Die Definition eines Typen kann rekursiv sein, d.h. Typ-Konstruktoren dürfen Elemente des zu definierenden Typ erhalten.

Die Definition eines Typen kann rekursiv sein, d.h. Typ-Konstruktoren dürfen Elemente des zu definierenden Typ erhalten. 4.5.5 Rekursive Typen Die Definition eines Typen kann rekursiv sein, d.h. Typ-Konstruktoren dürfen Elemente des zu definierenden Typ erhalten. datatype IntList = Nil Cons o f ( i n t IntList ) ; Damit

Mehr

Gliederung. Algorithmen und Datenstrukturen I. Listen in Haskell: Listen in Haskell: Listen in Haskell. Datentyp Liste Strings Listenkomprehension

Gliederung. Algorithmen und Datenstrukturen I. Listen in Haskell: Listen in Haskell: Listen in Haskell. Datentyp Liste Strings Listenkomprehension Gliederung Algorithmen und Datenstrukturen I D. Rösner Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg 1 Winter 2009/10, 16. Oktober 2009, c

Mehr

Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 15 (Linearer Speicher, Listen, Bäume)

Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 15 (Linearer Speicher, Listen, Bäume) Fachrichtung 6.2 Informatik Universität des Saarlandes Tutorenteam der Vorlesung Programmierung 1 Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 15 (Linearer Speicher, Listen,

Mehr

1. Referenzdatentypen: Felder und Strings. Referenz- vs. einfache Datentypen. Rückblick: Einfache Datentypen (1) 4711 r

1. Referenzdatentypen: Felder und Strings. Referenz- vs. einfache Datentypen. Rückblick: Einfache Datentypen (1) 4711 r 1. Felder und Strings Eigenschaften von Referenzdatentypen 1. Referenzdatentypen: Felder und Strings Referenzdatentypen sind Konstrukte, mit deren Hilfe wir aus einfachen Datentypen neue eigene Typen erzeugen

Mehr

1. Referenzdatentypen: Felder und Strings

1. Referenzdatentypen: Felder und Strings 1. Felder und Strings Eigenschaften von Referenzdatentypen 1. Referenzdatentypen: Felder und Strings Referenzdatentypen sind Konstrukte, mit deren Hilfe wir aus einfachen Datentypen neue eigene Typen erzeugen

Mehr

Klausur Kompaktkurs Einführung in die Programmierung Dr. T. Weinzierl & M. Sedlacek 25. März 2011

Klausur Kompaktkurs Einführung in die Programmierung Dr. T. Weinzierl & M. Sedlacek 25. März 2011 Kompaktkurs Einführung in die Programmierung Klausur Seite 1/10 Name, Vorname, Unterschrift: Matrikelnummer: Wichtig: Klausur Kompaktkurs Einführung in die Programmierung Dr. T. Weinzierl & M. Sedlacek

Mehr

4.2 Daten und Datenstrukturen

4.2 Daten und Datenstrukturen 4.2 Daten und Datenstrukturen Daten Fundamentale Objekte, die in der Rechenanlage erfasst gespeichert ausgegeben (angezeigt, gedruckt) bearbeitet gelöscht werden können. Beispiele: Zahlen, Zeichenfolgen

Mehr

Abschnitt 2: Daten und Algorithmen

Abschnitt 2: Daten und Algorithmen Abschnitt 2: Daten und Algorithmen 2. Daten und Algorithmen 2.1 Zeichenreihen 2.2 Datendarstellung durch Zeichenreihen 2.3 Syntaxdefinitionen 2.4 Algorithmen 2 Daten und Algorithmen Einf. Progr. (WS 08/09)

Mehr

Informatik I: Einführung in die Programmierung 3. Werte, Typen, Variablen und Ausdrücke

Informatik I: Einführung in die Programmierung 3. Werte, Typen, Variablen und Ausdrücke Informatik I: Einführung in die Programmierung 3. Werte,, n und Albert-Ludwigs-Universität Freiburg Peter Thiemann 30. Oktober 2018 1 30. Oktober 2018 P. Thiemann Info I 3 / 39 Bits Der Computer repräsentiert

Mehr

Grundlagen der Programmierung 3 A

Grundlagen der Programmierung 3 A Grundlagen der Programmierung 3 A Typen, Typberechnung und Typcheck Prof. Dr. Manfred Schmidt-Schauß Sommersemester 2017 Haskell, Typen, und Typberechnung Ziele: Haskells Typisierung Typisierungs-Regeln

Mehr

Programmieren in Haskell

Programmieren in Haskell Programmieren in Haskell Wir steigen ein... Programmieren in Haskell 1 Was wir heute machen Umfrage: Wer hat den Hugs ausprobiert? Ausdrücke und Werte Datentypen Funktionen Aufgabe für diese Woche Programmieren

Mehr

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Überladung und Konversion in Haskell. Typisierung in Haskell

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Überladung und Konversion in Haskell. Typisierung in Haskell Haskell, Typen, und Typberechnung Grundlagen der Programmierung 3 A Typen, Typberechnung und Typcheck Prof. Dr Manfred Schmidt-Schauß Ziele: Haskells Typisierung Typisierungs-Regeln Typ-Berechnung Sommersemester

Mehr

JAVA-Datentypen und deren Wertebereich

JAVA-Datentypen und deren Wertebereich Folge 8 Variablen & Operatoren JAVA 8.1 Variablen JAVA nutzt zum Ablegen (Zwischenspeichern) von Daten Variablen. (Dies funktioniert wie beim Taschenrechner. Dort können Sie mit der Taste eine Zahl zwischenspeichern).

Mehr

Kapitel 4. Kontrollstrukturen

Kapitel 4. Kontrollstrukturen Kapitel 4 Kontrollstrukturen Kontrollstrukturen 1 Ziele Kontrollstrukturen in imperativen Programmen kennenlernen und verstehen. Realisierung der Kontrollstrukturen in Java. Kontrollstrukturen 2 Anweisungen

Mehr

Praxis der Programmierung

Praxis der Programmierung Arrays, Pointer, Parameterbergabe Institut für Informatik und Computational Science Henning Bordihn Einige Folien gehen auf A. Terzibaschian zurück. 1 Arrays (Felder/Vectoren) 2 Arrays: Motivation Gegeben:

Mehr

Kapitel 12: Induktive

Kapitel 12: Induktive Kapitel 12: Induktive Datenstrukturen Felix Freiling Lehrstuhl für Praktische Informatik 1 Universität Mannheim Vorlesung Praktische Informatik I im Herbstsemester 2009 Folien nach einer Vorlage von H.-Peter

Mehr

Beschreibung von Werten: Beschreibung von Werten: (2) Begriffsklärung: (Ausdruck, expression) (2) Begriffsklärung: (Ausdruck, expression)

Beschreibung von Werten: Beschreibung von Werten: (2) Begriffsklärung: (Ausdruck, expression) (2) Begriffsklärung: (Ausdruck, expression) Beschreibung von Werten: Beschreibung von Werten: (2) mittels Konstanten oder Bezeichnern für Werte: 23 " Ich bin eine Zeichenreihe" True x durch direkte Anwendung von Funktionen: abs (-28382) "Urin" ++

Mehr

Grundlagen der Programmierung 2 (1.A)

Grundlagen der Programmierung 2 (1.A) Grundlagen der Programmierung 2 (1.A) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 18. April 2007 Grundlagen der Programmierung 2: Geplanter Inhalt der ersten Hälfte

Mehr

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Einige andere Programmiersprachen. Typisierung in Haskell

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Einige andere Programmiersprachen. Typisierung in Haskell Haskell, Typen, und Typberechnung Grundlagen der Programmierung 3 A Typen, Typberechnung und Typcheck Prof. Dr. Manfred Schmidt-Schauß Ziele: Haskells Typisierung Typisierungs-Regeln Typ-Berechnung Milners

Mehr

Grundlagen der Informatik. Prof. Dr. Stefan Enderle NTA Isny

Grundlagen der Informatik. Prof. Dr. Stefan Enderle NTA Isny Grundlagen der Informatik Prof. Dr. Stefan Enderle NTA Isny 2 Datenstrukturen 2.1 Einführung Syntax: Definition einer formalen Grammatik, um Regeln einer formalen Sprache (Programmiersprache) festzulegen.

Mehr

Programmieren in Haskell Programmiermethodik

Programmieren in Haskell Programmiermethodik Programmieren in Haskell Programmiermethodik Peter Steffen Universität Bielefeld Technische Fakultät 12.01.2011 1 Programmieren in Haskell Bisherige Themen Was soll wiederholt werden? Bedienung von hugs

Mehr

Reihungen. Prof. Dr. Christian Böhm. In Zusammenarbeit mit Gefei Zhang. WS 07/08

Reihungen. Prof. Dr. Christian Böhm. In Zusammenarbeit mit Gefei Zhang.   WS 07/08 Reihungen Prof. Dr. Christian Böhm In Zusammenarbeit mit Gefei Zhang http://www.dbs.ifi.lmu.de/lehre/nfinfosw WS 07/08 2 Ziele Die Datenstruktur der Reihungen verstehen: mathematisch und im Speicher Grundlegende

Mehr

Methoden. Gerd Bohlender. Einstieg in die Informatik mit Java, Vorlesung vom

Methoden. Gerd Bohlender. Einstieg in die Informatik mit Java, Vorlesung vom Einstieg in die Informatik mit Java, Vorlesung vom 2.5.07 Übersicht 1 2 definition 3 Parameterübergabe, aufruf 4 Referenztypen bei 5 Überladen von 6 Hauptprogrammparameter 7 Rekursion bilden das Analogon

Mehr

DieÜbersetzung funktionaler Programmiersprachen

DieÜbersetzung funktionaler Programmiersprachen DieÜbersetzung funktionaler Programmiersprachen 107 11 Die Sprache PuF Wir betrachten hier nur die Mini-Sprache PuF( Pure Functions ). Insbesondere verzichten wir(vorerst) auf: Seiteneffekte; Datenstrukturen;

Mehr

Workshop Einführung in die Sprache Haskell

Workshop Einführung in die Sprache Haskell Workshop Einführung in die Sprache Haskell Nils Rexin, Marcellus Siegburg und Alexander Bau Fakultät für Informatik, Mathematik und Naturwissenschaften Hochschule für Technik, Wirtschaft und Kultur Leipzig

Mehr

Programming 101. Carl Herrmann IPMB & DKFZ

Programming 101. Carl Herrmann IPMB & DKFZ Programming 101 Carl Herrmann IPMB & DKFZ Programmieren Kommandozeile Einfache Befehle Shell Skript aneinanderketten von Befehlen Schleifen Programmiersprache Komplexere Aufgaben Gemeinsamkeiten Alle Programmiersprachen

Mehr

Programmieren 1 C Überblick

Programmieren 1 C Überblick Programmieren C Überblick. Einleitung 2. Graphische Darstellung von Algorithmen 3. Syntax und Semantik 4. Einstieg in C: Einfache Sprachkonstrukte und allgemeiner Programmaufbau 5. Skalare Standarddatentypen

Mehr

PROCESSING EINE ZUSAMMENFASSUNG. Created by Michael Kirsch & Beat Rossmy

PROCESSING EINE ZUSAMMENFASSUNG. Created by Michael Kirsch & Beat Rossmy PROCESSING EINE ZUSAMMENFASSUNG Created by Michael Kirsch & Beat Rossmy INHALT 1. Typen und Operatoren 1. Datentypen 3. Klassen und Objekte 1. Klassen und Objekte 2. Operatoren 2. Konstruktor 3. Typkonversion

Mehr

Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zur Übungsklausur -

Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zur Übungsklausur - Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zur Übungsklausur - Punkte: A1: 30, A2: 20, A3: 20, A4: 20, A5: 10, A6: 20 Punkte: /120 12.02.2012 Hinweis: Geben Sie bei allen

Mehr

Algorithmen und Datenstrukturen

Algorithmen und Datenstrukturen Algorithmen und Datenstrukturen Dynamische Datenobjekte Pointer/Zeiger, Verkettete Liste Eigene Typdefinitionen 1 Zeigeroperatoren & und * Ein Zeiger ist die Speicheradresse irgendeines Objektes. Eine

Mehr

Theoretische Informatik 1 WS 2007/2008. Prof. Dr. Rainer Lütticke

Theoretische Informatik 1 WS 2007/2008. Prof. Dr. Rainer Lütticke Theoretische Informatik 1 WS 2007/2008 Prof. Dr. Rainer Lütticke Inhalt der Vorlesung Grundlagen - Mengen, Relationen, Abbildungen/Funktionen - Datenstrukturen - Aussagenlogik Automatentheorie Formale

Mehr

26 Hierarchisch strukturierte Daten

26 Hierarchisch strukturierte Daten Algorithmik II Peter Wilke Sommersemester 2005 Teil III Funktionale Programmierung 26 Hierarchisch strukturierte Daten Peter Wilke Algorithmik II Sommersemester 2005 1 Peter Wilke Algorithmik II Sommersemester

Mehr

Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zu Übung 4 -

Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zu Übung 4 - Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zu Übung 4 - Dozent: Prof. Dr. G. Rote Tutoren: J. Fleischer, T. Haimberger, N. Lehmann, C. Pockrandt, A. Steen 11.11.011 Ziele

Mehr

Reihungen. Martin Wirsing. in Zusammenarbeit mit Michael Barth, Fabian Birzele und Gefei Zhang

Reihungen. Martin Wirsing. in Zusammenarbeit mit Michael Barth, Fabian Birzele und Gefei Zhang Reihungen Martin Wirsing in Zusammenarbeit mit Michael Barth, Fabian Birzele und Gefei Zhang http://www.pst.informatik.uni-muenchen.de/lehre/ws0506/infoeinf/ WS 05/06 2 Ziele Die Datenstruktur der Reihungen

Mehr

Informatik 1 ( ) D-MAVT F2010. Rekursion, Signaturen. Yves Brise Übungsstunde 8

Informatik 1 ( ) D-MAVT F2010. Rekursion, Signaturen. Yves Brise Übungsstunde 8 Informatik 1 (251-0832-00) D-MAVT F2010 Rekursion, Signaturen Nachbesprechung Blatt 6 Aufgabe 1 - Strukturen und Zeiger Genau die Variablen angeben, die sich geändert haben. Implizite Initialisierung ergänzt

Mehr

Wiederholung: Zusammenfassung Felder. Algorithmen und Datenstrukturen (für ET/IT) Definition Abstrakter Datentyp. Programm heute

Wiederholung: Zusammenfassung Felder. Algorithmen und Datenstrukturen (für ET/IT) Definition Abstrakter Datentyp. Programm heute Wiederholung: Zusammenfassung Felder Algorithmen und Datenstrukturen (für ET/IT) Wintersemester / Dr. Tobias Lasser Computer Aided Medical Procedures Technische Universität München Ein Feld A kann repräsentiert

Mehr

Schwerpunkte. Verkettete Listen. Verkettete Listen: 7. Verkettete Strukturen: Listen. Überblick und Grundprinzip. Vergleich: Arrays verkettete Listen

Schwerpunkte. Verkettete Listen. Verkettete Listen: 7. Verkettete Strukturen: Listen. Überblick und Grundprinzip. Vergleich: Arrays verkettete Listen Schwerpunkte 7. Verkettete Strukturen: Listen Java-Beispiele: IntList.java List.java Stack1.java Vergleich: Arrays verkettete Listen Listenarten Implementation: - Pascal (C, C++): über Datenstrukturen

Mehr

7. Verkettete Strukturen: Listen

7. Verkettete Strukturen: Listen 7. Verkettete Strukturen: Listen Java-Beispiele: IntList.java List.java Stack1.java Version: 4. Jan. 2016 Vergleich: Schwerpunkte Arrays verkettete Listen Listenarten Implementation: - Pascal (C, C++):

Mehr

WS 2013/2014. Robert Giegerich. 11. Dezember 2013

WS 2013/2014. Robert Giegerich. 11. Dezember 2013 WS 2013/2014 Robert AG Praktische Informatik 11. Dezember 2013 höherer Ordnung Worum geht es heute? In Haskell gibt es, die als Argument haben oder als Ergebnis liefern. Diese nennt man höherer Ordnung.

Mehr

Programmieren in Haskell Einstieg in Haskell

Programmieren in Haskell Einstieg in Haskell Programmieren in Haskell Einstieg in Haskell Peter Steffen Universität Bielefeld Technische Fakultät 24.10.2008 1 Programmieren in Haskell Was wir heute machen Umfrage: Wer hat den Hugs ausprobiert? Ausdrücke

Mehr

Universität München, Hans-Peter Kriegel und Thomas Seidl Informatik II a[0] a[1] a[2] a[3] a[n 1]

Universität München, Hans-Peter Kriegel und Thomas Seidl Informatik II a[0] a[1] a[2] a[3] a[n 1] Universität München, Hans-Peter Kriegel und Thomas Seidl Informatik II -108 Kapitel 5: Arrays Einführung Ein Array ist eine Reihung gleichartiger Objekte. a: a[0] a[1] a[2] a[3] a[n 1] Bezeichner a steht

Mehr

Kapitel 4. Kontrollstrukturen

Kapitel 4. Kontrollstrukturen Kapitel 4 Kontrollstrukturen Kontrollstrukturen 1 Ziele Kontrollstrukturen in imperativen Programmen kennenlernen und verstehen. Realisierung der Kontrollstrukturen in Java. Kontrollstrukturen 2 Anweisungen

Mehr

Basiskonstrukte von Haskell

Basiskonstrukte von Haskell Basiskonstrukte von Haskell PD Dr. David Sabel Goethe-Universität Frankfurt am Main 29. September 2015 Basistypen und Operationen Ganzzahlen: Int = Ganzzahlen beschränkter Länge Integer = Ganzzahlen beliebiger

Mehr

WS 2011/2012. Robert Giegerich Dezember 2013

WS 2011/2012. Robert Giegerich Dezember 2013 WS 2011/2012 Robert 1 AG Praktische Informatik 11. Dezember 2013 1 robert@techfak.uni-bielefeld.de Vorschau Themen heute: Funktionen höherer Ordnung (Fortsetzung) künstliche Striktheit mehr zu fold für

Mehr

Vorkurs C++ Programmierung

Vorkurs C++ Programmierung Vorkurs C++ Programmierung Funktionen Rückblick Operatoren logische Verknüpfungen Zusammengesetzte Operatoren ( Zuweisungsoperatoren ) Kontrollstrukturen Bedingte Anweisungen (if-abfrage, switch-konstrukt)

Mehr

Praktische Informatik 3

Praktische Informatik 3 Praktische Informatik 3 Christian Maeder WS 03/04 Vorlesung vom 12.1.2004: Ein/Ausgabe in funktionalen Sprachen Vorlesung vom 12.1.2004: Ein/Ausgabe in funktionalen Sprachen 3 Inhalt Wo ist das Problem?

Mehr

4.1 Bäume, Datenstrukturen und Algorithmen. Zunächst führen wir Graphen ein. Die einfachste Vorstellung ist, dass ein Graph gegeben ist als

4.1 Bäume, Datenstrukturen und Algorithmen. Zunächst führen wir Graphen ein. Die einfachste Vorstellung ist, dass ein Graph gegeben ist als Kapitel 4 Bäume 4.1 Bäume, Datenstrukturen und Algorithmen Zunächst führen wir Graphen ein. Die einfachste Vorstellung ist, dass ein Graph gegeben ist als eine Menge von Knoten und eine Menge von zugehörigen

Mehr

Kapitel 3: Eine einfache Programmiersprache. Programmieren in Haskell 1

Kapitel 3: Eine einfache Programmiersprache. Programmieren in Haskell 1 Kapitel 3: Eine einfache Programmiersprache Programmieren in Haskell 1 Datentypen, Datentypdefinitionen data Instrument = Oboe HonkyTonkPiano Cello VoiceAahs data Musik = Note Ton Dauer Pause Dauer Musik

Mehr