Praktikum Praktische Informatik Aufgabenblatt Nr. 5 Aufgabe 1

Werbung
Prof. Dr. Manfred Schmidt-Schauß
Künstliche Intelligenz/Softwaretechnologie
Fachbereich Biologie und Informatik / Institut für Informatik
Johann Wolfgang Goethe-Universität Frankfurt am Main
Praktikum Praktische Informatik
Sommersemester 2002
Aufgabenblatt Nr. 5
Lösungen bis Mittwoch, 19. Juni 2002 spätestens 18:00
Aufgabe 1: Stellenwertsysteme
Nicht nur in der maschinennahen Programmierung werden häufig Stellenwertsysteme zu einer
von 10 abweichenden Basis verwendet. In dieser Aufgabe sollen Sie Datentypen und Typklassen
in Haskell entwickeln, mit denen Zahlen verschiedener Stellenwertsysteme ineinander umgerechnet werden können. Betrachten Sie dazu die folgende Klassendefinition:
> class Digit d where
>
digitToInteger :: d -> Integer
>
integerToDigit :: Integer -> Maybe d
>
>
>
base :: d -> Integer
-- falls der Integer überhaupt
-- in einer Stelle dargestellt
-- werden kann
Die beiden Methoden digitToInteger und integerToDigit erinnern dabei ein wenig an
fromInteger aus der Klasse Num und toInteger aus der Klasse Integral; entscheidend ist
hier aber die zusätzliche Methode base zum Ermitteln der Basis.
a) Implementieren Sie zunächst zwei Datentypen BinDigit und HexDigit, um Ziffern des
Binär- bzw. Hexadezimalsystems repräsentieren zu können, und machen Sie diese auf sinnvolle Weise zu Instanzen der Typklassen Digit und Show, wobei Sie auch die Methode
showList definieren sollten.
b) Überlegen Sie sich eine geeignete Datenstruktur für die Darstellung von Zahlen1 in einem Stellenwertsystem und beschreiben Sie präzise deren Semantik, d.h. wo sich welche Stelle wiederfindet. Definieren Sie dann mit Hilfe dieser Datenstruktur einen Typen2
ValuePlaceSystem d für ein Stellenwertsystem über dem Ziffer-Typ d.
c) Programmieren Sie nun polymorphe Funktionen numberToInteger und integerToNumber
mit den Typen
> numberToInteger :: Digit d => ValuePlaceSystem d -> Integer
> integerToNumber :: Digit d => d -> Integer -> ValuePlaceSystem d
um eine Zahl in einem Stellenwertsystem zur Basis d in einen Integer umzurechnen und
umgekehrt. Das erste Argument von integerToNumber dient zur Ermittlung der Basis.
1
2
Diese sind ja eine eine Ansammlung von Ziffern
Auch ein Typsynonym mittels type oder ein newtype wären hier möglich.
1
Aufgabe 2: Stackmaschine
Mikroprozessoren gehen allgemein nach dem Schema Befehl holen und abarbeiten vor. Generell
benötigt man dazu einen Arbeitsspeicher3 , in dem neben den Daten, auf denen operiert werden
soll, auch die Befehle, die abzuarbeiten sind, abgelegt werden4 .
Damit die Abarbeitung eines Programmes fortschreitet, wird ein Befehlszähler immer auf die
Adresse des als nächstes zu holenden Befehls gesetzt. Der Befehlszähler ist im allgemeinen ein
Register, d.h. eine im Prozessor eingebaute Speicherstelle. Je nachdem, wie viele solcher Register
ein Prozessor besitzt und wozu diese benutzt werden können, unterscheidet man grob5 in die
folgenden drei Klassen von externen6 Architekturen:
Registersatzmaschine: Besitzt einen relativ großen Satz (16 und mehr) gleichberechtigter
Universalregister, d.h. jeder Befehl kann mit jedem Register oder auch mehreren benutzt werden, aber eben auch nur mit Registern. Im Speicher stehende Werte müssen
über spezielle Load–Befehle in Register geladen bzw. über Store–Befehle in den Speicher
zurückgeschrieben werden.
Stackmaschine: Stellt dem Programmierer keine Register zur Verfügung, sondern bezieht alle
Befehle implizit auf einen Stack, einen speziellen Bereich des RAM.
Akkumulatormaschine: Kennt nur ein oder zwei Register, auf die sich alle Befehle beziehen7 ,
und darüberhinaus nur noch wenige weitere, die anderen speziellen Zwecken dienen wie
z.B. Adressierung.
Zusammen mit dem nächsten Aufgabenblatt soll ein Simulator für eine einfache Stackmaschine
entwickelt werden. Hier gehen wir erst einmal von dem in Tabelle 1 aufgeführten Befehlssatz
aus. Alle dort erwähnten Zahlen sollen vom Typ Integer sein. Achten Sie auf einen modularen
Entwurf, indem Sie schrittweise u.a. nach folgenden Punkten vorgehen:
a) Definieren Sie zuerst — jeweils in separaten Moduln — abstrakte Datentypen Stack e
für einen Stack von Elementen des Typs e und Memory a e, um das RAM zu modellieren.
Dabei soll e der Typ der Elemente sein und der Typ a die Adressen repräsentieren.
b) Spezifizieren Sie einen Typ State a für den Zustand der Maschine, wobei a den Typ für
die Adressen vorgibt. Verwenden Sie einen Record-Typen8 , damit der Zustand leicht um
weitere Komponenten erweitert werden kann.
Sie können den Aufbau der Stackmaschine einfach halten, indem Sie den Stack nicht als
ein Teil des RAM betrachten sondern separat verwalten. Auch die Instruktionen müssen
nicht im Speicher ablegt werden.
3
4
5
6
7
8
Random Access Memory
sogenannte Von–Neumann Architektur
Natürlich kommen in der Praxis durchweg Mischformen vor.
im Gegensatz zum internen Aufbau eines Prozessors
u.U. implizit
Im Haskell-Report werden diese Datatypes with Field Labels“ genannt.
”
2
Befehl
push
pop
dbl
load
Parameter
zahl
stor
addr
addr
add,
sub,
mul,
div
Bedeutung
die Konstante zahl wird auf den Stack gelegt
das oberste Stackelement wird gelöscht
das oberste Stackelement wird erneut auf den Stack gelegt
eine Zahl wird von der Speicheradresse addr gelesen und auf den Stack
gelegt
das oberste Element wird vom Stack genommen und im Speicher unter addr
abgelegt
Die beiden obersten Elemente werden vom Stack genommen und das Ergebnis der entsprechenden Rechenoperation auf dem Stack abgelegt
Tabelle 1: Befehlssatz der Stackmaschine
c) Implementieren Sie die Instruktionen aus Tabelle 1 als Funktionen auf dem Zustand der
Maschine und zwar derart, daß pop, dbl, add, sub, mul und div genauso wie die partiellen Anwendungen von push, load und stor auf jeweils ein Argument sämtlich vom Typ
Instruction a sind, welcher polymorph über dem Adresstyp a wie folgt definiert sei:
> type Instruction a = State a -> State a
Hinweis: Identifizieren Sie hierbei wiederkehrende Operationen und abstrahieren Sie mittels Funktionen höherer Ordnung.
d) Programmieren Sie eine Funktion run :: State a -> State a, die die Maschine auf dem
angegebenen Startzustand laufen läßt, bis keine Befehle mehr abzuarbeiten sind.
Testen Sie Ihre Implementierung — startend jeweils mit leerem Stack und leerem Heap —
insbesondere mittels der folgenden drei Instruktionsfolgen:
push
push
push
add
dbl
stor
mul
dbl
stor
4
3
2
"3und2"
"undMal4"
push
stor
push
stor
push
stor
load
load
dbl
mul
add
3
17
0
13
0
11
11
0
11
push 17
dbl
stor 1
load 17
add
Herunterladen