ws 2002/2003 programmierung 1 - repetitorium andreas augustin und marc wagner
DESCRIPTION
WS 2002/2003 Programmierung 1 - Repetitorium Andreas Augustin und Marc Wagner Homepage: http://info1.marcwagner.info. Montag, den 07.04.03 Kapitel 3 Typen und Prozeduren. 3.1 Typen und Typdisziplin. - PowerPoint PPT PresentationTRANSCRIPT
Programmierung 1 - Repetitorium
WS 2002/2003
Programmierung 1 - Repetitorium
Andreas Augustin und Marc Wagner
Homepage: http://info1.marcwagner.info
Programmierung 1 - Repetitorium
3.1 Typen und Typdisziplin
int { 1 , 2 , ... } real { 3.4 , 5.0 , ... } unit { ( ) } bool { true , false }
Die Typdisziplin einer Programmiersprache sorgt dafür,dass auf Werte nur solche Operationen angewendet werden können,die die Werte richtig interpretieren.
Eine vereinfachte Typdisziplin von SML ist die monomorphe Typdisziplin :
1. Jeder Konstante und jedem gebundenen Bezeichner ist genau ein Typzugeordnet.
2. Jedem Operationssymbol sind endlich viele Typen zugeordnet.
3. Für Operationsausdrücke wird die folgende Typregel verwendet :
a1 : t1 : t1 t2→t a2 : t2
a1 a2 : t
4. Für Konditionale wird die folgende Typregel verwendet :
a1 : bool a2 : t a3 : t
if a1 then a2 else a3 : t
Programmierung 1 - Repetitorium
3.1 Typen und Typdisziplin
5. Für Tupelausdrücke wird die folgende Typregel verwendet :
a1 : t1 ... an : tn
(a1,...,an) : t1...tn
6. Für Prozeduranwendungen wird die folgende Typregel verwendet :
p : t1 → t2 a : t1
p a : t2
7. Die obigen Regeln sorgen dafür, dass jedem zulässigen Ausdruckgenau ein Typ zugeordnet werden kann.
8. Wenn die Auswertung eines Audrucks, dem der Typ t zugeordnet wurde,einen Wert liefert, handelt es sich immer um einen Wert des Typs t.Diese wichtige Eigenschaft der Typdisziplin nennt sich Typsicherheit.
Typen können nicht mit der Menge ihrer Werte identifiziert werden.Typen legen zusätzlich eine Interpretation für ihre Werte fest.
Programmierung 1 - Repetitorium
3.2 Prozeduren sind Werte
Prozeduren können als Komponenten von Tupeln und als Argumenteund Ergebnisse von Prozeduren vorkommen.
Prozeduren mit prozeduralen Argumenten oder Ergebnissen bezeichnetman als höherstufige Prozeduren.
Prozeduren lassen sich mit speziellen Ausdrücken (Abstraktionen) beschreiben :
fn (x:int, y:int) => x*y
Abstraktionen sind Ausdrücke, die zu Prozeduren auswerten.Eine Abstraktion beschreibt eine Prozedur ohne ihr einen Namen zu geben.
val produkt = fn (x:int, y:int) => x*y
Rekursive Prozeduren kann man wegen der Selbstreferenz nicht unmittelbarmit Abstraktionen beschreiben.
val rec f = fn n:int => if n<1 then 1 else f(n-1)
Eine Abkürzung dafür ist : fun f (n:int) = if n<1 then 1 else f(n-1)
Programmierung 1 - Repetitorium
3.2 Prozeduren sind Werte
Beispiel für den Nutzen prozeduraler Argumente :
n
i
ifnfsum1
)(),(
val rec sum = fn (f:int->int, n:int) => if n<1 then 0 else sum(f, n-1) + f(n)
fun sum (f:int->int, n:int) = if n<1 then 0 else sum(f, n-1) + f(n)
Die Summe der ersten 10 Quadratzahlen erhält man hiermit :
sum (fn i:int => i*i, 10)
Programmierung 1 - Repetitorium
3.3 Kartesische und kaskadierte Prozeduren
Prozeduren, deren Argumenttyp ein Produkttyp t1...tn ist, sind kartesisch.
plus : int * int -> intfun plus (x:int, y:int) = x+y Alternativ : val plus = fn (x:int, y:int) => x+yplus (3,4)
Prozeduren, deren Ergebnistyp ein Pfeiltyp t1 → t2 ist, sind kaskadiert.
plus : int -> int -> intfun plus (x:int) (y:int) = x+yAlternativ : val plus = fn x:int => fn y:int => x+yplus 3 4
Der Typkonstruktor -> klammert nach rechts.
int -> int -> int int -> ( int -> int )
Prozeduranwendungen klammern nach links.
plus 3 4 ( plus 3 ) 4
Programmierung 1 - Repetitorium
3.4 Polymorphe Prozeduren
Prozeduren, die auf mehr als einen Typ anwendbar sind, sind polymorph.
Sei X eine Menge, f X → X und n 0. Wir definieren fn X → X rekursiv :
fn(x) = if n = 0 then x else fn-1(f(x))
Wir deklarieren jetzt eine kaskadierte Funktion iter, die für einen beliebigenTyp t anwendbar ist und die Funktion f n-mal hintereinander auf x anwendet :
fun iter (f:‘a->‘a) (n:int) (x:‘a) = if n<1 then x else iter f (n-1) (f x)
Typschema : val iter : (‘a -> ‘a) -> int -> ‘a -> ‘a
Hierbei ist ‘a (gesprochen : alpha) eine sogenannte Typvariable.
Anwendungsbeispiele von iter :
iter (fn x:int => x*x) 4 2
iter (fn x:real => x*x) 5 2.0
iter (fn x:bool => not x) 3 true
Instanz des Typschemas :
(int -> int) -> int -> int -> int
(real -> real) -> int -> real -> real
(bool -> bool) -> int -> bool -> bool
Programmierung 1 - Repetitorium
3.4 Polymorphe Prozeduren
Jedes Typschema hat unendlich viele Instanzen. (aufgrund des Pfeiles)
Ein Bezeichner heißt polymorph, wenn ihm ein Typschema zugeordnet ist.
Ein Ausdruck heißt polymorph, wenn man ihm mehrere Typ zuordnen kann.
Ein Ausdruck heißt expansiv, wenn er eine Prozedur- oder eine Operations-Anwendung enthält, die nicht innerhalb einer Abstraktion steht.
Man unterscheidet folgende Arten von Deklarationen :
1. Monomorphe Deklaration : Wenn a ein monomorpher Ausdruck mit demTyp t ist, wird x monomorph mit dem Typ t typisiert.Beispiel : fun inc x = x+1 val inc : int -> int
2. Polymorphe Deklaration : Wenn a ein polymorpher Ausdruck ist, der nichtexpansiv ist, wird x polymorph mit dem Typschema typisiert, dass alle Typenvon a beschreibt.Beispiel : fun id (x:‘a) = x val id : ‘a -> ‘a
Programmierung 1 - Repetitorium
3.4 Polymorphe Prozeduren
3. Ambige Deklaration : Wenn a ein polymorpher und expansiver Ausdruck ist,wird x monomorph mit einem der dem Ausdruck a zugeordneten Typentypisiert. Dabei wird der Typ so gewählt, dass er mit den nachfolgendenVerwendungen des Bezeichners x verträglich ist (wenn möglich).Beispiel : val f = id id val f : ‘b -> ‘b (Warning!)
Bei rekursiven Deklarationen und Prozedurdeklarationen kann der ambige Fallnicht auftreten ! (rechte Seite ist Abstraktion und somit nicht expansiv)
fun f (x:int) = f x val f : int -> ‘a
Der Bezeichner f wird polymorph mit dem Schema ‘a (int -> ‘a)typisiert, da als Ergebnistyp von f jeder Typ zulässig ist.
Programmierung 1 - Repetitorium
3.5 Typinferenz
Das automatische Herleiten von fehlenden Typconstraints nennt man Typinferenz.
Typinferenz berechnet immer die allgemeinsten Typconstraints.
Bei überladenen Operationssymbolen gibt die Typinferenz int den Vorzug.
fun sum f n = if n<1 then 0 else sum f (n-1) + f nval sum : (int -> int) -> int -> int
fun plus (x,y) = x+yval plus : int * int -> int
Durch die Angabe von expliziten Typconstraints können bei größeren ProgrammenTypfehler schneller lokalisiert werden und diese Fehler direkt bei der Deklarationdiagnostiziert werden.
fun f(x,y) = (2*x,y)val f : int * ‘a -> int * ‘a
Programmierung 1 - Repetitorium
3.6 Op-Ausdrücke
Op-Ausdrücke sind Abkürzungen für Abstraktionen, die Operationen alsKartesische Prozeduren zur Verfügung stellen.
op+ fn (x,y) => x+y
op< fn (x,y) => x<y
op= fn (x,y) => x=y
op div fn (x,y) => x div y
op ~ fn x => ~x
Da die meisten Operationssymbole überladen sind, ist das Weglassen derTypconstraints für die Argumentvariablen der Abstraktionen wesentlich.
Programmierung 1 - Repetitorium
3.7 Typen und Gleichheit
Typen, für deren werte ein Test auf Gleichheit mit = möglich ist, heißenTypen mit Gleichheit.
Die Typen int, bool und unit sind Beispiele für Typen mit Gleichheit.
Dagegen sind die Pfeiltypen t1 → t2 Typen ohne Gleichheit.
Auf Produkttypen t1 ... tn ist der Gleichheitstest genau dann zulässig,wenn er auf allen Komponenten t1 , ... , tn zulässig ist.
Um die Unterscheidung zwischen Typen mit und ohne Gleichheit in Typschemataausdrücken zu können, gibt es zwei Arten von Typvariablen :
Typvariablen mit nur einem Hochkomma ‘a für beliebige Typen
Typvariablen mit zwei Hochkommas ‘‘a nur für Typen mit Gleichheit
Programmierung 1 - Repetitorium
3.8 Bezeichnerbindung
1. Dynamische Bezeichnerbindungen (bei Ausführung einer Phrase) :
val f = fn x => x*xf 5
Die Ausführung der Anwendung bindet die Argumentvariable x dynamischan die Zahl 5.
2. Statische Bezeichnerbindungen (bei semantischer Analyse einer Phrase) :
val f = fn x => x*x
Die Analyse der Deklaration bindet den Bezeichner f statisch an den Typ int -> int und den Bezeichner x statisch an int.
3. Lexikalische Bezeichnerbindungen :
Lexikalische Bindungen beschreiben den strukturellen Rahmen fürstatische und dynamische Bindungen.
Programmierung 1 - Repetitorium
3.8 Bezeichnerbindung
Für das Auftreten (Vorkommen) von Bezeichnern gibt es zwei Möglichkeiten :definierend und benutzend
Definierende Auftreten führen eine Bindung ein.
Benutzende Auftreten benutzen eine bestehende Bindung.
Benutzende Auftreten, die innerhalb der betrachteten Phrase lexikalischungebunden sind, werden als freie Auftreten bezeichnet.
fn x => fn y => z x (y x)
Die überstrichenen Bezeichner sind definierende Auftreten,die anderen sind benutzende Auftreten.Der Bezeichner z ist hier frei vorkommend, da er keinem definierenden Vorkommenvon z innerhalb der betrachteten Phrase zugeordnet werden kann.
Programmierung 1 - Repetitorium
3.8 Bezeichnerbindung
Die lexikalischen Bindungen lassen sich auch textuell darstellen.
Alle Bezeichnerauftreten einer Bindungsgruppe werden dabei mit demselbenIndex markiert. Dagegen bekommen freie Bezeichner keinen Index.Mehrere definierende Auftreten desselben Bezeichner werden mit verschiedenenIndizes versehen.
fn x => fn y => (fn y => (fn x => z x y) x) y
fn x1 => fn y1 => (fn y2 => (fn x2 => z x2 y2) x1) y1
Lexikalische Bindungen textuell dargestellt :
Diese Phrase lässt sich nun wie folgt bereinigen (konsistente Umbenennung) :
fn x => fn y => (fn u => (fn v => z v u) x) y
Bei einer Abstraktion fn x => a ist der Gültigkeitsbereich des definierendenAuftretens von x der Ausdruck a. Alle benutzenden Auftreten von x, die lexikalischan dieses definierende Auftreten von x gebunden sind, müssen sich innerhalb desAusdrucks a befinden.
Programmierung 1 - Repetitorium
3.9 Prozeduren und Auswertungsprotokolle
Wenn ein Ausdruck keine freien Bezeichner enthält, heißt er geschlossen,sonst nennt man ihn offen.
Offene Ausdrücke sind unvollständige Beschreibungen, die nur im Kontext vonexternen Bezeichnerbindungen interpretiert werden können.
val x = 1fun inc y = x+y
liefert die dynamischen Bindungen
x → 1inc → fn y => 1+y
Eine Redeklaration des Bezeichners x hat damit keine Auswirkung auf die bereitsBerechnete Prozedur inc :
val x = 5
liefert die dynamischen Bindungen
x → 5inc → fn y => 1+y (die Bindung von inc ist also unverändert)
Programmierung 1 - Repetitorium
3.9 Prozeduren und Auswertungsprotokolle
fun f x y = if x = 0 then y else y + f (x-1) y
führt folgende dynamische Bindung ein :
f → rec f => fn x => fn y => if x=0 then y else y + f (x-1) y
Das Auswertungsprotokoll für den Ausdruck f 1 5 ergibt :
f 1 5 → 1 5 (wobei die rekursive Abstraktion ist) → (fn y => if 1=0 then y else y + (1-1) y) 5→ if 1=0 then 5 else 5 + (1-1) 5→ if false then 5 else 5 + (1-1) 5→ 5 + (1-1) 5→ 5 + 0 5→ 5 + (fn y => if 0=0 then y else y + (0-1) y) 5→ 5 + (if 0=0 then 5 else 5 + (0-1) 5)→ 5 + (if true then 5 else 5 + (0-1) 5)→ 5 + 5→ 10