Beispiellösung zum Übungsblatt aus Übung 14

Werbung
Fachrichtung 6.2 — Informatik
Universität des Saarlandes
Tutorenteam der Vorlesung Programmierung 1
Programmierung 1 (Wintersemester 2015/16)
Lösungsblatt: Aufgaben für die Übungsgruppen: 14
(Kapitel 15 & 16)
Hinweis: Dieses Übungsblatt enthält von den Tutoren für die Übungsgruppe erstellte Aufgaben.
Die Aufgaben und die damit abgedeckten Themenbereiche sind für die Klausur weder relevant noch irrelevant.
Reihungen
Aufgabe 14.1
Schreiben Sie eine Prozedur swapPairwise: α array → unit, die in einer gegebenen Reihung die Komponenten
vorne beginnend paarweise tauscht. Beispielsweise soll die Reihung [2, 3, 4, 5] durch diese Prozedur zu
[3, 2, 5, 4] vertauscht werden. Bei einer ungeraden Anzahl an Komponenten darf die letzte Komponente
unverändert bleiben.
Lösung 14.1:
fun swap a i j = Array . update (a , i ,
#1( Array . sub (a , j ) , Array . update (a , j , Array . sub (a , i ))))
fun swapPairwise a =
( iter (( Array . length a ) div 2) 0 ( fn i ⇒ ( swap a i ( i + 1); i + 2)); ())
Aufgabe 14.2 (Sortieren durch Auswählen)
Ein einfacher, als Sortieren durch Auswählen bezeichneter, Algorithmus geht beim Sortieren einer durch ihre
linkeste und rechteste Position gegebenen nicht leeren Teilreihung (l, r) wie folgt vor:
• Bestimme die Position m einer Komponente minimaler Größe.
• Vertausche die Komponenten an den Positionen l und m.
• Sortiere die Teilreihung (l + 1, r).
Schreiben Sie eine Prozedur ssort: int array → unit, die Reihungen durch Auswählen sortiert. Bestimmen
Sie die Laufzeit Ihrer Prozedur.
Lösung 14.2:
fun min a l r =
if l=r then l else
let val p = min a ( l+1) r
in
if Array . sub (a , p ) < Array . sub (a , l ) then p else l
end
fun swap a i j = Array . update (a , i ,
#1( Array . sub (a , j ) ,
Array . update (a , j , Array . sub (a , i ))))
fun ssort ’ a l r =
if l=r then ()
else (
1
swap a l ( min a l r );
ssort ’ a ( l+1) r
)
fun ssort a = ssort ’ a 0 ( Array . length a −1)
Für l ≤ r hat die Prozedur min die Laufzeit O(r − l); swap hat konstante Laufzeit. Anwenden des Rekurrenzsatzes
für polynomielle Rekurrenzen ergibt für ssort’ daher die Laufzeit O((r − l)2 ). Insbesondere hat ssort eine
2
quadratische Laufzeit O(|a| ), wobei |a| für die Länge einer Reihung a steht.
Imperative Schlangen
Aufgabe 14.3
Erweitern Sie imperative Schlangen um
(a) eine Operation add: α queue → α → unit, die ein Element in konstanter Laufzeit vorn an eine Schlange
anfügt.
(b) eine Operation delete: α queue → unit, die das letzte Element einer Schlange in linearer Laufzeit
entfernt. Wenn nötig, werfen Sie Empty.
Lösung 14.3:
structure Queue :> QUEUE = struct
...
fun add (h , f ) x = let val neu = E (x ,! h ) in h := ref neu end
fun delete (h , f ) =
( case !(! h ) of
D ⇒ raise Empty
| E (x , r ) ⇒ ( case ! r of
D ⇒ let val d = ref D in ( h := d ; f := d ) end
| E (x ’ ,r ’) ⇒ ( case !r ’ of
D ⇒ let val d = ref D in
( r := d ; f := ref ( E (x , r ))) end
| _ ⇒ delete ( ref r , f ))))
end
Aufgabe 14.4
Ergänzen Sie die Signatur und Implementierung der imperativen Schlangen aus dem Buch (S. 312) um eine
Prozedur insertPosition: α queue → α → int → unit, die, gegeben eine Schlange, einen Wert x und
eine Zahl n, einen neuen Eintrag mit Inhalt x an die n-te Position der Schlange einfügt. Für Position 0, soll das
Element als Kopf der Schlange eingefügt werden, für Position 1 als zweites Element usw. Sollte die übergebene
Position die Länge der Schlange überschreiten, soll der Eintrag wie gewohnt an das Ende der Schlange angefügt
werden.
Hinweis: Überlegen Sie sich zunächst eine sinnvolle Hilfsprozedur, die Sie innerhalb der Struktur deklarieren.
Diese Prozedur soll es Ihnen ermöglichen, einen neuen Eintrag zwischen zwei bereits existierenden Einträgen der
Schlange einzufügen.
Lösung 14.4:
fun insertPosition ’ (( h , f ): α queue ) x 0 ( pred ) = let
val e = ref ( E (x ,! h ))
in
( case !( pred ) of
( E (y , r )) ⇒ pred := ( E (y , e ))
| D
⇒ h := e )
end
| insertPosition ’ (h , f ) x n pred =
2
( case !(! h ) of D ⇒ insert (h , f ) x
| E (_ , r ) ⇒ insertPosition ’ ( ref r , f ) x ( n−1) (! h ))
fun insertPosition q x n = insertPosition ’ q x n ( ref D )
Schleifen
Aufgabe 14.5 (Kleine Gemeinheit)
Was tut das folgende Programm?
let
val i = 0
val n = 1
val _ = while ( i < 10) do ( i = n + 1; n = n * i )
in n end
Lösung 14.5:
Es divergiert, da i < 10 immer wahr ist.
Aufgabe 14.6
Nutzen Sie im Folgenden while-Schleifen. Verzichten Sie auf Rekursion.
(a) Schreiben Sie eine Prozedur gauss: int → int, die die Summe aller Zahlen von 0 bis zu einer Eingabe n
berechnet.
(b) Schreiben Sie eine Prozedur fib: int → int, die die n-te Fibonacci-Zahl berechnet.
(c) Schreiben Sie eine Prozedur prim: int → bool, die prüft, ob eine Eingabe n eine Primzahl ist.
(d) Schreiben Sie eine Prozedur sum: int list → int, die die Zahlen einer Liste von Zahlen aufaddiert.
(e) Schreiben Sie eine Prozedur foldli: (α * β → β) → β → α list → β, die Faltung von links implementiert.
Lösung 14.6:
(a) fun gauss n = let
val sum = ref 0
val z = ref 0
in
while ! z <= n do (
sum := ! sum + ! z ;
z := ! z + 1);
!z
end
(b) fun fib n = let
val eins = ref 0
val zwei = ref 1
val buf = ref 0
val z = ref 1
in
while ! z < n do (
buf := ! eins ;
eins := ! zwei ;
zwei := ! buf + ! zwei ;
z := ! z + 1);
! eins
end
(c) fun prim n = let
3
val z = ref 2
val b = ref true
in
while ! z <
if n
else
z :=
!b
end
n do (
mod ! z = 0 then b := false
();
! z + 1);
(d) fun sum xs = let
val x = ref 0
val xr = ref xs
val sum = ref 0
in
while not ( null (! xr )) do (
sum := ! sum + ! x ;
x := hd (! xr );
xr := tl (! xr ));
! x + ! sum
end
(e) fun foldli f s xs =
( case xs of
nil ⇒ s
| ( y :: yr ) ⇒
let
val x = ref y
val xr = ref yr
val akku = ref s
in
while not ( null ! xr )) do (
akku := f (! x , ! akku );
x := hd (! xr );
xr := tl (! xr ));
f (! x , ! akku )
end )
Aufgabe 14.7
Betrachten Sie die folgenden Prozeduren. Geben Sie semantisch äquivalente Prozeduren an, die nicht rekursiv
sind, sondern Schleifen benutzen.
Hinweis: Stellen Sie zuerst sicher, dass die Prozedur endrekursiv ist.
(a) fun pot 0 x a = a
| pot n x a = pot ( n − 1) x ( a * x )
(b) fun square 0 = 0
| square n = square ( n − 1) + 2 * n − 1
(c) fun mystery n a b c d = if n + b + d < 0 then ( b + c ) * a
else mystery ( n − 1) b a ( c * d ) ( d − a )
Lösung 14.7:
(a) fun pot n x a = let
val (n , a ) = ( ref n , ref a )
in ( while ! n > 0 do ( n := ! n − 1; a := ! a * x ); ! a ) end
(b) Endrekursiv:
fun square 0 a = a
4
| square n a = square ( n − 1) ( a + 2 * n − 1)
Schleifen:
fun square n = let
in
val (n , a ) = ( ref n , ref 0)
( while ! n > 0 do ( a := ! a + 2 * ! n − 1; n := ! n − 1);
!a)
end
(c) fun mystery n a b c d = let val (n ,a ,b ,c , d ) = ( ref n , ref a , ref b ,
ref c , ref d )
in ( while ! n + ! b + ! d >= 0 do (
let val (a ’ ,b ’ ,c ’ ,d ’) = (! a , !b , !c , ! d )
in ( n := ! n − 1;
a := b ’;
b := a ’;
c := c ’ * d ’;
d := d ’ − a ’) end
); (! b + ! c ) * ! a )
end
Aufgabe 14.8 (Reverse)
Schreiben Sie eine Prozedur reverse: int array → unit, die eine Reihung mithilfe einer Schleife durch
Umordnung Ihrer Komponenten reversiert.
Lösung 14.8:
Wir geben direkt eine polymorphe Prozedur zum Reversieren an.
fun reverse a = let
fun swap i j =
Array . update (a , i , #1( Array . sub (a , j ) ,
Array . update (a , j , Array . sub (a , i ))));
val l = ref 0;
val r = ref ( Array . length a − 1);
in
while ! l <= ! r do
swap (! l )
l := ! l +
r := ! r −
)
(
(! r );
1;
1
end
Halde/Linearer Speicher
Aufgabe 14.9
Geben Sie die Werte aller allozierten Zellen an. Sie können dies (wie die Prozedur show) als Liste darstellen oder
alternativ die verzeigerte Blockdarstellung benutzen.
(a) val
val
val
val
a
_
_
b
=
=
=
=
new 10
update a 0 1
update a 1 1
iter 8 2 ( fn i ⇒ ( update a i ( sub a ( i−1) + sub a ( i−2)); i+1))
(b) val a = new 4
val _ = update a 0 ( a+2)
val _ = update a 1 ( a+0)
5
val _ = update a 2 ( a+3)
val _ = update a 3 ( a+1)
Lösung 14.9:
(a) [1,1,2,3,5,8,13,21,34,55]
(b) a
a+1
a+2
a+3
−−−−−−−−−−−−−−−−−−−−−−
<−|−− |
−−| →
|
|−−−−/\−−−−/\−−−−−/−−−
\−−−−−\−−−−/
/
\−−−−−−−−/
Aufgabe 14.10
Im Folgenden sollen Operationen auf der Halde (Buch S. 314) betrachtet werden.
(a) Schreiben Sie eine Prozedur put: int → int → unit, die eine Zahl an eine Adresse schreibt.
(b) Schreiben Sie eine Prozedur putints: int → int list → unit, die eine Folge von Zahlen hintereinander in die Halde schreibt.
(c) Schreiben Sie eine Prozedur rev: int → int, die eine Liste reversiert. Sei dazu die Prozedur
append: int → int → int, die zwei Listen konkateniert, gegeben.
(d) Schreiben Sie eine Prozedur counter: unit → int, die in einer einzigen Speicherzelle einen Zähler
implementiert, die beim n-ten Aufruf die Zahl n liefert. Die Halde sei leer.
Lösung 14.10:
(a) fun put a n = update a 0 n
(b) fun putints a xs = foldl ( fn (x , z ) ⇒ ( update z 0 x ; z+1)) a xs
(c) fun reverse a = if null a then a
else append ( reverse ( tail )) ( cons ( head a ) ∼1)
(d) fun counter () = update 0 0 (( sub 0 0) + 1)
handle Address ⇒ ( new 1; update 0 0 0; 0)
Aufgabe 14.11 (Lineare Darstellung von Listen)
Sie haben in Kapitel 15.10 eine Möglichkeit kennen gelernt, Listen von Zahlen in einer Halde darzustellen. Auf
diese können Sie die schon bekannten Prozeduren auf Listen aus Kapitel 4 anwenden. Allozieren Sie außer für
tabulate keinen neuen Speicherplatz, wenn dies nicht unbedingt notwendig ist.
(a) Schreiben Sie eine Prozedur length: address → int, die zu einer angegebenen Adresse die Länge der
angegebenen verketteten Liste bestimmt.
(b) Schreiben Sie eine Prozedur map: address → (int → int) → address, die auf die Liste einer angegebenen Adresse die Prozedur map anwendet.
(c) Schreiben Sie eine Prozedur exists: address → (int → bool) → bool, die auf die Liste einer angegebenen Adresse die Prozedur exists anwendet.
(d) Schreiben Sie eine Prozedur all: address → (int → bool) → bool, die auf die Liste einer angegebenen Adresse die Prozedur all anwendet.
(e) Schreiben Sie eine Prozedur filter: address → (int → bool) → address, die auf die Liste einer
angegebenen Adresse die Prozedur filter anwendet.
Tipp: Sie benötigen eine Hilfsprozedur, um sich das letzte gültige Argument merken zu können.
(f) Schreiben Sie eine Prozedur tabulate: int * (int → int) → address, die für eine Zahl n die Prozedur
tabulate anwendet.
6
(g) Schreiben Sie eine Prozedur rev: address → address, die die Liste einer Adresse reversiert.
Lösung 14.11:
(a) fun length a = if a = ∼1 then 0 else 1 + length ( sub a 1)
(b) fun map a f = if a = ∼1 then ∼1 else ( update a 0 ( f ( sub a 0));
map ( sub a 1) f ; a )
(c) fun exists f a = if a = ∼1 then false else f ( sub a 0) orelse
exists f ( sub a 1)
(d) fun all f a = if a = ∼1 then true else f ( sub a 0) andalso all f ( sub a 1)
(e) fun filter ’ f a last = if a= ∼1 then ∼1 else if f ( sub a 0)
then filter f ( sub a 1) a
else if sub a 1 = ∼1 then ( update last 1 ∼1; last )
else ( update a 0 ( sub ( sub a 1) 0);
update a 1 ( sub ( sub a 1) 1);
filter ’ f a last ; a )
fun filter f a = filter ’ f a a
(f) fun tabulate (n , f ) = putList ( List . tabulate (n , f ))
(g) fun rev ’ a last = if a = ∼1 then ( last ) else
( let val res = rev ’ ( sub a 1) a in ( update a 1 last ; res ) end )
fun rev a = rev ’ a ∼1
Stapelmaschinen
Aufgabe 14.12
Geben Sie an, wie sich der Stapel bei der Ausführung der folgenden Programme schrittweise entwickelt.
Zum Beispiel soll zu [con 1, con 2, sub, halt] die Entwicklung [ ] → [1] → [1, 2] → [1] angegeben werden.
(a) [con 1, con 2, getS 0, getS 1, add, putS 0, halt]
(b) [con 3, con 4, con 5, con 6, getS 3, getS 2, add, getS 1, getS 0, sub, halt]
Lösung 14.12:
(a) [ ] → [1] → [1, 2] → [1, 2, 1] → [1, 2, 1, 2] → [1, 2, 3] → [3, 2]
(b) [ ] → [3] → [3, 4] → [3, 4, 5] → [3, 4, 5, 6] → [3, 4, 5, 6, 6] → [3, 4, 5, 6, 6, 5] → [3, 4, 5, 6, 11] → [3, 4, 5, 6, 11, 4] →
[3, 4, 5, 6, 11, 4, 3] → [3, 4, 5, 6, 11, ∼ 1]
Aufgabe 14.13
Schreiben Sie Maschinenprogramme, die die folgenden Ausdrücke auswerten:
(a) 20 + 7 − 3
(b) 20 + (7 − 3)
(c) 1 + (2 + 3) ∗ (4 − 5)
(d) if 2 ∗ 3 ≤ 12 − 3 then 3 else 5
(e) if (2 + 5) ≤ (1 ∗ 7) then (3 + 2) else (2 + 1) ∗ (3 + 4)
Lösung 14.13:
(a) [con 3, con 7, con 20, add, sub, halt]
(b) [con 3, con 7, sub, con 20, add, halt]
7
(c) [con 5, con 4, sub, con 3, con 2, add,
mul, con 1, add, halt]
(d) [ con 3 , con 12 , sub , con 3 , con 2 , mul , leq , cbranch 3 , con 3 , branch 2 ,
con 5 , halt ]
(e) [ con 7 , con 1 , mul , con 5 , con 2 , add , leq , cbranch 5 , con 2 , con 3 , add ,
branch 8 , con 4 , con 3 , add , con 1 , con 2 , add , mul , halt ]
Aufgabe 14.14
(a) Schreiben Sie ein Programm, das zu einer natürlichen Zahl n mit einer Schleife die Fakultät n! berechnet.
Schreiben Sie das Programm zuerst in SML und dann in M (dazu nehmen wir an, dass die Zahl n an
der Position 0 auf dem Stapel liegt). Achten Sie darauf, dass Ihr Programm nur die Fakultät im Stapel
zurücklässt.
(b) Schreiben Sie ein Programm, das zu einer natürlichen Zahl n mit einer Schleife die Gauß-Summe für n
berechnet.
Lösung 14.14:
(a) Zunächst schreiben wir unser Programm in SML:
fun fac n = let
val a = ref n
val b = ref 1
in
while ! a>=1 do
( b := (! a ) * (! b ) ;
a := ! a−1 );
!b
end
Wir übersetzen nun unser Programm in M. Dazu nehmen wir an, dass unser n an der Position 0 auf dem
Stapel liegt. Man kann das Programm also mit exec(con n :: xs) starten und es berechnet die Fakultät.
[ con 1 , getS 0 , cbranch 10 ,
getS 1 , getS 0 , mul , putS 1
con 1 , getS 0 , sub , putS 0 ,
branch ∼10 ,
putS 0 , halt ]
(b) fun sum n = let
val a = ref n
val b = ref 0
in
while ! a>=1 do
( b := (! a ) + (! b ) ;
a := ! a−1 );
!b
end
Wir übersetzen nun unser Programm in M und nehmen an, dass unser n an der Position 0 auf dem Stapel
liegt. Startet man das Programm also mit exec(con n :: xs), so berechnet es die Summe der Zahlen
von 1 bis n.
[ con 0 , getS 0 , cbranch 10 ,
getS 1 , getS 0 , add , putS 1
con 1 , getS 0 , sub , putS 0 ,
branch ∼10 ,
putS 0 , halt ]
8
Aufgabe 14.15
Übersetzen Sie das folgende W -Programm in M :
var n
var res := 1
while 1 <= n do
res := res * n
n := n − 1
end
Lösung 14.15:
Das hier angegebene Programm geht davon aus, dass das Argument n schon auf dem Stack liegt. Die Zeile var n
wird nicht übersetzt.
[( con 1) ,
( getS 0) ,( con 1) , leq , ( cbranch 10) ,
( getS 1) , ( getS 0) , mul , ( putS 1) ,
( con 1) , ( getS 0) , sub , ( putS 0) ,
( branch ∼12) ,
halt ]
(*
(*
(*
(*
res := 1 *)
while 1<=n do *)
res := res * n *)
n := n−1 *)
Aufgabe 14.16
Scheiben Sie ein Programm (in W und M), das zu n die Liste [n, ..., 0] berechnet.
Lösung 14.16:
In W:
var n
var c := 0
var xs := −1
while c <= n do
xs := new (c , xs ) ;
c := c+1
end
return xs
In M:
val list = [
con 0 ,
con ∼1 ,
getS 0 , getS 1 , leq , cbranch 10 ,
getS 2 , getS 1 , new 2 , putS 2 ,
con 1 , getS 1 , add , putS 1 ,
branch ∼12 ,
putS 1 , putS 0 , halt
]
Aufgabe 14.17
Schreiben Sie ein Maschinenprogramm, dass die Länge einer Liste berechnet. Halten Sie sich dabei an folgende
Skizze in W:
var xs
var n := 0
while 0 <= xs do
n := n + 1
xs := xs .1
end
return n
xs.1 liefert den Wert in der zweiten Zelle des Blocks, dessen Adresse der Wert der Variablen xs ist.
Lösung 14.17:
val prog = [
con 0 ,
getS 0 , con 0 , leq , cbranch 9 ,
con 1 , getS 1 , add , putS 1 ,
getS 0 , getH 1 , putS 0 ,
branch ∼11 ,
putS 0 , halt ]
9
Aufgabe 14.18 (Eigene Prozeduren aufrufen)
Schreiben Sie ein Programm in M, welches mithilfe einer Prozedur, die das Minimum zweier Zahlen berechnet,
den Ausdruck min(17, 8) − min(5, 7) berechnet.
Lösung 14.18:
[ proc (2 , 9) , arg 1 , arg 2 , leq , cbranch 3 , arg 2 , return ,
arg 1 , return ,
con 7 , con 5 , call 0 , con 8 , con 17 , call 0 , sub , halt ]
Aufgabe 14.19
Übersetzen Sie die folgende Prozedur nach M:
fun fac n = if n < 2 then 1 else fac ( n − 1) * n
Nehmen Sie dabei an, dass die Befehlssequenz für die Prozedur im Programmspeicher mit der Adresse 0 beginnt.
Lösung 14.19:
val fac =
[
proc (1 , 14) ,
con 1 , arg 1 , leq , cbranch 3 ,
con 1 , return ,
con 1 , arg 1 , sub , call 0 , arg 1 , mul , return
]
Z. B. liefert nun der Aufruf exec (fac @ [con 4, call 0, halt]) die int list [24].
Aufgabe 14.20
Übersetzen Sie die folgende Prozedur nach M:
fun fib n = if n < 2 then n else fib ( n − 2) + fib ( n − 1)
Lösung 14.20:
val fib =
[
proc (1 , 17) ,
con 1 , arg 1 , leq , cbranch 3 , arg 1 , return ,
con 1 , arg 1 , sub , call 0 ,
con 2 , arg 1 , sub , call 0 ,
add , return
]
Z. B. liefert nun der Aufruf exec (fac @ [con 4, call 0, halt]) int list [3].
Aufgabe 14.21 (Rätsel)
Was tut das folgende Programm? Das Programm erwartet, dass zwei Argumente auf dem Stack liegen, bevor es
ausgeführt wird.
[( getS 0) , ( con 1) , ( getS 1) , leq , ( cbranch 2) , ( branch 8) ,
( getS 0) , mul , ( con 1) , ( getS 1) , sub , ( putS 1) , ( branch ∼11) , halt ]
Lösung 14.21:
Das Programm berechnet xn , wobei x das erste Argument und n das zweite Argument auf dem Stack ist.
10
Herunterladen