PPT

Werbung
Programmierung 1 - Repetitorium
WS 2002/2003
Programmierung 1 - Repetitorium
Andreas Augustin und Marc Wagner
Homepage: http://info1.marcwagner.info
Programmierung 1 - Repetitorium
Mittwoch, den 09.04.03
Kapitel 6
Konstruktortypen und Ausnahmen
Programmierung 1 - Repetitorium
6.1 Varianten und Konstruktoren
Ziel:
Darstellung der drei geometrischen Formen Kreis, Quadrat und Dreieck
als mathematische Objekte
Das Paar < 1 , r > stellt einen Kreis mit Radius r dar.
Das Paar < 2 , a > stellt ein Quadrat mit der Seitenlänge a dar.
Das Paar < 3 , < a , b , c > > stellt ein Dreieck mit den Seitenlängen a,b,c dar.
Die erste Komponente des Paares bezeichnet man als Variantennummer.
Sie gibt die Form des geometrischen Objekts an.
Die zweite Komponente des Paares bezeichnet man als Datum.
Sie spezifiziert die Dimensionen des geometrischen Objekts.
datatype shape =
Circle of real
| Square of real
| Triangle of real * real * real
Programmierung 1 - Repetitorium
6.1 Varianten und Konstruktoren
Der Datentyp shape liefert uns jetzt die Konstruktoren Circle, Square und Triangle.
Circle
Square
4.0
3.0
Circle 4.0
Square 3.0
Triangle
4.0
3.0
5.0
Triangle ( 4.0 , 3.0 , 5.0 )
Berechnung des Flächeninhalts :
fun area (Circle r) = Math.pi * r * r
| area (Square a) = a * a
| area (Triangle (a,b,c)) = let val s = (a+b+c)/2.0
in Math.sqrt(s*(s-a)*(s-b)*(s-c))
end
Programmierung 1 - Repetitorium
6.1 Varianten und Konstruktoren
Die Prozedur area ist mit drei Regeln definiert, die jeweils für eine der drei
Varianten von shape zuständig sind.
Die Muster der Regeln haben die Form einer Konstruktoranwendung.
Die Deklaration von Konstruktoren ermöglicht es, die verschiedenen Varianten
eines Konstruktortyps durch frei gewählte Namen zu bezeichnen.
Konvention:
Konstruktoren = Bezeichner mit Großbuchstaben beginnend
Typen und Werte = Bezeichner mit Kleinbuchstaben beginnend
Programmierung 1 - Repetitorium
6.2 Enumerationstyp
datatype day = Monday | Tuesday | Wednesday | Thursday
| Friday | Saturday | Sunday
Die Werte des Typs day werden durch nullstellige Konstruktoren beschrieben,
die wie Konstanten verwendet werden können.
fun weekend Saturday = true
| weekend Sunday = true
| weekend _ = false
Typ: weekend : day → bool
map weekend [ Monday , Wednesday , Friday , Saturday, Sunday ]
= [ false , false , false , true, true ]
Typen, die nur mit Hilfe von nullstelligen Konstruktoren definiert sind,
werden als Enumerationstyp bezeichnet.
vordeklarierter Enumerationstyp:
datatype order = LESS | EQUAL | GREATER
Programmierung 1 - Repetitorium
6.3 Typsynonyme
type point = real * real
point ist hierbei kein neuer Typ, sondern eine neue Bezeichnung für einen
bereits existierenden Typ.
sinnvolle Verwendungen :
datatype object =
Circle of point * real
| Triangle of point * point * point
Wir können auch einen neuen Typ einführen :
datatype point = P of real * real
P ( 2.0 , 3.0 ) : point
fun mirror ( P ( x , y ) ) = P ( ~x , y )
Programmierung 1 - Repetitorium
6.4 Ausnahmen
Ausnahmen sind Werte des Typs exn.
Neue Ausnahmen deklariert man mithilfe von exception.
exception New
exception Newer of int
New sowie Newer sind Ausnahmekonstruktoren, diese können wie normale
Konstruktoren verwendet werden.
Zusätzlich kann man mit Ausdrücken der Form
raise e
die Ausnahme e werfen (e vom Typ exn)
raise Newer 6
!Uncaught exception: Newer 6
Raise-Ausdrücke liefern keinen Wert, daher können sie jeden Typ annehmen.
Programmierung 1 - Repetitorium
6.4 Ausnahmen
Geworfene Ausnahmen können mithilfe von Handle-Ausdrücken gefangen werden.
( raise New ) handle New => ( )
( raise Newer 7 ) handle Newer x => 7
fun test f = f() handle Newer x => x | Overflow => ~1
Beim Programmieren mit Ausnahmen sind manchmal Sequenzialisierungen
( e1 ; ... ; en) hilfreich.
Auswertung einer Sequenz:
Alle Teilausdrücke e1 , ... , en von links nach recht auswerten.
Bei Terminierung aller Ausdrücke, liefert die Sequenz den Wert des letzten
Teilausdrucks en.
(e1,...,en) = let val _ = e1
...
val _ = en-1
in en
end
Programmierung 1 - Repetitorium
6.5 Arithmetische Ausdrücke
type var = int
datatype exp =
(Variablen als ganze Zahlen)
C of int | V of var | A of exp*exp
| M of exp*exp
Jede Ausdrucksform ( Konstante, Variable, Addition, Multiplikation ) wird durch
einen entsprechenden Konstruktor realisiert.
Darstellung der rekursiven Struktur von exp:
A
M
exp
C
int
V
var
Programmierung 1 - Repetitorium
6.5 Arithmetische Ausdrücke
( 2 x + y ) * ( x + 3 )
val e = M ( A ( M ( C 2 , V 1 ) , V 2 ) , A ( V 1 , C 3 ) )
M
A
M
C
V
2
1
A
V
V
C
2
1
3
datatype exp =
C of int
| V of var
| A of exp*exp
| M of exp*exp
Um den Ausdruck auszuwerten, benötigen wir eine Umgebung,
die den Variablen x und y Werte zuweist. { x → 5 , y → 3 }
val env = fn 1 => 5 | 2 => 3 | _ => raise Unbound
Programmierung 1 - Repetitorium
6.5 Arithmetische Ausdrücke
Unser Ziel : die Evaluierungsprozedur eval : exp → env → int
fun
|
|
|
eval
eval
eval
eval
(
(
(
(
C
V
A
M
c ) _ = c
v ) env = env v
(e,e‘) ) env = eval e env + eval e‘ env
(e,e‘) env = eval e env * eval e‘ env
Programmierung 1 - Repetitorium
6.6 Optionen
datatype ‘a option = NONE | SOME of ‘a
NONE ist die uneingelöste Option, SOME ist die eingelöste Option.
fun get xs n = SOME (List.nth(xs,n)) handle Subscript => NONE
liefert das n-te Element einer Liste als eingelöste Option, wenn es existiert.
get [3,4,5] 2 = SOME 5
get [3,4,5] 3 = NONE
fun valOf (SOME x) = x
| valOf NONE = raise Option.Option
erlaubt bequemen Zugriff auf eingelöste Optionen
fun isSome NONE = false
| isSome (SOME _) = true
testet, ob es sich um eine eingelöste Option handelt
Programmierung 1 - Repetitorium
6.7 Case-Ausdrücke und abgeleitete Formen
fun sign x = case Int.compare (x,0) of
LESS => ~1
| EQUAL => 0
| GREATER => 1
case e of M1 => e1 | ... | Mn => en
= ( fn M1 => e1 | ... | Mn => en ) e
if e1 then e2 else e3 = (fn true => e2 | false => e3) e1
Programmierung 1 - Repetitorium
6.8 Bäume informell und formal
Größe des Baumes = Anzahl der Knoten des Baumes
Pfad = Verbindung zwischen 2 bel. Knoten eines Baumes
Länge des Pfades = Anzahl der Kanten des Pfades
Tiefe des Knotens = Länge des Pfades von der Wurzel zu den Knoten
Tiefe des Baumes = Max. Tiefe seiner Knoten
Wurzel
3
Kante
Vorgänger
Marke
2
4
1
Knoten
Blatt
1.Nachfolger
1
2.Nachfolger
3
0
3.Nachfolger
2
7
Programmierung 1 - Repetitorium
6.8 Bäume informell und formal
Eigenschaften von Bäumen :
1.
Zwischen 2 Knoten eines Baumes existiert immer genau ein Pfad.
2.
Die Wurzel eines Baumes hat keinen Vorgänger und alle anderen Knoten
haben genau einen Vorgänger.
3
2
[]
0
4
1
1
2
7
7
7
7
[1,1,1]
[1]
[2]
[3]
[1,1]
[3,1]
[3,2]
[1,1,2]
[1,1,3]
Tiefe eines Knotens = Länge seiner Adresse
u Adresse des Knotens ⇒ u@[n] Adresse seines n-ten Nachfolgers
[3,3]
Programmierung 1 - Repetitorium
6.8 Bäume informell und formal
Bäume mit genau einem Knoten heißen atomar.
Bäume mit mind. zwei Knoten heißen zusammenhängend.
Die Blätter eines Baumes sind alle Knoten ohne Nachfolger.
Jeder Baum hat genau eine Wurzel, mindestens ein Blatt und mind. einen Knoten.
Ein Baum ist aus seinem Kopf (Marke der Wurzel) und seinen Unterbäumen
(Teilbäume der Nachfolger der Wurzel) zusammengesetzt. ( x , [ t1 , ... , tn ] )
1
(1,[(2,[]),(7,[]),(7,[])])
2
7
7
Ein Baum heißt linear gdw. jeder Knoten höchstens einen Nachfolger hat.
Ein Baum heißt binär gdw. jeder Knoten außer den Blättern genau zwei
Nachfolger hat.
Ein Baum heißt balanciert gdw. alle Blätter gleiche Tiefe haben.
Jeder atomare Baum ist linear, binär und balanciert.
Programmierung 1 - Repetitorium
6.9 Ein ausführbares Modell für Bäume
datatype ‘a tree = T of ‘a * ( ‘a tree list )
1
T(1,[T(2,[]),T(7,[]),T(7,[])])
2
7
7
fun head (T(x,_)) = x
liefert den Kopf des Baumes
fun dst (T(_,ts)) n = List.nth(ts,n-1)
liefert den n-ten Unterbaum von t
fun atomic (T(_,ts)) = null ts
testet, ob ein Baum atomar ist
Programmierung 1 - Repetitorium
6.9 Ein ausführbares Modell für Bäume
fun subtree t nil = t
| subtree t (n::ns) = subtree (dst t n) ns
liefert zu einem Baum und einer Liste den zugeordneten Teilbaum
fun node t ns = (subtree t ns; true) handle Subscript => false
testet für einen Baum, ob es sich bei einer Liste um einen Knoten des
Baumes handelt
fun leaf t ns = atomic (subtree t ns) handle Subscript => false
testet für einen Baum, ob es sich bei einer Liste um ein Blatt des Baumes handelt
fun label t ns = head (subtree t ns)
liefert die Marke eines Knotens
fun pred t nil = raise Subscript
| pred t ns = (subtree t ns; rev(tl(rev ns)))
liefert zu einem Baum und einem Knoten den Vorgänger des Knotens
Programmierung 1 - Repetitorium
6.9 Ein ausführbares Modell für Bäume
fun succ t ns n = let val ns‘ = ns@[n] in
subtree t ns‘; ns‘ end
liefert zu einem Baum, einem Knoten und einer Zahl n den n-ten Nachfolger des
Knotens
fun size (T(_,ts)) = foldl op+ 1 (map size ts)
liefert die Größe eines Baumes ( = 1 + Summe der Größen seiner Unterbäume )
fun depth (T(_,ts)) = 1 + foldl Int.max -1 (map depth ts)
liefert die Tiefe eines Baumes ( = 1 + max. Tiefe seiner Unterbäume )
Programmierung 1 - Repetitorium
6.10 Test auf Balanciertheit
Ein Baum ist genau dann balanciert, wenn für jeden seiner Teilbäume t gilt,
dass alle seine Unterbäume die gleiche Höhe haben bzw. alle Blätter die
gleiche Tiefe haben.
fun balanced t =
let exception Unbalanced
fun depthb (T(_,nil)) = 0
| depthb (T(_,t::tr)) = 1 + foldl forward (depth t) tr
and forward (t,n) = if depthb t = n then n
else raise Unbalanced
in
(depthb t; true) handle Unbalanced => false
end
depthb und forward sind verschränkt rekursiv, daher ist eine Deklaration
mit and erforderlich.
Programmierung 1 - Repetitorium
6.11 Linearisation und Projektionen
Eine Liste heißt Linearisierung eines Baumes, wenn sie genau die Knoten des
Baumes enthält (ohne Doppelauftreten), und wenn sie die Knoten jedes
Teilbaumes als Segment enthält.
Präfixlinearisierung :
1.
Für jeden Teilbaum gilt : Die Wurzel erscheint vor den Knoten der
Unterbäume.
2.
Für jeden Teilbaum mit mindestens zwei Unterbäumen gilt :
Die Knoten der Unterbäume erscheinen in der Reihenfolge der Unterbäume.
fun pre (T(x,ts)) = x :: List.concat (map pre ts)
Die Projektion einer Liste von Knoten erhält man, indem man jeden Knoten
durch seine Marke ersetzt.
fun project t nss = map (label t) nss
Programmierung 1 - Repetitorium
6.11 Linearisation und Projektionen
Postfixlinearisierung :
Analog zur Präfixlinearisierung, nur mit dem Unterschied, dass die Wurzel
eines Teilbaums diesmal nach dem Knoten seiner Unterbäume erscheint.
fun post (T(x,ts)) = List.concat (map post ts) @ [x]
3
0
5
4
1
2
7
Präfix : [ 3 , 0 , 5 , 4 , 1 , 2 , 7 , 7 ]
Postfix : [ 5 , 0 , 4 , 2 , 7 , 7 , 1 , 3 ]
7
Herunterladen