3.1 Datenstrukturen und Typen in Haskell. Bisher haben wir nur die eingebauten Basisdatentypen wie Zahlen und Wahrheitswerte benutzt.
|
|
- Victoria Imke Maier
- vor 5 Jahren
- Abrufe
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) 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
MehrGrundlagen 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,
MehrListen 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
MehrGrundlagen 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,
MehrAuswertungsregeln 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
MehrListenverarbeitung 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]
MehrGrundlagen 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.
MehrEinfü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
MehrListen 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
MehrGrundlagen 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
MehrGrundlagen 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
MehrOperationale 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
MehrGrundlagen 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
MehrStackmaschine; 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
MehrGrundlagen 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
MehrGrundlagen 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 [] = []
MehrFunktionale 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
MehrGrundlegende Datentypen
Funktionale Programmierung Grundlegende Datentypen Fakultät für Informatik und Mathematik Hochschule München Letzte Änderung: 14.11.2017 15:37 Inhaltsverzeichnis Typen........................................
MehrFunktionale 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........................................
MehrGrundlegende 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
MehrGrundlegende 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
MehrParadigmen 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)]
MehrEinfü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
MehrHASKELL 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,
MehrStrö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
MehrEinfü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
MehrKapitel 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
MehrKapitel 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
MehrWS 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
MehrWS 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
MehrAlgorithmen 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
MehrAlgorithmen 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
MehrWS 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
MehrWertebereich 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
MehrTag 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
MehrBeispiele 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
MehrGrundlagen 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
MehrBeispiele: 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
MehrKapitel 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
MehrALP 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
MehrProgrammieren 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
MehrAufgabe 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
MehrEinfü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,
MehrDie 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
MehrGliederung. 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
MehrProgrammierung 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,
Mehr1. 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
Mehr1. 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
MehrKlausur 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
Mehr4.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
MehrAbschnitt 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)
MehrInformatik 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
MehrGrundlagen 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
MehrProgrammieren 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
MehrHaskell, 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
MehrJAVA-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).
MehrKapitel 4. Kontrollstrukturen
Kapitel 4 Kontrollstrukturen Kontrollstrukturen 1 Ziele Kontrollstrukturen in imperativen Programmen kennenlernen und verstehen. Realisierung der Kontrollstrukturen in Java. Kontrollstrukturen 2 Anweisungen
MehrPraxis 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:
MehrKapitel 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
MehrBeschreibung 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" ++
MehrGrundlagen 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
MehrHaskell, 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
MehrGrundlagen 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.
MehrProgrammieren 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
MehrReihungen. 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
MehrMethoden. 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
MehrDieÜ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;
MehrWorkshop 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
MehrProgramming 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
MehrProgrammieren 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
MehrPROCESSING 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
MehrAlgorithmen 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
MehrAlgorithmen und Datenstrukturen
Algorithmen und Datenstrukturen Dynamische Datenobjekte Pointer/Zeiger, Verkettete Liste Eigene Typdefinitionen 1 Zeigeroperatoren & und * Ein Zeiger ist die Speicheradresse irgendeines Objektes. Eine
MehrTheoretische 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
Mehr26 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
MehrAlgorithmen 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
MehrReihungen. 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
MehrInformatik 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
MehrWiederholung: 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
MehrSchwerpunkte. 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
Mehr7. 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++):
MehrWS 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.
MehrProgrammieren 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
MehrUniversitä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
MehrKapitel 4. Kontrollstrukturen
Kapitel 4 Kontrollstrukturen Kontrollstrukturen 1 Ziele Kontrollstrukturen in imperativen Programmen kennenlernen und verstehen. Realisierung der Kontrollstrukturen in Java. Kontrollstrukturen 2 Anweisungen
MehrBasiskonstrukte 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
MehrWS 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
MehrVorkurs C++ Programmierung
Vorkurs C++ Programmierung Funktionen Rückblick Operatoren logische Verknüpfungen Zusammengesetzte Operatoren ( Zuweisungsoperatoren ) Kontrollstrukturen Bedingte Anweisungen (if-abfrage, switch-konstrukt)
MehrPraktische 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?
Mehr4.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
MehrKapitel 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