219
Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität Duisburg-Essen, Abteilung Duisburg Fakultät 5

Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Informatik A:

Rechnerstrukturen undProgrammierparadigmen

Prof. Dr. Norbert Fuhr

SS 2003

Universität Duisburg-Essen,

Abteilung Duisburg

Fakultät 5

Page 2: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Autor des Skriptes:

Prof. Dr. Wolfram Luther.

Zuletzt editiert von:

André Schaefer

mailto:[email protected]

Page 3: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Inhaltsverzeichnis

1 Einführung in die Grundlagen der Informatik 61.1 Ziele der Informatik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.2 Teilgebiete der Informatik . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.3 Rechnerentwicklung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

1.3.1 Problemorientierte Programmiersprachen . . . . . . . . . . . . . . 121.4 Grundlagen der Logik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.4.1 Geschichtlicher Überblick . . . . . . . . . . . . . . . . . . . . . . . 141.4.2 Aussagenlogik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151.4.3 Prädikatenlogik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.4.4 Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2 Schaltfunktionen 262.1 Zahldarstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

2.1.1 Komplementdarstellungen . . . . . . . . . . . . . . . . . . . . . . . 272.1.2 Darstellung von Gleitpunktzahlen nach IEEE 754/854 . . . . . . . 29

2.2 Boolesche Algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302.3 Schaltfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322.4 Schaltnetze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382.5 Ringsummennormalform . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

3 Schaltnetze und ihre Optimierung 453.1 Beispiele für Schaltnetze . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453.2 Vereinfachung von Schaltnetzen . . . . . . . . . . . . . . . . . . . . . . . . 48

3.2.1 Das Verfahren von Karnaugh . . . . . . . . . . . . . . . . . . . . . 483.2.2 Das Verfahren von Quine und McCluskey . . . . . . . . . . . . . . 53

3.3 Fehlerdiagnose von Schaltnetzen . . . . . . . . . . . . . . . . . . . . . . . 583.4 Hazards in Schaltnetzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

4 Schaltwerke 684.1 Flip–Flops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684.2 Sequentielle Schaltungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724.3 Lineare Schaltkreise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

3

Page 4: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

5 Programmierbare Logische Arrays (PLA) 805.1 Aufbau und Arbeitsweise eines PLA . . . . . . . . . . . . . . . . . . . . . 805.2 Programmierung eines PLA, universelles PLA . . . . . . . . . . . . . . . . 845.3 Anwendungen von PLA’s . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

6 Bemerkungen zum Entwurf von VLSI Schaltungen 886.1 Komplexität von VLSI Schaltungen . . . . . . . . . . . . . . . . . . . . . . 896.2 Layout von VLSI–Schaltungen — H–Bäume . . . . . . . . . . . . . . . . . 95

7 Grundlegende Additions– und Multiplikationsalgorithmen und Schaltungen 977.1 Addition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

7.1.1 Von–Neumann–Addierwerk . . . . . . . . . . . . . . . . . . . . . . 987.1.2 Carry–Look–ahead Addition . . . . . . . . . . . . . . . . . . . . . . 100

7.2 Multiplikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

8 Organisationsplan eines von–Neumann–Rechners 1048.1 Struktur eines Rechners . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048.2 Arbeitsweise einer CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1078.3 Der Speicher eines von–Neumann–Rechners . . . . . . . . . . . . . . . . . 1098.4 Busse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1108.5 I/O Einheit und Steuerung durch Interrupts . . . . . . . . . . . . . . . . . 1118.6 Erweiterungen des von–Neumann–Konzepts . . . . . . . . . . . . . . . . . 113

9 Eine kleine Programmiersprache 1159.1 Syntaktische Beschreibungsmittel . . . . . . . . . . . . . . . . . . . . . . . 1159.2 Die Syntax von Mini–Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . 1189.3 Semantik von Mini–Pascal∗ . . . . . . . . . . . . . . . . . . . . . . . . . . 1239.4 Übersetzung des Mini–Pascalprogramms in Maschinencode . . . . . . . . . 129

10 Von Mini–Pascal zu Pascal 14010.1 Datentypen von Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14010.2 Kontrollstrukturen, Prozeduren und Funktionen . . . . . . . . . . . . . . . 14810.3 Unit–Konzepte und objektorientierte Programmierung . . . . . . . . . . . 151

10.3.1 Allgemeiner Programmaufbau . . . . . . . . . . . . . . . . . . . . 15110.3.2 Objektorientierte Programmierung . . . . . . . . . . . . . . . . . . 15210.3.3 Implementierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

10.4 C versus Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

11 Logiksprachen und Prolog 16811.1 Was ist logische Programmierung? . . . . . . . . . . . . . . . . . . . . . . 16811.2 Von Logik zu Prolog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16911.3 Syntax und Semantik von Prolog–Programmen . . . . . . . . . . . . . . . 17311.4 Rekursive Regeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17611.5 Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17911.6 Fail und Cut–Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

4

Page 5: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

11.7 Standard–Prädikate (Built–in Predicates) . . . . . . . . . . . . . . . . . . 19111.8 Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

12 Funktionale Programmierung 19312.1 Einführung in funktionale Programmiersprachen . . . . . . . . . . . . . . 19312.2 Operatoren, Bezeichner, Typen . . . . . . . . . . . . . . . . . . . . . . . . 19512.3 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198

12.3.1 Mustererkennung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20112.3.2 Einfache rekursive Funktionen . . . . . . . . . . . . . . . . . . . . . 202

12.4 Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20512.4.1 Einfache Listenfunktionen . . . . . . . . . . . . . . . . . . . . . . . 20812.4.2 Rekursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210

12.5 Curryfunktionen und Funktionen höherer Ordnung . . . . . . . . . . . . . 21212.6 Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217

5

Page 6: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

1 Einführung in die Grundlagen derInformatik

1.1 Ziele der Informatik

Informatik

• ist die Wissenschaft von der systematischen Verarbeitung von Informationen. Siebefasst sich mit dem Beschaffen, Erfassen, Strukturieren, Bearbeiten, Verteilen undSpeichern von Daten. Dazu entwickelt sie konstruktiv-formale und operationaleKalküle auf der Basis von künstlichen Sprachen.

• ist Grundlage zur Konzeption, Erstellung, Verifikation, Bewertung und Anwendunggroßer Informations– und Kommunikationssysteme, ihrer Hardware und Software.

• schlägt eine Brücke zwischen technisch-naturwissenschaftlichem und geisteswissen-schaftlichem Bereich und spielt in Wirtschafts–, Rechts– und Gesellschaftswissen-schaften wie auch in Kunst, moderner Philosophie und Linguistik eine herausra-gende Rolle.

Damit sind informatische Grundlagen für Schüler und Studenten fast aller Fachrichtun-gen unerlässlich. Informatikkompetenz erzeugt Eignung für neue kreative Arbeitsplätzein einem Hochtechnologieland. Im Mittelpunkt des Informatikunterrichts steht eine Ver-mittlung von Sach–, Handlungs– und Beurteilungskompetenz im Umgang mit Informa-tiksystemen auf wissenschaftlicher Grundlage, Modellierung von Problemen, Prozessenund Abläufen, ihre angemessene sprachliche Beschreibung, Abstraktion und Strukturie-rung, ihre algorithmische Durchdringung und eine Lösung und Steuerung mit adäquatemWerkzeug.

Eine Anwendung von Informations– und Kommunikationssystemen allein ist jedoch nichthinreichend, um Basis für den Bildungswert der Informatik zu sein.

Nun wollen wir zunächst einige der verwendeten Begriffe erklären: Bei der Kommuni-kation von Rechnern werden Daten oder Nachrichten ausgetauscht. Diese erhalten nacheiner Interpretation den Charakter einer Information. Daten haben eine Form und einenTräger. Als Träger kommen magnetisierte Schichten, Bildschirme, elektromagnetischeFelder, Schallfelder, Drähte etc. in Frage; die Form der Daten wird in der Regel in einerSprache definiert. Sprachen bestehen aus Sätzen, diese aus Wörtern und letztere aus Zei-chen aus einem Alphabet. Typische Beispiele sind die Umgangssprache, Ausdrücke aus

6

Page 7: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Operatoren und ganzen Dezimalzahlen oder Programmiersprachen, wie man sie aus derSchule her kennt. Wir werden uns im Laufe der Vorlesung mit verschiedenen Sprachklas-sen auseinandersetzen. Sprachen genügen einer genau festgelegten textitSyntax. Nachderen Regeln kann entschieden werden, ob ein Satz zur Sprache gehört oder nicht. DieInterpretation der Wörter und Sätze einer Sprache ist im Allgemeinen durch Regeln aus-drückbar, die man Semantik der Sprache nennt. Den Übergang von einer Sprache zueiner anderen bei gleichbleibender Interpretation nennt man Übersetzung. Ein Code istdie Zuordnung zwischen Zeichen und Zeichengruppen von zwei verschiedenen oder auchgleichen Alphabeten. Geschieht diese Zuordnung zeichenweise, so spricht man auch vonChiffrierung.

Zum Problemlösen werden oft Algorithmen entwickelt und eingesetzt. Das Wort Algo-rithmus kommt vom Namen des persischen Mathematikers Mohammed Ibn Musa AbuDjafar Al Khowarizmi (ca. 783 – 850), der ein weit verbreitetes Lehrbuch für die „Be-rechnung durch Vergleich und Reduktion”, das bereits Lösungen von Gleichungen mitmehreren Unbekannten behandelt, geschrieben hat. In der lateinischen Übersetzung die-ses Buchs, das durch die Kreuzfahrer nach Europa kam, begannen die Abschnitte jeweilsmit ”Dixit algorismi”, woraus sich die Bezeichnung Algorismus für eine Rechenvorschriftableitete.

Ein Algorithmus ist eine endliche Anweisungsfolge in einer Befehlssprache, die bei In-terpretation eine Klasse von Verarbeitungsprozessen genau und vollständig beschreibt.Er enthält Operationen auf Variablen aus wohldefinierten Wertebereichen (Datentypen).Gewisse Operationen sind standardisiert und haben eine Standardinterpretation, wiezum Beispiel die Addition von Zahlen. Man kann für die Beschreibung von Algorith-men verschiedene Abstraktionsebenen wählen, eine umgangssprachliche, eine eher for-male im Wortschatz eingeschränkte Sprache (Pseudocode), die typische Befehlselementewie Zuweisungen, wiederholte oder bedingte Anweisungen enthält oder eine ”formaleProgrammiersprache”, deren Syntax in Regeln genau beschrieben und die von einem In-terpreter am Rechner ausgeführt werden kann.

Diese Sprachen und ihre Paradigmen werden wir im Laufe der Vorlesung noch genauerbetrachten. Beispiele werden jedoch schon jetzt aufgeführt. Vor der Entwicklung eines Al-gorithmus ist zunächst das Problem durch eine funktionale Spezifikation zu beschreiben.Dabei geht es um die Angabe der gültigen Eingabe– und möglichen Ausgabegrößen so-wie den funktionalen Zusammenhang zwischen beiden. Es werden die problemspezifischenObjekte beschrieben, Konstanten, Datentypen, Funktionen, und dabei insbesondere Prä-dikate, das sind Funktionen, deren Ausgabewerte ”wahr” und ”falsch” sind. Die Eingabengenügen gewissen Vorbedingungen; bei der Ausgabe werden gewisse Leistungen des Al-gorithmus zugesichert. Im Allgemeinen ist der Ablauf des Algorithmus in jedem Punktfest vorgeschrieben (Determiniertheit), jeder Schritt ist ausführbar (Effektivität) unddas Verfahren endet nach endlich vielen Schritten (Terminiertheit). Es gibt allerdingsProbleme, die nicht algorithmisierbar sind. Selbst wenn ein Problem algorithmisierbarist, dann kann doch die Ausführung des Algorithmus so viele Schritte enthalten, dassdie Berechnung praktisch nicht ausführbar ist (Nichteffizienz). Wichtig ist auch, dassder Algorithmus auf einer (abstrakten) Maschine ausgeführt werden kann. Diese nimmt

7

Page 8: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

alle oben genannten Schritte vor. Maschinen sind in verschiedenen Ausformungen inAutomatenmodellen definiert worden. Die einfachste ist der endliche Automat, der beider Abarbeitung einer Folge von Eingabezeichen eines Wortes aus einem Startzustandüber Zwischenzustände in einen Endzustand übergeht. Wörter aus mächtigeren Sprachenkönnen mit der Turingmaschine abgearbeitet werden, deren Schreiblesekopf über einemunendlichenArbeitsband frei beweglich ist. Diese Maschine ist auch Grundlage für mo-derne Computer, und man kann auf ihr alle bekannten zahlentheoretischen Algorithmen,wie den euklidischen, den GGT– oder einen Faktorisierungsalgorithmus ausführen.

Bis aber der Algorithmus eine Form hat, in der er auf dieser Maschine ausgeführt werdenkann, sind viele Transformationen in Form von Zerlegungen und Übersetzungen erforder-lich. Aus diesen Vorbetrachtungen ergeben sich die für das Grundstudium der Informatikwichtigen Inhalte:

• Programmieren, besonders im Sinne kreativen, explorativen Problemlösens undGestaltens, sollte als wichtige Basiserfahrung aktiv vermittelt werden. Dabei sindgrundlegende Prinzipien des Programmierens (Spezifizieren, Parametrisieren, Mo-dularisieren, Kontrollieren) darzustellen und am konkreten Beispiel zu unterrichten.

• Der Computer als Werkzeug zur Informationserzeugung, –verwaltung, ihrer Ver-arbeitung, Darstellung, Übermittlung und Speicherung sollte einen gebührendenPlatz einnehmen.

• Das Kennenlernen der theoretischen Grundlagen der klassischen Anwendungen(Programmiersysteme, Textverarbeitung, Graphikprogramm, Tabellenkalkulation,Datenbanken, Computeralgebrasysteme, Kommunikationsprogramme) und ihr Ein-satz im jeweiligen Kontext geistiger Tätigkeiten ist wichtig. Grundlagen des Rech-neraufbaus und der Konzeption von Rechner– und Kommunikationsnetzen, ihrerHard– und Software sind zu vermitteln.

Grundlegende Prinzipien der Programmierung und den Aufbau eines Rechners wollenwir in dieser Vorlesung vertieft behandeln.

1.2 Teilgebiete der Informatik

Die Informatik teilt sich in die folgenden vier Teilgebiete auf:

• Theoretische InformatikMathematische Grundlagen der Informatik (Logik, Algebra, Graphentheorie, Kom-binatorik) – Automatentheorie – Künstliche Sprachen – Theorie der Berechenbar-keit – Maschinelles Beweisen

• Praktische InformatikCompilerbau – Programmiersprachen – Datenbanktheorie – Softwareengineering –Geometrische Datenverarbeitung – Künstliche Intelligenz – Verteilte Systeme

8

Page 9: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 1.1: Pascalsche Addiermaschine

• Technische InformatikBetriebssysteme – Rechnerarchitektur – Hardware verteilter Systeme – Mikropro-zessoren – Kommunikationssysteme – Rechnernetze

• Angewandte InformatikBildverarbeitung – Neuroinformatik – Bioinformatik – Wirtschaftsinformatik –Computerlinguistik – CAD, CAE, etc.

1.3 Rechnerentwicklung

Im Folgenden geben wir einen kurzen Überblick über die Entwicklung der Rechner: Zwi-schen 1623 und 1818 entstanden die ersten mechanischen Rechenmaschinen, z.B. dieSchickardsche Rechenuhr, die Additionsmaschine von Blaise Pascal und eine Staffelwal-zenmaschine von Leibniz, die das Zweiersystem benutzt. Daher kann man mit Fug undRecht behaupten, dass die Entwicklung von Rechnern zunächst dadurch motiviert war,die Ausführung von Rechnungen in den vier Grundrechenarten +, -, *, / zu unterstützen.Aber mit der Entwicklung eines automatischen Webstuhls von Jacquard um 1800, dermittels Lochkarten gesteuert wurde, kam bereits ein anderer Aspekt neben dem Rech-nen hinzu, nämlich der des Automaten, bei dem eine gewisse Abfolge von Zuständengesteuert durchlaufen wird.

1833 plante Babbage den ersten programmgesteuerten Rechner, der eine Ein– und Aus-gabeeinheit, eine arithmetisch– logische Einheit (ALU) zur Ausführung von Rechnungenund logischen Operationen, wie Vergleich von Zahlen, und eine Befehlseinheit zur Steue-rung der Maschine vorsieht. Die zur Rechnung und Steuerung nötigen Daten werden voneinem Datenträger in einen Speicher eingelesen und im Takt bearbeitet. Das Ergebnis

9

Page 10: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 1.2: Babaggerechner

wird sodann an einer Ausgabeeinheit angezeigt. Moderne Eingabegeräte sind Tastatur,Maus, Graphiktablett, Scanner, Ausgabegeräte dagegen Monitor, Drucker oder eine Da-tei.

Gebaut wurden die ersten programmgesteuerten Rechner von Zuse (1934/41) als loch-streifengesteuerte Maschine mit 2000 Relais und 64 Speichern, von Aiken 1944 in Zu-sammenarbeit mit IBM als Großrechenanlage MARK II mit 15 m Front und 2.5 m Höhe,bestehend aus 80 km Leitungsdraht, 700 000 Einzelteilen mit 3.5 t Gewicht. Hier kommtbereits ein anderer Aspekt ins Spiel, nämlich der Einsatz eines Rechners zu anderenAufgaben außerhalb des Rechnens, beispielsweise zur Übernahme von Büroarbeiten undLösung von Anwendungsproblemen, wie es schon Hollerith 1886 mit einer Lochkarten-maschine zum Einsatz bei einer Volkszählung vorgegeben hatte. In der Folgezeit setzteine stürmische Entwicklung über die Einführung von Elektronenröhren in der ENIAC,Transistoren, Mikroschaltelementen, hoch– und höchstintegrierten Schaltkreisen ein, diein der Einführung von Mikrochips gipfelt. Während der Zuserechner die Multiplikationzweier 10stelligen Zahlen in ca. 3 sec ausführt, ist die ENIAC schon tausendmal schnel-ler. Ein moderner Prozessor ist mit über 100 MHz getaktet, also mit über 100 MillionenZyklen pro Sekunde. Viele Befehle werden in einem Zyklus ausgeführt. Eine Additionbenötigt 3 Takte, eine Multiplikation zweier 32 Bit Zahlen 5, eine Division 18 bis 38Takte und das Wurzelziehen 29 bis 69 Takte.

Beim INTEL P6 sind auf einer Fläche von wenigen Hundert Quadratmillimetern 20 Mil-lionen und mehr Transistoren auf vier Ebenen platziert, die zwischen 5 und 20 WattLeistung aufnehmen, in fünf Pipelines Befehle parallel abarbeiten können und in der

10

Page 11: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 1.3: Prozessor P6

Herstellung unter 500 DM kosten. Diese Chips kommen neben Spezialchips zur Orga-nisation des Datentransfers oder von Ein– und Ausgabe insbesondere auf graphischenTerminals in modernen Personalcomputern zum Einsatz.

Parallel zur technologischen Entwicklung verläuft eine Entwicklung der Software, derenBedeutung immer mehr zunimmt. Die nachfolgend genannten Generationen verlaufennicht aufbauend aufeinander, sondern überlappen sich.

1. Generation: Programmierung in Maschinencode

2. Generation: Assemblersprachen und Programmiersprachen wie Fortran, Algol, Co-bol

3. Generation: Strukturierte Programmierung mit Pascal, C, Ada, Modula

4. Generation: Verteilte Systeme, Programmierumgebungen, Objektorientierung

5. Generation: Logiksprachen, funktionale Programmiersprachen, Expertensysteme,Datenbanksysteme, Graphiksprachen

6. Generation: Netzsprachen, Generatoren, graphische Entwicklungssysteme

Diese summarische Darstellung wollen wir etwas weiter vertiefen: Jeder Prozessor besitzteine durch seine Bauart festgelegte Programmiersprache, die er lesen und unmittelbarin Steuersignale umsetzen kann. Man bezeichnet sie als Maschinensprache. Programmein Maschinensprache werden meist in einer leichter lesbaren mnemotechnischen Notation

11

Page 12: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

beschrieben, der Assemblersprache. Befehle, wie das Sprachkürzel LD heißen laden, ADDaddieren, ST speichern. Leider ist, obwohl schnell ausführbare Programme entstehen, dieProgrammierung in Assembler aufwändig, unübersichtlich und extrem maschinenabhän-gig. Assemblerprogramme werden daher heute nur noch für spezielle systemspezifischeProgramme eingesetzt, oder wenn es auf höchste Effizienz ankommt. Für alle anderenAnwendungen wird das Programmieren durch den Einsatz problemorientierter Sprachenwesentlich erleichtert.

1.3.1 Problemorientierte Programmiersprachen

Gegenüber den Assemblersprachen sind die problemorientierten Programmiersprachennicht an einen bestimmten Rechnertyp gebunden. Problemorientierte Sprachen verwen-den in weitem Maße gebräuchliche mathematische oder sonstige anwendungsorientierteSchreibweisen und erlauben eine leicht verständliche, dem Problem angepasste Formulie-rung von Algorithmen. Sie lassen sich deshalb besonders leicht erlernen. Programme ineiner problemorientierten Sprache können ohne Rücksicht auf einen speziellen Rechnertypformuliert werden, sie lassen sich (zumindest im Prinzip) leicht auf andere Rechner über-tragen. Zur Programmierung mathematischer, naturwissenschaftlicher und technischerProbleme haben u.ä. folgende problemorientierte Sprachen zumindest zeitweise eine ge-wisse Verbreitung gefunden:

AdaAda wurde im Rahmen eines Wettbewerbs des amerikanischen Verteidigungsministeri-ums in den Jahren 1977 – 1980 entwickelt. Es soll durch seine universelle Einsetzbarkeitsowohl im kommerziellen als auch im technisch wissenschaftlichen Bereich dazu dienen,Wartungskosten für Software möglichst gering zu halten. Dadurch wurde Ada extrem um-fangreich: neben den Konzepten von Pascal umfasst es u.Ä. die Definition von Modulen,Paketen (zusammenhängende Daten und Operationen), generischen Unterprogrammen(mit unterschiedlichen Typen) und Prozessen (für die Parallelverarbeitung).

ALGOL 60 (ALGOrithmic Language)Die Sprache wurde in Europa gemeinsam von vielen wissenschaftlichen Institutionen aussechs Ländern entwickelt und 1960 publiziert. Sie hat heute keine praktische Bedeutungmehr. Viele Konzepte der strukturierten Programmierung wurden aber von Algol inmoderne Programmiersprachen übernommen.

BASIC (Beginners All–purpose Symbolic Instruction Code)Ein vereinfachtes, FORTRAN–ähnliches Programmiersystem, besonders auf Kleinrech-nern verbreitet. Die Entwicklung ging 1960 von J. Kemmeny und Th. Kurtz mit eineminterpretierenden System aus, bei dem Linie für Linie des Programmes ausgeführt wurde,und hat heute zu Pascal–ähnlichen Programmiersystemen geführt, bei dem ein ausführ-bares Maschinenprogramm mittels eines Compilers erstellt wird.

COBOL (COmmon Business Orientated Language)Die Sprache wurde Ende der 50er Jahre entwickelt und ist in der kaufmännischen Da-tenverarbeitung bis heute weit verbreitet.

12

Page 13: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

C, C++C wurde Ende der 70er Jahre gemeinsam mit dem Betriebssystem UNIX für Minicompu-ter entwickelt und hat sich seither auf Workstations, Personal–Computer und Großrech-ner ausgebreitet. Es stellt in mancher Hinsicht einen Kompromiss zwischen strukturierterHochsprache und effizienter Maschinensprache dar. C–Programme neigen daher oft da-zu, schwer lesbar zu sein. Da C inzwischen sehr weit verbreitet und standardisiert ist,eignet es sich gut zur Übertragung von Programmen auf unterschiedliche Rechner. Alsobjektorientierte Erweiterung von C setzt sich C++ seit 1986 immer weiter durch.

FORTRAN (FORmula TRANslation)Fortran wurde 1954 als erste problemorientierte Programmiersprache von J. W. Backusentworfen, bei IBM implementiert und von internationalen Gremien in mehreren Ver-sionen weiterentwickelt. Der aktuelle Sprachstandard Fortran 90 hat viele strukturierteKonzepte von Pascal übernommen.

JavaJava ist der Sprache C++ ähnlich und objektorientiert. Es wurde Anfang der neunzigerJahre von Sun entwickelt und beschreitet einen Mittelweg zwischen interpretierendenund compilierenden Sprachen. Ein Java–Compiler übersetzt ein in Java geschriebenesProgramm in Code für eine virtuelle Java–Maschine, die wiederum auf allen Rechner-plattformen emuliert werden kann. Zusätzlich unterstützt es Sicherheitskonzepte für dasInternet, kann in Internet–Seiten einbezogen werden und ist somit Netzwerk–geeignet.

PascalPascal wurde von Kathleen Jensen und Niklaus Wirth (ETH Zürich) Anfang der 70erJahre auf der Basis von Algol und ähnlichen Sprachen (z.B. Simula) entwickelt. Es um-fasst ein erweitertes Typ–, Ausdrucks– und Anweisungskonzept, wurde aber ansonstenbewusst einfach gehalten. Pascal ist international standardisiert. Später werden die Dia-lekte Pascal–XSC (portabel, Erweiterungen für das wissenschaftliche Rechnen) und Tur-bo Pascal (Erweiterungen u.ä. für Grafik, systemnahe Programmierung, objektorientierteKonzepte) besprochen. Die Sprache ist nach dem französischen Mathematiker, Philosophund Theologen Blaise Pascal benannt (1623 – 1662), der u.ä.\,eine der ersten mechani-schen Rechenmaschinen konstruierte. Er leistete Beiträge zu zahlreichen mathematischenGebieten (Geometrie der Kegelschnitte, Wahrscheinlichkeitsrechnung, Kombinatorik, Bi-nomialkoeffizienten /Pascal’sches Dreieck, Prinzip der vollständigen Induktion, Ansätzezur Infinitesimalrechnung).

Modula-2Modula-2 wurde 1978 als Weiterentwicklung der Sprache Pascal von Niklaus Wirth ent-worfen. Einige Sprachelemente von Pascal wurden überarbeitet und dabei zum Teil sys-tematischer, zum Teil komplizierter. Ein Modulkonzept erlaubt die Entwicklung großerProgrammpakete. Standardmodule ermöglichen z.B. systemnahe Programme und dieProgrammierung von Prozessen.

OberonOberon wurde von N. Wirth und M. Reiser seit 1985 als Weiterentwicklung von Pascalund Modula 2 entworfen. Es stellt ein kleines Betriebssystem für PC und Workstation dar

13

Page 14: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

mit integrierter pascalartiger objektorientierter Programmiersprache und ist sehr spar-sam im Umgang von Rechnerressourcen. Wie man an den Jahreszahlen erkennen kann,dauert es oft Jahre oder Jahrzehnte, bis sich eine neue Programmiersprache etabliert hat.Bestehende Sprachen werden immer wieder überarbeitet und aktualisiert.

In der Ausbildung ist Pascal nach wie vor hervorragend geeignet, da es die Entwicklungleistungsfähiger Programme erlaubt, also genügend universell ist, zum strukturierten Pro-grammentwurf erzieht (im Gegensatz zu BASIC, C und Fortran) und trotzdem nicht allzukomplex ist (im Gegensatz etwa zu Fortran 90 oder Ada).

Neben den genannten gibt es eine Vielzahl weiterer Programmiersprachen, z.B. die beiExpertensystemen weitverbreitete Sprachen PROLOG und LISP, zur Prozesssteuerungdie Sprache PERL. In einer prädikativen Programmiersprache wird Programmieren alsBeweisen in einem System von Tatsachen und Schlussfolgerungen aufgefasst. Der Anwen-der gibt eine Menge von gültigen Prädikaten und Regeln zum Gewinnen neuer Faktenvor, und die Aufgabe des Rechners ist es, eine gestellte Frage mit ”Richtig” oder ”Falsch”zu beantworten. Funktionale Programmiersprachen verstehen Programme als Funktio-nen, die Mengen von Eingabewerten in Mengen von Ausgabewerten abbilden. Dabei isteine Grundmenge von wichtigen einfachen Funktionen vorgegeben.

Im zweiten Teil dieser Veranstaltung werden wir uns mit der imperativen Programmier-sprache Pascal, der prädikativen Sprache PROLOG und der funktionalen Sprache Mi-randa beschäftigen.

Eine Sprache muss durchaus nicht immer zum Erstellen von Programmen dienen, sondernkann wie dBASE auch zur Verwaltung einer Datenbank, TEX zur Gestaltung eines Textes,VHDL zum Beschreiben und Produzieren von elektronischen Schaltungen auf Chips oderHTML zur Gestaltung von WWW–Dokumenten dienen.

1.4 Grundlagen der Logik

1.4.1 Geschichtlicher Überblick

Der Wunsch, logisches Schließen zu automatisieren oder Apparate zu konstruieren, dieso ähnlich wie der Mensch denken können, geht auf R. Descartes und G.W. Leibniz imsiebzehnten Jahrhundert zurück. Descartes Entdeckung, dass die klassische EuklidischeGeometrie allein mit algebraischen Methoden entwickelt werden kann, war für die Ent-wicklung von Deduktionssystemen bedeutsam. Leibniz Idee war die Entwicklung eineruniversellen formalen Sprache, die lingua characteristica, in der jegliche Wahrheit for-muliert werden könne, und dazu eines Kalküls, dem sogenannten calculus rationator,für diese Sprache. Damit wollte er natürlichsprachige Beschreibungen auch über Sach-verhalte, die nicht aus der Zahlentheorie kommen, in eine formale Sprache und einendazugehörigen Kalkül übersetzbar machen. Seiner Vorstellung nach müsse ein solcherKalkül mechanisierbar sein und auf diese Weise dem menschlichen Denken alle langwei-lige Arbeit ersparbar sein.

14

Page 15: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Moderne Logik entstand 1879 unter Gottlob Frege, insbesondere mit der Schaffung sei-ner Begriffsschrift. Diese enthält die erste vollständige Entwicklung desjenigen Anteilsder mathematischen Logik, der heute als Prädikatenlogik erster Stufe bezeichnet wird.Durch den Gebrauch Boolescher Operatoren und die Verwendung von Quantoren, Rela-tionen und Funktionen wurde der ganze Aufbau der Logik das erste Mal beschrieben. AlsHerleitungsregel verwendete er den Modus Ponens. Zudem wurden Syntax und Semantikeiner formalen Sprache das erste Mal entwickelt. Skolem beschrieb in Arbeiten von 1920und 1928 eine systematische Vorgehensweise, wie man die Erfüllbarkeit einer beliebigenFormelmenge nachweisen kann. Ebenfalls im Jahre 1928 erschien das Buch Grundzüge dertheoretischen Logik von D. Hilbert und W. Ackermann. Hier wird auch das Entscheidbar-keitsproblem eingeführt, bei dem es um die Frage geht, ob es einen Algorithmus gibt, mitdem entschieden werden kann, ob eine beliebige vorgegebene prädikatenlogische Aussagewahr oder falsch ist. Herbrand bewies in seiner Doktorarbeit 1930 den Satz, dass für einenkorrekten mathematischen Satz dies mit endlich vielen Schritten nachgewiesen werdenkann. Gelingt dies nicht, so gibt es zwei Möglichkeiten, entweder kann die Inkorrektheitnach endlich vielen Schritten bewiesen werden, oder das Programm terminiert nicht, undman weiß es nicht. Später zeigten Alan Turing und Alonzo Church, dass es keine all-gemeine Entscheidungsprozedur dafür gibt, ob eine Aussage der Prädikatenlogik wahrist oder nicht. Turing gelang 1936 der Beweis durch Rückführung auf das Halteproblem.Um 1950 wurde die Entwicklung des Computers vorangetrieben, und es entstand daserste Programm zur Überprüfung von Aussagen. Um 1954 veröffentlichte Robinson Er-gebnisse zur Prüfung von Theoremen in Klauselform mit dem Resolutionsprinzip. In denfolgenden Jahren entwickelte er diese Ideen weiter und veröffentlichte 1965 die Arbeit Amachine oriented logic based on the resolution principle. Kowalski stellte dann schließlichmit seinem SLD–System einen Weg dar, der mittels Hornklauseln Wissen speichert unddeklarativ wie prozedural Verwendung findet. Schließlich entwickelte Alan Colmerauer1972 ein Logik–Programmiersystem PROLOG, das Grundlage für die Programmierungin Logik ist.

1.4.2 Aussagenlogik

Logik ist die Lehre von der Folgerichtigkeit des Denkens und Schließens. MathematischeLogik ist ein formales System, in dem gewissen Aussagen und Theoremen Wahrheits-werte zugeordnet werden können. Die einfachste Form der mathematischen Logik ist dieAussagenlogik.

Unter einer Aussage oder atomaren Formel, abgekürzt mit einem Großbuchstaben(A, B, . . .) versteht man einen Satz, der entweder wahr oder falsch ist, z.B. ”Heute istDienstag”. Man wird ihm mittels einer Interpretation einen Wahrheitswert zuordnen,wahr (w) oder falsch (f). Dieser Wert kann in einem Bit mit 1 bzw. 0 gespeichert wer-den. Die Menge der Wahrheitswerte wird mit B abgekürzt. Aussagen können miteinanderverknüpft werden, und das Ergebnis dieser ”Operation” ist wieder eine Aussage mit ei-nem wohldefinierten Wahrheitswert. Als Negation erklärt man eine einstellige Operation¬ ( ), die jeder Aussage das Negat mit dem entgegengesetzten Wahrheitswert zuordnet.

15

Page 16: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Atomare Formeln (Aussagenvariablen) oder deren Negate heißen Literale L ∈ A,¬A.

Nunmehr erklären wir wichtige zweistellige Operationen. Die Konjunktion mit dem Kon-jungator ∧ (und, and) ordnet zwei Aussagen ihr Konjugat zu. Dabei hängt die Bedeutungder Junktoren nicht von den beteiligten Aussagen, sondern nur von ihren Wahrheitswer-ten ab. Es ergibt sich die folgende Tabelle:

∧ w f

w w f

f f f

z.B. erhält man w ∧ w = w, was man am mittleren Kästchen ablesen kann.

Die Disjunktion mit dem Disjungator ∨ (oder, or) ordnet zwei Aussagen ihr Disjungatzu. Dabei ergibt sich die erste der folgenden Tabellen:

∨ w f

w w w

f w f

→ w f

w w f

f w w

←→ w f

w w f

f f w

⊕ w f

w f w

f w f

z.B. erhält man f ∨ f = f , was man am rechten unteren Kästchen der ersten Tabelleprüfen kann.

Neue Formeln F können über einen induktiven Prozess aus (atomaren) Formeln mit Hilfeder Operatoren ¬, ∨, ∧ gebildet werden.

Die Subjunktion mit dem Subjungator → (wenn – dann) ordnet zwei Aussagen ihr Sub-jungat zu. Auch hier haben wir die Tabelle notiert, aus der wir entnehmen, dass f → wbzw. f → f eine wahre Aussage ist, da aus einer falschen Aussage der Wahrheitswert derzweiten Aussage nicht geprüft werden kann.

Die Bijunktion mit dem Bijungator ←→ (genau dann – wenn) ordnet zwei Aussagen ihrBijungat, die Antivalenz mit dem Junktor ⊕ bzw. 6←→ (entweder – oder, xor) ordnetzwei Aussagen die Summe ihrer Wahrheitswerte modulo zwei zu.

Für die Junktoren gilt die folgenden Bindungshierarchie: ¬ bindet stärker als ∧, diesesstärker als ∨, ∨ stärker als →, → stärker als ←→.

Wir wollen nun einige im Laufe der Vorlesung vorkommende Begriffe kurz einführen:Eine Klausel ist eine Disjunktion von Literalen. Eine Hornklausel enthält höchstens einpositives Literal, eine Hornformel besteht nur aus Hornklauseln, die durch Konjunktionmiteinander verknüpft werden, wie zum Beispiel

(A ∨ ¬B ∨ ¬C) ∧ ¬C ∧A ∧ (¬A ∨ ¬C) .

Eine aussagenlogische Form liegt in konjunktiver Form (KF) vor, wenn sie die Konjunk-tion von mehreren Klauseln ist:

F =

n∧

i=1

ni∨

j=1

Li,j

.

16

Page 17: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Sie liegt in disjunktiver Form (DF) vor, wenn sie die Disjunktion mehrerer durch Kon-jungatoren verknüpften Literale ist:

F =

n∨

i=1

ni∧

j=1

Li,j

.

Beispiel 1.4.1

F1 = (A ∨B ∨ ¬C) ∧ (¬B ∨ C) KF

F2 = (A ∧B ∧ ¬C) ∨ (¬B ∧ C) DF.

Aussagenvariablen werden mit den Werten aus der Menge der Wahrheitswerte belegt. Siesind Platzhalter für Aussagen. Aus den Wahrheitswerten, den Aussagen, Aussagenvaria-blen und den Verknüpfungsoperatoren kann man zulässige aussagenlogische Ausdrückebilden. Auch auf diese können wieder Operationen angewandt werden.

Jeder Formel kann für eine Belegung ein Wahrheitswert zugeordnet werden. Eine Inter-pretation, unter der die Formel wahr ist, heißt Modell. Eine Formel heißt erfüllbar, wennes mindestens ein Modell gibt, ansonsten unerfüllbar. Eine Formel F heißt Tautologie,wenn sie für alle Belegungen wahr ist. Wir schreiben dann ² F . (Es ist allgemein wahr,wie z.B. A ∨ ¬A). Die Allgemeingültigkeit kann mit Hilfe einer Wahrheitstafel, durchAnwendung von syntaktischen Umformungen bis zum Wahrheitswert w oder durch Zu-rückführung auf eine konjunktive Form bewiesen werden, in der in jeder Klausel mitmindestens einer Aussagenvariablen auch deren Negat vorkommt.

Beispiel 1.4.2Zu zeigen ist ² ((P → Q) ∧ P )→ Q.

a)P Q P → Q (P → Q) ∧ P ((P → Q) ∧ P )→ Q

f f w f wf w w f ww f f f ww w w w w

b) Äquivalent sind die folgenden Formeln

((P → Q) ∧ P )→ Q¬ ((¬P ∨Q) ∧ P ) ∨Q((P ∧ ¬Q) ∨ ¬P ) ∨Q((P ∨ ¬P ) ∧ (¬Q ∨ ¬P )) ∨Q(w ∧ (¬P ∨ ¬Q)) ∨Q(¬P ∨ ¬Q) ∨Q¬P ∨ ww

17

Page 18: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

c) Überführung in konjunktive Form

((P → Q) ∧ P )→ Q¬ ((¬P ∨Q) ∧ P ) ∨Q((P ∧ ¬Q) ∨ ¬P ) ∨Q((P ∨ ¬P ) ∧ (¬Q ∨ ¬P )) ∨Q((P ∨ ¬P ∨Q) ∧ (¬Q ∨Q ∨ ¬P ))

Bei der Herleitung benutzen wir folgende Definitionen und Ergebnisse:

Zwei Formeln F und G heißen äquivalent ≡, wenn sie für alle passenden Belegungen dengleichen Wahrheitswert haben: ² G ←→ F . Für jede Formel F gibt es eine äquivalenteFormel in KF oder DF. Es gelten folgende Äquivalenzen:

Tabelle 1.4.3

(F ∧ F ) ≡ F, (F ∨ F ) ≡ F Idempotenz(F ∧G) ≡ (G ∧ F ) , (F ∨G) ≡ (G ∨ F ) Kommutativität

((F ∧G) ∧H) ≡ (F ∧ (G ∧H)) ,((F ∨G) ∨H) ≡ (F ∨ (G ∨H)) Assoziativität(F ∧ (F ∨G)) ≡ F, (F ∨ (F ∧G)) ≡ F Absorption(F ∧ (G ∨H)) ≡ ((F ∧G) ∨ (F ∧H)) ,(F ∨ (G ∧H)) ≡ ((F ∨G) ∧ (F ∨H)) Distributivität

¬¬F ≡ F Doppelnegation¬ (F ∨G) ≡ (¬F ∧ ¬G) ,¬ (F ∧G) ≡ (¬F ∨ ¬G) deMorganscheRegelnF → G ≡ ¬F ∨G bedingteEliminierungF ∧G ≡ F, F ∨G ≡ G, falls F unerfüllbarF ∧G ≡ G, F ∨G ≡ F, falls F Tautologie

Ist S eine Menge von Formeln oder Prämissen F1, . . . , Fk und F eine Formel, so ist Feine logische Konsequenz von S, in Zeichen S ² F , wenn jede Belegung von S, die einModell von S ist, auch ein Modell von F ist. Dies kann auch in der Form

²

k∧

j=1

Fj → F

geschrieben werden. Eine Belegung mit Wahrheitswerten von S heißt konsistent, wennalle Formeln unter dieser Belegung wahr sind.

Es gelten die folgenden Regeln für logische Konsequenz:

18

Page 19: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Tabelle 1.4.4

F ² F ∨GF ² G→ F

F ∧G ² FF ∧G ² G↔ F

(F → G) ∧ (G→ H) ² F → H TransitivitätG ∧ (G→ F ) ² F Modus Ponens (Schlussregel)¬F ∧ (G→ F ) ² ¬G Modus Tollens

Eine Theorie definiert zunächst eine Menge von wahren logischen Aussagen und Fakten,die Axiome. Dabei spielen die Begriffe Korrektheit, Vollständigkeit und Entscheidbarkeiteine Rolle. Sie besteht aus einer Sprache, mit deren Hilfe gewisse Sätze ausgedrücktwerden können. Axiome stellen dabei die Grundsätze der Theorie dar. Aus der Mengeder Axiome können neue, kompliziertere Sätze als logische Konsequenz abgeleitet werden.Dies geschieht über die oben angegebenen Inferenzregeln, wie den Modus Ponens undden Modus Tollens oder auch die Transitivitätsregel.

Beispiel 1.4.5 (Transitivität)

A r =( a ist eine gerade Zahl und b ist eine gerade Zahl)B (a+ b ist eine gerade Zahl)B1 (a = 2n und b = 2m, n, m ganze Zahlen)B2 (a+ b = 2k, k ganze Zahl)

A a und b sind gerade ZahlenA→ B1 Dann gibt es Zahlen n, m mit a = 2n und b = 2mB1 → B2 Aus a = 2n und b = 2m folgt a+ b = 2(n+m) = 2kB2 → B Aus a+ b = 2k folgt a+ b ist eine gerade ZahlB Mit der Transitivität gilt B

Für eine Theorie sollte ein korrektes (semantisch widerspruchsfreies) und vollständigesAxiomensystem zur Verfügung stehen. Ein Axiomensystem AS ist korrekt, wenn jede For-mel F , die aus einer Theorie T mittels der Ableitungsregeln abgeleitet wird (T `AS F ),eine logische Konsequenz aus T ist (T ² F ). Ein Axiomensystem AS ist vollständig, wennfür jede Theorie T jede Formel, die aus der Theorie logisch folgt, auch ableitbar ist (dasist die Umkehrung).Ist ein derartiges Axiomensystem vorhanden, so heißt es Basis der Theorie. Ein Axio-mensystem darf auch nicht inkonsistent sein, d.h. es darf nicht gleichzeitig F und ¬Fherleitbar sein. Ferner sollten die Axiome voneinander unabhängig sein, also nicht einesaus den anderen folgen. Eine Theorie heißt unentscheidbar, wenn es eine Formel F gibt,für die gilt, dass weder die Formel selbst noch ihre Negation aus der Theorie ableitbarist. Der Begriff der Entscheidbarkeit kann auf Axiomensysteme als Basis einer Theorieübertragen werden. Ein Axiomensystem ist entscheidbar, wenn sich immer prüfen lässt,

19

Page 20: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

ob eine Theorie in AS konsistent ist oder nicht, wenn sich also von jedem Satz herleitenlässt, ob er allgemeingültig ist oder nicht. Aussagenlogik ist entscheidbar und be-sitzt widerspruchsfreie, vollständige und unabhängige Axiomensysteme. EinAxiomensystem AS ist halbentscheidbar, wenn es immer möglich ist, die Inkonsistenz zuprüfen.

Aus den logischen Axiomen der Aussagenlogik (Hilbert)AS1: A ∨A→ A,AS2: A→ (A ∨B),AS3: (A ∨B)→ (B ∨A),AS4: (A→ B)→ ((C ∨A)→ (C ∨B)),der Definition A→ B ≡ ¬A ∨B, der Schlussregel (Modus Ponens) und der Ersetzungs-regel

Ergibt sich ein Ausdruck B aus einem abgeleiteten Ausdruck oder Axiom A,indem man in A eine Variable an jeder Stelle ihres Auftretens durch einenAusdruck ersetzt, so kann man von A zu B übergehen

können die Äquivalenzregeln aus Tabelle 1.4.3 hergeleitet werden.

Beispiel 1.4.6Wir leiten(F ∨ F ) ≡ F bzw. ² F ∨F ←→ F mit Hilfe der oben angegebenen Mittel her.Dies ist äquivalent zu

² ((F ∨ F → F ) ∧ (F → F ∨ F )) mit A ∧B := ¬ (¬A ∨ ¬B) .

Wir zeigen somit die Allgemeingültigkeit von(F ∨ F → F ) und von (F → F ∨ F ):

(F ∨ F ) → F AS1 .F → (F ∨G) AS2 ,F → (F ∨ F ) Ersetzungsregel (Variable G durch Formel F ) .

Bemerkung:Im vorigen Beispiel haben wir die Junktoren ∧ und ←→ mit Hilfe der Junktoren ¬ und∨ definiert.

Die Grundresolution ist eine eingeschränkte Form der Methode von Robinson, mit derenHilfe eine vorgegebene endliche Menge von Klauseln auf ihre Unerfüllbarkeit hin getestetwerden kann.

Durch geeignete Operationen zum Vereinfachen von veroderten Formeln lassen sich dieDarstellungen verkürzen: Kommt in einer Klausel eine Variable P und in einer anderen

20

Page 21: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

die Negation ¬P vor, so lassen sich diese Klauseln unter Weglassung dieser Variablen zueiner Klausel zusammenfassen:

P ∨A1 ∨A2 ∨ . . . ∨An

¬P ∨B1 ∨B2 ∨ . . . ∨Bm

A1 ∨A2 ∨ . . . ∨An ∨B1 ∨B2 ∨ . . . ∨Bm Resolution

Dann lässt sich das Verfahren folgendermaßen beschreiben:Es sei die Aussage A zu beweisen.Setze das negierte Theorem ¬A zu den Formeln der Theorie, wandele sie in Klauselformum (KF) und versuche, durch Resolution die leere Klausel herzuleiten. In diesem Fall istdas Theorem A zur Theorie gehörig.

Beispiel 1.4.7T = A ∨B, A→ ¬B, ¬A. Leite B her. Wir formen in KF um:

T = A ∨B, ¬A ∨ ¬B, ¬A ,

oder in der Schreibweise als Klauselmenge

T = (A, B) , (¬A, ¬B) , ¬A ,

und untersuchen die Klauselmenge

(A, B) , (¬A, ¬B) , ¬A, ¬B

auf Unerfüllbarkeit:B ist ein Resolvent von (A,B) und ¬A. Der Resolvent von B und ¬B ist aber die leereMenge, und damit enthält die Klauselmenge die leere Menge und ist unerfüllbar. Somitist B bewiesen.

1.4.3 Prädikatenlogik

Die gebräuchlichste Form der mathematischen Logik ist die Prädikatenlogik als Erwei-terung der Aussagenlogik. Sie dient zur Formulierung von Axiomen, zum Beweis vonEigenschaften von Sätzen und Programmen. Sie bedient sich einer Menge von Objekten,Relationen und Funktionen. Wie in der Aussagenlogik werden Formeln (Symbolisierungeiner sprachlichen Aussage, syntaktisches Gebilde aus Symbolen) mit Hilfe von logischenVerbindungen aus Atomen (elementaren logischen Aussagen) und anderen Formeln ge-bildet. Sie sind nach präzisen Regeln einer Grammatik aufgebaut. Als Atome (logisch un-zerlegbarer Ausdruck, Zeichenfolgen aus Prädikaten und Operatoren, gebildet aus demAlphabet als Menge der zugelassenen Symbole, Konstanten a, b, c, Variablen x, y, z,Prädikate P , Q, R, Funktions– und Hilfszeichen f , g, h, →, . . .) sind jedoch nicht nurLiterale (Aussagenvariablen und deren Negation) sondern auch Prädikate (Eigenschaf-ten von Objekten, P (a1, . . . , an)) mit Argumenttermen erlaubt. Terme sind Konstanten,

21

Page 22: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Variablen oder Funktionssymbole. Prädikatenlogik erlaubt damit viel reichere Aussagenals die Aussagenlogik.

Im Allgemeinen erklären Prädikate Teilmengen von bekannten Obermengen, wie zumBeispiel die Prädikate Primzahl oder gerade Zahl in der Menge der natürlichen Zahlen.Die Mengenoperationen ∪, ∩, ⊂, Komplement korrespondieren mit den Verknüpfungendes Aussagenkalküls ∨, ∧, →, ¬.

Die Aussage

”Wenn x die Eigenschaft P besitzt, so besitzt y die Eigenschaft Q.”

wird formalisiert zu Px → Qy. Wichtig sind auch die Quantoren: der Existenzquantor(∨, ∃), der Allquantor (∧, ∀). Der Existenzquantor erlaubt, die Existenz eines Objektesmit gewissen Eigenschaften anzunehmen, ohne es selbst zu definieren. Der Allquantorerstreckt eine Eigenschaft auf alle Objekte. Variablen werden im gemeinsamen Auftre-ten von Quantoren gebunden, können aber auch frei in Formeln auftreten. Die Semantikder Prädikatenlogik gibt den nach den syntaktischen Regeln geschaffenen Formeln einenWahrheitsgehalt in einer Domäne als geschaffener Welt. Durch Interpretation wird eineBeziehung zwischen dem Alphabet der Sprache und der Domäne hergestellt. Konstan-ten entsprechen Objekten, Prädikaten Relationen und Funktionssymbolen Funktionen.Variablen müssen Werte zugeordnet werden.

Beispiel 1.4.8Gegeben sei die Formel

F : ∀xP (f(x, a), x).

Die betrachtete Domäne sei die der natürlichen Zahlen N := 1, 2, 3, . . .. Der Konstantena ordnen wir die Zahl 1 zu. Unter der Funktion f(x, a) verstehen wir die Multiplikationx ·a. Das zweistellige Prädikat P sei als die Eigenschaft Gleichheit definiert. Damit lautetdie Interpretation der Formel:

Für alle natürlichen Zahlen x ∈ N gilt x · 1 = x.

Ist eine Formel in einer Interpretation wahr, so ist diese Interpretation ein Modell derFormel. Eine Formel ist erfüllbar, wenn es für sie ein Modell gibt, allgemeingültig, wennalle Interpretationen wahr sind. Auch hier wenden wir die Definition der logischen Kon-sequenz einer Formel F aus einer Formelmenge S an: Wenn jede Interpretation undBelegung, die ein Modell von S ist, auch ein solches von F ist, so gilt S ² F .

Zu unseren bekannten Schlussregeln der Aussagenlogik gesellen sich weitere Schlussre-geln:

Tabelle 1.4.9

∀xF ² ∃xF für eine nichtleere Domäne

∀xF ∨ ∀xG ² ∀x(F ∨G)

∃x(F ∧G) ² ∃xF ∧ ∃xG

22

Page 23: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Wir erhalten auch weitere logische Äquivalenzen:

Tabelle 1.4.10

¬∀xF ≡ ∃x¬F

¬∃xF ≡ ∀x¬F

∀x∀yF ≡ ∀y∀xF

∃x∃yF ≡ ∃y∃xF

∀x(F ∧G) ≡ ∀xF ∧ ∀xG

∃x(F ∨G) ≡ ∃xF ∨ ∃xG

Auch in der Prädikatenlogik können der Zeichenvorrat und syntaktische Regeln zur Ab-leitung von prädikatenlogischen Ausdrücken definiert werden. Auf Hilbert geht ein Axio-mensystem mit sechs Axiomen zurück. Sodann sind die Ableitungsregeln zu definieren.Leider stellt es sich heraus, dass das Erfüllbarkeits– und das Allgemeingültigkeitsproblemfür prädikatenlogische Formeln unentscheidbar ist. Die Wahrheitstafelmethode kann nichtübertragen werden. Immerhin können die Formeln in eine Normalform überführt werden,die sogenannte Skolemform, und ein Algorithmus angegeben werden, der bei unerfüllba-ren Formeln nach endlicher Zeit mit der Ausgabe ”unerfüllbar” stoppt.

Wir wollen nun die Peano–Axiome zur Begründung der natürlichen Zahlen und ihrerArithmetik kennenlernen.

Axiome 1.4.11 (Peano–Axiome)1. 1 ist eine natürliche Zahl: P1

Hier benötigen wir das einstellige Prädikat Px: x ist eine natürliche Zahl.

2. Zu jeder natürlichen Zahl gibt es eine natürliche Zahl als deren Nachfolger:

∀x (Px→ ∃y (Py ∧Qxy)) .

Dazu definieren wir das zweistellige Prädikat Qxy: Die natürliche Zahl x hat dienatürliche Zahl y zum Nachfolger.

3. Die Zahl 1 ist Nachfolger keiner natürlichen Zahl: ¬∃x (Px ∧Qx1).

4. Verschiedene natürliche Zahlen haben auch verschiedene Nachfolger:

∀x1∀x2∀y1∀y2 (Px1 ∧ Px2 ∧Qx1y1 ∧Qx2y2 ∧ ¬(x1 = x2)→ ¬(y1 = y2)) .

5. Jede Teilmenge M aus den natürlichen Zahlen, die die 1 und mit jedem Elementauch deren Nachfolger enthält, ist mit der Menge N der natürlichen Zahlen identisch(vollständige Induktion):

∀M (M1 ∧ ∀x∀y (Px ∧ Py ∧Mx ∧Qxy →My)→ (M = N)) .

23

Page 24: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Dazu führt man die PrädikatenvariableM ein sowie das PrädikatMx: x ist Elementvon M . Ferner findet die Konstante N Verwendung.

Hier ist zu bemerken, dass über die Prädikatenvariable quantisiert wurde. Damitsprechen wir von der Prädikatenlogik höherer Stufe. In der ersten Stufe sind nurQuantifizierungen über Objektvariablen erlaubt.

Die Prädikatenlogik ist auch Basis einer Programmiersprache, nämlich Prolog. Dabeispielen die Hornklauseln eine wesentliche Rolle. Sie werden in der Form

P : −Q1, Q2, . . . , Qn (Prozedurklausel)

notiert. Dies steht für

Q1 ∧Q2 ∧ . . . ∧Qn → P bzw. äquivalent ¬Q1 ∨ ¬Q2 ∨ . . . ∨ ¬Qn ∨ P.

Dabei steht das Komma für die Konjunktion. Hornklauseln enthalten nur eine Atomfor-mel als Konklusion im Kopf auf der linken Seite und mehrere durch ∧ verknüpfte Atom-formeln als Prämissen im Rumpf auf der rechten Seite. Sie stellen in Prolog die Regelndar und können als Prozeduren abgearbeitet werden. Hornklauseln ohne Rumpf (Prämis-sen) stellen Fakten dar. Diese beiden Typen von Klauseln machen ein Prolog–Programmaus und heißen daher Programmklauseln. Hornklauseln ohne Kopf (Konklusion) sind dieAnfragen, die zu beweisenden Aussagen; leere Hornklauseln sind immer falsch und ent-sprechen in Prolog dem Fail. Es gelingt, Formeln der Prädikatenlogik dahingehend zuvereinfachen, dass die Quantoren verschwinden und nur noch Klauseln in konjunktiverForm auftreten. Wir führen ein Beispiel für ein Prolog–Programm auf:

Beispiel 1.4.12Fakten:mag(heinrich, tiere).

mag(heinrich, sport).

mag(heinrich, biologie).

mag(fritz, sport).

mag(fritz, biologie).

Regel:interessiert(X, biologie):- mag (X, biologie), mag(X, tiere).

Anfrage:?- interessiert(X, biologie).

Antwort:X=heinrich.

Das Resolutionsverfahren erlaubt ähnlich wie in der Aussagenlogik das mechanische Be-weisen von Aussagen der Prädikatenlogik, die in Klauselform vorliegen. Ein Programmbesteht aus einer endlichen Menge von Klauseln. Das angewandte Schema gliedert sichin die folgenden Schritte:

24

Page 25: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Ordne Klauseln so an, dass die nicht negierten Literale links, die negierten rechts stehen.Taucht in zwei Klauseln dasselbe Literal auf entgegengesetzten Seiten auf, kann darauseine neue Klausel gebildet werden, indem man die beiden Klauseln unter Fortlassung derentgegengesetzten Literale zu einer neuen addiert. Treffen zwei je einelementige Klauselnso aufeinander, so entsteht die leere Klausel, die Widerspruchsklausel, und das Verfahrenterminiert. Somit lässt sich zwar keine Klausel ableiten, jedoch ein Widerspruchsbeweisführen, indem man das Gegenteil der Annahme als Klausel hinzufügt.

SLD–Resolution ist nur für Hornklauseln definiert und spielt eine entscheidende Rollebei der Logikprogrammierung und insbesondere bei Prolog–Programmen. Sei

F = K1,K2, . . . ,Kn, N1, . . . , Nm,

wobeiKi die Programmklauseln undNi die negativen Klauseln sind. Eine SLD–Resolutionist eine Herleitung der leeren Klausel: Ausgehend von j ∈ 1, 2, . . . ,m sowie der Ziel-klausel Nj wird dann mittels einer Folge i1, . . . , il mit Ki1 bis Kil zunächst Nj mit Ki1

resolviert. Dabei entsteht ein Zwischenresultat, das nur negative Klauseln enthält, unddann in jedem Schritt ν mit Kiv , v = 1, . . . , l, weiter resolviert wird, wiederum nur mitnegativen Klauseln als Zwischenergebnis, bis der letzte Schritt dann auf die leere Klausel,die Halteklausel führt.

Diese Form der Resolution ist vollständig für die Klasse der Hornformeln, d.h. für jedeunerfüllbare Klauselmenge F gibt es eine Klausel K ∈ F , so dass die leere Klausel durchdiese lineare SLD–Resolution aus F basierend auf K hergeleitet werden kann.

1.4.4 Literatur

Gert Böhme: Einstieg in die Mathematische Logik. Hanser, München 1981.

V. Claus und A. Schwill: Schülerduden Informatik. Dudenverlag, Mannheim, 1997.

Herbert Klaeren: Vom Problem zum Programm. Teubner, Stuttgart 1991.

Uwe Schöning: Logik für Informatiker. 4. Auflage. Spektrum–Verlag, Mannheim 2000.

Ramin Yasdi: Logik und Programmieren in Logik. Prentice Hall, München 1995.

25

Page 26: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

2 Schaltfunktionen

2.1 Zahldarstellung

Bei den in der Theoretischen Informatik behandelten Maschinenmodellen, wie z.B. end-licher Automat oder Turingmaschine, wird stets von zu verarbeitenden Worten übereinem Alphabet E ausgegangen, die eine variable Länge haben, die auch nicht durch eineMaximallänge beschränkt sind.

Bei realen Rechnern ist dies nicht sinnvoll, da hier mit einer festen Wortlänge gearbei-tet wird. Wir haben somit im Folgenden stets die generelle Voraussetzung, dass zumAlphabet E noch eine feste Konstante n hinzukommt, die die Wortlänge angibt.

Wichtige Zahlensysteme und Alphabete sind die b–adischen Zahlensysteme, die für einenatürliche Zahl b auf dem Alphabet Eb = 0, 1, . . . , b− 1 basieren.

Beispiel 2.1.1Wichtige b–adische Zahlensysteme sind die folgenden:

a) Dezimalsystem mit E10 = 0, 1, 2, . . . , 9, in dem wir im Allgemeinen rechnen.Eine feste Wortlänge erreichen wir hierbei, indem wir ggf. durch führende Nullenauffüllen. So ist z.B. 123 bei einer Wortlänge n = 4 als 0123 darzustellen.

b) Dual– oder Binärsystem mit E2 = 0, 1, welches im Computer zum Einsatzkommt.Oktalsystem mit E8 = 0, 1, . . . , 7.Hexadezimalsystem mit E16 = 0, 1, . . . , 9, A,B,C,D,E, F. Hierbei ist E16 ei-gentlich das Alphabet 0, 1, . . . , 9, 10, . . . , 15, anstelle von Ziffern 10, 11, . . . werdenaber generell bei Alphabeten mit b > 9 neue, einstellige Symbole eingeführt. Geradediese Zweierpotenzbasen haben in der Informatik eine besondere Bedeutung.

Satz 2.1.2 (b–adische Darstellung von Fixpunktzahlen)Sei b ∈ N mit b > 1. Dann ist jede Fixpunktzahl z (mit n Vorkomma– und k Nachkom-mastellen) mit 0 ≤ z · bk ≤ bn+k − 1 (und n ∈ N, k ∈ N0) eindeutig als Wort der Längen+ k über Eb darstellbar durch

z =n−1∑

i=−k

zibi, zi ∈ 0, 1, . . . b− 1.

26

Page 27: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abkürzend wird z geschrieben als z = (zn−1zn−2 . . . z0.z−1z−2 . . . z−k)b. Eine Einheit derletzten Stelle, also des Faktors von b−k, wird als 1 ulp (unit last place) bezeichnet, undgibt eine Genauigkeitsschranke des Fixpunktsystems an. Für k = 0 gilt obige Darstellunginsbesondere für die natürlichen Zahlen 0, 1, . . . , bn − 1.

Aufgrund von physikalischen Gegebenheiten (Spannung = 0V, oder Spannung > 0V bzw.Schwellwert) ist im Rechner das System für b = 2 sinnvoll. Häufig werden jedoch auchhier zur Darstellung Systeme mit b = 8 oder b = 16 verwendet, da hiermit kürzere undlesbarere Darstellungen erzielt werden. Auch arbeiten moderne Prozessoren in der Regelnicht auf Bitebene, sondern auf Wortebene, wobei hier Wortlängen von b = 16, 32 oder64 anzutreffen sind.

Für natürliche Zahlen ist auch eine we9itere Zahldarstellung möglich, die polyadischeDarstellung.

Satz 2.1.3 (Polyadische Darstellung natürlicher Zahlen)Es sei

(bn)n∈N

eine Folge natürlicher Zahlen mitbn > 1

für allen ∈ N

. Dann gibt es für jede natürliche Zahl z genau eine Darstellung der Formz = (zN . . . z0)PD =

∑Ni=0 zi

∏i−1j=0 bj = z0 + z1b0 + z2b1b0 + . . .+ zNbN−1 . . . b0

mit 0 ≤ zi < bi für i = 0, . . . , N .

2.1.1 Komplementdarstellungen

Eine weitere wichtige Zahldarstellung beim Rechnereinsatz ist die Zweierkomplement-darstellung von Fixpunktzahlen, über die eine Darstellung negativer Zahlen möglich ist.Das Zweierkomplement und andere Komplementdarstellungen sind wie folgt definiert:

Definition 2.1.4 (Komplementdarstellungen für n+k Fixpunktzahlen)Sei x = (xn−1 . . . x0.x−1 . . . x−k)2 einen+ k Dualzahl in Fixpunktdarstellung.

a) Das Einerkomplement K1(x) ist definiert als

K1(x) = (xn−1xn−2 . . . x0.x−1 . . . x−k)2

wobei xi = 0↔ xi = 1, und xi = xi.

b) Das Zweierkomplement K2(x) ist definiert als

K2(x) = (xn−1xn−2 . . . x0.x−1 . . . x−k)2 + 1 ulp = K1(x) + 1 ulp (modulo 2n)

.

27

Page 28: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Das Einerkomplement erhält man also durch Invertieren aller Bits, das Zweierkomple-ment durch anschließendes Addieren von einer Einheit zur letzten Stelle. Da hier ggf. einÜberlauf auftreten kann (Bitmuster nur mit Einsen), ist modulo 2n zu rechnen. Analogzum Einer– und Zweikomplement lässt sich zu jeder beliebigen Basis b das (b− 1)– undb–Komplement definieren als:

Definition 2.1.5 (b–Komplement für n+k Fixpunktzahlen)Sei x = (xn−1 . . . x0.x−1 . . . x−k)b ∈ 0, 1, . . . , b − 1n+k eine n + k Fixpunktdarstellungzur Basis b .

a) Das (b− 1)–Komplement Kb−1(x) ist definiert als

Kb−1(x) = (xn−1xn−2 . . . x0.x−1 . . . x−k)b,

xi := (b− 1)− xi, i = −k, . . . , n− 1.

b) Das b–Komplement Kb(x) ist definiert als

Kb(x) = (xn−1xn−2 . . . x0.x−1 . . . x−k)b + 1 ulp

.

Für die Darstellung von ganzen Zahlen werden im Rechner im Allgemeinen Zweierkom-plementdarstellungen (mit k = 0) verwendet. Bei Komplementdarstellungen ist aber zubeachten, dass diese generell bzgl. einer festen Wortlänge gebildet werden. Mit der Kom-plementdarstellung kann man nun negative Zahlen nach folgender Idee darstellen. Gehenwir von einer Registerlänge von n Bit aus, so gibt es N = 2n verschiedene Bitmuster.

• Eine positive Zahl x wird nun dargestellt als

+x = x.

• Eine negative Zahl −x wird nun dargestellt als

-x = N - x.

Um Mehrdeutigkeiten auszuschließen, muss nun noch festgelegt werden, dass alle Dar-stellungen, deren höchstwertiges Bit gesetzt ist, als negative Zahlen interpretiert werden.

Beispiel 2.1.6Bei einer Wortlänge von n = 8 Bits lauten die Darstellungen von +92 und -92 im Einer–bzw. Zweierkomplement:

Komplement +92 -92Einer– dual 01011100 dual 10100011

hexadezimal 5C hexadezimal A3Zweier– dual 01011100 dual 10100100

hexadezimal 5C hexadezimal A4

28

Page 29: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Zur Ausführung einer Subtraktion der Form x − y ist im Zweierkomplement lediglichK2(y) zu bestimmen und zu x zu addieren. Ein eventuell auftretender Übertrag wirdignoriert. Ist das höchstwertige Bit des Ergebnisses wieder gesetzt, so handelt es sich umeinen negativen Wert, der ebenfalls im Zweierkomplement dargestellt ist.

Beispiel 2.1.7Bei einer Wortlänge von n = 8 Bits ist x − y mit x = 45 und y = 92 mit Hilfe derZweierkomplement–Addition zu bestimmen:

45 = 32 + 8 + 4 + 1: (00101101)2+ K2(92): (10100100)2

(11010001)2 = K2(z)

mit z = x−y. Rückumwandeln nach dem gleichen Algorithmus ergibt: |z| = (00101110)2+1 = (00101111)2 = 32+8+4+2+1 = 47, somit ergibt sich das korrekte Ergebnis z = −47.

2.1.2 Darstellung von Gleitpunktzahlen nach IEEE 754/854

Zum Abschluss des ersten Abschnittes wollen wir noch kurz auf die Darstellung „reel-ler“ Zahlen in Form einer Gleitkommadarstellung zur Basis b = 2 eingehen, wie sie imStandard IEEE 754/854 festgelegt ist. Eine Gleitkommazahl besteht dabei aus einemVorzeichen S, l Bit zur Darstellung des Exponenten und m Bit zur Darstellung der Man-tisse. Die Mantisse ist dabei bei normalisierten Zahlen, die im Allgemeinen verwendetwerden, so dargestellt, dass diese eine führende 1 vor dem Dezimalpunkt besitzt, diedann in der Regel nicht kodiert wird. Wir erhalten somit die folgende Darstellung füreine Gleitkommazahl:

Vorzeichen–Bit↓ Exponent MantisseS el−1el−2 . . . . . . . . . e1e0 fm−1fm−2 . . . . . . . . . . . . f1f0

Exponentencharakteristik: E = (el−1el−2 . . . . . . e1e0)2Mantisse: M = (fm−1fm−2 . . . . . . f1f0)

Dargestellte Zahl:(−1)S · (1.M)2 · 2

(E−EBias) 1 ≤ E ≤ 2 · EBias normalisiert(−1)S · (0.M)2 · 2

1−EBias E = 0, M 6= 0 denormalisiert(−1)S · 0 E = 0, M = 0 NullmitVorz.(−1)S · ∞ E = 2 · EBias + 1, M = 0 ∞mitVorz.NaN E = 2 · EBias + 1, M 6= 0 NotaNumber

Die Konstanten l und m bestimmen die Genauigkeit, der Summand EBias dient zurVerschiebung des Exponentenwertes, um auch negative Exponenten zu erhalten. Dadurchist es nicht erforderlich, den Exponenten in Zweierkomplementdarstellung zu schreiben,was einen Vergleich von Exponenten erleichtert.

29

Page 30: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Die im IEEE Standard festgelegten Genauigkeiten „single“ und „double“ haben folgendeParameter:

Genauigkeit Exponent Mantisse EBias Bereich Stellensingle: 32 Bit l = 8 m = 23 127 1.5 · 10−45 . . . 3.4 · 1038 7 – 8

double: 64 Bit l = 11 m = 52 1023 5.0 · 10−324 . . . 1.7 · 10308 15 – 16

Beispiel 2.1.8Die Darstellung der Zahl 1.0 ergibt sich also in normalisierter Darstellung wie folgt:single

1.0 = 1.0 ∗ 20 = 1.0 ∗ 2127−127 = 0

Exponent︷ ︸︸ ︷01111111

Mantisse︷ ︸︸ ︷00000000000000000000000

= $3F800000

double

1.0 = 1.0 ∗ 20 = 1.0 ∗ 21023−1023 = 0

Exponent︷ ︸︸ ︷01111111111

Mantisse︷ ︸︸ ︷00000...............00000︸ ︷︷ ︸

52 Nullen

= $3FF0000000000000

Bei dieser Zahl sieht man, dass die Mantisse identisch Null sein kann, ohne dass die Zahlselbst Null ist. Will man eine beliebige Zahl normalisiert darstellen, so transformiertman sie erst in den Bereich [1, 2) durch Division durch eine geeignete Zweierpotenz undverfährt wie unten gezeigt:

6.5 = 22 · 1.625

= 22 ·

(1 +

1

2+

0

22+

1

23

)

= 2129−127 ·

(1 +

1

2+

0

22+

1

23

)

= 2129−127 · (1.101)2= 0 10000001 10100000000000000000000

= $40D00000.

2.2 Boolesche Algebra

Von den angegebenen Beispielen b–adischer Zahlsysteme ist der Fall b = 2 aus der Sichtder Schaltungstechnik ausgezeichnet. Diesen Fall kann man leicht als „wahr – falsch“bzw. „Strom – kein Strom“ interpretieren. Wir wollen daher das Alphabet E2 = 0, 1mit B bezeichnen, in Erinnerung an den Mathematiker George Boole, der sich mit denfolgenden Strukturen aus mathematischer Sicht beschäftigt hat.

30

Page 31: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Seien x, y ∈ B. Erklärt man auf B die folgenden drei Verknüpfungen

x ∪ y := Max(x, y),

x ∩ y := Min(x, y),

x := 1− x,

so ist (B, ∪, ∩, ) eine Boolesche Algebra, d.h. ein distributiver, komplementärerVerband, in welchem es ein kleinstes (0) und ein größtes (1) Element gibt.

In einer Booleschen Algebra gelten die folgenden Gesetze:

1. Kommutativgesetze: x ∪ y = y ∪ x, x ∩ y = y ∩ x

2. Assoziativgesetze: (x ∪ y) ∪ z = x ∪ (y ∪ z), (x ∩ y) ∩ z = x ∩ (y ∩ z)

3. Verschmelzungsgesetz: (x ∪ y) ∩ x = x, (x ∩ y) ∪ x = x

4. Distributivgesetze: x ∩ (y ∪ z) = (x ∩ y) ∪ (x ∩ z), x ∪ (y ∩ z) = (x ∪ y) ∩ (x ∪ z)

5. Komplementgesetz: x ∪ (y ∩ y) = x, x ∩ (y ∪ y) = x

6. x ∪ 0 = x, x ∩ 0 = 0, x ∩ 1 = x, x ∪ 1 = 1

7. de Morgansche Regeln: x ∪ y = x ∩ y, x ∩ y = x ∪ y

8. x = x ∪ x = x ∩ x = x

Satz 2.2.1Für B = 0, 1 ist (B, ∪, ∩, ) eine Boolesche Algebra.

Beweisidee: Da B hier nur zwei Elemente besitzt, kann der Beweis über Wertetafelnerfolgen. Für die Regel c) erhält man dabei exemplarisch:

Argumente linke Seite rechte Seitex y x ∪ y (x ∪ y) ∩ x x0 0 0 0 00 1 1 0 01 0 1 1 11 1 1 1 1

Im Folgenden wollen wir die Verknüpfungen der Booleschen Algebra wie folgt identifizie-ren: Maximum ∪mit der Addition + (logisch OR) und Minimum ∩mit der Multiplikation· (logisch AND).

31

Page 32: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Bemerkung:Die Potenzmenge P(A) einer Menge A, d.h. die Menge aller Teilmengen von A, ist mitden Mengenoperationen ∪, ∩ und = C = Komplement, 0 = ∅, 1 = A, eine boolescheAlgebra.

2.3 Schaltfunktionen

Definition 2.3.1Eine Funktion F : Bn → Bm heißt Schaltfunktion.

Eine totale Schaltfunktion F liefert zu den n Inputs (I) m eindeutige Outputs (O).

Beispiel 2.3.2Die Addition von zwei 16 Bit Zahlen kann als Schaltfunktion mit einem Inputvektorder Länge 32 und einem Output der Länge 17 realisiert werden. Hierbei seien der Inputb1b2 . . . b16b17 . . . b32, wobei die ersten 16 Bit den ersten Summanden, und die zweiten 16Bit den zweiten Summanden bilden. Wir erhalten somit eine Schaltfunktion A : B32 →B17. Analog kann man die Multiplikation als Schaltfunktion darstellen. Hier erhält manM : B32 → B32 mit b1 . . . b16b17 . . . b32 → c1 . . . c32.

Beispiel 2.3.3Ein weiteres Beispiel ist das Sortieren von z.B. 30 Zahlen der Länge 16 Bit und Ausgabeder Zahlen in sortierter Reihenfolge. Hier erhält man eine Schaltfunktion S : B480 →B480.

Einen wichtigen Spezialfall einer Schaltfunktion erhalten wir für m = 1:

Definition 2.3.4Eine Schaltfunktion f : Bn → B heißt n–stellige Boolesche Funktion.

Mit dieser Definition können wir nun eine beliebige Schaltfunktion F : Bn → Bm mitF (x1, . . . , xn) = (y1, . . . , ym) als Vektor von Booleschen Funktionen auffassen. Setzen wirfür 1 ≤ i ≤ m die Boolesche Funktion fi : Bn → B definiert durch

fi(x1, . . . , xn) = yi,

so ist F für alle x1, . . . , xn ∈ B darstellbar als

F (x1, . . . , xn) = (f1(x1, . . . , xn), f2(x1, . . . , xn), . . . , fm(x1, . . . , xn)).

Somit sind alle folgenden Betrachtungen für Boolesche Funktionen auch auf die einzelnenKomponenten einer beliebigen Schaltfunktion anwendbar.

Für n = 1, 2 wollen wir in den Tabellen 2.2, 2.3 und 2.4 alle Booleschen Funktionennotieren.

32

Page 33: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Tabelle 2.2:

x f0(x) f1(x) f2(x) f3(x)

0 0 0 1 11 0 1 0 1

Tabelle 2.3:

(1) x · x x · y x · y x x · y y ⊕ x+ y

(2) ≡ 0 Min > x < y 6= Max(3) ∧ 6→ x 6← y 6↔ ∨

x y f0 f1 f2 f3 f4 f5 f6 f70 0 0 0 0 0 0 0 0 00 1 0 0 0 0 1 1 1 11 0 0 0 1 1 0 0 1 11 1 0 1 0 1 0 1 0 1

Tabelle 2.4:

(1) x+ y x⊕ y y x+ y x x+ y x · y x+ x

(2) 1 - Max = 1− y ≥ 1− x ≤ 1 - Min ≡ 1

(3) ↓ ↔ ¬y ← ¬x → ↑

x y f8 f9 f10 f11 f12 f13 f14 f150 0 1 1 1 1 1 1 1 10 1 0 0 0 0 1 1 1 11 0 0 0 1 1 0 0 1 11 1 0 1 0 1 0 1 0 1

33

Page 34: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Einige der obigen Funktionen werden auch mit Namen versehen:f1 Konjunktion (AND)f6 Antivalenz (Exclusive OR, XOR)f7 Disjunktion (OR)f8 Peircescher Pfeil (Not Or, NOR)f9 Äquivalenzf14 Shefferscher Strich (Not And, NAND)

Die in obigen Tabellen in der Zeile (1) verwendete Notation wird in Zusammenhang mitder Booleschen Algebra verwendet, die Notation (2) stammt aus der Arithmetik und (3)schließlich aus der Logik.

Hiermit haben wir gesehen, dass es für n = 1 vier verschiedene und für n = 2 sechzehnverschiedene Boolesche Funktionen gibt. Allgemein gilt:

Satz 2.3.5Für jedes n ∈ N gibt es 22

n

verschiedene Boolesche Funktionen f : Bn → B.

Beweisidee: Für n Argumente existieren insgesamt 2n verschiedene Belegungen. Fürjede Belegung wiederum kommen die Funktionswerte 0 und 1 vor, als insgesamt 22

n

verschiedene Funktionen.

Für Schaltfunktionen gilt allgemeiner:

Satz 2.3.6Für m,n ∈ N gibt es 2m2

n

verschiedene Schaltfunktionenf : Bn → Bm.

Um eine systematische Darstellung Boolescher Funktionen zu erhalten, wollen wir imFolgenden einige Normalformen diskutieren. Hierzu benötigen wird die folgenden Begriffe:

Definition 2.3.7Sei i = (i1i2 . . . in)2 von i. i heißt genau dann einschlägiger Index zu f : Bn → B, wennf(i1, i2, . . . , in) = 1 gilt.

Definition 2.3.8Sei i = (i1i2 . . . in)2 eines Index i, f : Bn → B Boolesche Funktion. Dann heißt dieFunktion mi : B

n → B definiert durch mi(x1, x2, . . . , xn) := xi11 xi22 . . . x

inn i–ter Minterm

von f . Hierbei ist xijj :=

xj falls ij = 1xj falls ij = 0

. d.h. es gilt mi = 1 genau dann, wenn das

Argument die duale Codierung von i ist.

Beispiel 2.3.9Gegeben sei die folgende Boolesche Funktion f : B3 → B durch die Wertetabelle

34

Page 35: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

i x1 x2 x3 f(x1, x2, x3)

0 0 0 0 01 0 0 1 02 0 1 0 03 0 1 1 14 1 0 0 05 1 0 1 16 1 1 0 07 1 1 1 1

Die einschlägigen Indizes sind somit 3, 5, 7. Die zugehörigen Minterme sindm3(x1, x2, x3) =x1x2x3, m5(x1, x2, x3) = x1x2x3 undm7(x1, x2, x3) = x1x2x3.

Satz 2.3.10 (Darstellungssatz für Boolesche Funktionen)Jede Boolesche Funktion f : Bn → B ist eindeutig darstellbar als Summe der Mintermeihrer einschlägigen Indizes, d.h ist I ⊆ 0, 1, . . . , 2n − 1 die Menge der einschlägigenIndizes zu f , so gilt:

f =∑

i∈I

mi.

Keine andere Mintermsumme stellt f dar.

Beweis Zu zeigen ist: (1) Existenz einer solchen Darstellung und (2) die Eindeutigkeit.

1) ExistenzWir zeigen, dass die Funktionen f und

∑i∈I mi für alle Argumente den gleichen

Funktionswert besitzen. Sei dazu j ∈ 0, 1, . . . , 2n − 1 und (j1j2 . . . jn)2 dessen .Betrachte die Fälle

a) f(j1, . . . , jn) = 1. Hieraus folgt, j ist einschlägiger Index von f und somitj ∈ I. Daher ist

∑i∈I mi = 1.

b) f(j1, . . . , jn) = 0. Hieraus folgt, j ist kein einschlägier Index von f und somitj 6∈ I. Dann kommt j aber nicht in der Indexmenge bei der Summation von∑

i∈I mi vor, aber nur der Minterm mj liefert für das Argument j den Wert1, womit also in diesem Fall

∑i∈I mi = 0 gilt.

Damit stellen sowohl f als auch∑

i∈I mi dieselbe Funktion dar und die Existenzist gezeigt.

2) EindeutigkeitAngenommen, es existieren zwei verschiedene Darstellungen als Summe von Minter-men für eine Funktion f , d.h. es existieren zwei Indexmengen I, J ⊆ 0, 1, . . . , 2n−

35

Page 36: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

1, mit I 6= J undf =

i∈I

mi =∑

j∈J

mj

. Wegen I 6= J existiert dann mindestens ein Index k, der o.B.d.A. in I enthaltenist aber nicht in J . Sei (k1 . . . kn)2 dessen . Dann gilt:

i∈I

mi(k1 . . . kn) = 1, da k ∈ I,

j∈J

mj(k1 . . . kn) = 0, da k 6∈ J.

Somit stellen beide Darstellungen verschiedene Funktionen dar, im Widerspruchzur Annahme.

Also ist gezeigt, dass die Darstellung als Summe von Mintermen eindeutig ist, wo-mit die nächste Definition gerechtfertigt ist.

Definition 2.3.11Die eindeutige Darstellung einer Booleschen Funktion als Mintermsumme laut Satz 2.1.2heißt disjunktive Normalform (DNF) einer Booleschen Funktion f .

Bemerkung:Wir halten an dieser Stelle bereits fest, dass in einer DNF Darstellung einer Funktionhöchstens ein Summand gleich 1 ist.

Beispiel 2.3.12Im Fall unseres Beispiels 2.3.9 ergibt sich die DNF:

f(x1, x2, x3) = m3 +m5 +m7 = x1x2x3 + x1x2x3 + x1x2x3

.

Aus der Darstellung einer Booleschen Funktion in DNF lässt sich unmittelbar folgern,dass jede Boolesche Funktion durch die zweistelligen Grundfunktionen + und · sowie mitder einstelligen Funktion dargestellt werden kann. Eine solcher Satz ausgezeichneterFunktionen hat eine besondere Bezeichnung:

Definition 2.3.13Ein System B = f1, . . . , fn Boolescher Funktionen heißt (funktional) vollständig, wennjede Boolesche Funktion allein durch Einsetzungen bzw. Kompositionen von Funktionenaus B dargestellt werden kann.

Es gilt:

Satz 2.3.14a) +, ·, ist funktional vollständig.

36

Page 37: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

b) +, ist funktional vollständig.

c) ·, ist funktional vollständig.

d) NAND ist funktional vollständig.

e) NOR ist funktional vollständig.

Der Beweis zu a) ergibt sich aus der Darstellung als DNF, b) – e) verbleiben als Übung.

Eine zweite Normalform, die sozusagen „dual“ zur DNF ist, ergibt sich wie folgt.

Definition 2.3.15Sei i ein Index von f : Bn → B, und sei mi der i–te Minterm von f . Dann heißt dieFunktion

Mi : Bn → B

, definiert durch

Mi(x1, . . . , xn) := mi(x1, . . . , xn) = xi11 + . . .+ xinn

i–ter Maxterm von f .

Beispiel 2.3.16Im Fall des Minterms m3 aus obigem Beispiel ergibt sich also

M3(x1, x2, x3) = m3(x1, x2, x3) = x1x2x3 = x1 + x2 + x3

.

In Analogie zu Mintermen gilt dann: Ein Maxterm Mi nimmt genau dann den Wert 0an, wenn das Argument (x1 . . . xn) die von i ist.

Dies liefert den folgenden Satz:

Satz 2.3.17a) Jede Boolesche Funktion f : Bn → B ist eindeutig darstellbar als Produkt der

Maxterme ihrer nicht einschlägigen Indizes. Diese Darstellung heißt konjunktiveNormalform (KNF) von f .

b) Es gilt: KNF(f) = DNF(f).

Beispiel 2.3.18Die konjunktive Normalform für unser Beispiel 2.3.9 ergibt sich somit als:

f(x1, x2, x3) = M0 ·M1 ·M2 ·M4 ·M6

= (x1 + x2 + x3) · (x1 + x2 + x3) · (x1 + x2 + x3)

·(x1 + x2 + x3) · (x1 + x2 + x3).

37

Page 38: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Beim Vergleich dieser Darstellung mit der DNF sieht man, dass eine DNF dann zu bevor-zugen ist, wenn die Anzahl der einschlägigen Indizes geringer ist als die der nicht einschlä-gigen. Im anderen Fall liefert die KNF eine Darstellung mit weniger Operationen. Wiewir später sehen werden, können die Normalformen durch verschiedene Algorithmen inkostengünstigere verkürzte Formen transformiert werden. Die Normalformen eignen sichin der Regel zu Beweisen oder als Ausgangspunkt verschiedener Algorithmen sehr gut,da man hier z.B. Aussagen über die Häufigkeit der Einssummanden oder Nullfaktorenmachen kann.

Bemerkung:In der Literatur werden die Darstellungen, die wir im ersten Kapitel ”konjunktive” bzw.”disjunktive Form” genannt haben, häufig mit dem Begriff ”konjunktive” bzw. ”disjunktiveNormalform” bezeichnet. Um Missverständnissen vorzubeugen, haben wir die verschie-denen Bezeichnungen gewählt.

2.4 Schaltnetze

Im letzten Abschnitt haben wir gesehen, dass Boolesche Funktionen über die DNF dar-gestellt werden können, und dass hieraus vollständige Funktionensysteme für BoolescheFunktionen resultierten. Führen wir für diese Grundfunktionen spezielle Schaltsymboleein, so kann man aus den Normalformen Schaltnetze entwickeln. Wir wollen hier diefolgenden, nach der neuen deutschen DIN–Norm gültigen, Schaltsymbole benutzen:

Hieraus lässt sich für unser Beispiel

f(x1, x2, x3) = x1x2x3 + x1x2x3 + x1x2x3

folgendes Schaltnetz entwickeln:

38

Page 39: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

1 1

&

&

&

&

&

&

x1

x2

x3

³ 1

³ 1

f x x x( , , )1 2 3

In verschiedenen Literaturstellen werden die Negatoren sofort am Eingang eines Gattersplatziert, dies vereinfacht zwar das Schaltbild, ist technisch aber nicht sinnvoll, da eskeine Gatterbausteine gibt, die negierte Eingänge haben. Auch werden häufig Gatter mitmehr als zwei Inputs verwendet. In gewissem Maße ist dies auch technisch sinnvoll, daz.B. Gatter mit drei, vier oder acht Eingängen existieren. Soll ein Schaltnetz tatsächlichmit logischen Bausteinen verdrahtet werden, so sollte man bereits beim Entwurf daraufachten, dass man nur Gatter benutzt, die auch als Bausteine verfügbar sind.

Wie obige Schaltung zeigt, benötigt eine Boolesche Funktion mit drei Variablen bei derVerschaltung der DNF und Verwendung von Gattern mit lediglich zwei Inputs ein Schalt-netz mit fünf Stufen, wenn mindestens eine negierte Variable und mehr als zwei Summan-den vorkommen. In diesem Fall entspricht also jeder Operator der DNF einem Gatterdes Schaltnetzes und wir erhalten so im Beispiel insgesamt 10 Gatter. Fasst man dasSchaltnetz nun als Black–Box mit drei Eingängen und einem Ausgang auf, so hängt dieVerzögerungszeit zwischen Anlegen der Inputs und zur Verfügung stehen des Outputsvon den verwendeten Gattern und insbesondere von der Anzahl der Schaltungsstufen ab.

39

Page 40: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Auf heute üblichen hochintegrierten Schaltungen befinden sich große Anzahlen von Gat-tern und Verbindungsleitungen auf einer sehr kleinen Fläche. Beim Entwurf sind hiereine Reihe von Beschränkungen zu berücksichtigen:

1) Geschwindigkeit:Jedes Gatter hat, wie bereits erwähnt, eine gewisse Verzögerungszeit, die im Bereichvon wenigen Picosekunden liegt. Die Verzögerung eines komplexen Schaltnetzeshängt somit stark von der Anzahl der notwendigen Stufen der Schaltung ab. Gene-rell sollte man versuchen, Schaltungen mit möglichst wenigen Stufen zu realisieren(vgl. Vereinfachungsmethoden für Boolesche Funktionen im nächsten Kapitel).

2) Größe:Die Herstellungskosten eines Chips sind proportional zur Anzahl der verwendetenGatter. Somit ist aus diesem Grund eine möglichst geringe Anzahl von Gatternerstrebenswert. Hierdurch wird auch im Allgemeinen die Anzahl der Stufen unddamit die Geschwindigkeit beeinflusst. Auch benötigt ein großer Chip ggf. länge-re Verbindungen zwischen einzelnen Chipteilen. Aufgrund der Lichtgeschwindigkeitkann ein Signal etwa 0.3 mm/psec zurücklegen, so dass es auch hierdurch zu Schalt-verzögerungen kommen kann. Hinzu kommt, dass bei wachsender Chipfläche auchdie Wahrscheinlichkeit eines Produktionsfehlers steigt.

3) Fan–In/Out:Die Anzahl der Inputs, mit denen ein Output (oder die Anzahl der Auffächerungeneines Eingangs) verbunden ist, wird Fan–Out genannt. Analog heißt die Anzahl derInputs eines Gatters Fan–In. Gatter mit hohem Fan–In und/oder hohem Fan–Outsind im Allgemeinen langsamer als solche mit einer geringeren Zahl. Zu bevorzu-gen sind also Schaltungen mit möglichst geringem Fan–In/Out. Zudem kann esvorkommen, dass bei zu hohem Fan–Out zusätzliche Treiberschaltungen verwendetwerden müssen.

Die genannten Entwurfsziele sind im Allgemeinen nicht alle gleichzeitig voll zu erfüllen,so muss man bei jeder Schaltung abwägen, welches das wichtigere Optimierungskriteriumist.

Formal kann ein Schaltnetz wie folgt über einen Graphen definiert werden:

Sei P eine endliche Punktmenge, o.B.d.A. gelte P ⊆ N. Ist K ⊆ P×P eine symmetrische,nicht–reflexive Relation über P (d.h. mit (x, y) ist auch (y, x) aber nicht (x, x) aus K),so heißt das Paar G := (P,K) ein Graph mit der Punktmenge P und der KantenmengeK. Bei einem ungerichteten Graphen werden im Allgemeinen zwei zueinander inverseKanten (pi, pj) und (pj , pi) zu einer ungerichteten Kante pi, pj zusammengefasst. Einn–Tupel w = (p1, p2, . . . , pn) von Punkten aus P heißt dann Weg in G, falls für allei = 1, . . . , n − 1 die ungerichtete Kante pi, pi+1 zu G gehört. Ist p1 = pn, so sprechenwir von einem Kreis oder Zykel. Lassen wir für K eine beliebige Relation zu (nichtnotwendig symmetrisch) und geben den Kanten somit eine Richtung, so sprechen wirvon einem gerichteten Graphen.

40

Page 41: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Definition 2.4.1Ein Schaltnetz ist ein gerichteter zykelfreier Graph (engl. Directed Acyclic Graph, kurzDAG). Als Input (des Schaltnetzes) bezeichnet man die Punkte des DAG, in die keineKante hineinführt, und entsprechend als Output die Punkte, aus denen keine Kanteherausführt.

Erstellt man zu einem Schaltnetz den zugehörigen DAG, so erhält man ein Verbindungs-netz, fügt man an den Knoten zusätzlich die entsprechenden Bezeichnugen der BooleschenVerknüpfungen an, so erhält man ein Operator–Schaltnetz. Als Operator–Schaltnetz zuunserem Beispiel erhalten wir dann:

Für einen DAG, und somit auch für Schaltnetze, gilt folgender Satz:Satz 2.4.2Jeder nichtleere DAG mit endlich vielen Punkten hat mindestens einen Input und einenOutput.

Beweis Sei G = (P,K) ein DAG mit P 6= ∅, |P | < ∞. Angenommen, G hat keinenInput. Sei dann p1 ein beliebiger Punkt von G. Dann hat p1 mindestens einen Vorgängerp2 und dieser wiederum einen Vorgänger p3 usw. Da G endlich ist und laut Annahme injeden Punkt mindestens eine Kante hineinführt, gilt irgendwann pi = pj mit i < j, d.h. esliegt ein Zykel vor, im Widerspruch zur Annahme der Zykelfreiheit. Analog argumentiertman mit den Nachfolgeknoten um zu zeigen, dass mindestens ein Output existiert.

2.5 Ringsummennormalform

Wir wollen nun, neben den bereits oben angegebenen vollständigen Systemen, zwei wei-tere vollständige Systeme, und hierauf basierende Normalformen kennenlernen. Einesdieser beiden Systeme wird dann auch ohne Negationen auskommen.

41

Page 42: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Wir gehen hierzu davon aus, dass für eine Boolesche Funktion f : Bn → B die Mengeder einschlägigen Indizes mit I bezeichnet sei, und dass somit f in der DNF als

f =∑

i∈I

mi

gegeben ist. In dieser Darstellung ist also immer höchstens ein Summand gleich 1. Hierausresultiert der folgende Satz:

Satz 2.5.1 (Ringsummennormalform, RNF)Sei f : Bn → B und I = α1, . . . , αk die Menge der einschlägigen Indizes zu f . Danngilt:

f = mα1⊕mα2

⊕ . . .⊕mαk.

Der Operator ⊕ bezeichnet die XOR–Verknüpfung, die auch als Antivalenz, Additionmodulo 2 oder Ringsumme bezeichnet wird.

Beweis f liege in DNF vor, d.h.

f =

k∑

i=1

mαi.

Sei x ∈ Bn. Dann sind zwei Fälle zu unterscheiden:

1.) f(x) = 0⇒ alle Summanden in der DNF und RNF sind gleich 0.

2.) f(x) = 1⇒ genau ein Summand in der DNF ist gleich 1, nämlich derjenige, dessenIndex αi die Darstellung von x hat.

Hieraus folgt die Behauptung, da eine Ringsumme genau dann 1 ist, wenn eine ungeradeAnzahl von Summanden gleich 1 ist, was im zweiten Fall zutrifft.

Der folgende Satz stellt einige Eigenschaften der Ringsumme zusammen, die uns dannerlauben werden, ein komplementfreies vollständiges System anzugeben.

Satz 2.5.2Für alle x, y, z ∈ B gilt:

a) x⊕ 1 = x, x⊕ 0 = x

b) x⊕ x = 0, x⊕ x = 1

c) x⊕ y = y ⊕ x (Kommutativität)

42

Page 43: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

d) x⊕ (y ⊕ z) = (x⊕ y)⊕ z (Assoziativität)

e) x · (y ⊕ z) = x · y ⊕ x · z (Distributivität bzgl. ·)

f) 0⊕ 0⊕ . . .⊕ 0 = 0

g) 1⊕ 1⊕ . . .⊕ 1︸ ︷︷ ︸n−−mal

=

1 falls n ungerade0 falls n gerade

Satz 2.5.3 (Komplementfreie Ringsummennormalform, Reed–Muller Form)Jede Boolesche Funktion f : Bn → B ist eindeutig darstellbar als Polynom (Multinom) inden Variablen x1, x2, . . . , xn mit den Koeffizienten a0, a1, . . . , a1...n ∈ B. Die Darstellungist wie folgt:

f = a0

⊕ a1x1 ⊕ a2x2 ⊕ . . .⊕ anxn

⊕ a12x1x2 ⊕ . . .⊕ an−1,nxn−1xn...

⊕ a1...nx1x2 · . . . · xn.

Beweis Für einen Beweis ist zum einen die Existenz und zum anderen die Eindeutigkeitzu zeigen. Die Existenz ergibt sich aus der Konstruktion der Normalform (vgl. folgendesBeispiel). Hierbei werden zuerst in der DNF alle Summen durch Ringsummen ersetzt. Imzweiten Schritt werden alle Literale xi durch xi ⊕ 1 ersetzt, der resultierende Ausdruckausmultipliziert, und jeweils gleiche Terme zusammengefasst (jeweils zwei gleiche Termeergeben in der Summe 0).

Zum Beweis der Eindeutigkeit überlegt man sich, dass ein Polynom 2n verschiedene Sum-manden haben kann, es also 22

n

verschiedene Polynome gibt, genauso viel, wie BoolescheFunktionen existieren.

Beispiel 2.5.4Die Umwandlung einer DNF in eine komplementfreie Ringsummenform ergibt sich fürunser Beispiel wie folgt:

f(x1, x2, x3) = x1x2x3 + x1x2x3 + x1x2x3

= x1x2x3 ⊕ x1x2x3 ⊕ x1x2x3

= (x1 ⊕ 1)x2x3 ⊕ x1(x2 ⊕ 1)x3 ⊕ x1x2x3

= x1x2x3 ⊕ x2x3 ⊕ x1x2x3 ⊕ x1x3 ⊕ x1x2x3

= x2x3 ⊕ x1x3 ⊕ x1x2x3.

Also gilt:a0 = a1 = a2 = a3 = a12 = 0; a13 = a23 = a123 = 1.

43

Page 44: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Als unmittelbare Folgerung der beiden letzten Sätze ergibt sich der folgende Satz:

Satz 2.5.5[a)]⊕, ·, ist funktional vollständig.

[b)]⊕, ·, 1 ist funktional vollständig.

44

Page 45: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

3 Schaltnetze und ihre Optimierung

3.1 Beispiele für Schaltnetze

In diesem ersten Abschnitt wollen wir aus den bisher bekannten Grundbausteinen neue,größere Einheiten spezieller Schaltnetze entwickeln, die wir in Form einer Black–Box imweiteren verwenden wollen.

Wie wir bereits im Satz 2.3.14 d) und e) gesehen haben, sind die Systeme NANDund NOR funktional vollständig. Es ist daher möglich, jede Boolesche Funktion mitHilfe dieser Grundfunktionen, die auch als eigenständige Gatter existieren, zu realisieren.Eine weitere Grundfunktion war das XOR. Diese Funktion wollen wir nun als Schaltnetzmittels des Systems +, ·, darstellen. Als Wertetabelle der XOR Funktion ergab sich:

x y x⊕ y

0 0 00 1 11 0 11 1 0

woraus sich als DNF unmittelbar die Darstellung x⊕ y = xy + xy ergibt.

Mittels NAND–Verknüpfungen stellt sich die XOR–Funktion wie folgt dar:

x⊕ y = xy + yx = xy + yx = xy · yx = x(x+ y) · y(x+ y) = x · xy · y · xy (4 Gatter)

= xyy · yxx (5 Gatter)

Dies ergibt das folgende Schaltnetz:

45

Page 46: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Realisierung der XOR–Funktion mittels NAND–Gattern

Weitere Beispiele für grundlegende Schaltnetze ergeben sich aus der Addition von bi-nären Zahlen. Ist die Aufgabe der Addition von zwei n–stelligen Dualzahlen zu lösen, sokann dies als eine Schaltfunktion f : B2∗n → Bn+1 gelöst werden. Nehmen wir einmalals realistischen Wert n = 16 an, so hätte die resultierende Funktion f : B32 → B17

eine Funktionstafel mit ca. 3.4 · 1010 Termen. Die Reduktion einer solchen Funktionbedarf exponentieller Laufzeit (Bestimmung einer kostengünstigsten Darstellung ist NP–vollständig). Aus diesem Grund kommen üblicherweise andere Ansätze zur Anwendung.Eine Möglichkeit besteht darin, ein Schaltnetz zu designen, welches nur zwei Bit ggf. unterBerücksichtigung eines Übertrages aus der nächstniedrigeren Stelle addiert. Ein solchesSchaltnetz kann mit Hilfe der XOR–Funktion (Addition modulo 2) realisiert werden. Wirwollen dies in zwei Stufen tun.

• 1) Addition von zwei Bit ohne Übertrag aus vorheriger Stelle:Wenn wir die Summe z = x + y bestimmen wollen, so ergibt sich die folgendeFunktionstafel für die Summe z und den Übertrag c zur nächsten Stelle:

x y z c

0 0 0 00 1 1 01 0 1 01 1 0 1

woraus sofort die Darstellungen

z = x⊕ y; c = x · y

folgen. Ein Schaltnetz hierfür wird Halbaddierer (HA) genannt und ist folgender-maßen aufgebaut:

46

Page 47: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Realisierung eines Halbaddierers

Mit Hilfe eines solchen Halbaddierers können wir nun aber nur die niederwertigsteStelle unserer Summe bestimmen, da hier kein eingehender Übertrag auftritt. Fürdie anderen Stellen benötigen wir eine Erweiterung des Schaltnetzes.

• 2) Addition von zwei Bit mit Übertrag aus vorheriger Stelle:Ist ein Übertrag aus der vorherigen Stelle bei der Addition mit zu berücksichtigen,so ist eine Summe der Form z = x + y + c−1 zu bestimmen, und hierfür wieder-um das Summen– und Übertragsbit zu berechnen. Hier ergibt sich somit folgendeFunktionstafel:x y c−1 z c

0 0 0 0 00 0 1 1 00 1 0 1 00 1 1 0 11 0 0 1 01 0 1 0 11 1 0 0 11 1 1 1 1

Für die Summe gilt:

z = x⊕ y ⊕ c−1.

Der Übertrag zur nächsten Stelle ergibt sich dann zu:

c = x · y + c−1(x⊕ y),

was zu folgendem Schaltnetz führt, welches bereits Gebrauch von oben hergeleitetenHalbaddierern macht:

Realisierung eines Volladdierers mittels zweier Halbaddierer

47

Page 48: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

3.2 Vereinfachung von Schaltnetzen

Zur Vereinfachung von Schaltnetzen können wir die Resolutionsregel der Schaltalgebraanwenden. Diese besagt, das zwei Summanden einer disjunktiven Form, die sich genauin einer Variablen unterscheiden, also in einem Summand kommt xi, im anderen xi vor,und alle anderen Variablen sind gleich, durch ihren gemeinsamen Teil ersetzt werdenkönnen, z.B. x1x2x3 + x1x2x3 = x1x3. Die Resolutionsregel darf auf einen Summandeneiner disjunktiven Form mehrfach angewendet werden, da x+ x = x gilt und somit eineDoppelung von Summanden möglich ist. Die Regel kann auch iteriert werden, d.h.ãuf dieErgebnisse der ersten Resolution kann wieder die Resolution angewendet werden, fallsein entsprechender Partnerterm existiert.

Beispiel 3.2.1Für die Boolesche Funktion f : B4 → B mit

f(x1, x2, x3, x4) = x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4

ergibt sich die folgende Vereinfachung:

3.2.1 Das Verfahren von Karnaugh

Die Resolution werden wir im Folgenden verwenden, um zwei systematische Verfahren zurVereinfachung Boolescher Schaltfunktionen in DNF kennenzulernen. Das erste Verfahrenist hauptsächlich für den Fall einer Funktion f : Bn → B mit n ∈ 3, 4 gedacht:

Definition 3.2.2 (Karnaugh–Diagramm)Ein Karnaugh Diagramm zu f : Bn → B mit n ∈ 3, 4 ist eine graphische Darstellungder Funktionstabelle von f durch eine 0 − 1–Matrix der Größe 2 × 4 für n = 3 bzw.4 × 4 für n = 4, deren Zeilen mit den möglichen Belegungen von x1 (n = 3) bzw. x1x2(n = 4) und deren Spalten mit den Belegungen von x2x3 (n = 3) bzw. x3x4 (n = 4)beschriftet sind. Die Reihenfolge der Beschriftung erfolgt dabei so, dass sich zwei zyklischbenachbarte Zeilen bzw. Spalten in genau einer Komponente unterscheiden:

48

Page 49: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Das Karnaughverfahren arbeitet dann wie folgt:

• Trage alle Einsen des Funktionsergebnisses in die zugehörige Stelle des Diagrammsein.

• Zwei zyklisch benachbarte Einsen können wegen der gewählten Anordnung mitHilfe der Resolutionsregel zusammengefasst werden und der entstehende Term hatgenau eine Variable weniger. Verallgemeinert bedeutet dies, dass rechteckige 2r ×2s–Blöcke (r, s ∈ 0, 1, 2) von zyklisch benachbarten Einsen 2r × 2s Mintermenentsprechen, die sich paarweise höchstens in r + s Variablen unterscheiden. Durchwiederholte Resolution lässt sich dieser Block zum gemeinsamen Bestandteil allerdieser Minterme vereinfachen.

• Überdecke jede Eins daher mindestens einmal mit einem Block.

• Wähle möglichst große Blöcke der Kantenlänge 2r × 2s. Die Blöcke dürfen hierbeiüber die Kanten und Ecken des Diagramms gebildet werden. Jede Eins kann durchbeliebig viele Blöcke überdeckt werden.

• Lies die Terme, die zu den gebildeten Blöcken gehören, am Diagramm ab. Ein Blockmit 2k Einsen liefert hierbei einen disjunktiven Term mit 2n−k Faktoren.

Beispiel 3.2.3Auf unser obiges Beispiel angewendet ergibt sich also folgendes Karnaugh–Diagramm:

49

Page 50: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Wir erhalten also

f(x1, x2, x3, x4) = x2x4 + x1x3x4.

Beispiel 3.2.4Für die Boolesche Funktion f : B4 → B mit

f(x1, x2, x3, x4) = x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4 (3.1)

+x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4 (3.2)

lassen sich alle Einsen durch zwei Viererblöcke überdecken, so dass sich folgende Verein-fachung der Funktion ergibt:

f(x1, x2, x3, x4) = x2x4 + x2x4

50

Page 51: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Es ist jedoch nicht immer notwendig (oder sinnvoll), die größten möglichen Blöcke aus-zuwählen. Betrachten wir dazu das Karnaugh–Diagramm in folgendem Beispiel

Wählen wir den maximalen Viererblock in der Mitte des Diagramms, so bleiben dievier isolierten Einsen übrig. Überdecken wir diese durch einen Einerblock, so ergibt sichinsgesamt eine Darstellung mit vier Termen von vier Variablen und einem Term mit zweiVariablen. Die isolierten Einsen können wir jedoch unter Verwendung der bereits durchden Vierblock überdeckten Einsen zu Zweierblöcken zusammenfassen, was zu vier Termenmit drei Variablen führen würde. Jetzt ist aber die Überdeckung mit dem Vierblocküberflüssig, da die vier Einsen ja bereits durch die Zweierblöcke erfasst sind. Insgesamtwäre durch die zweite Alternative eine kostengünstigere Darstellung erreicht. Als Fazithalten wir hier fest, dass es nicht immer notwendigerweise die beste Lösung ist, allemaximalen Blöcke zu verwenden.

Bei der Verwendung der Karnaugh–Diagramme ist ein weiterer Umstand ggf. vorteil-haft auszunutzen. Bisher waren wir nämlich davon ausgegangen, dass die darzustellendeFunktion total ist, d.h. für alle möglichen 2n Eingaben (bei n Variablen) ist die Ausgabedefiniert. Es gibt jedoch Fälle, bei denen gewisse Eingaben gar nicht vorkommen, d.h.die Funktion nur partiell ist. Ein Beispiel hierfür ist die Siebensegmentanzeige, wenngewährleistet ist, dass die Ansteuerung mit Argumenten größer als 10 nicht vorkommt.In solchen Fällen werden die nicht vorkommenden Argument–Tupel ’Don’t–care’–Fällegenannt und im Karnaugh–Diagramm mit dem Eintrag eines ’D’ bezeichnet. Bei derÜberdeckung der Einsen können diese Don’t–care–Fälle wie Einsen behandelt werden,wenn hierdurch größere Blöcke entstehen. Es ist aber nicht notwendig, alle Don’t caresim Karnaugh–Diagramm zu überdecken. Folgendes Beispiel nutzt dies aus:

Beispiel 3.2.5Sei f für x ∈ 0, . . . , 9 definiert durch f(x) :=

1 falls x ∈ 1, 5, 8, 90 sonst

.

Da wir für die zehn möglichen Argumente 4 Bit zur Codierung benötigen, ergibt sich fürf eine Funktion von B4 → B, bei der 6 Argumente nicht auftreten. Im Folgenden sehenwir die zugehörigen Karnaugh–Diagramme ohne und mit Ausnutzung der Don’t cares.

51

Page 52: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Im ersten Fall ergibt sich

f(x1, x2, x3, x4) = x1x3x4 + x1x2x3.

Mit Ausnutzung der Don’t cares erhalten wir jedoch die kürzere Darstellung

f(x1, x2, x3, x4) = x3x4 + x1.

Das Karnaugh–Diagramm kann auch zur Erstellung von verkürzten Ringsummendarstel-lungen (nicht Reed–Muller Form) verwendet werden. Hierzu trägt man neben den Einsenauch alle Nullen in das Diagramm ein, und erzeugt nun Blöcke, die auch inhomogen seindürfen, d.h. sowohl Nullen als auch Einsen enthalten. Hierbei ist jedoch darauf zu ach-ten, dass jede 1 durch eine ungerade Anzahl und jede Null durch eine gerade Anzahl(oder keinmal) von Blöcken überdeckt wird. Da die Ringsumme eine gerade Anzahl vonEins–Summanden in der Summe zu Null macht, wird hierdurch eine korrekte Darstellungerzielt.Beispiel 3.2.6Für die Funktion

f(x1, x2, x3, x4) = x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4

ergibt sich das folgende Diagramm:

52

Page 53: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Hieraus erhalten wir also die Ringsummenform

f(x1, x2, x3, x4) = x4 ⊕ x1x2x4 ⊕ x1x2x3x4

Bemerkung:Die Beschriftung der Ränder des Karnaugh–Diagramms hat nach Konstruktionsvorschriftso zu erfolgen, dass sich benachbarte Felder in horizontaler und vertikaler Richtung angenau einer Stelle unterscheiden. Dieses Prinzip stammt vom Gray–Code, der z.B. für dieCodierung der Dezimalzahlen verwendet wird und in A/D–Wandler zu Einsatz kommt.Der Code ist ebenfalls so gebaut, dass sich der Code aufeinanderfolgender Ziffern (undzwar zyklisch, d.h. der 9 folgt die 0) an einem Bit unterscheidet. Ein solcher Gray–Codeist z.B.

x Gray–Code zu x Alternativer Code0 0000 00001 0001 00012 0011 00113 0010 00104 0110 01105 0111 11106 0101 10107 0100 10118 1100 10019 1000 1000

Wie wir sehen, gibt es natürlich unterschiedliche Möglichkeiten, einen Gray–Code zuerzeugen.

3.2.2 Das Verfahren von Quine und McCluskey

Bisher haben wir mit dem Verfahren von Karnaugh eine Möglichkeit kennengelernt, mitder wir Boolesche Funktionen von 3 und 4 Variablen vereinfachen können. Das Verfahrenlässt sich auch auf n = 5 und n ≥ 6 übertragen, erfordert aber z.B. für n = 5 schon dieVerwendung von zwei Oberflächen eines Würfels, und es ist somit nicht leicht zu erkennen,wo die Blöcke maximaler Einsen sind. Für n ≥ 6 ist dies noch schlechter möglich.

Im Folgenden wollen wir daher ein weiteres Verfahren, welches für beliebige n gilt, erarbei-ten. Hierzu sind jedoch einige vorbereitende Überlegungen und Definitionen notwendig.

Definition 3.2.7Eine Boolesche Funktion f : Bn → B liegt in disjunktiver Form vor, wenn f als Summevon Termen

f =k∑

i=1

mi, k ≥ 1

53

Page 54: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

dargestellt ist. Ein Term mi hat dabei die Form

mi =l∏

j=1

xαj

ij, l ≥ 1

.

Die DNF ist somit eine disjunktive Form, bei der alle mi Minterme sind, und somit aus nFaktoren bestehen. Terme einer beliebigen disjunktiven Form enthalten im Allgemeinenweniger als n Faktoren. Zu solchen disjunktiven Formen wollen wir nun ein Kostenmaßdefinieren:

Definition 3.2.8Sei f : Bn → B eine Boolesche Funktion, und sei d eine Darstellung von f in disjunktiverForm. Für d erklären wir die Kosten K(d) wie folgt:

(i) Für d ≡ xα1

i1· xα2

i2· . . . · xαt

it: K(d) := t− 1

(ii) Für d ≡ m1 + m2 + . . .+ mk : K(d) := (k − 1) +∑k

i=1K(mi).

Obige Definition bedeutet also, dass jede der Operationen Addition und Multiplikationdie Kosten Eins verursacht. Somit ist das folgende Problem zu lösen:

Vereinfachungsproblem Boolescher Funktionen

Bestimme zu einer gegebenen Booleschen Funktion f : Bn → B eine die Funktion dar-stellende disjunktive Form d, so dass deren Kosten minimal sind, d.h. es gilt:

K(d) = mindisjunktive Formen d′,

die f darstellen

K(d′).

Definition 3.2.9Sei f : Bn → B eine Boolesche Funktion. Ein Term µ heißt Implikant von f , kurz µ ≤ f ,falls für alle x ∈ Bn gilt:

µ(x) = 1⇒ f(x) = 1.

Ein Implikant µ heißt Primimplikant, falls keine echte Verkürzung von µ ebenfalls Impli-kant von f ist.

Bemerkungen:

1. Minterme zu einschlägigen Indizes einer Funktion f sind Implikanten von f .

2. Ist µ Implikant von f und m ein Minterm von f derart, dass µ eine Verkürzungvon m ist, so gilt m ≤ µ, d.h. m ist Implikant von µ.

54

Page 55: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

3. Im Karnaugh–Diagramm entsprechen rechteckige Blöcke von Einsen den Implikan-ten und die maximalen Blöcke den Primimplikanten.

Satz 3.2.10Sei f : Bn → B eine Boolesche Funktion, f 6≡ 0. Ist d = µ1 + µ2 + . . . + µk eineDarstellung von f als disjunktive Form mit minimalen Kosten, so sind die µi, i = 1, . . . , k, Primimplikanten von f .

Beweis Da f eine Disjunktion der µi ist, ist jedes µi eine Implikant von f . Nehmenwir an, ein µi ist kein Primimplikant, so existiert eine Verkürzung ν von µi, so dass νImplikant von f ist. Ersetzt man in obiger disjunktiver Darstellung d µi durch ν, soerhält man eine neue disjunktive Darstellung von f , wobei K(ν) < K(µi) gilt, d.h. dieneue Darstellung hat geringere Kosten, was ein Widerspruch zur Minimalität von d ist.

Unser Vereinfachungsproblem lässt sich somit auf die folgenden zwei Schritte reduzieren:

1.) Bestimme alle Primimplikanten von f .

2.) Treffe eine kostenminimale Auswahl der Primimplikanten, so dass deren Summe fdarstellt.

Das sich daraus ergebende Verfahren wurde zuerst 1952 von W. Quine und E. McCluskeyangegeben und sieht wie folgt aus:

Algorithmus 3.2.11 (Quine & McCluskey)Eingabe: Funktionstabelle (i, f(i)), i ∈ Bn, f : Bn → B

Ausgabe: PI(f), Darstellung von f durch Primimplikanten

Schritt 1: Bestimme Primimplikanten

1.) Berechne Qn := Menge der Minterme aller einschlägigen Indizes von f .Setze j := n; PI(f) := ∅ .

2.) Solange Qj 6= ∅ führe aus:

a) j := j − 1

b) Qj := µ | ∃l : xl, xl 6∈ µ;µxl, µxl ∈ Qj+1

c) Pj+1 := µ | µ ∈ Qj+1; es ex. keine Verkürzung von µ in Qj

d) PI(f) := PI(f) ∪ Pj+1

Schritt 2: Kostenminimale Darstellung durch Primimplikanten

Problem: Auswahl der geeigneten Primimplikanten

Vorgehen: Matrixverfahren (Überdeckungsmatrix Primimplikanten ↔ Minterme)

55

Page 56: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Der Schritt 1 des Algorithmus liefert die Menge der Primimplikanten und hat eine worst–case–Laufzeit von O(3nn2).

Die Bestimmung der minimalen Überdeckung der Menge der Primimplikanten ist NP–vollständig.

Das Vorgehen des Algorithmus wollen wir nun an einem Beispiel erläutern. Da der Teil 2b)aus Schritt 1 wiederum auf der Resolution beruht, und hierfür die Terme an genau einerVariable unterschiedlich sein müssen, geht man nun wie folgt vor:

Man gruppiert die Minterme in Gruppen nach der Anzahl der vorkommenden negiertenVariablen und notiert den zugehörigen Implikanten, den Index in Dualcodierung unddie dezimale Nummer des Minterms. Nun können nur zwei Terme aus benachbartenGruppen mit Hilfe der Resolution verkürzt werden. Für den verkürzten Term notierenwir wiederum den Implikanten, den Index, der nun allerdings an der verkürzten Positioneinen ’*’ enthält sowie alle Nummern der Minterme, die durch den verkürzten Implikantenrepräsentiert werden. Man erhält so schrittweise neue Tabellen, die nach dem gleichenVorgehen bearbeitet werden, bis keine Änderung mehr eintritt. Wird ein Implikant einerGruppe in einem Schritt nicht verwendet, so ist dies bereits ein Primimplikant, da erin keinem späteren Schritt mehr verwendet werden kann (die Implikanten werden vonSchritt zu Schritt kürzer). Betrachten wir das Vorgehen nochmals am Beispiel:

Beispiel 3.2.12Gegeben sei eine Funktion f : B4 → B als DNF in der Form

f = x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4 + x1x2x3x4

Die Gruppierung nach Anzahl der negativen Terme ergibt folgende Tabelle:

56

Page 57: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Gruppe Minterm einschlägiger Index Minterm–Nummer1 x1x2x3x4 1011 11

x1x2x3x4 1101 13x1x2x3x4 1110 14

Q4 2 x1x2x3x4 0110 6x1x2x3x4 1100 12

3 x1x2x3x4 0100 44 x1x2x3x4 0000 0

P4 1 x1x2x3x4 1011 11x2x3x4 *110 6, 14x1x2x3 110* 12, 13

Q3 x1x2x4 11*0 12, 142 x1x2x4 01*0 4, 6

x2x3x4 *100 4, 123 x1x3x4 0*00 0, 41 x1x2x3x4 1011 11

P3 x1x2x3 110* 12, 13Q2 = P2 x2x4 *1*0 4, 6, 12, 14P3 3 x1x3x4 0*00 0, 4

Verwendet man alle Primimplikanten, so ergibt sich die Darstellung

f = x1x2x3x4 + x1x2x3 + x2x4 + x1x3x4.

Vergleicht man die Kosten dieser Darstellung mit der der DNF, so ergibt sich hierfürK(d) = 11 und K(DNF ) = 27 . Wir haben hier allerdings noch nicht den Schritt 2 desAlgorithmus durchgeführt, d.h. eine kostenminimale Überdeckung der Primimplikantenbestimmt. Im Allgemeinen ist nämlich nicht die Menge aller Primimplikanten notwendig,um die Funktion f in disjunktiver Form darzustellen. Eine solche Menge zu finden ist,wie bereits bemerkt, ein NP–vollständiges Problem. Das folgende Verfahren liefert jedochmit einer Heuristik eine recht gute Näherung für die optimale Lösung.

Wir halten dazu den in Schritt 1 festgestellten Zusammenhang zwischen Primimplikantund Minterm (letzte Spalte der Tabelle) in einer Matrix A = (aij) fest. Hierbei wird aijgleich eins gesetzt, falls der i–te Primimplikant ein Implikant des j–ten Minterms ist. Fürunser Beispiel ergibt sich:

Minterm 0 4 6 11 12 13 14Primimplikantx1x2x3x4 0 0 0 1 0 0 0x1x2x3 0 0 0 0 1 1 0x2x4 0 1 1 0 1 0 1x1x3x4 1 1 0 0 0 0 0

57

Page 58: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Aus dieser Matrix ist nun eine Auswahl der Primimplikanten (Zeilen) so zu treffen, daseinerseits die Kosten minimal sind und andererseits in der aus den ausgewählten Zeilenresultierenden Teilmatrix in jeder Spalte mindestens eine 1 enthalten ist. Im Beispielerkennt man, dass alle vier Primimplikanten notwendig sind, um alle Minterme zu über-decken, womit die obige disjunktive Darstellung mit K(d) = 11 die kostengünstigsteist.

Allgemein kann man für die Lösung des Vereinfachungsproblems folgende Heuristik ver-wenden:

• Bestimme alle Primimplikanten mit dem Verfahren von Quine–McCluskey

• Wähle aus der Menge der Primimplikanten sukzessive solche aus, die möglichstviele einschlägige, noch nicht überdeckte Minterme neu überdecken.

Bei dieser Heuristik werden alle absolut notwendigen Primimplikanten ausgewählt, undes ist bekannt, dass auch im ungünstigsten Fall gilt:

Anzahl der durch die Heuristik ausgewählten PrimimplikantenAnzahl der minimal erforderlichen Primimplikanten

≤ 1 + lnm

wobei mit m die Anzahl der einschlägigen Minterme bezeichnet sei.

3.3 Fehlerdiagnose von Schaltnetzen

In diesem Abschnitt wollen wir zwei Methoden zur Fehlerdiagnose von Schaltnetzendiskutieren. Man denke hierbei z.B. an eine CPU, die ein Schaltnetz mit mehr als 106

Bauteilen darstellt. Hier kann man sicherlich nicht alle möglichen Eingaben anlegen unddie Ausgaben überprüfen, da dies viel zu aufwändig wäre. Auch kann man wegen dergeringen Fertigungsgröße (Kantenlänge ca. 4 cm) nicht nach gerissenen Verbindungensuchen. Im Allgemeinen werden solche Chips als Ganzes getestet, und es besteht nun dieAufgabe, eine möglichst kleine Testmenge zu bestimmen, um einen Chip möglichst genauzu testen, d.h. wähle eine Teilmenge aller möglichen Eingaben aus, die Aufschluss überdie Qualität der Schaltung gibt.

Wir werden hierzu zwei verschiedene Verfahren kennenlernen, die Fehler bestimmter Artmit möglichst wenigen Testtupeln erkennen, allerdings nicht notwendigerweise lokalisie-ren. Die erste Methode ist die schaltungsabhängige Fehlerdiagnose, bei der wir von einemgegebenen Schaltnetz ausgehen und folgende Grundannahme machen:

• a) Es tritt im gegebenen Schaltungsnetz höchstens ein Fehler auf.

• b) Der Defekt, welcher einen Fehler verursacht, ist ein gerissener Draht.

58

Page 59: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Annahme a) kann man eventuell aufgrund der Güte des Fertigungsprozesses und derdamit verbundenen Fehlerwahrscheinlichkeit treffen, Annahme b) beschreibt den wahr-scheinlichsten Fehler. Andere Fehler könnten natürlich defekte Gatter oder andere defekteHalbleiter sein. Bei Annahme b) geht man im Allgemeinen von einer sog. 0–Verklemmungaus, d.h. ein gerissener Draht leitet keinen Impuls und erzeugt am Eingang eine 0. Jenach verwendeter Technologie kann aber auch eine 1–Verklemmung sinnvoll sein, d.h.bei einem gerissenen Draht liegt am Eingang eine 1 an. Unter diesen Annahmen wol-len wir nun die Diagnose–Methode anhand des folgenden Beispiels (vgl. Beispiel 2.3.9)diskutieren:

Gehen wir von einem DAG zu einem Schaltnetz aus, bei dem die verschiedenen Drähtedurchnumeriert sind. Wir erhalten für unser Beispiel den folgenden DAG:

Wir definieren nun zu den Drahtnummern i = 1, . . . , 18 die zugehörige Funktion fi, dieentsteht, wenn der Draht i reißt und hierdurch eine 0–Verklemmung auftritt. Wir erhaltendann die 18 verschiedenen Funktionen.

59

Page 60: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Aus f(x1, x2, x3) = x1x2x3 + x1x2x3 + x1x2x3 ergibt sich dann:

f1(x1, x2, x3) = 0x2x3 + x1x2x3 + x1x2x3 = x2x3 + x1x3

f2(x1, x2, x3) = 0x2x3 + x1x2x3 + x1x2x3 = x1x3

f3(x1, x2, x3) = x1x2x3 + 0x2x3 + x1x2x3 = x2x3

f4(x1, x2, x3) = x1x2x3 + x1x2x3 + 0x2x3 = x1x2x3 + x1x2x3

f5(x1, x2, x3) = x10x3 + x1x2x3 + x1x2x3 = x1x3

f6(x1, x2, x3) = x1x2x3 + x10x3 + x1x2x3 = x2x3 + x1x3

f7(x1, x2, x3) = x1x2x3 + x10x3 + x1x2x3 = x2x3

f8(x1, x2, x3) = x1x2x3 + x1x2x3 + x10x3 = x1x2x3 + x1x2x3

f9(x1, x2, x3) = x1x20 + x1x2x3 + x1x2x3 = x1x3

f10(x1, x2, x3) = x1x2x3 + x1x20 + x1x2x3 = x2x3

f11(x1, x2, x3) = x1x2x3 + x1x2x3 + x1x20 = x1x2x3 + x1x2x3

f12(x1, x2, x3) = 0x3 + x1x2x3 + x1x2x3 = x1x3

f13(x1, x2, x3) = x1x2x3 + 0x3 + x1x2x3 = x2x3

f14(x1, x2, x3) = x1x2x3 + x1x2x3 + 0x3 = x1x2x3 + x1x2x3

f15(x1, x2, x3) = 0 + x1x2x3 + x1x2x3 = x1x3

f16(x1, x2, x3) = x1x2x3 + 0 + x1x2x3 = x2x3

f17(x1, x2, x3) = 0 + x1x2x3 = x1x2x3

f18(x1, x2, x3) = x1x2x3 + x1x2x3 + 0 = x1x2x3 + x1x2x3

Zu diesen Fehlerfunktionen (hier f1 bis f18) wird die Wertetabelle, die sogenannte Aus-falltafel erstellt.

Wir erhalten in unserem Beispiel die Ausfalltafel in Tabelle 3.3.

Diese Ausfalltafel lässt sich zur sogenannten Ausfallmatrix verkürzen, indem man dop-pelte Spalten eliminiert, d.h. gleiche Fehlerfunktionen zusammenfasst. Die Ausfallmatrixin unserem Beispiel ergibt sich aufgrund der folgenden Gleichheiten zu:

f1 = f6 = f

f2 = f5 = f9 = f12 = f15

f3 = f7 = f10 = f13 = f16

f4 = f8 = f11 = f14 = f18

Die zu den Spalten der Ausfallmatrix in Tabelle 3.3 gehörenden Fehlerfunktionen fiwerden nun mit der Originalfunktion f mit XOR verknüpft, wodurch die sogenannteFehlermatrix entsteht, die an den Stellen, an denen sich die Fehlerfunktionen von derOriginalfunktion unterscheiden, eine 1, an den anderen Stellen eine 0 besitzt.

Wir erhalten hier die Fehlermatrix in Tabelle 3.3.

60

Page 61: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

x1 x2 x3 f1 f2 f3 f4 f5 f6 f7 f8 f90 0 0 0 0 0 0 0 0 0 0 00 0 1 0 0 0 0 0 0 0 0 00 1 0 0 0 0 0 0 0 0 0 00 1 1 1 0 1 1 0 1 1 1 01 0 0 0 0 0 0 0 0 0 0 01 0 1 1 1 0 1 1 1 0 1 11 1 0 0 0 0 0 0 0 0 0 01 1 1 1 1 1 0 1 1 1 0 1

x1 x2 x3 f10 f11 f12 f13 f14 f15 f16 f17 f180 0 0 0 0 0 0 0 0 0 0 00 0 1 0 0 0 0 0 0 0 0 00 1 0 0 0 0 0 0 0 0 0 00 1 1 1 1 0 1 1 0 1 0 11 0 0 0 0 0 0 0 0 0 0 01 0 1 0 1 1 0 1 1 0 0 11 1 0 0 0 0 0 0 0 0 0 01 1 1 1 0 1 1 0 1 1 1 0

Tabelle 3.1: Ausfalltafel

x1 x2 x3 f1 f2 f3 f4 f170 0 0 0 0 0 0 00 0 1 0 0 0 0 00 1 0 0 0 0 0 00 1 1 1 0 1 1 01 0 0 0 0 0 0 01 0 1 1 1 0 1 01 1 0 0 0 0 0 01 1 1 1 1 1 0 1

Tabelle 3.2: Ausfallmatrix

61

Page 62: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Zeilen–Nr. x1 x2 x3 f ⊕ f1 f ⊕ f2 f ⊕ f3 f ⊕ f4 f ⊕ f170 0 0 0 0 0 0 0 01 0 0 1 0 0 0 0 02 0 1 0 0 0 0 0 03 0 1 1 0 1 0 0 14 1 0 0 0 0 0 0 05 1 0 1 0 0 1 0 16 1 1 0 0 0 0 0 07 1 1 1 0 0 0 1 0

Tabelle 3.3: Fehlermatrix

Das Ziel ist es nun, aus der Menge aller möglichen Eingaben (hier 23 = 8) eine minimaleTeilmenge so auszuwählen, dass alle Fehler erkannt werden, d.h. dass jede Spalte mitmindestens einer 1 in der Fehlermatrix mindestens einmal überdeckt wird. Ein analo-ges Problem hatte sich bereits beim Algorithmus von Quine & McCluskey ergeben. ImBeispiel erkennt man leicht, dass die Eingaben der Zeilen 3, 5, 7 eine solche minimaleTestmenge bilden, d.h. von den 8 möglichen reichen 3 Eingaben aus, um alle Fehler zudiagnostizieren. Man kann hierbei allerdings keinen Rückschluss führen, welcher Draht ge-rissen ist, da zum einen in einer Zeile einer Fehlermatrix mehrere Einsen stehen können,d.h. bei verschiedenen Fehlerfunktionen tritt derselbe Fehler auf, zum anderen führenverschiedene gerissene Drähte zu gleichen Fehlerfunktionen. Auch ist es möglich, dassDefekte nach außen hin gar nicht relevant sind, da hierdurch die gleiche Funktion wie dieOriginalfunktion entsteht (vgl. f1). Dies tritt dadurch auf, dass wir in unserem Beispielnicht von der verkürzten Form, sondern von der DNF ausgegangen sind.

Die zweite Methode zur Fehlerdiagnose ist die schaltungsunabängige. Hierbei ist eineTestmenge gesucht, die unabhängig von der speziellen Implementierung Fehler, die derfolgende Fehlerannahme genügen, erkennt:

Es tritt ein Defekt auf, welcher die tatsächliche Abhängigkeit von f von der i–ten Variablezerstört.

Durch diese recht allgemeine Annahme werden jedoch nicht alle möglichen Fehler abge-deckt.

Beispiel 3.3.1Sei f : B3 → B definiert durch

f(x1, x2, x3) = x1x3 + x2

Da f(0, 0, 1) = 1 und f(1, 0, 1) = 0 gilt, hängt f offensichtlich von x1 ab. Ist nunirgendeine Realisierung von f gegeben, so kann man durch Anlegen der beiden Inputs(0, 0, 1) und (1, 0, 1) testen, ob in der Schaltung die Abhängigkeit von x1 gegeben ist. Diebeiden Inputs sind somit ein Testpaar für die Eingabe x1.

62

Page 63: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Allgemein gilt:

Definition 3.3.2Sei f : Bn → B eine Boolesche Funktion. Ein n–Tupel a ∈ Bn heißt dann Test. ZweiTests a und b bilden ein f–Testpaar zu xi, wenn sich a und b nur genau an der Stelle iunterscheiden und f(a) 6= f(b) gilt. Eine minimale Testmenge ist dann eine Menge T vonTests, so dass zu jeder Variablen xi, von der f tatsächlich abhängt, ein Testpaar a, bexistiert mit a, b ⊆ T und die minimal bzgl. Mengeninklusion unter allen möglichenTestmengen ist.

In obigem Beispiel ergeben sich also die folgenden Testpaare:

x1 : (0, 0, 1) und (1, 0, 1)

x2 : (1, 0, 0) und (1, 1, 0)

(1, 0, 1) und (1, 1, 1)

(0, 0, 0) und (0, 1, 0)

x3 : (0, 0, 0) und (0, 0, 1)

Hieraus ergibt sich als minimale Testmenge

(0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1)

bzw.

(0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 1).

Somit reichen also vier Tests aus, um unter obiger Fehlerannahme jede beliebige Reali-sierung von f auf Defekte zu testen.

3.4 Hazards in Schaltnetzen

Bisher haben wir bei unseren Betrachtungen von Schaltnetzen stets technisch physi-kalische Zusammenhänge ausgeklammert. Aber auch diese Zusammenhänge können dieZuverlässigkeit von Schaltnetzen beeinflussen, z.B. die Signallaufzeiten durch unterschied-lich tiefe Teilschaltungen etc. Wir wollen dies im Folgenden diskutieren und treffen fol-gende Annahmen:

1. Jedes Signal, welches ein Gatter durchläuft, hat eine zwar kurze, aber nicht zuvernachlässigende Laufzeit.

2. Änderungen von Inputsignalen an verschiedenen Eingängen, welche logisch gleich-zeitig erfolgen, können im Allgemeinen physikalisch nicht gleichzeitig erfolgen.

3. Signallaufzeiten können für unterschiedliche Gattertypen, die in einem Schaltnetzverwendet werden, unterschiedlich sein (hängt z.B. vom FanIn und FanOut ab).

63

Page 64: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Diese Annahmen haben für das tatsächliche Verhalten einer Schaltung Konsequenzen.So kann z.B. das nicht gleichzeitige Wechseln von Inputs nach Annahme 2 dazu führen,dass kurzzeitig am Output falsche Werte anliegen (Flimmern am Output), was eventuellunerwünscht oder sogar verboten sein kann. Betrachten wir hierzu das folgende Beispiel:

Beispiel 3.4.1Sei f(x1, x2, x3) = x1x3 + x2x3. Es gilt:

f(1, 1, 0) = 1

f(1, 1, 1) = 1

f(1, 0, 0) = 0

f(1, 0, 1) = 1

Falls nun bei einer Realisierung der Funktion durch ein Schaltnetz von der Eingabe(1,1,0) auf die Eingabe (1,0,1) umgeschaltet werden soll, und wir davon ausgehen, dassdie beiden zu ändernden Inputbits nicht gleichzeitig umgeschaltet werden, so ergebensich zwei Umschaltungsreihenfolgen:

(1, 1, 0) → (1, 0, 0)→ (1, 0, 1) oder

(1, 1, 0) → (1, 1, 1)→ (1, 0, 1)

Im ersten Fall wird der Output kurzzeitig 0, im zweiten Fall bleibt er stets 1, d.h. hierist die zweite Schaltreihenfolge zu bevorzugen.

Ein Phänomen, wie es in obigem Beispiel beschrieben ist, wird Hazard (engl. Gefahr,Risiko) genannt. Man unterscheidet hier verschiedene Typen von Hazards, einmal dieFunktionshazards, die unabhängig von der konkreten Realisierung durch das Übergangs-verhalten der Funktion gegeben sind, zum anderen Schaltungshazards, die durch die kon-krete Realisierung erzeugt werden. Weiter wird nach statischen und dynamischen Hazardsunterschieden. Der erste Typ bewirkt eine (zwischenzeitliche) unerwünschte Änderungdes Outputs, der bei Inputwechsel konstant bleibt, dynamische Hazards können auftreten,wenn bei einem Wechsel des Outputs dieser erst nach einem kurzen Flimmern konstantwird. Im Folgenden werden wir uns nur mit den statischen Hazards beschäftigen, die wirhier nun präzisieren wollen:

Definition 3.4.2Sei f : Bn → B eine Boolesche Funktion und seien a0 ∈ Bk (1 ≤ k ≤ n), a1 ∈ Bn−k , a =(a0, a1), b = (a0, a1), wobei die Mengenklammern bedeuten, dass die Komponentenvon a0 und a1 den Vektor a bilden, aber die Reihenfolge nicht festgelegt ist. f besitzteinen (statischen) Funktionshazard beim Inputwechsel von a nach b, falls gilt:

(i) f(a) = f(b);

(ii) es gibt ein a′1 ∈ Bn−k so, dass für c = (a0, a

′1) gilt: f(a) 6= f(c)

64

Page 65: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Funktionshazards für Funktionen f : Bn → B mit n = 3 oder n = 4 lassen sich amKarnaugh–Diagramm ablesen. Hier sind zu jedem Paar (a, b) von Eingaben, die gleicheFunktionswerte besitzen, alle kürzesten Wege im Karnaugh–Diagramm von a nach bzu bestimmen, die nur horizontale oder vertikale Schritte (in jedem Schritt kippt einInput) benutzen (hierbei sind auch wieder Wege über die Ränder zu berücksichtigen).Unterscheidet sich der Eintrag im Diagramm in einem Feld dieses Weges vom Eintrag imAusgangsfeld (und damit auch vom Endfeld), so liegt ein Hazard vor. Wir können hiernoch zwischen vermeidbarem und unvermeidbarem Hazard unterscheiden. Ein Hazard isthier vermeidbar, wenn es mindestens einen kürzesten Weg von a nach b gibt, bei dem nurgleiche Einträge (also gleiche Funktionswerte) im Diagramm vorliegen. Liegen auf allenkürzesten Wegen unterschiedliche Funktionswerte vor, so ist der Hazard unvermeidbar,da er bei allen denkbaren Schaltreihenfolgen auftritt.

Betrachten wir dies wiederum an einem Beispiel:

Beispiel 3.4.3Gegeben sei eine Funktion f : B4 → B durch folgendes Karnaugh–Diagramm

Betrachten wir nun die Inputwechsel, die zu Funktionswerten 1 gehören, so ergeben sichfolgende Hazards:

0000 ↔ 0101 unvermeidbar0000 ↔ 1100 vermeidbar0000 ↔ 1101 vermeidbar0000 ↔ 0111 unvermeidbar0000 ↔ 1111 vermeidbar1100 ↔ 0101 vermeidbar1100 ↔ 1111 vermeidbar1100 ↔ 0111 vermeidbar1000 ↔ 0101 unvermeidbar1000 ↔ 0111 vermeidbar1000 ↔ 1101 vermeidbar1000 ↔ 1111 vermeidbar

65

Page 66: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Auf analoge Weise könnte man dynamische Hazards erkennen. Hier werden Paare un-tersucht, die unterschiedliche Funktionswerte besitzen. Tritt der Wechsel von 0 nach 1(bzw. umgekehrt) auf einem kürzesten Weg mehrfach auf, so bedeutet dies ein Flimmernam Ausgang, also einen dynamischen Funktionshazard.

Betrachten wir nun zum Abschluss des Kapitels noch die Schaltungshazards, die sichals Folge von unterschiedlichen Signallaufzeiten ergeben. Wir gehen hierbei von einerkonkreten Realisierung einer Booleschen Funktion aus. Wir wollen das Phänomen zuerstan einem Beispiel studieren:

Beispiel 3.4.4Gegeben sie die Boolesche Funktion f(x1, x2, x3) = x1x3 + x2x3, die durch folgendesSchaltnetz realisiert sei:

1

B

D

E

A

C& &

x1

x2

x3

³ 1

Wir wollen einen Input–Wechsel von (1,1,1) nach (1,1,0) untersuchen. Hier hat die Funk-tion jeweils den Funktionswert 1, und es liegt dort kein Funktionshazard vor, da es beidiesem Übergang kein ”Zwischentupel” gibt. Beim Umschalten des Inputs x3 von 1 nach0 sind in der Schaltung die Signalwege ACE und BDE zu durchlaufen, wobei wir an-nehmen, dass wegen des Inverters im Weg BDE dieser eine längere Schaltzeit benötigt.Hierdurch bedingt, hat der Ausgang von Gatter C aber bereits eine 0, wenn der Ausgangvon D noch eine 0 hat, was kurzzeitig zu zwei Nullen am Eingang von E und damitauch am Ausgang von E bewirkt. Hierdurch entsteht ein kurzzeitiges Fehlverhalten derSchaltung.

Wir wollen nun noch die Definition des Schaltungshazards genauer notieren und einenSatz angeben, der ein Kriterium zur Vermeidung von Schaltungshazards angibt.

66

Page 67: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Definition 3.4.5Sei f : Bn → B eine Boolesche Funktion, S ein Schaltnetz, welches f realisiert, unda, b ∈ Bn. S besitzt einen statischen Schaltungshazard (logischen Hazard) für den Input–Wechsel von a nach b, falls gilt:

(i) f(a) = f(b)

(ii) f besitzt keinen Funktionshazard für den Wechsel von a nach b.

(iii) während des Wechsels von a nach b ist am Ausgang von S eine vorübergehendeFehlfunktion beobachtbar.

Satz 3.4.6 (Eichelberger 1965)Ein zweistufiges Schaltnetz S für eine Boolesche Funktion f in disjunktiver Form istfrei von statischen Schaltungshazards, wenn die UND–Gatter von S in einer 1–1–Korres-pondenz zu den Primimplikanten von f stehen, d.h. jedes UND–Gatter von S realisiertgenau einen Primimplikanten von f und jedem Primimplikanten entspricht genau einUND–Gatter in S.

67

Page 68: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

4 Schaltwerke

Nachdem wir im letzten Kapitel (asynchrone) Schaltnetze kennengelernt haben, wollenwir uns nun mit sogenannten Schaltwerken befassen. Hierbei werden ggf. Teilergebnisse(Ausgaben) wieder in die Schaltung eingespeist, um in zeitlicher Abfolge ein bestimmtesVerhalten einer Schaltung zu erzielen, z.B. bei Zählerschaltungen, wo die neue Ausga-be von letzten Zählerstand abhängt. Hierzu ist es notwendig, gewisse Verzögerungs–und Speicherelemente in ein Schaltung einfließen zu lassen und eine Schaltung mit Hilfeeines Taktsignals zu steuern. Wir wollen daher zuerst einige Bausteine, die als solcheVerzögerungs– und Speicherelemente dienen können, kennenlernen.

4.1 Flip–Flops

Unter einem Flip–Flop (FF) versteht man ein Speicherglied, das zwei stabile Zustän-de einnehmen kann. Durch geeignete Ansteuerung lässt sich das Flip–Flop von einemZustand in den anderen überführen.

Um die Funktionsweise des RS–Flip–Flops, das wir später betrachten wollen, näher zuerklären, wollen wir zunächst die bistabile Kippstufe, die aus zwei NAND–Gattern auf-gebaut ist, untersuchen. Dabei geben wir an den beiden Gattern die Signale x und y zurZeit t ein. Am Ausgang Q liegt dann nach Durchlauf durch das Gatter Q = x + P an,am Ausgang P dagegen P = y +Q.

Q = +x P

P = +y Q

&

&

x

y

tt

t

t + tD

t + tDt

t

t

Q

P

Dabei wird an jedem NAND–Gatter als zweites Signal jeweils das Ausgangssignal desanderen eingespeist. Nun kommt es entscheidend darauf an, welches der Gatter zuerstschaltet. Ist es das Q–Gatter und ist die Laufzeit des Signals ∆t, so ergibt sich

Qt+∆t = xt + P t, Pt+∆t = yt +Qt+∆t,

anderenfalls jedoch gerade umgekehrt

Qt+∆t = xt + P t+∆t, Pt+∆t = yt +Qt.

68

Page 69: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Wir wollen nun in einer Tabelle bei festen Eingangssignalen x und y das Verhalten imZeitablauf an den Ausgängen in Abhängigkeit von der Schaltreihenfolge notieren.

Schaltet P zuerst, so erhalten wir:

xt yt Pt+∆t = yt +Qt Qt+∆t = xt + P t+∆t Pt+2∆t = yt +Qt+∆t Qt+2∆t = xt + P t+2∆t

0 0 1 1 1 1

0 1 Qt 1 0 1

1 0 1 0 1 0

1 1 Qt Qt Qt Qt

Schaltet dagegen Q zuerst, so ergibt sich das folgende Bild:

xt yt Qt+∆t = xt + P t Pt+∆t = yt +Qt+∆t Qt+2∆t = xt + P t+∆t Pt+2∆t = yt +Qt+2∆t

0 0 1 1 1 1

0 1 1 0 1 0

1 0 P t 1 0 1

1 1 P t Pt P t Pt

Man erkennt das folgende Verhalten:

• Nach zwei Takten ist die Ausgabe eingeschwungen und hängt nur noch von derSchaltreihenfolge und den Eingabewerten x, y ab.

• Die Eingaben (0, 0) führen zu denselben Ausgaben unabhängig von der Schaltungs-reihenfolge der Gatter.

• Während für die beiden Eingaben (1, 0) und (0, 1) immer P = Q gilt, muss dieseletzte Bedingung gefordert werden, damit das Ergebnis bei der Eingabe von (1, 1)von der Schaltreihenfolge unabhängig ist.

Das hat jedoch zur Folge, dass die Eingabe (0, 0) ausgeschlossen werden muss, da sie dieAusgabe (1, 1) zur Folge hat. Dies verletzt jedoch die Bedingung P = Q und hat einunbestimmtes Ergebnis in der bistabilen Kippstufe zur Folge.

Fassen wir zusammen. Bei der bistabilen NAND–Kippstufe kann durch eine geeigneteEingabe am Ausgang Q eine 1 (Set) oder 0 (Reset) erzeugt werden, die Eingabe (1, 1)bewirkt ein Halten des Zustandes Q. Dagegen ist die Eingabe (0, 0) verboten. Da jedochbei Schaltvorgängen von (0, 1) → (1, 0) immer die Gefahr besteht, dass kurzfristig dieEingabe (0, 0) erzeugt wird, geht man direkt zum RS–Flip–Flop über.

Das RS–FF hat zwei Eingänge R (Reset) und S (Set). Liegen beide Eingänge R undS auf 0, so behält das FF seinen Zustand bei. Ist R = 1 und S = 0, dann nimmt dasFF den Zustand Q = 0 an, das FF wird zurückgesetzt. Ist R = 0 und S = 1, dannwird das FF gesetzt, d.h. Q = 1. Die Eingangskombination R = 1, S = 1 hingegenergibt die mehrdeutige Ausgangssituation Q = 0, Q = 0 und ist deshalb unzulässig. Alle

69

Page 70: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Ansteuerschaltungen sind also so zu entwerfen, dass die Situation R = S = 1 unmöglichist.

Zwischen x und S bzw. y und R finden wir den folgenden Zusammenhang:

x = S + C = SC, y = R+ C = RC,

und die Nebenbedingung S ·R = 0, denn S ·R = 1 entspricht x = y = 0.

Für die Ausgangsfunktion finden wir

Qt+2∆t = xt + P t+∆t

= xt + ytQt

= SC +RCQt

= SC +(R+ C

)Qt

= SC +(RQt + CQt

)

= C · (S +RQt) + CQt.

C ist ein Taktsignal, das periodisch zwischen 0 und 1 hin und herschaltet. Wir erkennen,dass bei C = 0 der Wert von Qt erhalten bleibt. Bei C = 1 schaltet das RS–Flip–Flop,und es gilt das folgende Verhalten beim Übergang vom Zustand n ' t und n+1 ' t+2∆t:

a) Übergangsfunktion:

Qn+1 = S + (R ·Qn), Nebenbedingung: S ·R = 0

b) Wahrheitswertetabelle:

R S Qn+1 Qn+1

0 0 Qn Qn

0 1 1 01 0 0 11 1 1∗ 1∗

(∗ nicht zugelassen)

n deutet hierbei den Zustand vor und n+ 1 den Zustand nach dem Schaltschritt an.

Mittels NAND Gattern sieht eine Realisierung folgendermaßen aus:

S

R

C

Q

S

C

R

Q

Q

Q

&

&

&

&

70

Page 71: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Hierbei gibt das Symbol am Takteingang Aufschluss über das Schaltverhalten des Flip–Flops. Es bedeuten hierbei:

Es existieren nun zwei Abwandlungen des RS–FF, bei denen der verbotene ZustandR=S=1 ausgeschlossen ist. Zum einen das D–Flip–Flop (Delay–FF) bzw. das JK–Flip–Flop, deren Schaltungen und Wahrheitswertetabellen wie folgt aussehen:

Wahrheitswertetabelle des JK–FF:

J K Qn+1 Qn+1

0 0 Qn Qn

0 1 0 11 0 1 01 1 Q

nQn

In der folgenden Schaltung haben wir dann:

R(J,K,Qn) = KQn,

S(J,K,Qn) = JQn,

Qn+1(J,K,Qn) = JQn+KQn.

Genauer gilt die folgende Tabelle:

J K Qn Qn+1 S R Aktion0 0 0 0 0 − lesen0 0 1 1 − 0 lesen0 1 0 0 0 − löschen0 1 1 0 0 1 löschen1 0 0 1 1 0 setzen1 0 1 1 − 0 setzen1 1 0 1 1 0 invertieren1 1 1 0 0 1 invertieren

71

Page 72: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

J

K

C

Q

J

C

K

S

R

Q

Q

Q

&

&

Wahrheitswertetabelle des D–FF:

D Qn+1 Qn+1

0 0 11 1 0

4.2 Sequentielle Schaltungen

Anwendungen von Flip–Flops finden sich z.B. bei Schieberegistern oder Zählerschaltun-gen. Wir wollen nun hierfür exemplarisch einige Beispiele ansehen.

Ein Schieberegister dient dazu, eine Information Bitweise mit jedem Takt zu verschiebenund damit in zeitlichem Abstand am Ausgang bereitzustellen. Im einfachsten Fall kanndas Register den Inhalt nur in einer Richtung verschieben, es handelt sich also um einLinks– oder Rechtsschieberegister. In anderen Fällen kann das Register durch einen odermehrere Steuereingänge beeinflusst werden. Wir wollen hier ein Beispiel betrachten, indem eine Information parallel oder seriell in ein Register eingelesen und auch wiederausgegeben werden kann. In der folgenden Schaltung bewirkt der Schalter E entwederserielles oder paralleles Lesen, der Schalter A schaltet die Ausgänge durch oder belegtdie Ausgabe mit dem Wert 1. Das Register verändert seinen Inhalt mit jedem Takt T .

72

Page 73: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Will man das Verhalten einer mit Speicherbausteinen aufgebauten Schaltung systema-tisch beschreiben und analysieren, eignet sich hierzu ein Mealy–Automat, der als sequen-tielle Maschine betrachtet wird. Diese ist wie folgt definiert:

Definition 4.2.1Eine sequentielle Maschine M = (E,S, Z, δ, γ, s0) (Mealy–Automat) ist beschriebendurch ein EingabealphabetE = e1, . . . , er, einer Menge von Zuständen S = s0, . . . , snmit einem ausgezeichneten Zustand s0, einem Ausgabealphabet Z = z1, . . . , zn sowieder partiell definierten Übergangsfunktion δ : E × S → S und der Ausgabefunktionγ : E × S → Z.

Beispiel 4.2.2 (Modellierung einer 1–Bit Addition durch Automat)Als Eingabemenge E dienen die Codierungen der beiden möglichen Eingaben durch 2Bit, also E = 00, 01, 10, 11. In den Zuständen ist der Übertrag der vorherigen Stellezu speichern, d.h. S = 0, 1 und als Ausgabe erhalten wir das Summenbit, also somitZ = 0, 1. Damit ergibt sich folgender Automat:

Es ergibt sich somit für die Zustände und Eingaben/Ausgaben folgende Übergangstabelle:

73

Page 74: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

S E δ(s, e) γ(s, e)

0 00 0 00 01 0 10 10 0 10 11 1 01 00 0 11 01 1 01 10 1 01 11 1 1

Wollen wir dieses Übergangsverhalten mit Hilfe von Flip–Flops erreichen und wollen wirz.B. JK–FF verwenden, so untersuchen wir, bei welcher Ansteuerung der Eingänge J undK der Ausgang Q das gewünschte Verhalten zeigt, d.h. der Carry des vorhergehendenBits anliegt. Es ergibt sich generell beim Vergleich des Ausgangs Q zur Zeit n und n+1folgendes Bild:

Qn Qn+1 J K

0 0 0 -0 1 1 -1 0 - 11 1 - 0

Wendet man dies hier an, so ergibt sich für die Ansteuerung:

S E δ(s, e) J K

0 0 0 0 0 -0 0 1 0 0 -0 1 0 0 0 -0 1 1 1 1 -1 0 0 0 - 11 0 1 1 - 01 1 0 1 - 01 1 1 1 - 0

74

Page 75: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Beispiel 4.2.3Als ein zweites Beispiel wollen wir einen Modulo–6 Vorwärts–/Rückwärtszähler anschau-en. Der Zähler verfügt über einen Eingang e ∈ 0, 1, der darüber entscheidet, ob vor-wärts oder rückwärts gezählt wird. Hierbei stehe e = 1 für rückwärts Zählen. Die mög-lichen Zählfolgen sind also (jeweils beginnend bei 0) 0 − 1 − 2 − 3 − 4 − 5 − 0 bzw.0 − 5 − 4 − 3 − 2 − 1 − 0. Codieren wir die Zählerstände binär mit 3 Bit, und nehmendiese als Zustandsbeschreibung des zugehörigen Automaten an, so hat unser Mealy–Automat die Zustandsmenge S = 000, 001, 010, 011, 100, 101. Da der Zustand zugleichden Zählerstand darstellt, gilt Z = S. Als Anfangszustand wählen wir 000. Als Über-gangsdiagramm ergibt sich das folgende:

Wollen wir den Zähler mit Hilfe von RS–Flip–Flops aufbauen, so benötigen wir für jedesBit der Zustandscodierung ein Flip–Flop. Der Zustand des Flip–Flops (Wert am Aus-gang Q) ist das entsprechende Bit des momentanen Zählerstandes, und die Ansteuerungder Eingänge ist so zu wählen, dass beim nächsten Taktsignal die Flip–Flops in denFolgezustand übergehen. Wir haben also für die sechs Flip–Flop–Eingänge BoolescheAnsteuerfunktionen zu designen, die von der Eingabe e und den Ausgängen q0, q1, q2 derFlip–Flops abhängen.

Bezeichnen wir den momentanen Ausgabewert eines FF mit Q, und den Wert am Aus-gang im nächsten Takt als Q′, so erhalten wir aus dem Übergangsverhalten der FF diefolgenden Ansteuerungen der Eingänge, wobei ”*” wiederum ein Don’t care bedeutet.

Q Q′ R S

0 0 ∗ 00 1 0 11 0 1 01 1 0 ∗

75

Page 76: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Wenden wir dies nun auf den obigen Ringzähler modulo 6 an, so erhalten wir folgendeTabelle:

e q0 q1 q2 q′0 q′1 q′2 R0 S0 R1 S1 R2 S20 0 0 0 0 0 1 ∗ 0 ∗ 0 0 10 0 0 1 0 1 0 ∗ 0 0 1 1 00 0 1 0 0 1 1 ∗ 0 0 ∗ 0 10 0 1 1 1 0 0 0 1 1 0 1 00 1 0 0 1 0 1 0 ∗ ∗ 0 0 10 1 0 1 0 0 0 1 0 ∗ 0 1 01 0 0 0 1 0 1 0 1 ∗ 0 0 11 0 0 1 0 0 0 ∗ 0 ∗ 0 1 01 0 1 0 0 0 1 ∗ 0 1 0 0 11 0 1 1 0 1 0 ∗ 0 0 ∗ 1 01 1 0 0 0 1 1 1 0 0 1 0 11 1 0 1 1 0 0 0 ∗ ∗ 0 1 0

Hieraus ergeben sich die folgenden Ansteuergleichungen (unter Ausnutzung der don’tcares):

R0 = eq0q2 + eq0q2 = q0 · (e⊕ q2)

S0 = e q0q1q2 + eq0q1q2

R1 = eq1q2 + eq1q2 = q1 · (e⊕ q2)

S1 = e q0q1q2 + eq0q2

R2 = q2

S2 = q2

Als Schaltung ergibt sich dann:

1

&

&

³ 1

= 1

R R R

q0 q

1 q2

FF 0 FF 1 FF 2T T T

S S S

Takt

e

&

&&

³ 1

&

76

Page 77: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

4.3 Lineare Schaltkreise

Definition 4.3.1Ein linearer Schaltkreis über einem Körper K ist ein Schaltwerk, welches aus den folgen-den drei Grundbausteinen aufgebaut ist:

a) Addierer, wobei am Ausgang die Summe der beiden Körperelemente, die an denEingängen angelegt sind, anliegt.

b) Skalar–Multiplizierer, wobei das Körperelement am Eingang mit einem festen Ele-ment a multipliziert wird.

c) Delay, welches ein eingegebenes Körperelement für die Länge eines Taktes speichert.

Wir wollen nun die Anwendung von linearen Schaltkreisen bei der Codierung/Decodierungstudieren. Zuvor führen wir einige Begriffe über Codierung ein, die die Umsetzung alslinearen Schaltkreis begründen.

Definition 4.3.2a) E sei endliches Alphabet, En die Menrge aller n–Tupel, ~0 ∈ E. Jede Teilmenge

C ⊆ En mit (0, 0, . . . , 0) ∈ C heißt Code über E, x ∈ C Codewort.

b) F sei ein endlicher Körper, En Vektorraum über F , C sei k–dimensionaler Unter-raum, dann heißt C ein linearer (n, k) Code.

Definition 4.3.3Sei E endlicher Körper, Z : En → En, Z(x0, x1, . . . , xn−1) = (xn−1, x0, x1, . . . , xn−2) diezyklische Verschiebung des Arguments. Ein linearer Code C heißt zyklisch, wenn er unterZ invariant ist.

Wenn wir nun Polynome a(x) = a0+a1x+. . .+amxm mit Koeffizienten aus einem Körper

F und am 6= 0 betrachten, so gilt der Divisionsalgorithmus für Polynome und man kanninsbesondere modulo des Polynoms xn − 1 rechnen. Ordnen wir jedem Codewort aus Cin natürlicher Weise ein Polynom wie folgt zu: (a0, . . . , am)=a0 + a1x + . . . + amx

m, soentspricht ein Shift (nach links) des Codewortes einer Multiplikation mit x, wobei im Fallm = n− 1 modulo xn − 1 gerechnet werden muss. Dann gilt der folgende Satz:

Satz 4.3.4C ⊆ En ist ein zyklischer Code genau dann, wenn es ein Polynom g gibt, welches Teilervon xn − 1 ist und für das die Beziehung u ∈ C ↔ g|u gilt.

(g = g0 + g1x+ . . .+ gn−kxn−k erzeugt damit einen linearen zyklischen Code.)

77

Page 78: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Codierung:

a0, . . . , ak−1 → a0 + a1x+ . . .+ ak−1xk−1

·g(x)→ f0, . . . , fn−1 (Codewort)

·1/g(x)→ a0 + a1x+ . . .+ ak−1x

k−1 Decodierung

Sowohl das Codieren (Multiplizieren) als auch das Decodieren (Dividieren) bzgl. eineszyklischen Linearcodes kann mit Hilfe von linearen Schaltkreisen erfolgen. Soll ein Po-lynom a(x) = a0 + a1x + a2x

2 + . . . + ak−1xk−1 mit einem festen Polynom h(x) =

h0 + h1x+ h2x2 + . . .+ hn−kx

n−k multipliziert werden, so wird dies durch die folgendeSchaltung realisiert:

Ist das Polynom h(x) von Grad n− k, so benötigen wir ein n− k stelliges Schieberegis-ter, n − k Addierer und n − k + 1 Multiplizierer, welche jeweils ihren Input mit einembestimmten Koeffizienten von h multiplizieren.

Die Delays werden zu Anfang mit Nullen vorbelegt. Dann werden die Koeffizientenvon a(x) beginnend mit dem höchsten ak−1 der Reihe nach eingegeben. Ist der ersteKoeffizient eingegeben, so erhalten wir am Ausgang den Wert ak−1 · hn−k. Nach ei-nem Takt steht im ersten Delay ak−1 und am Input ak−2. Der Ausgang liefert dannak−2 · hn−k + ak−1 · hn−k−1, also den zweithöchsten Koeffizienten des Ergebnisses. Nach-dem der letzte Koeffizient a0 eingegeben ist, folgen wieder Nullen, der letzte Koeffizientdes Produktes ergibt sich nach n Takten somit zu a0 · h0. Wenn wir über dem KörperE = 0, 1 arbeiten, so entfallen die Multiplikationsglieder und alle Addierer, die zuKoeffizienten hi = 0 gehören. Die Addierglieder sind dann Ringsummen–Addierer.

Beispiel 4.3.5Sei h(x) = 1⊕ x3 ⊕ x4 ⊕ x5. Hierfür ergibt sich dann folgender linearer Schaltkreis

Auf ähnliche Art und Weise ergibt sich ein Schaltkreis für die Division eines beliebigenPolynoms f(x) mit Grad n − 1 durch ein festes Polynom h(x) vom Grad n − k. Wirwollen uns dies am folgenden Beispiel klarmachen:

78

Page 79: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Beispiel 4.3.6Sei f(x) = f2x

2 + f1x+ f0 und h(x) = h1x+ h0 . Dann gilt:

f(x) : h(x) =f2h1x+

f1 −f2h0

h1

h1

mit Rest

f0 − h0 ·f1 −

f2h0

h1

h1

Hieraus ergibt sich:

f(x) : h(x) = f2h−11 x+ (f1 − f2h0h

−11 )h−11 + Rest

Die Division geht auf, falls der Rest gleich Null ist. Allgemein erhält man als höchstenKoeffizienten des Quotienten den Faktor h−1n−k, als Koeffizient der zweithöchsten Potenzden Faktor h−2n−k usw. Zudem wird in jedem Schritt mit einer Differenz multipliziert. DenVorzeichenwechsel erhält man durch Multiplikation mit negativen Zahlen.

Im allgemeinen Fall leistet der folgende lineare Schaltkreis die Division:

Gehen wir wieder von einer Vorbelegung der Delays mit Null aus, so bleibt währendder ersten n − k − 1 Takte der Output Null, dann hat fn−1 das Ende des Registerserreicht und es wird fn−1 · h

−1n−k ausgegeben. Nach dem nächsten Takt erhält man dann

(fn−2 − fn−1hn−k−1h−1n−k)h

−1n−k usw.

Im Fall des Booleschen Körpers ergeben sich analoge Vereinfachungen wie bei der Mul-tiplikation, wie wir an folgendem Beispiel exemplarisch erkennen.

Beispiel 4.3.7Sei h(x) = 1⊕ x⊕ x4. Wegen hi = −hi und hn−k = 1 (Beachte K = B) ergibt sich dannfolgender linearer Schaltkreis

79

Page 80: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

5 Programmierbare Logische Arrays(PLA)

Wir wollen nun die in den letzten beiden Kapiteln besprochenen Schaltnetze und Schalt-werke mit Hilfe eines universellen, programmierbaren Bausteins realisieren.

Wir gehen hierzu von der Tatsache aus, dass man die DNF oder KNF einer BooleschenFunktion mit Hilfe von Gattern mit ausreichend hoher Zahl von Eingängen über einezweistufige Schaltung darstellen kann. Wir wollen nun einen Einheitsbaustein betrachten,der in der Lage ist, eine beliebige zweistufige Schaltung zu realisieren.

5.1 Aufbau und Arbeitsweise eines PLA

Ein solcher Einheitsbaustein ist das sogenannte Programmierbare Logische Array (PLA).Dieser Baustein besteht aus einer Gitterstruktur von Drähten, an deren Kreuzungspunktejeweils ein einheitlich formatierter Baustein platziert ist, der vier verschiedene Funktioneneinnehmen kann. Die vier verschiedenen Funktionen seien im Folgenden mit 0, 1, 2 und3 bezeichnet, und haben die im folgendem Bild gezeigte Wirkung als Identer, Addierer,Multiplizierer und Negat–Multiplizierer.

80

Page 81: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Alle Bausteine enthalten zwei Eingänge, von links und oben, und zwei Ausgänge, nachrechts und unten. Ihnen ist gemeinsam, dass mindestens ein Eingang unverändert aneinen Ausgang übergeben wird, beim Identer sogar beide. Der Addierer gibt auf denrechten Ausgang die Summe der Eingänge und an den unteren den oberen Eingangaus. Der Multiplizierer gibt an den unteren Ausgang das Produkt der beiden Eingän-ge und schaltet den linken Eingang auf den rechten Ausgang durch. Analoges passiertbeim Negat–Multiplizierer, hier wird allerdings der linke Eingang vor der Multiplikationnegiert.

81

Page 82: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Die Realisierung der PLA Bausteintypen mittels Gattern sieht dann so aus:

x

y y

x

x

y y

x · y x · y

x

³ 1

&&

1

y x + y

x

x

y y

Identer

MultipliziererNegat-Multiplizierer

Addierer

Beispiel 5.1.1Wählen wir ein PLA mit n = 5 Inputs auf der linken Seite und m = 5 Outputs auf derrechten Seite sowie k = 4 Spalten, so können wir die Schaltfunktion F : B3 → B2 mitF (x, y, z) = (yz + xyz, xz + xyz) = (u, v) wie folgt in diesem PLA verschalten:

Von den 5 möglichen Inputs werden nur drei benötigt. Die unteren beiden ”sperren”wir deshalb durch anlegen einer ’0’ am linken Eingang des linken Bausteins. Außerdemwerden die oberen Eingänge der oberen Bausteine nicht nach außen geführt und durchEingabe ’1’ neutralisiert. Der links an der oberen Bausteinreihe anliegende Input wirdsomit unverändert (Baustein 2) oder negiert (Baustein 3) an den unteren Output deroberen Bausteinreihe weitergeleitet. In jeder Spalte des PLA wird nun in den oberen 3

82

Page 83: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Zeilen einer der vier Literale der disjunktiven Darstellung von u und v erzeugt, wir benö-tigen hierzu nur die Bausteine 0, 2 und 3. In den unteren beiden Zeilen werden dann diezusammengehörenden Terme addiert, was lediglich mit den Bausteinen 0 und 1 geschieht.Diese Trennung ist im Bild durch die gestrichelte Linie angedeutet.

Die im Beispiel angesprochene Trennung findet grundsätzlich in einem PLA statt. Manunterscheidet hier die ’UND–Ebene’, die lediglich aus Identer und Multiplizierer (Bau-steine 0, 2, 3) und der ’ODER–Ebene’, die aus Identer und Addierer aufgebaut ist. Liegteine zu verschaltende Funktion in disjunktiver Form vor, so werden in der UND–Ebenealle benötigten Produktterme aufgebaut und in der ODER–Ebene die entsprechendenTerme addiert. Die Inputs führen nur noch in die UND–Ebenen hinein und die Out-puts kommen aus der ODER–Ebene heraus. Demzufolge werden die linken Eingänge derODER–Ebene durch ’0’ gesperrt, die oberen Eingänge der UND–Ebene durch ’1’ neu-tralisiert. Die Ausgänge der ’UND–Ebene’ und die unteren Ausgänge der letzten Zeilewerden nicht herausgeführt. Somit stellt sich ein allgemeines PLA wie folgt dar:

Vereinfachend lässt sich nun ein PLA mit n Eingängen, m Ausgängen und k Spaltenals (n +m) × k Matrix darstellen, in der nur noch die Ziffern 0, 1, 2 und 3 eingetragenwerden.

Eine der ersten Realisierungen als Baustein war der DM 7575 von National Semiconduc-tor, der mit 14 Inputs, 8 Outputs und 96 Spalten versehen war, d.h. es waren insgesamt14×96+8×96 = 2112 Bausteine verdrahtet. Von den insgesamt 28·2

14verschiedenen Funk-

tionen F : B14 → B8 konnten mit diesem Baustein insgesamt 28·96 = 2768 = 1.5 · 10231

verschiedene solcher Funktionen geschaltet werden, falls sie in DNF vorliegen.

Generell kann man festhalten:Satz 5.1.2Durch geeignete Eintragung in die PLA–Matrix kann jede Schaltfunktion in einem hin-reichend großen PLA realisiert werden. Die Anzahl der Inputs legt die Anzahl der Zeilen

83

Page 84: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

der UND–Ebene fest, die Anzahl der Outputs bestimmt die Zeilen der ODER–Ebeneund die Anzahl der verschiedenen Produktterme legt die Anzahl der Spalten fest.

Bei der Beschaltung eines PLA ist nur dann eine Vereinfachung der darzustellenden Funk-tion, z.B. mit dem Algorithmus von Quine & McCluskey, notwendig, wenn die Anzahl derverfügbaren Spalten des PLA’s nicht ausreicht, um die DNF zu verschalten. Ansonstenwähle man der Einfachheit halber stets die DNF.

5.2 Programmierung eines PLA, universelles PLA

Wir haben bei der Diskussion des Aufbaus und der Arbeitsweise eines PLA festgestellt,dass eine beliebige Funktion in das PLA ”einprogrammiert” werden kann, indem mandie Funktion der einzelnen Bausteine festlegt. Eine solche Programmierung kann durcheinen Ätzprozess erfolgen, womit eine einmal realisierte Funktion in einem PLA nichtmehr geändert werden kann. Diese Vorgehensweise eignet sich z.B. bei der Herstellungvon großen Anzahlen von Bausteinen mit gleicher Funktion z.B. für eine Steuerung.

Ein anderer Ansatz geht wie folgt vor:An jeder Zelle des PLA’s werden zusätzlich zwei weitere Eingänge angelegt, die eineSteuerfunktion für diese Zelle übernehmen. Mittels dieser beiden Eingänge kann nun diejeweilige Funktion der Zelle durch Auswahl der Codierungen 0, 1, 2 und 3, die ja durchzwei Bit möglich ist, festgelegt werden. Dies sähe dann wie folgt aus:

Nun wird über ein ROM das PLA angesteuert, d.h. das PLA wird über ein ”Programm”,welches bei M PLA Bausteinen aus einer Folge von 2M Bits besteht, programmiert. Fürdas oben beschriebene DM 7575 PLA wären also 2 · 2112 = 4224, also etwas mehr als4 KByte notwendig. Dies ist bei den heute zur Verfügung stehenden EPROM (ElectricalProgrammable ROM) Bausteinen kein Problem. Diese EPROM können mit speziellen”Brennern” elektrisch programmiert werden und über UV–Strahlung auch wieder gelöschtwerden. Andere Möglichkeiten sind EEPROM (Electrical Erasable Programmable ROM)Bausteine, die sowohl elektrisch programmiert als auch gelöscht werden können.

Andere Abwandlungen von PLA’s speisen sowohl die Variablen als auch die negiertenInputs ein, so dass in der UND–Ebene ebenfalls neben dem Identer nur noch ein weite-rer Baustein (der Baustein 2) notwendig ist. Abwandlungen dieses Typs sind dann die

84

Page 85: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

sogenannten PAL’s (Programmable And Logic), die in der ODER–Ebene zu jedem Out-put eine feste Anzahl von Implikanten verdrahtet haben, und nur noch die UND–Ebenefrei programmierbar ist. Hierbei ist es dann nicht möglich, dass ein in der UND–Ebeneerzeugtes Produkt in verschiedene Summen des Outputs eingeht.

5.3 Anwendungen von PLA’s

Eine erste Anwendung von PLA’s ist der bereits erwähnte Festwertspeicher, das ROM.Wollen wir etwa 2n Worte der Länge m speichern, können wir den Speicher als einem× 2n Matrix auffassen, deren Spalten die Adressen von 0 bis 2n − 1 entsprechen. MitHilfe eines PLA kann man dies wie folgt realisieren: Man fasst die ODER–Ebene desPLA als Speicher auf und codiert in der UND–Ebene die Adressen von 0 bis 2n− 1, d.h.alle Minterme der Länge n. In der Spalte, die zum Minterm k gehört, wird dann in derODER–Ebene das entsprechende Datum abgelegt, welches an dieser Adresse gespeichertwerden soll. Wollen wir Daten der Länge m Bit speichern, benötigen wir also m Zeilenin der ODER–Ebene, und um 2n Informationen speichern zu können, n Zeilen in derUND–Ebene. Die Anzahl der Spalten muss 2n betragen.

Beispiel 5.3.1Für den Fall n = 3 und m = 4 sieht dies dann wie folgt aus, wobei die Daten im ROM–Teil willkürlich gewählt sind. Durch Anlegen der binär codierten Adresse 5 wird genauin der Spalte der UND–Ebene, die den Minterm 5 implementiert, eine ’1’ am unterenAusgang der UND–Ebene erzeugt, bei allen anderen Mintermen wird eine ’0’ generiert.Hierdurch wird in der ODER–Ebene der Inhalt der Zelle, die zur Adresse 5 gehört, zuden ”Nullen” aus den anderen Adressspalten addiert und somit am Ausgang ausgegeben.

85

Page 86: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Allgemein sieht also die Realisierung eines ROM’s über ein PLA wie folgt aus:

Eine weitere Anwendung von PLA’s ist die Verwendung in Schaltwerken. Hier werden einTeil der Outputs über Delays an einen Teil der Inputs zurückgekoppelt. So lassen sich z.B.Zählerschaltungen mittels PLA’s realisieren. Eine allgemeine Architektur hierfür könnteetwa wie folgt aussehen:

Bausteine, die sowohl ein PLA als auch eine Rückkoppelung mittels Delays enthalten,werden integrierte PLA’s genannt.

Mit dieser Architektur kann z.B. auch ein Addierer unter Verwendung eines PLA’s auf-gebaut werden. Ein Akku und ein zweites Register für die Operanden sowie ein Delay fürden Übertrag werden hier zurückgekoppelt. Abhängig von zwei weiteren Steuerleitungenkönnte man z.B. die Addition und Subtraktion gleichzeitig realisieren.

86

Page 87: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Ein derartiger Addierer könnte also wie folgt aussehen:

87

Page 88: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

6 Bemerkungen zum Entwurf von VLSISchaltungen

Bisher haben wir bei unseren Betrachtungen technologische Gesichtspunkte im wesentli-chen ausgeklammert. Diese spielen jedoch beim Design und bei der zu erzielenden Leis-tungsfähigkeit eines Rechners eine entscheidende Rolle.

Die Entwicklung ist gerade bei der Fertigungstechnologie in den letzten Jahren sehr rasantfortgeschritten, womit gleichzeitig die Leistung stieg und der Preis der Rechner fiel.

Hatte der 1946 an der University of Pennsylvania entwickelte ENIAC Rechner noch 18000Röhren, benötigte eine Standfläche von 300 m2, wog 30 t, hatte eine Leistungsaufnahmevon 50000 W und kostete damals in der Herstellung 500000 Dollar, so wird seine Leistungheute von einem Taschenrechner erreicht. Die rasante Verkleinerung der Geräte wurdemit der Erfindung des Transistors im Jahre 1948 ausgelöst. Schnell danach erschienendie ersten integrierten Bausteine, in denen Gatter, Delays und Verbindungsdrähte inner-halb eines Gehäuses, dem sogenannten Chip, in einem gemeinsamen Herstellungsprozessgefertigt werden.

Ein Maß für die fortschreitende Technologie ab 1965 ist die Anzahl der Gatter pro Chip

SSI Small Scale Integration ≤ 10 Gatter pro ChipMSI Medium Scale Integration > 10 und ≤ 102 Gatter pro ChipLSI Large Scale Integration > 102 und ≤ 105 Gatter pro ChipVLSI Very Large Scale Integration > 105 Gatter pro Chip

Hierbei sei bemerkt, dass man verschiedentlich auch die Angabe Transistoren pro Chipvorfindet. In diesem Fall sind obige Zahlen mit 3 bis 5 zu multiplizieren. Als Fortfüh-rung der VLSI Technologie wird bereits von der ULSI (Ultra Large Scale Integration)Technologie gesprochen.

Die bei den VLSI Bausteinen benötigten Elemente, Gatter, Delays und Verbindun-gen sind in drei verschiedenen Ebenen untergebracht. In der heute üblichen NMOS–Technologie (Negative Channel Metal Oxid Semiconductor) werden diese Ebenen ent-sprechend der folgenden Graphik angeordnet.

88

Page 89: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Metall-Ebene

Isolation

Isolation

Polysilizium-Ebene

Diffusions-Ebene

Eine Verbindungsleitung unterbricht die Isolation zwischen den Ebenen, und es lassensich durch gezielte Unterbrechung der Isolation und Schaffung eines Kontaktes zwischender Metall– und der Polysilizium–Ebene auch Transistoren erzeugen.

Die uns in diesem Zusammenhang vorrangig interessierende Frage ist, wie klein undwie schnell ist ein Chip, der eine bestimmte vorgegebene Aufgabe lösen soll, in einervorgegebenen Technologie überhaupt zu designen? Hierzu wollen wir einige einführendeBemerkungen machen.

6.1 Komplexität von VLSI Schaltungen

Wir wollen den Zusammenhang zwischen der Größe eines Chips, der ein bestimmtesProblem löst, und dessen Ausführungszeit zur Lösung des Problems analysieren. Wirgehen dazu von folgendem VLSI–Modell aus:

Ein VLSI–Chip besteht logisch aus einer Gitterstruktur von aufeinander senkrecht ste-henden Gitterlinien pro Ebene. Wir nehmen an, dass Verbindungsdrähte zwischen deneinzelnen Schaltelementen nur entlang dieser Gitterlinien verlaufen können. Weiterhindürfen sich Drähte in verschiedenen Ebenen zwar kreuzen (wegen der Isolierung), abernicht stückweise übereinander verlaufen, um Störungen durch Induktion zu vermeiden.Ein Ebenenwechsel kann durch einen Kontakt an einem Gitterpunkt erfolgen. Aufgrundder Fertigungstechnologie müssen wir davon ausgehen, dass nicht alle Drähte exakt aufeiner Gitterlinie platziert werden, sondern um maximal einer ”technologischen Abstands-konstante” λ > 0 hiervon abweichen. Der Abstand von zwei Gitterlinien ist dann einkleines Vielfaches, z.B. 5λ. Bereits 1975 war ein Wert von λ = 6 · 10−6 m erreichbar,1997 war eine Verkleinerung auf weniger als 0.65µm − 0.35µm erreicht. Zu dieser Zeitaktuelle Prozessoren wie Pentium 200MMX oder PowerPC 604e waren in 0.25µm Tech-nologie gefertigt (zum Vergleich: ein menschliches Haar ist etwa 400µm dick). Ein fürden Servereinsatz konzipierter Microprozessor wie der Intel PentiumPro verfügte z.B.auf einer Fläche von 306 mm2 über 20.5 Millionen Transistoren (davon 15 Mill. für denintegrierten L2 Cache), war in 0.5µm Technologie gefertigt und hatte 387 Pins.Heute aktuelle Prozessoren werden in 0.13µm Technologie gefertigt und nehmen bis zu100 Millionen Transistoren auf. Die internen Taktraten liegen bei 1.4 Gigahertz.

Wegen der Gitterstruktur ist somit die Kantenlänge auch ein Vielfaches des Gitterab-standes und damit von λ. Die Chipfläche sei mit A bezeichnet und ist dann ein Vielfaches

89

Page 90: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

von λ2. Die Größe des Chips hat entscheidenden Einfluss auf die Herstellungskosten, dabei kleineren Chips mehr aus einem Wafer gewonnen werden können und vor allen Din-gen die Ausschussrate von nahezu 90 %, die aus Fehler auf den Wafern resultiert, kannbei kleiner Chipfläche besser minimiert werden.

Die auf der Schaltung platzierten Gatter liegen in den Kreuzungspunkten des Gitters,und nehmen je nach Anzahl der Gatterinputs und –outputs mehrere Gitterpunkte ein.Wir wollen den maximal vorkommenden Fan–In (und damit die maximal Anzahl Gitter-punkte, die durch ein Gatter überdeckt werden) mit κ bezeichnen.

Die in einen VLSI–Baustein hinein– und herausführenden Kontakte sind groß im Ver-gleich zu der inneren Strukturgröße λ, so z.B. (100λ)2. Daher ist die gesamte Flächeeines Chips häufig wesentlich größer als durch den eigentlichen Kern notwendig wäre.Wir wollen dies momentan außer Acht lassen, und davon ausgehen,dass von den Kontak-ten, den sog. Pads, Verbindungsleitungen in der Breite der anderen Verbindungsdrähtein den Chipkern hineinführen.

Wir wollen uns nun der folgenden Frage nach den Grenzen der Möglichkeiten von VLSI–Schaltungen widmen. Genauer heißt dies:Gegeben sei ein durch eine Schaltfunktion F beschreibbares Problem. Gibt es eine untereGrenze für die Fläche A bzw. die Arbeitszeit T eines VLSI–Chips, welcher dieses Problemlöst, d.h. welcher zu jedem Input x ∈ Bn einen Output F (x) ∈ Bm berechnet?

Aussagen der folgenden Form scheinen sinnvoll:

Je größer die Komplexität von F , destoa) größer muss die Chipfläche A sein bzw.b) länger muss die Rechenzeit T sein.

Häufiger werden allerdings Aussagen über das Produkt von Fläche und Zeit gemacht, damit wachsender Komplexität nicht notwendig Fläche und Zeit anwachsen müssen. Wirwerden später sehen, dass eine Aussage folgender Art gilt:

90

Page 91: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Je größer die Komplexität von F , desto größer ista) A · T bzw.b) A · T 2.

Dabei geben wir A und T in Einheiten einer Grundfläche A0 und einer Grundzeit T0 an.

Der folgende Satz gibt eine erste, einfache untere Schranke an, die allerdings nicht be-sonders scharf ist.

Satz 6.1.1Sei F : Bn → Bm eine Schaltfunktion und k = maxn,m. Dann gilt für einen Modell-schaltkreis (MSK), der F berechnet:

A · T ≥ k.

Beweis Sei C ein Musterschaltkreis mit Fläche A, dann hat C höchstens A Ports. Erbenötigt daher mindestens k/A Takteinheiten, um alle Input–Bits zu lesen (falls k = n)oder alle Output–Bits zu schreiben, also gilt:

T ≥k

A, d. h. A · T ≥ k.

Ein zweiter Satz gibt nun eine Schranke für A · T 2:

Satz 6.1.2Sei C ein MSK der Fläche A, welcher n Dualzahlen der Stellenzahl k (k ≥ dld n+1e) inT Zeittakten sortiert. Dann gibt es eine Konstante K > 0 so, dass

A · T 2 ≥ K · n2

gilt.

Dabei ist

K =1

9 · L2 · κ2

wobei L die Anzahl der Ebenen und κ den maximalen Gatter–Fan–In beschreibt.

Beweisidee: (vollständiger Beweis siehe Oberschelp, Vossen [7])Wir betrachten einen Modellschaltkreis (MSK), welcher die Sortierfunktion S : Bnk →Bnk berechnet, und schneiden diesen parallel zur kürzeren Seite etwa in der Mitte auf.Pro Takt kann dann nur ein gewisser Informationsfluss über diesen Schnitt gehen. DerSchnitt wird nun so gewählt, dass er ein Maß für die Komplexität von S darstellt. Wirzeigen, dass eine gewisse Information über den Schnitt laufen muss, und schätzen die

91

Page 92: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Anzahl ab.Genauer betrachten wir zwei Fälle:

1. Fall:T ist groß, etwa T ≥ n

3 . Wegen A ≥ 1 folgt dann unmittelbar:

A · T 2 ≥ 1 ·n2

9= K · n2 mit K =

1

9

2. Fall:Sei T < n

3 , d.h. auf jeden Port von C werden weniger als n3 Input– bzw. Output–Bits

entfallen. Für die Sortierfunktion S : Bnk → Bnk hat jeder Input die Form

(x1, ..., xk, xk+1, ..., x2k, x2k+1, ..., xnk)

und jeder Output die Form

(y1, ..., yk, yk+1, ..., y2k, y2k+1, ..., ynk).

Von den Output–Bits betrachten wir die n niederwertigsten yk, y2k, . . . , ynk und markie-ren an jedem Port von C, wieviele niederwertigste Bits den Schaltkreis dort verlassen.Dann teilen wir den Schaltkreis mit Hilfe eines Vertikalschnittes so auf, dass auf jederSeite des Schnittes möglichst die Hälfte der Output–Bits liegt. Da dies natürlich nichtimmer möglich ist, fordern wir nur, dass auf jeder Seite des Schnittes mindestens einDrittel der Bits liegen. Auch das können wir nur erreichen, wenn wir im Schnitt einmaleinen ”Haken” um die Länge eines Einheitsquadrates nach links zulassen.

Beispiel: Für n = 30 kann man sich z.B. folgende Verteilung der niederwertigsten Bitsauf die Ports denken:

Ein solcher Schnitt lässt sich immer konstruieren, falls durch einen Port weniger als n3

Bit die Schaltung verlassen. Man beginnt dazu mit einer senkrechten Scanlinie am linken

92

Page 93: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Rand und führt diese nach rechts, bis erstmals mindestens n3 der niederwertigsten Bits

die Schaltung links vom Schnitt verlassen. Liegen weniger als 2n3 Bits links, so ist der

Schnitt gefunden. Anderenfalls sucht man von oben beginnend eine Stelle, durch die einhorizontaler Haken um die Länge eines Einheitsquadrates diese Forderung erfüllt. Dain jedem senkrechten Schritt höchstens n

3 Outputs hinzukommen, existiert ein solcherHaken in jedem Fall.

Wenn der Schnitt konstruiert ist, betrachten wir die Verteilung der niederwertigstenInput–Bits. Hiervon werden auf einer Seite des Schnitts wenigstens n

2 gelesen, dies seio.B.d.A. die linke Seite. Wir haben somit folgende Situation:

xk, x2k, x3k, . . . , xn2k werden links gelesen,

yi1k, yi2k, yi3k, . . . , yin3k werden rechts geschrieben.

Der MSK liest Input — sortiert — schreibt Output. Jede Eingabe α zerfällt dann in einenTeil αL, der links vom Schnitt S gelesen wird, und αR, der rechts vom Schnitt gelesenwird. Sei dann β = (βL, βR) eine weitere Eingabe, die mit α rechts übereinstimmt. Dannsagen wir, dass α und β die Schaltung C täuschen, falls für die zu berechnende FunktionF gilt:

F (αL, αR) = F (β) im rechten Teil.

Eine Menge X ⊆ Bnk heißt dann Unterscheidungsmenge für C, falls je zwei Elementeα, β die Funktion S nicht täuschen.

Man kann nun zeigen, dass es bzgl. des konstruierten Schnittes eine Unterscheidungs-menge A gibt mit mindestens 2n/3 Elementen. Damit muss für die ersten n/3 unterenInput–Bits Information über den Schnitt fließen, da sonst zwei Eingaben α und β existie-ren würden, die den Schaltkreis täuschen. Hierzu sind aber die Verbindungsdrähte überden Schnitt notwendig. Wenn der MSK C die Höhe h und Breite w hat, können maxi-mal h− 1 (bzw. h, falls der Schnitt S einen Haken hat) Verbindungsdrähte den Schnittkreuzen. Dem Schnitt wird dann ein Wert zugeordnet, der sich aus der binären Codie-rung des Informationsflusses pro Verbindungsdraht bzw. Schaltelement, welches auf demSchnitt liegt, ergibt. Bei den Verbindungsdrähten wird die Codierung so gewählt, dass dieRichtung des Informationsflusses über den Schnitt hierdurch beschrieben wird, bei denSchaltelementen werden alle Inputbits in dieses berücksichtigt. Da ein Schaltelement nachVoraussetzung maximal κ Inputs besitzen kann, und in einem Takt über jede Leitung nurein Bit Information fließt, ergibt sich für einen Schaltkreis mit maximal h Verbindungen,die den Schnitt treffen und L Ebenen dann die Möglichkeit der Darstellung des gesamtenInformationsflusses über den Schnitt mit maximal h ·κ ·L Bits. Hat der Schaltkreis einenZeitbedarf von T Takten, so können maximal h ·κ ·L ·T Bits insgesamt über den Schnittfließen, einen solchen gesamten Informationsfluss nennt man dann eine Schnittsequenz.Da deren Länge h · κ · L · T ist, gilt für die Anzahl c von Schnittsequenzen:

c ≤ 2h·κ·L·T

93

Page 94: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Man zeigt nun weiter, dass die Schnittsequenzen zu zwei Elementen einer Unterschei-dungsmenge bzgl. eines festen Schnittes unterschiedlich sind. Da wir aber bereits wissen,dass eine Unterscheidungsmenge X mit |X| ≥ 2n/3 gibt, erhalten wir für die Anzahl derSchnittsequenzen c ≥ 2n/3. Somit gilt also insgesamt:

2h·κ·L·T ≥ c ≥ 2n/3

⇔ h · T · L ≥n

Wegen w ≥ h folgt weiter:

w · T · L ≥n

Nach Multiplikation erhalten wir dann insgesamt:

h · w · T 2 · L2 ≥n2

9κ2

⇔ A · T 2 ≥n2

9 · L2 · κ2,

was zu zeigen war.

Beispiel 6.1.3Ist z.B. κ = L = 10, so gilt

A · T 2 ≥n2

9 · 104'

n2

105

Angenommen, der Sortierchip hat die Fläche AA0 = 1 cm2 , dessen Einheitsquadrate A0im Gitter die Kantenlänge 5λ, mit λ = 6 · 10−6 m, haben, so gilt:

(5λ)2 = 900 · 10−12 m2

⇒ A =10−4 m2

900 · 10−12 m2' 105

Falls der Schaltkreis in der Lage ist, n eingegebene k–stellige Dualzahlen zu sortieren, sobenötigt er z.B. für n = 1012 und k ' 41 die Zeit:

T 2 ≥1024

105 · 105= 1014

⇒ T ≥ 107

Bei einer Taktdauer T0 von 10−8 Sekunden ergibt sich somit:

TT0 ≥107

108sec = 0.1 sec.

94

Page 95: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

6.2 Layout von VLSI–Schaltungen — H–Bäume

Wir wollen abschließend noch kurz eine Möglichkeit diskutieren, wie man ein Schaltnetzfür eine Boolesche Funktion statt in Form eines DAG anders darstellen kann, um die-se unmittelbar in ein VLSI Layout, welches unserer Annahme nach einer gitterförmigaufgebauten Struktur entspricht, umzusetzen.

Wir gehen hierbei von einem binären Minterm–Baum aus, der beginnend von der Wur-zel ”1” in jedem Level eine Variable und deren Komplement neu einspeist und so allemöglichen Minterme erzeugt.

Im Fall von drei Variablen sieht dies dann folgendermaßen aus:

1

x1

Level 1

x2

x3

Level 2

Level 3

Dieser binäre Mintermbaum kann wie folgt in einen rechteckigen H–Baum umorganisiertwerden, die folgende Graphik zeigt den Fall des Levels 2. Bei höheren Levels werden anden Blättern entsprechende ”H” hinzugefügt.

Im vollständigen H–Baum repräsentiert jetzt jedes Blatt genau einen Minterm. JedeBoolesche Funktion kann somit durch Weglassen der nicht benötigten Minterme in Formeines Teilbaumes des vollständigen H–Baums organisiert werden. An den Blättern befin-den sich dann die entsprechenden Minterme, die in einer zweiten Stufe durch Hinzufügenvon ODER–Gattern zur Funktion aufaddiert werden.

95

Page 96: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Beispiel 6.2.1Betrachten wir die Funktion

f(x1, x2, x3) = x1x2x3 + x1x2x3 + x1x2x3.

Wir erhalten dann den folgenden H–Baum:

4 0

f

1

1 x1

x1

x1

x1 2

x

x1 2 3

x x

x x1 2 3

x

x3

x2

x x1 2

x x x1 2 3

x3

x x1 2 3

x

x2

x1 2

x

x x1 2 3x

x1 2 3

x x

x3

x x1 2

x x x1 2 3

x x1 2 3

x

x3

x2 x

3

3

2

7

6

·

·

·

· ·

·

·

·

·

·

·

··

·

5

+ +

96

Page 97: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

7 Grundlegende Additions– undMultiplikationsalgorithmen undSchaltungen

In diesem Kapitel sollen grundlegende Algorithmen und zugehörige Schaltungen oderArchitekturen diskutiert werden, die zur Addition oder Multiplikation von zwei binär co-dierten Zahlen dienen. Hieran wollen wir auch noch einmal den Zusammenhang zwischenAnzahl (Fläche) benötigter Gatter und Geschwindigkeit der Schaltung diskutieren.

7.1 Addition

Im dritten Kapitel hatten wir bereits die Grundbausteine Halbaddierer und Volladdie-rer und deren Realisierung mittels elementarer Gatter diskutiert. Aus dem Volladdiererkann nun ein Schaltkreis zur Addition von Dualzahlen designt werden, der bitseriell arbei-tet und daher mit minimalem Hardwareaufwand auskommt, allerdings sehr viele Taktebenötigt, um das Ergebnis zu bestimmen. Eine solche Schaltung könnte prinzipiell soaussehen:

VA

x0

zn-1

c-1

c

y0

x1

y1

x2

y2

xn-1

........

yn-1

........

........

z1

z2

z0

97

Page 98: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Ein andere Möglichkeit wäre ein Pipeline–Ansatz. Dieser ist dann sinnvoll, wenn eineKolonne mit k Paaren aus jeweils 2 Zahlen der Länge n–Bit addiert werden soll.

z0

x0

y0

z1

x1

y1

z2

x2

y2

z3

x3

y3

z4

HA

HA

HA

HA

³ 1

HA

HA

HA

HA

HA

HA

D

D

D D

D

D

Die Summe der ersten beiden Zahlen ergibt sich hierbei nach (n− 1) Takten. Nach demn–ten Takt steht allerdings schon das Ergebnis der nächsten Addition am Ausgang an.So sind nach n+ k− 1 Takten alle Summen bestimmt, statt in n · k Takten wie bei einerreinen seriellen Addition. Allerdings ist hier ein erhöhter Hardwareaufwand notwendig.Wir benötigen für Zahlen der Länge n–Bit insgesamt 0.5 · n · (n + 1) Halbaddierer und0.5 · (n− 1) · n Delays.

7.1.1 Von–Neumann–Addierwerk

Als nächstes wollen wir ein von–Neumann–Addierwerk betrachten. Wir wollen dies für4–Bit Zahlen tun, eine Erweiterung auf n–Bit wird aber sofort ersichtlich sein. Das von–Neumann–Addierwerk besteht aus einem n–Bit breiten Akku und einem n–Bit breitenPuffer sowie einem Status–Delay S und einem Übertragsdelay U . Die beiden Summan-den stehen zu Beginn im Akku und im Puffer, das Ergebnis steht im Akku und imÜbertragsdelay. Das Statusdelay gibt an, ob die aktuelle Berechnung abgeschlossen istoder nicht.

98

Page 99: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Eine Schaltung hierfür sieht folgendermaßen aus:

HA

D

D

&

HA

D

D

³1

D

HA

D

D

HA

D

D

D

³1

U

S

A0

A1

A2

A3

P0

P1

P2

P3

Zuerst werden alle Stellen parallel addiert. Dies geschieht für jede Stelle mit einem Halb-addierer, der das Resultat Ai ⊕ Pi wieder in die entsprechende Zelle des Akkus und denÜbertrag Ai · Pi nach Pi+1 bzw. in das Übertragsdelay schreibt. In P0 wird durch diezusätzliche Logik dafür gesorgt, dass ab dem zweiten Takt dort eine Null steht und so-mit auch der Übertrag, der vom Halbaddierer für die niederwertigste Stelle erzeugt wird,im zweiten Takt Null ist. Im nächsten Schritt werden dann die Teilüberträge nach demgleichen Schema wieder zum Akku hinzuaddiert. Sind nach einer gewissen Zeit nur nochNullen im Puffer, erscheint am Status Delay eine Null, was gleichbedeutend mit abge-schlossener Rechnung ist. Die Logik vor dem Übertragsdelay sorgt dafür, dass ein einmalerzeugter Übertrag erhalten bleibt, egal in welcher Phase der Addition er entsteht.

Allgemein gilt:Im (i+1)–ten Schritt der Addition sind mindestens die i rechten Stellen des Puffers Null,d.h. nach höchstens n+1 Schritten ist eine n–stellige Addition abgeschlossen. Es gilt derfolgende Satz, der mit wahrscheinlichkeitstheoretischen Ansätzen über die Verteilung vonNull– und Eins–Bits in den Summanden argumentiert:

Satz 7.1.1Das n–Bit von–Neumann–Addierwerk addiert zwei Summanden durchschnittlich in ld n+1 Schritten.

Betrachten wir zur Arbeitsweise ein Beispiel.

Beispiel 7.1.2Es sollen die Summen 13+11, 10+12, 15+15, 9+10 und 0+0 gebildet werden. In deneinzelnen Schritten hat das Addierwerk folgende Belegungen:

99

Page 100: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Puffer- Puffer- Akku- Akku-Inhalt Inhalt Inhalt Inhaltdual dezimal dual dezimal

Zeile S U P3P2P1P0 A3A2A1A0 Schritt1 0 0 0000 0 0000 02 1 0 1101 13 1011 11 13 1 1 0010 2 0110 22 24 1 1 0100 4 0100 20 35 1 1 1000 8 0000 16 46 0 1 0000 0 1000 24 57 1 0 1010 10 1100 12 18 0 1 0000 0 0110 22 29 1 0 1111 15 1111 15 110 1 1 1110 14 0000 16 211 0 1 0000 0 1110 30 312 1 0 1001 9 1010 10 113 0 1 0000 0 0011 19 214 1 0 0000 0 0000 0 115 0 0 0000 0 0000 0 2

7.1.2 Carry–Look–ahead Addition

Ein Problem bei der Addition ist das Auftreten von Überträgen bei der Addition dereinzelnen Bits modulo zwei. Da ein entstehender Übertrag auf alle weiteren Bits linksdieser Position Einfluss nehmen kann, müssen selbst Schaltungen, die die Bits der Sum-manden parallel addieren, ggf. die entstehenden Überträge verarbeiten (siehe z.B. von–Neumann–Addierwerk). Somit müssen die verschiedenen Volladdierer in diesem Fall alshintereinandergeschaltete Stufen angesehen werden.

Eine Beschleunigungsmöglichkeit könnte nun darin bestehen, durch zusätzliche Hard-ware, die mit möglichst wenigen Stufen auskommt, alle Überträge vorherzusagen unddiese dann schon im ersten Additionsschritt mit zu verwenden. Es ist jedoch einsichtig,dass bei größerer Länge der Summanden der Aufwand für die höherwertigen Stellen im-mer weiter steigt. Ein Kompromiss ist daher, die Bits zu gruppieren und innerhalb derGruppen die Überträge der einzelnen Stellen vorherzubestimmen. Sinnvolle Gruppengrö-ßen sind 4, 5 oder 6. Ein Verfahren, welches mit einer solchen Technik arbeitet, ist derCarry–Look–ahead Addierer (CLA), den wir kurz erläutern wollen.

Wir gehen aus von den gegebenen Bits einer Gruppe (yk . . . y0) und (xk . . . x0). Weiterseien die Überträge mit (rk . . . r0) bezeichnet, wobei man sich unter r0 den einlaufendenÜbertrag aus der vorherigen Gruppe vorstellen kann. Die Überträge lassen sich dann bei

100

Page 101: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

gegebenem r0 rekursiv wie folgt bestimmen:

r1 := x0y0 + r0(x0 ⊕ y0)

r2 := x1y1 + r1(x1 ⊕ y1) = x1y1 + x0y0(x1 ⊕ y1) + r0(x0 ⊕ y0)(x1 ⊕ y1)...

...

ri := xi−1yi−1 + ri−1(xi−1 ⊕ yi−1)

= xi−1yi−1 + xi−2yi−2(xi−1 ⊕ yi−1) + xi−3yi−3(xi−2 ⊕ yi−2)(xi−1 ⊕ yi−1)

+ · · ·+ r0

i−1∏

j=0

(xj ⊕ yj)

Führen wir folgende Abkürzungen ein:

pi := xi ⊕ yi =

1 Übertrag setzt sich fort0 Übertrag wird aufgezehrt

gi := xiyi =

1 Übertrag generiert0 Übertrag absorbiert,

so ist pi = 1, falls xi und yi verschieden sind, und somit muss der Übertrag ri weiterge-leitet werden (Propagation). Sind xi und yi beide gleich Eins, so wird ein neuer Übertragri+1 erzeugt (Generation), der unabhängig vom einlaufenden Übertrag ri entsteht. Nurim Fall von xi = yi = 0 wird ein einlaufender Übertrag absorbiert und es entsteht für dienächste Stelle kein neuer Übertrag.

Mit den Abkürzungen erhalten wir also für die einzelnen Überträge folgende Darstellung:

r0 gegeben

r1 := g0 + r0p0

r2 := g1 + g0p1 + r0p0p1...

...

ri := gi−1 + gi−2pi−1 + gi−3pi−2pi−1 + . . .+ r0p0p1 · . . . · pi−1

Pro Gruppe wird also eine Logik erzeugt, die aus den Eingaben nach obigen Gleichungendie Überträge für alle Stellen der Gruppe bestimmt. Diese werden dann mittels XOR zuder bereits für pi berechneten Summe der Summandenbits addiert.

Bei steigender Gruppengröße benötigt man Gatter, die eine immer größere Anzahl vonInputs haben, um eine Zweistufigkeit zu gewährleisten. Bei Gattern mit einem hohen Fan–In steigen in der Regel aber die Gatterlaufzeiten an, so dass bei großen Gruppenlängenhierdurch der Vorteil gegenüber einer höheren Stufenanzahl bei einer reinen seriellenAddition verloren gehen kann.

101

Page 102: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Beispiel 7.1.3Die Addition der folgenden Bitgruppen zeigt die Propagation und Generation der Grup-penüberträge:

7.2 Multiplikation

Zum Abschluss des Kapitels wollen wir noch kurz auf eine Architektur zur Multiplikationvon zwei n–Bit Zahlen eingehen. Der Multiplizierer besteht aus drei n–stelligen Registern,einem Delay und einem n–Bit Addierer.

Multiplikator R Akku 1AC Akku 0AC

Bit 0AC 0

Addierer

Der Algorithmus läuft dann für die Multiplikation natürlicher Zahlen wie folgt:

Gesucht ist das Produkt A ·B, A, B n–stellige Binärdarstellungen. Das Register für denMultiplikator M wird mit der Zahl B geladen und während der ganzen Rechnung nichtverändert. Der Multiplikand wird in das Register AC0 geladen, AC1 und R werden mit0 initialisiert. Algorithmisch sieht die Multiplikation dann folgendermaßen aus:

z := n; Anzahl der Ziffern

while z <> 0 do begin

add;

shift;

dec(z);

end;

Die Prozeduren add und shift haben folgende Funktion:

102

Page 103: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

add

AC00 = 0 tue nichts

AC00 = 1 Addiere AC1 +M

Schreibe Ergebnis nach R,AC1,wobei der Übertrag in R steht.

shift

Shifte die drei Register R,AC1, AC0 um 1 Bit nach rechts

Hierbei bezeichnet AC00 das letzte Bit des Registers AC0. Die nicht mehr benötigtenrechten Stellen des Inhalts von AC0 gehen durch die Shift–Operation verloren, und proTakt entsteht ein neues korrektes Bit des Ergebnisses in AC0. Nach insgesamt n Schrittenist das komplette Produkt in den Registern R,AC1, AC0 abgelegt.

Beispiel 7.2.1Berechnen wir 12 · 14 in binärer Arithmetik mit obiger Architektur, so ergibt sich für dieeinzelnen Stufen des Algorithmus:

Multiplikator = (1100)2

z (nach Dec) op R|AC1|AC0 v. Add R|AC1|AC0 v. Shift R|AC1|AC0 n. Shiftinit = 4 0|0000|1110

3 0|0000|1110 0|0000|1110 0|0000|01112 + 0|0000|0111 0|1100|0111 0|0110|00111 + 0|0110|0011 1|0010|0011 0|1001|00010 + 0|1001|0001 1|0101|0001 0|1010|1000

12 · 14 = (10101000)2 = 128 + 32 + 8 = 168

103

Page 104: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

8 Organisationsplan einesvon–Neumann–Rechners

8.1 Struktur eines Rechners

Wir wollen in diesem Kapitel einen Organisationsplan eines Rechners anschauen, nachdem real existierende Ein–Prozessor–Computer designt werden. Organisation bedeutethierbei, dass wir uns zuerst nur für die logische Anordnung und das Zusammenspiel dereinzelnen Komponenten eines Rechners interessieren.

In einem realen Rechner werden zu einem Zeitpunkt eine große Menge von Daten ver-arbeitet bzw. zwischen einzelnen Komponenten des Rechners hin und her transportiert.Der prinzipielle Aufbau eines sequentiellen Rechners sieht dabei wie folgt aus:

Abhängig vom aktuellen Input und einem aktuellen Zustand wird durch die Next–State–Logik ein Folgezustand berechnet und durch die Output–Logik ein Output erzeugt. Einrealer Rechner kann dabei durch eine Programmsteuerung sein Verhalten ändern.

Ein genaueres Modell eines solchen Rechners, welches auf Arbeiten von Burks, Goldstineund von Neumann zurückgehen, ist wie folgt gekennzeichnet:

1. Ein (zentralgesteuerter) Rechner besteht aus den drei Grundbestandteilen

• Zentraleinheit (engl.: Central Processing Unit, kurz CPU)

• Speicher

• Ein–/Ausgabe–Einheit

Dazu kommen noch Verbindungen zwischen diesen Einheiten, sog. Busse. Die CPUübernimmt die Ausführung von Befehlen sowie die dazu erforderliche Ablaufsteue-rung. Im Speicher werden Daten und Programme als Bitfolgen abgelegt. Die Ein–/Ausgabe–Einheit stellt die Verbindung zur Außenwelt her; über sie werden Pro-gramme und Daten ein– bzw. ausgegeben.

2. Die Struktur des Rechners ist unabhängig von einem speziellen, zu bearbeitendenProblem. Dies wird erreicht, indem man für jedes neue Problem ein eigenes Pro-gramm im Speicher ablegt, welches dem Rechner sagt, wie er sich zu verhalten hat.Speziell dieser Aspekt hat zu der Bezeichnung (programmgesteuerter) Universal-rechner geführt.

104

Page 105: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 8.1: Struktur eines von–Neumann–Rechners (vgl. Oberschelp, Vossen)

3. Programme und von diesen benötigte Daten werden in demselben Speicher abge-legt. Dieser wiederum besteht aus Plätzen fester Wortlänge, die über eine festeAdresse einzeln angesprochen werden können.

Im Folgenden wollen wir die einzelnen Komponenten genauer betrachten:

CPUDie CPU kann prinzipiell in zwei Teile unterteilt werden:

• Datenprozessor (Befehlsausführung)Verarbeitung der Daten, d.h Ausführen von Berechnungen. Komponenten des Da-tenprozessors sind:

– Rechenwerk, die sog. Arithmetisch–Logische Einheit (engl.: Arithmetic LogicalUnit, kurz ALU)

– (mindestens) drei Register zur Aufnahme von Operanden (vgl. z.B. von–Neumann–Addierwerk)

Akkumulator A (häufig kurz Akku genannt)

ein Multiplikator–Register MR (z.B. zur Aufnahme von Multiplikations-ergebnissen)

ein Link–Register L (häufig einstellig, zur Aufnahme z.B. eines Additionsüber-trags)

Puffer–Register (engl. Memory–Buffer–Register) MBR, über das die Kom-munikation mit dem Speicher abgewickelt wird

• Befehlsprozessor (Ablaufsteuerung)Befehle entschlüsseln und deren Ausführung steuern. Er besteht aus folgenden Re-gistern:

– Der aktuell bearbeitete Befehl befindet sich im Befehlsregister (engl.: Instruc-tion Register) IR

105

Page 106: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 8.2: Detailliertes Bild einer CPU (vgl. Oberschelp, Vossen)

– Die Adresse des Speicherplatzes, der als nächstes anzusprechen ist, ist imSpeicheradressregister (engl.: Memory Address Register) MAR abgelegt.

– Die Adresse des nächsten auszuführenden Befehls wird darüber hinaus imBefehlszähler (engl.: Program Counter) PC gespeichert.

– Die Entschlüsselung eines Befehls erfolgt durch einen separaten (Befehls–)Decodierer, die Steuerung der Ausführung schließlich durch ein Steuerwerk.

SpeicherDie zweite logische Komponente eines von–Neumann–Rechners ist der Speicher. Er be-steht in der Regel aus zwei Teilen, dem ROM und dem RAM. Beim ROM (Read OnlyMemory) handelt es sich um einen Festwertspeicher, in dem eine Information einmalabgelegt wird, dann kann nur noch lesend auf den Speicher zugegriffen werden. DasROM enthält z.B. Systemprogramme (BIOS). Als zweiten Teil des Speichers finden wirein RAM (Random Access Memory), wo auf jede Speicherzelle wahlfreier lesender oderschreibender Zugriff erfolgen kann. Beide Speicherteile können sowohl Daten als auchProgramme enthalten.

Ein–/Ausgabe EinheitDie dritte Komponente des generellen Plans ist die I/O Einheit, die Schnittstelle desRechners zum Benutzer. Über diese können Daten und Programme ein– bzw. ausgegebenwerden.

106

Page 107: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

BusseVerbindung der drei Hauptkomponenten.

8.2 Arbeitsweise einer CPU

Wir wollen nun die Arbeitsweise einer Zentraleinheit, deren Aufbau wir bereits gesehenhaben, genauer betrachten.

Die Bearbeitung eines speziellen Problems erfolgt gemäß einem Programm, welches eineFolge von Befehlen ist. Vor Beginn der Bearbeitung steht dieses zusammen mit den Daten,die es benötigt, im Speicher. Daraus leiten sich die Charakteristika des von–Neumann–Rechners ab:

1. Zu jedem Zeitpunkt führt die CPU genau einen Befehl aus, und dieser kann (höchs-tens) einen Datenwert bearbeiten (diese Philosophie wird im Allgemeinen durch”Single Instruction – Single Data”, kurz SISD abgekürzt).

2. Alle Speicherworte (d.h. Inhalte der Speicherzellen) sind als Daten, Befehle oderAdressen brauchbar. Die jeweilige Verwendung eines Speicherinhalts richtet sichnach dem momentanen Kontext.

3. Da Daten und Programme nicht in getrennten Speicherbereichen untergebrachtwerden, besteht grundsätzlich keine Möglichkeit, die Daten vor ungerechtfertigtemZugriff zu schützen.

Eine Befehlsfolge ist eine Folge von Binärzahlen festen Formats, die nach dem sog. Ma-schinencode aufgebaut ist.

Zentrale Bedeutung bei jeder Berechnung kommt dem Akku des Datenprozessors zu.Grundsätzlich ist dieser bei jeder arithmetischen oder logischen Operation beteiligt. Dar-aus folgt unmittelbar, dass auf die explizite Angabe des Akku in vielen Befehlen verzich-tet werden kann. Einstellige Operationen wie z.B. die Negation benötigen somit keinenOperanden (unter der Annahme, dass sich dieser im Akku befindet). Für zweistelligeOperationen wie Addition oder Multiplikation reicht die Angabe des zweiten Operanden;dieser wird dann mit dem Inhalt des Akkus verknüpft, und das Ergebnis wird wieder imAkku abgelegt. Diesen Befehlstyp nennt man auch Ein–Adress–Befehl; es sei angemerkt,dass man aus Gründen der Vereinfachung der Assembler–Programmierung auch Zwei–,Drei– oder sogar Vier–Adress–Befehle erlauben kann, insbesondere wenn mehr als einUniversalregister vorhanden ist, prinzipiell reichen jedoch Befehle mit einer Adresse aus.

Diese Voraussetzungen bedingen nun den für einen von–Neumann–Rechner typischenBefehlsablauf: Da der Inhalt einer Speicherzelle als Bitfolge weder selbstbeschreibendnoch selbstidentifizierend ist, muss der Rechner aufgrund des zeitlichen Kontextes selbstentscheiden, wie eine spezielle Bitfolge zu interpretieren ist. Technisch löst man diesesProblem, welches sich aus der von–Neumann–Philosophie ergibt, durch das sog. Zwei–Phasen–Konzept der Befehlsverarbeitung:

107

Page 108: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

1. In der sog. Interpretations– oder Fetch–Phase wird der Inhalt von PC nach MARgebracht und der Inhalt dieser Adresse aus dem Speicher über MBR nach IR geholt.Der Rechner geht zu diesem Zeitpunkt davon aus, dass es sich bei dieser Bitfolge umeinen Befehl handelt. Der Decodierer erkennt, um welchen Befehl und insbesondereum welchen Befehlstyp es sich handelt. Nehmen wir an, der aktuelle Befehl ist einMemory–Reference–Befehl, der also — im Gegensatz etwa zu einem Halt–Befehl— einen zweiten Operanden aus dem Speicher benötigt, so weiß der Rechner, dassals nächstes dieser Operand aus dem Speicher geholt (unter erneuter Beteiligungvon MAR) und in MBR abgelegt werden muss. Schließlich muss der Inhalt von PCaktualisiert werden.

2. In der darauf folgenden Execution–Phase erfolgt die eigentliche Befehlsausführungund die Initialisierung der Fetch–Phase für den nächsten Befehl.

Bei diesem zweistufigen Ablauf, der streng seriell zu erfolgen hat, spielt die Zeit, die benö-tigt wird zur Interpretation des Befehls, zum Lesen des Operanden aus dem Speicher, zumAusführen des Befehls und zum Ablegen des Ergebnisses im Speicher, eine große Rolle.Bei ersten Realisierungen eines von–Neumann–Rechners wie z.B. dem UNIVAC–Systemkostete die Befehlsausführung die meiste Zeit. Heute wird diese von den Speicherzugriffs-zeiten dominiert, d.h. die Ausführungszeit eines Befehls (durch die ALU) beträgt im All-gemeinen nur noch einen Bruchteil der Zeit, die benötigt wird, um einen Speicherinhaltzu lesen und über den Bus zur CPU zu übertragen bzw. umgekehrt. Daher spricht manbei dieser Kommunikation zwischen CPU und Speicher auch vom “von NeumannschenFlaschenhals” (engl.: Bottleneck). Wir kommen darauf unten zurück.

Eine Folge von Befehlen stellt ein Programm für einen Rechner dar. Ausgeführt werdendie Befehle eines Programms im Allgemeinen in der Reihenfolge, in der sie (hinterein-ander) im Speicher abgelegt sind (und die durch den Programmierer bestimmt wird).Dazu wird während der Interpretationsphase eines Befehls der Inhalt von PC, der dieAdresse des nächsten auszuführenden Befehls angibt, lediglich um eins erhöht. Eine Aus-nahme bilden (bedingte oder unbedingte) Sprungbefehle (z.B. bei Schleifenenden oderUnterprogramm–Sprüngen); in diesen Fällen ist PC neu zu laden.

Die Fetch–Phase lässt sich somit wie folgt zusammenfassen:

MAR ← PC;MBR ← <MAR>;IR ← MBR;decodiere IR;falls kein Sprungbefehl

dann stelle Operanden bereit; PC ← PC +1 sonst PC ← Sprungzieladresse;

108

Page 109: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 8.3: Speicherhierarchie eines von–Neumann–Rechners

8.3 Der Speicher eines von–Neumann–Rechners

Der Haupt– oder Arbeitsspeicher eines von–Neumann–Rechners wird durch die Größeeiner Speicherzelle (meist 8 Bit oder ein Vielfaches von 8 Bit, z.B. Halbwort oder Wort),die „Breite“ m des Speichers genannt wird, und durch die „Länge“ N , die Anzahl derSpeicherzellen insgesamt, gekennzeichnet. Die Länge N ist hierbei in der Regel eine große2–er Potenz, d.h. N = 2n. Diese Speicherkenngrößen werden durch die Auslegung derRegister MAR und MBR der CPU beeinflusst. Umfasst das Memory Address Register n–Bit, so können insgesamt N = 2n Speicheradressen linear adressiert werden. Das MemoryBuffer Register muss mindestens die Größe der kleinsten adressierbaren Einheit besitzen.

Da heute, wie bereits erwähnt, die Ausführungszeit eines Befehls wesentlich geringer alsdie Speicherzugriffszeit ist, muss eine Beschleunigung der Verarbeitung auch am Spei-chermodell ansetzen. Hier haben sich folgende Kategorien von Speicher etabliert, mitdenen der Prozessor in einer Top–Down Strategie kommuniziert:

• Register innerhalb der CPU, die sehr schnell angesprochen werden können und derCPU unmittelbar als Speicherzellen zugewiesen sind. Moderne Prozessoren verfü-gen über eine höhere Anzahl (z.B. 16) von Universalregistern, die eine ähnlicheFunktionalität aufweisen wie der Akkumulator.

• Die zweite Ebene stellt im Allgemeinen ein sogenannter Cache Speicher dar, der

109

Page 110: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

ebenfalls eine geringe Zugriffszeit besitzt. Die Verwendung eines solchen Speichersfolgt der Idee der „90:10–Regel“, die besagt, dass 90% aller Zugriffe nur auf 10%der Daten erfolgt. Liegen dann die richtigen Daten im Cache, so kann sehr schnellhierauf zugegriffen werden. Der Cache ist vielfach in zwei Kategorien unterteilt:

– L1–Cache, der direkt im Prozessorkern untergebracht ist. Da dieser sehr teuerist, wird meist nur ein L1–Cache in der Größe von 16 – 32 KByte verwendet,der häufig noch in Befehls– und Daten–Cache unterteilt ist.

– L2–Cache, der außerhalb des Prozessors liegt, der allerdings geringe Zugriffs-zeiten von etwa 10–12 ns aufweist. Gängige Größen sind hier 256, 512 oder1024 KByte.

Es existieren allerdings auch Prozessoren, die einen L1–Cache in Größe von z.B.512 KByte besitzen (Pentium–Pro). Deren Herstellung ist aber extrem aufwändig,die Prozessoren sind daher vergleichsweise teuer.

• Die nächste Stufe der Hierarchie ist der Hauptspeicher, der in Form von dynami-schem RAM (Fast–Page–Mode oder EDO RAM mit einer Zugriffszeiten von 50 –60 ns, oder SDRAM mit Zugriffszeiten von 7 – 12 ns) ausgeführt ist. Hier sindGrößenordnungen von mehreren Megabyte (16, 32, 64, 128, . . .) durchaus üblich.

• Schließlich stehen in der Regel Hintergrundspeicher in Form von Magnetplattenmit wahlfreiem Zugriff (Festplatten mit Größen von mehreren Gigabyte), Nur–Le-se–Speicher wie das CD–ROM (640 MByte), wiederbeschreibbare CD–ROM (CD–RW), die Digital–Versatile–Disk (DVD) mit mehreren Gigabyte sowie Floppy–Disketten (1.44, 2.88, 100 MByte) zum einfachen Datenaustausch und Bandspei-chermedien, die eine sequentielle Datenspeicherung erlauben und in Allgemeinenals Backupmedien eingesetzt werden, zur Verfügung. Bei Backupmedien sind Ka-pazitäten von 50 GByte und mehr pro Datenband erreichbar.

8.4 Busse

Ein weiterer wichtiger Bestandteil des von–Neumann–Rechners sind die Busse, die fürden Transport der Daten, Befehle und Adressen zwischen den einzelnen Komponentenverantwortlich sind. Prinzipiell wäre eine einzige Leitung zur Realisierung eines seriellenBusses ausreichend. Hierbei hat man allerdings den Nachteil der geringen Geschwindig-keit. Daher kommen im Allgemeinen parallele Busse zum Einsatz. Konzeptionell kämeman mit einem einzigen Bus aus, da die Interpretationen des Datums als Adresse, Befehloder Daten kontextabhängig ist. In der Regel wird jedoch zumindest zwischen einemDaten– und Adressbus unterschieden. Dies ist dadurch begründet, das zum einen derAdressbus nur unidirektional (von CPU zum Speicher) ausgelegt sein muss und zumanderen die Breite des Datenbusses nicht notwendig der Breite des Adressbusses ent-spricht. Die Größe der Busse ist ein weiteres Geschwindigkeitskriterium. Will man, wie

110

Page 111: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

oben bereits erwähnt, einen Speicher von N = 2n Zellen linear adressieren, so benötigtman einen Adressbus, der parallel n–Bits übertragen kann. Die Breite des Datenbusseswiederum gibt Aufschluss über die Größe eines Speicherplatzes, der adressiert werdenkann. So können z.B. mit einem 32 Bit breiten Datenbus 4 Bytes gleichzeitig übertragenwerden, auch wenn z.B. die kleinste adressierbare Einheit 1 Byte beträgt. Die Auslegungder Busse korrespondiert direkt mit dem MAR und MBR Register des Prozessors. Esgilt: Breite des Datenbusses gleich Länge des MBR, Breite des Adressbusses gleich Längedes MAR.

Neben den zwei zentralen Bussen, dem Daten– und Adressbus, bestehen häufig weitereBusse für spezielle Aufgaben, wie z.B. I/O Operationen.

8.5 I/O Einheit und Steuerung durch Interrupts

Die Kommunikation des Rechners mit dem Anwender oder externen Medien wird durcheine Ein–/Ausgabeeinheit durchgeführt. Häufig wird diese I/O Einheit durch einen wei-teren speziellen Bus mit dem System verbunden. Dieser eigenständige I/O Bus wirddann auch häufig nicht mehr von der CPU, sondern von einem speziellen I/O–Prozessorgesteuert.

Wenn wir davon ausgehen, dass die CPU jedoch die oberste Kontrolle über alle Abläufeim Rechner hat, so können sich bei I/O Operationen folgende Probleme ergeben:

1. Die CPU ist in dem Moment, wo ein I/O Gerät Daten übertragen möchte, miteiner anderen Aufgabe beschäftigt.

2. Das I/O Gerät ist wesentlich langsamer als die CPU, die CPU wird dann ggf. beiI/O Operationen unnötig lange blockiert.

Um diese Probleme abzufangen, existiert ein sogenannter I/O Controller, der die I/OGeräte ansteuert und die Daten in einem eigenen Puffer zwischenspeichern kann. Daseigentliche Endgerät kann dann nur noch mit dem I/O Controller Daten austauschenund nicht mehr direkt mit der CPU. Der I/O Controller wiederum verfügt dann überspezielle Steuerleitungen und Datenleitungen, mit denen er mit der CPU verbunden ist.

Der I/O Controller signalisiert der CPU, wenn er zur Übertragung von Daten zu odervon einem Endgerät bereit ist. Hierzu muss die CPU in gewissen Zyklen ein Statuswortvom I/O–Controller abfragen. Hierbei kommt es jedoch dazu, dass dieses Statuswortwesentlich häufiger abgefragt wird als es notwendig wäre. Dies führt zum sogenanntenKonzept des Interrupt–gesteuerten I/O. Hierbei wird bei einem speziellen Ereignis (z.B.Controller bereit zum Senden) über eine spezielle Leitung ein spezielles Interruptsignalan den Prozessor gesendet, dieser unterbricht die momentane Bearbeitung, ruft einenInterrupt Handler auf, und führt anschließend das unterbrochene Programm weiter. EineEingabe könnte somit konkret wie folgt ablaufen:

111

Page 112: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

1. Das Endgerät ist bereit zur Übertragung von Daten zum Rechner, der I/O–Controllersendet ein Interrupt–Signal an die CPU.

2. Die CPU unterbricht die momentane Programmbearbeitung, liest den I/O Control-ler Status aus, sendet dem Controller ein Start–Signal und setzt die unterbrocheneProgrammbearbeitung fort.

3. Der Controller empfängt Daten vom Endgerät und speichert diese in seinem Puffer.Sobald der Puffer voll oder die Eingabe beendet ist, sendet der Controller wiederein Interrupt–Signal an die CPU.

4. Die CPU unterbricht wieder die momentane Programmbearbeitung, überträgt dieDaten vom I/O Controller in den Speicher und setzt anschließend das unterbrocheneProgramm fort.

Bei diesen Konzepten hat die CPU stets nur eine Kontrollfunktion und keine eigentli-che Berechnungsfunktion. Daher liegt es nahe, auch eine andere Klasse von Controllerneinzusetzen, die unabhängig von der CPU Daten vom oder in den Speicher übertragenkönnen. Diese Controller werden Direct Memory Access (DMA–) Controller genannt,und kommen z.B. bei Festplattencontrollern zum Einsatz. Hierbei muss allerdings einMechanismus vorhanden sein, der einen Zugriffskonflikt zwischen DMA–Controller undCPU behandelt. Ein solcher Mechanismus entzieht bei einem Konflikt der CPU für einigeTaktzyklen den Zugriff zum Speicher und erlaubt dem DMA–Controller den vorrangigenZugriff.

Beim Konzept der Interrupt–gesteuerten Aktionen unterscheidet man im wesentlichenvier verschiedene Interrupts.

• Ein externer Interrupt wird außerhalb der CPU erzeugt und kommt typischerweisevon einem I/O–Controller, der hierdurch eine Übertragung einleitet. Im Gegen-satz hierzu werden auch innerhalb der CPU die sogenannten internen Interruptserzeugt, die z.B. auftreten, wenn Programmfehler, wie etwa eine Division durchNull, vorliegen. In diesem Fall kann der Interrupt eine Fehlerbehandlungsroutine,sogenannte Traps, anstoßen.

• Ein maskierbarer Interrupt kann (vorübergehend) außer Kraft gesetzt werden. Hier-durch kann z.B. bei Programmen, die ohne Unterbrechung ablaufen müssen, ein zwi-schenzeitlicher I/O verhindert werden. Falls ein maskierter Interrupt auftritt, wirddieser erst nach Beendigung des eigentlichen Programms ausgeführt, bzw. dann,wenn die Maskierung aufgehoben wird. Im Gegensatz hierzu führen die unmaskier-baren Interrupts stets zu einer Unterbrechung des momentanen Programms.

Zusätzlich hierzu werden Interrupts in der Regel mit Prioritäten versehen, so dass beieiner momentan aktiven Interruptbehandlung ein neu auftretender Interrupt nur dannzur Unterbrechung der momentanen Behandlung führt, falls seine Priorität höher als

112

Page 113: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 8.4: Einfaches Befehlsphasen Pipelining

die des momentan bearbeiteten ist. Ist die Priorität niedriger, so wird der Interrupterst nach Beendigung der ersten Interruptroutine abgearbeitet. Mittels Prioritäten undMaskierung ist es möglich, nur gewissen Interrupts die Unterbrechung der eigentlichenProgrammbearbeitung zu gestatten.

8.6 Erweiterungen des von–Neumann–Konzepts

Die heute im Einsatz befindlichen Ein–Prozessor–Systeme sind im wesentlichen nachdem beschriebenen von–Neumann–Konzept gestaltet. Es existieren jedoch verschiede-ne Ansätze zur Erweiterung des besprochenen Organisationsplanes. Eine Richtung derErweiterung ist die Milderung des sog. von–Neumannschen Flaschenhalses, der wegenunterschiedlicher Zeiten für die Befehlsausführung und den Speicherzugriff auftritt. Wirhaben hierzu bereits Ansätze unter Verwendung von hierarchischem Speicher mit schnel-len Zugriffszeiten in Prozessornähe (Register, Cache) und langsameren Zugriffszeiten fürden eigentlichen Hauptspeicher diskutiert.

Eine zweite Erweiterung betrifft nun die Verarbeitungsgeschwindigkeit innerhalb des Pro-zessors. Wir haben hierzu das Grundkonzept der Fetch– und Instruction–Phase diskutiertund waren hierbei von einer seriellen Abfolge der einzelnen Phasen und vor allem auchvom seriellen Ablauf der Phasen für verschiedene Befehle ausgegangen. Moderne Prozes-soren versuchen, diesen seriellen Ablauf mit Pipelining Konzepten zu umgehen.

Der erste Ansatz ist das sogenannte einfache Befehlsphasen Pipelining, das in Abbildung8.4 dargestellt ist.

In dieser Befehlphasen–Pipeline werden die einzelnen Phasen versetzt zueinander ausge-führt, d.h. wenn die Befehls–Fetch–Phase des ersten Befehls abgeschlossen ist und dieDaten–Fetch–Phase beginnt, wird bereits für einen zweiten Befehl die Befehls–Fetch–Phase begonnen. Dies kann auch mit einer höheren Schachtelungstiefe geschehen.

Ein zweiter Ansatz besteht in der weiteren Unterteilung der einzelnen Phasen. Damitwird in der Pipeline sogar eine Überlappung der ursprünglichen Phasen für aufeinanderfolgende Befehle erreicht, was eine weitere Schachtelungstiefe ermöglicht. Dieses Prinzipwird Superpipelining genannt und in der folgenden Abbildung graphisch gezeigt.

Schließlich besteht die Möglichkeit, mehrere Pipelines parallel zu betreiben, z.B. fürFloatingpoint– und Integer–Berechnungen. Ein solcher Ansatz ist die im Folgenden dar-

113

Page 114: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Abbildung 8.5: Prinzip des Superpipelining

Abbildung 8.6: Prinzip einer Superskalararchitektur

gestellte Superskalararchitektur. Hierbei wird zudem versucht, möglichst viele Teilkompo-nenten wie z.B. mehrfach vorhandene ALU’s für Floating–Point und Integer–Arithmetikoder einen vorhandene Arithmetik Coprozessor parallel einzusetzen.

114

Page 115: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

9 Eine kleine Programmiersprache

9.1 Syntaktische Beschreibungsmittel

Eine Programmiersprache ist nach genauen syntaktischen Regeln aufgebaut. Diese müs-sen so beschaffen sein, dass mit einem Automaten geprüft werden kann, ob ein Programmsyntaktisch korrekt ist. 1958 wurde daher von John Backus eine formale Beschreibungder Syntax von ALGOL vorgenommen, die von Peter Naur überarbeitet wurde. NiklausWirth, der Schöpfer von Pascal, hat die BNF nochmals überarbeitet und nennt das Er-gebnis erweiterte Backus–Naur–Form EBNF. Daher spricht man heute von der Backus–Naur–Form. Sie sind für eine Klasse von Sprachen gedacht, die in die Klassifizierungvon Chomsky als kontextfreie Sprachen eingeordnet werden können. Dieser hatte eineaufsteigende Folge von Sprachen definiert.

Definition 9.1.1Unter einer Chomsky–Grammatik versteht man ein QuadrupelG = (N,T, P, S). Dabeibezeichnet

• N die Menge der Nichtterminalsymbole,

• T die Menge der Terminalsymbole, T ∩N = ∅,

• P die Menge der Produktionen P ⊂ϕ→ ψ| ϕ ∈ (N ∪ T )+ , ψ ∈ (N ∪ T )∗

,

• S das Startsymbol.

Ferner ist (N ∪ T )+ die Menge der nichtleeren endlichen Wörter mit Zeichen aus N ∪T ,während (N ∪ T )∗ zusätzlich noch das leere Wort λ enthält. Alle Mengen N , T , P sindendlich.

Definition 9.1.2Sei G = (N,T, P, S) eine Chomsky–Grammatik. G heißt

• Typ–3–Grammatik oder rechtslinear, wenn alle Produktionen von einer der Formen

A→ λ, A→ a, A→ aB (a ∈ T, A,B ∈ N)

sind.

115

Page 116: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• Typ–2–Grammatik oder kontextfrei, wenn alle Produktionen von der Form

A→ ψ, (A ∈ N, ψ ∈ (N ∪ T )∗)

sind.

• Typ–1–Grammatik oder kontextsensitiv, wenn alle Produktionen von der Form

ϕ1Aϕ2 → ϕ1ψϕ2, (A ∈ N,ϕ1, ϕ2, ψ ∈ (N ∪ T )∗) , ψ 6= λ,

sind.

Ferner darf zusätzlich S → λ definiert sein. S darf dann aber sonst auf keinerrechten Seite auftreten.

• Typ–0–Grammatik oder allgemeine Chomsky–Grammatik, wenn die Form der Pro-duktionen nicht weiter eingeschränkt ist.

• Für eine Chomsky–Grammatik G ist die Menge aller Wörter w, die durch eineAbleitungsfolge mit Hilfe der Produktionen P aus G erzeugt werden können, diedurch G erzeugte Sprache.

Die Programmiersprachen werden nun im wesentlichen durch kontextfreie Sprachen de-finiert. Mit der BNF werden die Produktionen beschrieben. Wichtig ist der Unterschiedzwischen der zu definierenden Sprache, die aus den Terminalsymbolen besteht, und derzu ihrer Beschreibung notwendigen Metasprache, zu der man die Nichtterminalsymbolebenutzt.

Definition 9.1.3Die Metazeichen der EBNF sind

• das Definitionszeichen =,

• das Alternativzeichen |,

• die Anführungszeichen ” ”,

• die Wiederholungsklammern – mehrfache (auch nullfache) Wiederholung,

• die Optionsklammern [] – null– oder einfaches Auftreten,

• die Gruppenklammern (),

• der Punkt – zur Beendigung einer Regel.

Für die Menge E der EBNF–Terme gilt:

• Nichtterminalsymbole bestehen aus einer Folge von Buchstaben und Ziffern begin-nend mit einem Buchstaben

116

Page 117: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• ”w” mit einer beliebigen Folge w von Symbolen ist ein Terminalsymbol.

• Für α ∈ E ist auch (α), [α], α ∈ E.

• Für α1, . . . , αn ∈ E sind auch α1| . . . |αn ∈ E, α1α2 . . . αn ∈ E. Eine EBNF–Definition besteht dann aus einer endlichen Menge aus EBNF–Regeln der FormV = α, wobei V ein Nichtterminalsymbol ist und α ein EBNF–Term.

Beispiel 9.1.4Ziffer = "0"|"1"|"2"|...|"9".

Buchstabe = "A"|...|"Z"|ä"|...|"z".

Name = Buchstabe Buchstabe | Ziffer.

VorzeichenloseZahl = Ziffer Ziffer.

Statt VorzeichenloseZahl kann auch Ziffernfolge benutzt werden.

Vorzeichen = "+" | "-".

Zahl = [Vorzeichen] VorzeichenloseZahl.

Unterstrich = "_".

Bezeichner = (Buchstabe | Unterstrich) Buchstabe | Ziffer | Unterstrich.

In der Folge werden wir für Bezeichner auch den englischen Ausdruck ident und für Zahlnumber benutzen.

Die EBNF kann man sehr leicht in eine graphische Notation übersetzen, die sogenanntenSyntaxdiagramme.

Definition 9.1.5Syntaxdiagramme bestehen aus folgenden Bestandteilen mit folgenden Regeln:

• Runde Kästchen für die Terminalsymbole,

• eckige Kästchen für die Nonterminalsymbole,

• Verbindungen aus Linien und Pfeilen.

• An jedem Kästchen endet und beginnt genau ein Pfeil, in der Regel gegenüberlie-gend.

• Es gibt genau eine Linie, die oben links am Eingang in das Diagramm hineinführt.

• Es gibt genau eine Linie, die unten rechts am Ausgang aus dem Diagramm heraus-führt.

• Linien dürfen nur verzweigen und sich wieder treffen, aber sich nicht kreuzen.

117

Page 118: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Jedes Syntaxdiagramm hat einen eindeutigen Namen. Ein erlaubter Weg beginnt am Ein-gang, folgt den Pfeilen, wobei Kästchen durchquert werden können. An Verzweigungenist ein beliebiger Weg auswählbar. Die von einer Menge von Syntaxdiagrammen erzeugteSprache kann man durch einen erlaubten Weg unter Notation der angetroffenen Terminal-symbole und rekursives Durchqueren der zu Nonterminalsymbolen gehörigen Diagrammeerzeugen.

Definition 9.1.6 (Übersetzung von EBNF in Syntaxdiagramme)1. Jeder Regel in EBNF entspricht ein Syntaxdiagramm mit dem Namen der linken

Seite der Regel.

2. Die Übersetzung der rechten Regelseiten geschieht durch Rekursion über die Struk-tur der EBNF–Terme wie folgt:

a) Nichtterminalsymbole V werden in ein eckiges Kästchen übersetzt. - V

b) ”w”–Terminalsymbole werden in ein rundes Kästchen übersetzt. -½¼¾»

w

c) (α) wird wie α übersetzt

d) [α] wird zu - α6

e) α wird zu - α6

?

f) α1 . . . αn wird in eine Kette von Teildiagrammen übersetzt, die sich als Über-setzungen der einzelnen αi ergeben, und durch Pfeile verbunden.

g) α1| . . . | αn wird in eine Verzweigung in n Zweige übersetzt, die sich aus denÜbersetzungen der einzelnen αi ergeben.

- ?6

αn

. . .

. . .

α1

9.2 Die Syntax von Mini–Pascal

Als Beispielsprache wählen wir nun einen kleinen Ausschnitt von Pascal, der Mini–Pascalheißen soll. Damit folgen wir der Darstellung von N. Wirth in seinem Buch Compilerbaubeim Teubnerverlag.

Definition 9.2.1Die kontextfreie Syntax von Mini–Pascal ist durch die folgende EBNF–Definition gege-ben:

118

Page 119: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

program = "program" ident ";" block".".

block = [constDecl] [varDecl] procDecl

"begin" statement ";" statement "end".

constDecl = "const" ident "=" number ";" ident "=" number ";".

varDecl = "var" ident "," ident ":" "integer" ";".

procDecl = "procedure" ident ";" block ";".

statement = [varIdent ":=" expression | procIdent |

"begin" statement ";" statement "end" |

"if" condition "then" statement |

"while" condition "do" statement].

condition = expression ("=" | "<>" | "<" | "<=" | ">" | ">=") expression.

expression = ["+" | "-"] term ("+" | "-") term.

term = factor ("*" | "div") factor.

factor = constIdent | varIdent | number | "(" expression ")".

constIdent = ident.

procIdent = ident.

varIdent = ident.

Erklärungen:

In der Definition der Sprache finden wir als Terminalsymbole die Strukturierungszeichen :

, ; . Dazu kommen die unären Vorzeichenoperatoren, die Operatoren für die Ganzzahl-operationen +, -, *, div und die binären Vergleichsoperatoren. Durch die Schlüssel-wörter const, var werden Konstanten und Variablen vom Typ Integer deklariert. DieWörter begin und end strukturieren das Programm und die zugehörigen Prozeduren, diemit program und procedure eingeleitet werden. Weiter sind Kontrollstrukturen für wie-derholte Anweisungen (while - do) und bedingte Anweisungen (if - then) vorgesehen.Schließlich müssen arithmetische Ausdrücke über die rekursive Definition expression -

term - factor eingeführt werden, wobei die Regeln Punkt– vor Strichrechnung abgebil-det sind. Schlüsselwörter sollen in der Regel nicht als Variablen– oder andere Bezeichnerverwendet werden. Auf die Groß– oder Kleinschreibung kommt es nicht an. Weiterhinwollen wir verabreden, dass (* *) als Kommentarklammern gelten. Der hierin notierteText gehört nicht zum Programm und wird einfach überlesen.

Allerdings müssen wir einräumen, dass unser Mini–Pascal keine reine kontextfreie Spracheist.

Definition 9.2.2• Die Deklarationen constDecl, varDecl und block bilden eine Einheit. Die hierfür

deklarieren Bezeichner ident müssen alle verschieden sein.

• Alle Bezeichner können innerhalb der eingeschachtelten Blöcke nochmals deklariertwerden. In diesem Fall überlagert die innere Definition die äußere, d.h. der imäußeren Block deklarierte Bezeichner wird vorübergehend unsichtbar.

• Jeder Bezeichner constIdent, varIdent oder procIdent, der in einem statement

119

Page 120: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

oder factor eines Blocks vorkommt, muss vorher so deklariert worden sein, dasser die entsprechende Identität erhalten hat.

Mini–Pascal ist eine echte Teilmenge von Pascal, das wir im nächsten Kapitel näheruntersuchen werden. Dieses verfügt über eine Vielzahl weiterer Datentypen, die überinteger hinausgehen. Dazu kommen Funktionen und Prozeduren, die die Peripherie derCPU und des Rechners betreffen, wie Ein– und Ausgabe (ReadLn, WriteLn). Diese beidenProzeduren wollen wir der Einfachheit halber ohne zusätzliche syntaktische Definitionverwenden. Mittels ReadLn werden n globale Variablen eingeführt, von denen die erstedas Ergebnis des Rechenvorgangs liefern könnte, das dann mit WriteLn ausgegeben wird.Zusätzlich ist ein modularer Aufbau der Programme möglich.

Wir wollen noch einige weitere Regeln im Umgang mit der Syntax von Mini–Pascalansprechen.

• Das Semikolon dient als Trennungszeichen zwischen Anweisungen. Anweisungenvor einem end benötigen keines mehr. Da eine leere Anweisung erlaubt ist, istSetzen des Semikolons jedoch ohne Relevanz. Hinter einer condition steht jedochkein Semikolon.

• Pascal hat ein Blockschachtelungsprinzip, das bedeutet, dass Bezeichner, die ineinem Block deklariert worden sind, auch in den inneren Blöcken gelten (Sichtbar-keit). Durch Neudeklaration werden die Bezeichner zeitweise unsichtbar. Somit ha-ben Bezeichner und zugehörige Variablen und Prozeduren eine Lebensdauer. NachVerlassen ihres Deklarationsblockes können die zugehörigen Speicherbereiche wie-der freigegeben werden, während beim Betreten eines Blockes den hier deklariertenlokalen Variablen neue Speicherplätze zuzuweisen sind.

Beispiel 9.2.3program Beispiel;

var A, B, C: integer; (* Damit sind A, B, C globale Integervariablen *)

procedure p;

var A, B, D : integer;

(* Hier sind die globalen Variablen A, B unsichtbar und werden durch

die lokalen Variablen der Prozedur p ersetzt. C bleibt sichtbar, D

kommt als lokale Variable hinzu. *)

procedure q;

var C: integer;

(* Ab hier ist die globale Variable C unsichtbar *)

begin

.......

(* Es sind die Variablen A_p, B_p, C_q, D_p zugaenglich *)

120

Page 121: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

end; (* Ende der Prozedur q *)

begin

......

(* Es sind die Variablen A_p, B_p, C, D_p zugaenglich *)

end; (* Ende der Prozedur p *)

(* Von jetzt sind nur noch die globalen Variablen A, B, C sichtbar *)

begin

....

(* Hier steht nun das Hauptprogramm, in dem die Variablen A, B, C

neue Werte per Zuweisung auch innerhalb von Kontrollstrukturen

erhalten und die Prozeduren p und q ueber ihre Namen aufgerufen

werden koennen. *)

end.

121

Page 122: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Definition 9.2.4Mini–Pascal ist durch die folgenden Syntaxdiagramme definiert:

;

varDeclvar ident ident : integer

procDeclident blockprocedure ;

, ;

statementbegin statement ; statement end

if condition statement

while condition do statement

varIdent expression

procIdent

:=

then

122

Page 123: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

9.3 Semantik von Mini–Pascal∗

∗ Die folgenden beiden Unterkapitel werden in Informatik III ausführlich behandelt.

Man kann die Semantik einer Programmiersprache, d.h. die Interpretation ihrer Spra-chelemente einmal auf operationelle Weise definieren. Dabei ist eine Maschine zugrundegelegt, und man beschreibt, wie sich die Maschine bei Ausführung der Instruktionenoder des gesamten Programmes verhält. Dies ist bei maschinennahen Programmen, diewir in der Vorlesung Theoretische Informatik noch kennenlernen werden, sicherlich derbeste Weg. Für höhere Programmiersprachen mit einer komplexen Struktur wird an-dererseits die denotationelle Semantik vorgezogen, bei der man die Sprachelemente inmathematische Objekte umsetzt, die dann als einzelne Bestandteile einer komplexenTheorie angesehen werden.

Wir wollen nun die einzelnen Bestandteile der Sprache Mini–Pascal ansprechen und ihreSemantik beschreiben.

Namensräume dienen dazu, bei Deklarationen den notwendigen Speicherplatz zu reser-vieren. Den Konstanten und Variablen vom Typ ganze_Zahl werden nun Adressen imSpeicher zugeordnet, die durch natürliche Zahlen beschrieben sind. Die Gesamtheit derWerte, die auf diese Weise mit den Variablen in Verbindung gebracht werden, heißt Spei-cherzustand. Anweisungen bestehen nun letztlich aus der Zuordnung neuer Werte zu denVariablen und bewirken somit eine Zustandsänderung. Folglich ordnet man den einzelnenBezeichnergruppen sogenannte Umgebungen (environments) zu.

Zur Definition der Semantik bedarf es daher einer Anzahl semantischer Bereiche, überdenen semantische Funktionen operieren. Dabei handelt es sich um Mengen partiell de-finierter Abbildungen [A→ B] von A nach B. Ω bezeichne allgemein die überall undefi-nierte partielle Abbildung unabhängig von Urbild– und Bildbereichen.

Definition 9.3.1Die semantischen Bereiche von Mini–Pascal

Adr := N Adressen

Int := Z ganze Zahlen

Bool := w, f

Ide := A+, A := A, ..., Z, a..., z,_, 0, ..., 9.

Dabei beachte man die Einschränkung für den ersten Buchstaben. Gemeint sind dieverschiedenen Identifier–Mengen.

Ähnliche Definitionen gelten für die Nichtterminale Program, Block, Condition, Ex-

pression etc. Hier sind jeweils die entsprechenden Programmabschnitte gemeint.

V Env := [Ide→ Adr] Variablenumgebung.Sie ordnet einer Menge von Bezeichnern ihre jeweilige Adresse zu.

123

Page 124: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Store := [Adr → Int] Speicherzustände.Ein Speicherzustand ordnet einer Menge von Adressen die darunter jeweils gespeichertenganzzahligen Werte zu.

CEnv := [Ide→ Int] Konstantenumgebung.Sie ordnet einer Menge von Bezeichnern die dadurch jeweils definierten Konstanten zu.

PEnv := [Ide→ [Store→ Store]] Prozedurumgebungen.Sie ordnen einer Menge von Prozedurbezeichnern die jeweilige Speichertransformationihres Rumpfes zu.

Stat := [CEnv × V Env × PEnv × Store→ Store] Speichertransformation.Die von einer ausführbaren Anweisung induzierte Transformation auf dem Speicher hängtaußer vom Inhalt auch noch von diversen Umgebungen (vgl. Verdeckung) ab.

Die nun aufgeführten semantischen Funktionen sind alle partielle Funktionen. Wir habendie folgenden Typen:

C : constDecl× CEnv → CEnv.Die Semantik einer Konstantendeklaration ist eine Konstantenumgebung.

V : VarDecl× V Env → V Env.Die Semantik einer Variablendeklaration ist eine Variablenumgebung.

Pc : Proc×CEnv × V Env × PEnv → PEnv.Die Semantik einer Prozedurdeklaration ist eine Prozedurumgebung. Dabei steht Proc

für die Namen der ProcIdent.

E : expression × CEnv × V Env × Store→ Int.Die Semantik eines Ausdrucks ist eine ganze Zahl.

R : condition× CEnv × V Env × Store→ Bool.Die Semantik einer Relation (Bedingung) ist ein Wahrheitswert.

S : statement→ Stat.Die Semantik einer Anweisung ist eine Speichertransformation.

B : block→ Stat.Die Semantik eines Blocks ist eine Speichertransformation aus [Store→ Store] .

P r : program→ [Intn → Int].Die Semantik eines Programms ist eine partielle Abbildung von Intn nach Int.

Diese semantischen Funktionen haben alle die Gestalt

F [α]β,

wobei F der Name der Funktion ist, α innerhalb der Klammer ein Mini–Pascal Pro-grammstück, β eine Folge weiterer Argumente in klammerloser Notation.

Die einzelnen Funktionen werden nun in den folgenden Definitionen ausführlich erklärt:

124

Page 125: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Definition 9.3.2Die Semantik der Const –Deklaration ist eine Funktion

C : constDecl× CEnv → CEnv.

Sei ϑ eine Funktion aus CEnv. Dann ist

C [const i1 = n1; ...; im = nm; ]ϑ

:= C1 [im = nm]C1 [im−1 = nm−1] . . . C1 [i1 = n1]ϑ,

wobeiC1 [i = n]ϑ := ϑ [i/n]

ist. Dabei heißt die Notation ϑ [i/n] , dass die i–te Speicherzelle von ϑ durch n besetztwird und alle anderen sich nicht ändern.

So wird die Konstantenumgebung bei jeder zusätzlichen Definition ergänzt und verlän-gert; zu Beginn war sie als Ω definiert.

Definition 9.3.3Die Semantik der Var –Deklaration ist eine Funktion

V : varDecl× V Env → V Env.

Sei ϕ eine Funktion aus V Env. Dann ist

V [var i1, ..., im : Integer; ]ϕ

:= V1 [im]V1 [im−1] ...V1 [i1]ϕ,

wobeiV1 [i]ϕ := ϕ [i/new]

ist. Dabei bezeichnet new einen neuen Speicherplatz, der bislang noch nicht vergebenwar und der nun der Variablen i zugeordnet wird.

Um die Semantik von Prozedurdeklarationen zu begreifen, müssen wir eine Semantik fürBlock vom Typ B[α] voraussetzen. Diese Definition wird später nachgeholt. Das Problemist hier eine zirkuläre Definition der Semantik von zwei Prozeduren, die sich gegenseitigaufrufen, die durch einen induktiven Ansatz oder zusätzliche IF–Anweisungen aufgelöstwerden können.

125

Page 126: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Definition 9.3.4Die Semantik der procedure–Deklaration ist erklärt durch eine Funktion

Pc : proc× CEnv × V Env × PEnv → PEnv.

Sei π eine Funktion aus PEnv, ϑ ∈ CEnv, ϕ ∈ V Env und σ ∈ Store. Dann ist

Pc [procedure p;α; ]ϑϕπ := π [i/µ] ,

wobeiµ (σ) := B [α]ϑϕπ [i/µ]σ.

Hier ist nach der Definition von PEnv zu beachten, dass dem Prozedurbezeichner ieine Speichertransformation µ ∈ Stat = [Store→ Store] auf den Speicherzustand σ ∈Store = [Adr → Int] zuzuordnen ist. Dabei kann man ausnutzen, dass für den Zugriffauf nicht–lokale Variablen aus einer Prozedur heraus nicht die Aufrufumgebung, sonderndie Deklarationsumgebung maßgebend ist.

Definition 9.3.5Die Semantik von Ausdrücken ist erklärt durch eine Funktion

E : expression× CEnv × V Env × Store→ Int.

Seien ϑ ∈ CEnv, ϕ ∈ V Env, σ ∈ Store. Unter Weglassung der Klammern setzen wir

E [ident]ϑϕσ :=

σ (ϕ (ident)) , falls varIdentϑ (ident) , falls constIdent

E [number]ϑϕσ := number

E [f1 ∗ f2]ϑϕσ := E [f1]ϑϕσ · E [f2]ϑϕσ

E [f1 div f2]ϑϕσ := bE [f1]ϑϕσ/E [f2]ϑϕσc

E [t1 + t2]ϑϕσ := E [t1]ϑϕσ + E [t2]ϑϕσ

E [t1 − t2]ϑϕσ := E [t1]ϑϕσ − E [t2]ϑϕσ

E [+t]ϑϕσ := E [t]ϑϕσ

E [−t]ϑϕσ := −E [t]ϑϕσ

E [(expression)]ϑϕσ := E [expression]ϑϕσ

Dabei sind rechts immer die entsprechenden ganzen Zahlen notiert.

Definition 9.3.6Die Semantik von Bedingungen ist erklärt durch eine Funktion mit den booleschen Wer-ten w und f .

R : condition× CEnv × V Env × Store→ Bool.

Seien ϑ ∈ CEnv, ϕ ∈ V Env, σ ∈ Store. Unter Weglassung der Klammern setzen wir

126

Page 127: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

R [e1 = e2]ϑϕσ :=

w, falls E [e1]ϑϕσ = E [e2]ϑϕσ

f, sonst

R [e1 <> e2]ϑϕσ :=

w, falls E [e1]ϑϕσ 6= E [e2]ϑϕσ

f, sonst

R [e1 < e2]ϑϕσ :=

w, falls E [e1]ϑϕσ < E [e2]ϑϕσ

f, sonst

R [e1 <= e2]ϑϕσ :=

w, falls E [e1]ϑϕσ ≤ E [e2]ϑϕσ

f, sonst

R [e1 > e2]ϑϕσ :=

w, falls E [e1]ϑϕσ > E [e2]ϑϕσ

f, sonst

R [e1 >= e2]ϑϕσ :=

w, falls E [e1]ϑϕσ ≥ E [e2]ϑϕσ

f, sonst

Definition 9.3.7Die Semantik von Anweisungen ist erklärt durch eine Funktion vom Programmtextder Anweisungen auf die Speichertransformationen. Wir gehen induktiv vor. Seien ϑ ∈CEnv, ϕ ∈ V Env, π ∈ PEnv, σ ∈ Store. Wir geben hier nicht die Abbildung aus Statan, sondern wenden sie gleich auf Elemente ϑϕπσ an. Dann ist das Ergebnis aus Store.

S[]ϑϕπσ := σ Identität der Speicherabbildung

S[i := e]ϑϕπσ := σ [ϕ(i)/(E [e]ϑϕσ)]

S[p]ϑϕπσ := π(p)ϑϕσ

S[begin α1; ...;αn end]ϑϕπσ := S[αn]ϑϕπS[αn−1]ϑϕπ . . . S[α1]ϑϕπσ

S[if c then α]ϑϕπσ :=

S[α]ϑϕπσ, falls E [c]ϑϕσ = w

σ sonst.

S[while c do α]ϑϕπσ :=

σ, falls E [c]ϑϕσ = f

S[while c do α]ϑϕπS[α]ϑϕπσ, sonst

Zeile 2 bedeutet eine Zuweisung Zahl zur Speicherzelle ϕ (i) , π vom Typ Stat bildet alsπ(p)ϑϕσ auf Store ab. In der vierten Zeile liefern die Anwendungen

S[α1]ϑϕπσ = σ1, S[αi]ϑϕπσ = σi, i = 2, . . . , n,

sukzessive neue Speicherzustände aus Store. Die folgenden Zeilen sehen eine Anwendungvon α auf ϑϕπσ per S[α]ϑϕπσ, falls die Bedingung erfüllt ist. In Zeile 6 kommt dazueine rekursive Definition, die eventuell nicht terminiert.

Definition 9.3.8Die Semantik von Blöcken ist erklärt durch eine Funktion

127

Page 128: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

B : block→ Stat.

Seien ϑ, ϑi ∈ CEnv, ϕ, ϕi ∈ V Env, π = π0 ∈ PEnv, σ ∈ Store. Außerdem sei

Θ ∈ constDecl,Φ ∈ varDecl,Πi ∈ procDecl, i = 1, ...,m.

Dann ist

B[ΘΦΠ1...Πmα]ϑϕπσ := S [α]C [Θ]ϑV [Φ]ϕP [Πm]ϑmϕm...P [Π1]ϑ1ϕ1πσ.

Hier wird das Blockschachtelungsprinzip verwirklicht und die P [Πi]ϑiϕiπi−1 liefern je-weils mit den zugehörigen Werten ϑi ∈ CEnv, ϕi ∈ V Env ein neues πi ∈ PEnv, aufdas schließlich unter Berücksichtigung der neuen Deklarationen der Programmtext ausdem Block mit seinen statements Anwendung findet und als Mitglied von Stat einenSpeicherzustand ergibt.

Wir schließen mit der Semantik eines ganzen Mini–Pascalprogramms:

Definition 9.3.9Sei pg ein syntaktisch korrektes Mini–Pascalprogramm im Sinne der folgenden Definition:

pg = program pr;

Θ

Φ

Π1...Πm

begin

ReadLn(v1, ..., vn);

α

WriteLn (e)

end.

Dann ist mit den Setzungen aus Definition 9.3.8, ϑ, ϕ, π undefiniert (= Ω) und mit demanfänglichen Speicherzustand

σ = (ϕ(v1)/z1, ..., ϕ(vn)/zn)

die Semantik von pg erklärt durch eine Funktion Pr: program→ [Intn → Int] mit

Pr [pg] (z1, ..., zn) := E [e]C [Θ]ϑV [Φ]ϕS [α]C [Θ]ϑV [Φ]ϕP [Πm]ϑmϕm...P [Π1]ϑ1ϕ1πσ

128

Page 129: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Hier haben wir die vorige Definition auf den Block des Hauptprogramms angewendet: eswird die Anweisungsfolge α ausgewertet in den jeweiligen Umgebungen der Konstanten–,Variablen– und Prozedurdeklarationen. Schließlich ist für die Auswertung nur noch derWert der globalen Variablen e von Bedeutung, der einen Ausdruck in den Umgebungendes Hauptprogramms darstellt.

9.4 Übersetzung des Mini–Pascalprogramms inMaschinencode

Wir wollen nun eine passende Maschine postulieren, auf der wir die Programme unse-rer eingeführten Programmiersprache ausführen können. Wegen der rekursiven Strukturunseres Hauptinstruments, der Arithmetik expression, sowie der Möglichkeit von ge-schachtelten Prozeduren und Iterationen empfiehlt sich eine Maschine mit einem Daten-stapel und einem Prozedurstapel. Dieser ist durch Aktivierungsblöcke gekennzeichnet,die für jeweils einen Aufruf einer Prozedur ihre Umgebung, d.h. die lokalen Variablenund die Rücksprungadresse aufnehmen. Der Datenstapel entspricht einer Menge vonRegistern und Hilfsspeichern. Stapelregister sind auf verschiedenen Prozessoren hardwa-remäßig verwirklicht. Wir benötigen nur Lesen der beiden obersten Einträge, um binäreOperationen ausführen zu können. Die übrigen Elemente sind nicht zugänglich. Weiter-hin ist es erlaubt, neue Elemente auf den Stapel zu legen (kellern) oder Elemente zuentfernen (entkellern). Demgegenüber muss es auf dem Prozedurstack möglich sein, alleEnvironments zu lesen, ohne sie zu entkellern. Nun soll die Stapelmaschine SM definiertwerden:

Definition 9.4.1Die Menge C der SM–Operationen ist definiert durch

C := C0 ∪ C1 ∪ C2 ∪ C3,

wobei

C0 := ADD, SUB, MUL, DIV, RET

C1 := CONST, JMP, JE, JL, JNE, JLE, JG, JGE

C2 := LOAD, STORE

C3 := CALL .

Die Menge I der SM–Instruktionen ist definiert durch

I :=⋃

i∈0,1,2,3

Ci × Zi.

Hier sind die Operationen nach der Zahl ihrer Parameter geordnet. (Vergleiche Definiti-on 9.4.3; beispielsweise ist die Addition eine reine Stack–Operation und benötigt keinenParameter.)

129

Page 130: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Definition 9.4.2Die Stapelmaschine SM ist gegeben durch die Komponenten

• Datenstapel D, definiert durch D := Z∗.

• Prozedurstapel P, definiert durch P := A∗,wobei die Menge A der Aktivierungs-blöcke definiert ist als

A := N× N× Z∗.

Die ersten beiden Einträge in jedem Aktivierungsblock sind die RücksprungadresseRA und der statische Verweis SL. Der Vektor aus ganzen Zahlen dient zur Auf-nahme der lokalen Variablen und wird mit LV bezeichnet.

• Instruktionszähler Z, definiert durch Z := N.

• Programmspeicher S, definiert durch S := IN.

Ein Zustand der SM ist ein Tripel (δ, π, z) aus einem Zustandsraum U mit δ ∈ D,π ∈ Pund z ∈ Z. Für einen Prozedurstapel π und n,m ∈ N bezeichnet π(n) den n–ten Akti-vierungsblock in π. Die Komponenten von π(n) bezeichnen wir durch π(n)|RA, π(n)|SL,π(n)|LV . Die m–te lokale Variable in π(n) ist durch π(n)|LV (m) bezeichnet.

Der Datenstapel wird alsδ = z1.z2. . . .zn

bezeichnet mit dem letzen Eintrag rechts. Der Prozedurstapel ist als

π = 〈r1, s1 (l11, ..., l1n1)〉 . 〈r2, s2 (l21, ..., l2n2

)〉 . . ,

notiert, wobei die Aktivierungsblöcke in eckigen Klammern stehen und durch Punktegetrennt sind.

Wir müssen nun zunächst die Semantik der SM–Instruktionen erklären und dann Hin-weise zur Übersetzung eines Mini–Pascalprogramms in die Maschinensprache der SM–Maschine geben.

Beim Eintritt in den Code einer Prozedur bzw. des Hauptprogramms wird jeweils einneuer Aktivierungsblock auf dem Prozedurstapel angelegt, der dort bis zur Beendigungdieses Programmteils verbleibt. Die Rücksprungadresse ist die Nummer der nächstenInstruktion hinter dem Unterprogrammaufruf CALL. Mittels der RücksprunginstruktionRET kehrt man zur Weiterverarbeitung des Programmes nach Beendigung des Unter-programms und Löschung des Aktivierungsblocks an diese Stelle zurück. Der statischeVerweis SL ist die Nummer desjenigen Aktivierungsblocks im Prozedurstapel, der zu dersyntaktisch nächstäußeren Prozedur bzw. dem Hauptprogramm gehört. Aus diesem Teilerfolgte der Aufruf zur gegenwärtigen Prozedur. Aufgrund der Sichtbarkeitsbedingngenkönnen die in eine bestimmte Prozedur p eingeschachtelten inneren Prozeduren von Stel-len außerhalb von p nicht aufgerufen werden. Beim Aufruf ist die Prozedur p noch nicht

130

Page 131: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

abgeschlossen, daher ist ihr Aktivierungsblock noch nicht gelöscht. Eigentlich wird hiereine Rückwärtskette definiert, die für den Zugriff auf die globalen Variablen und dielokalen Variablen der äußeren Prozeduren erforderlich ist.

Der Speicherplatz einer Variablen ist durch ein Paar nicht negativer Zahlen (l, o) defi-niert. Dabei bezeichnet l die Niveaudifferenz zwischen der Blockschachtelungstiefe derAnweisung, in der sie verwendet wird, und der Blockschachtelungstiefe ihrer Definitions-stelle. Diese Anzahl von Blöcken muss durchquert werden, bis der Block erreicht wird, indem die Variable definiert wurde. Für eine frisch deklarierte lokale Variable ist demnachl = 0. Die zweite Zahl o ist ein Offset, der die relative Position der Variablen innerhalbihres Aktivierungsblocks angibt.

Nunmehr müssen wir für alle SM–Instruktionen die stattfindenden Zustandsänderungenbeschreiben.

Definition 9.4.3Die Semantik einer SM–Instruktion i [] : I → [U → U ] wird durch eine Zustandsänderung

131

Page 132: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

beschrieben, die sich für die einzelnen Instruktionen wie folgt darstellt:

1. ADD :

[δ = d1....dn.x.yz = i

]→

[δ = d1....dn.x+ yz = i+ 1

]

2. SUB :

[δ = d1....dn.x.yz = i

]→

[δ = d1....dn.x− yz = i+ 1

]

3. MUL :

[δ = d1....dn.x.yz = i

]→

[δ = d1....dn.x · yz = i+ 1

]

4. DIV :

[δ = d1....dn.x.yz = i

]→

[δ = d1....dn. bx/yc , y 6= 0z = i+ 1

]

5. RET :

[π = ... 〈r, s, (...)〉 . 〈r′, s′, (...)〉 ...z = i

]→

[π = ... 〈r, s, (...)〉 .z = r′

]

6. JMPn : [z = i]→ [z = n]

7. JEn :

[δ = d1....dn.xz = i

]→

δ = d1....dnz = n, falls x = 0z = i+ 1, sonst

8. JNEn :

[δ = d1....dn.xz = i

]→

δ = d1....dnz = n, falls x 6= 0z = i+ 1, sonst

9. JLn :

[δ = d1....dn.xz = i

]→

δ = d1....dnz = n, falls x < 0z = i+ 1, sonst

10. JLEn :

[δ = d1....dn.xz = i

]→

δ = d1....dnz = n, falls x ≤ 0z = i+ 1, sonst

11. JGn :

[δ = d1....dn.xz = i

]→

δ = d1....dnz = n, falls x > 0z = i+ 1, sonst

12. JGEn :

[δ = d1....dn.xz = i

]→

δ = d1....dnz = n, falls x ≥ 0z = i+ 1, sonst

13. CONSTc :

[δ = d1....dnz = i

]→

[δ = d1....dn.cz = i+ 1

]

Für die Erklärung der letzten drei Instruktionen benötigen wir die Hilfsfunktion base:N×N → N, die entlang einer Kette von Verweisen den richtigen Aktivierungsblock einerVariablen und ihren Speicherplatz findet:

base(0, x) := x

base(n+ 1, x) := base(n, π(x)|SL).

132

Page 133: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

14. LOAD l, o :

δ = d1....dnπ = π1.π2...πkz = i

δ = d1....dn.π (base (l, k))|LV (o)π = π1.π2...πkz = i+ 1

15. STORE l, o :

δ = d1....dn.xπ = π1.π2...πkz = i

δ = d1....dnπ = π′

z = i+ 1

wobei π′ (base (l, k))|LV (o) = x und π′ = π an den anderen Stellen gilt. Weiter be-zeichne l die Niveaudifferenz zwischen der Blockschachtelungstiefe, in der die zu laden-de/speichernde Variable definiert ist, und der Blockschachtelungstiefe, in der der Aufruferfolgt. Die Zahl o ist der Offset, der die relative Position dieser Variablen innerhalb ihresAktivierungsblocks angibt.

16. CALL l, n, v :

[π = π1.π2...πkz = i

]→

π = π1.π2...πk.

⟨i+ 1, base (l, k) ,

v︷ ︸︸ ︷0, 0, ..., 0

z = n

Der erste Parameter l von CALL ist die Niveaudifferenz zur aufrufenden Prozedur, diezur Ermittlung des statischen Verweises auf den neu zu schaffenden Aktivierungsblockerforderlich ist. Der zweite Parameter n ist die Adresse der aufzurufenden Prozedur, derdritte Parameter v gibt die Anzahl der lokalen Variablen dieser Prozedur an, die wir aufdem neuen Aktivierungsblock anlegen und willkürlich mit Null initialisieren.

Bevor nun das eigentliche Programm startet, werden die entsprechenden Speicherplätzemit den Eingabewerten gefüllt. Für das Programm wird zunächst ein einziger Aktivie-rungsblock mit Rücksprungadresse 0, statischem Verweis 0 und Platz für alle globalenVariablen bereitgestellt in Analogie zum CALL–Befehl. Hier werden die Startwerte mit-tels einer in–Instruktion eingebracht. Das Programm soll anhalten, wenn zum Ende desHauptprogramms eine Anweisung RET 0 angegeben ist und diese ausgeführt wird. DerAusgabewert ist dann der oberste und im Allgemeinen einzige verbleibende Wert auf demDatenstapel, wenn das Programm angehalten hat.

Beispiel 9.4.4program Beispiel;

var A, B, C : Integer;

procedure p;

var A, B, D: Integer;

procedure q;

var C: Integer;

133

Page 134: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

begin

.....

p; (* CALL 1, adr(p), 3 *)

.....

end;

begin

C:=0; (* CONST 0; STORE 1,3 *)

....

q; (* CALL 0,adr(q),1 *)

.....

end ;

begin (* Hauptprogramm *)

....

p; (* CALL 0, adr(p),3 *)

.....

end.

Wir betrachten die Aufrufkette Hauptprogramm → p→ q→ p.

Nach dem ersten Aufruf von p sieht der Prozedurstapel wie folgt aus:

π(1)︷ ︸︸ ︷〈0, 0, (a, b, c)〉 .

π(2)︷ ︸︸ ︷〈r1, 1, (ap, bp, dp)〉,

wobei a, b, c die Werte der Variablen sind. Der statische Verweis 1 in π(2) verweist aufπ(1). Der Befehl STORE 1,3 aus der Übersetzung C:=0 , das ist die dritte Variable, wirktentsprechend der Semantik von STORE auf π (base(1, 2))|LV (3) . Es gilt jedoch mit derrekursiven Definition von base(n + 1, x) := base(n, π(x)|SL), base(0, x) := x und demWert 1 an der Stelle SL von π(2)

π (base(1, 2))|LV (3) = π(base(0, π(2)|SL)

)|LV (3)

= π(π(2)|SL)

)|LV (3)

= π(1)|LV (3).

Dies ist der Platz der Variablen C im Aktivierungsblock des Hauptprogramms. Zufälligwurde hier auf den nächsten Aktivierungsblock im Prozedurstapel zugegriffen. Hier fallenstatische und dynamische Sichtbarkeit zusammen. Dies kann jedoch auch anders sein, wiedie Situation nach dem Aufruf von q und dem zweiten Aufruf von p zeigt.

π(1)︷ ︸︸ ︷〈0, 0, (a, b, c)〉 .

π(2)︷ ︸︸ ︷〈r1, 1, (ap1, bp1, dp1)〉 .

π(3)︷ ︸︸ ︷〈r2, 2, (cq)〉 .

π(4)︷ ︸︸ ︷〈r3, 1, (ap2, bp2, dp2)〉

134

Page 135: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

STORE 1,3 wirkt jetzt wie folgt:

π (base(1, 4))|LV (3) = π(base(0, π(4)|SL)

)|LV (3)

= π(π(4)|SL)

)|LV (3)

= π(1)|LV (3).

Das ist derselbe Speicherplatz wie im vorigen Aufruf. Schauen wir uns noch die Angabeπ(3)|SL = 2 im Aufruf von q an. Er bewirkt, dass die Variablen A,B,D in π(2) gefundenwerden.

Nunmehr wollen wir uns der Übersetzung von Mini–Pascal in SM–Code zuwenden. Dazuwerden wir eine formale Spezifikation für ein Übersetzungsprogramm (Mini–Compiler)angeben. Im Zyklus der Informatikveranstaltungen widmet sich die Vorlesung Compiler-bau dieser Aufgabe. Da es sich hier um recht komplexe Theorien handelt, die auf einerVorlesung über Theoretische Informatik aufbauen, werden wir nur einzelne Aspekte derAufgabe erläutern.

Compiler verarbeiten den Text eines Programmes, indem sie zunächst eine lexikalische,sodann eine syntaktische und semantische Analyse vornehmen. Nach Aufbau einer kom-plexen Baumstruktur wird schließlich ein äquivalentes Programm in der speziellen Spra-che der Zielmaschine erzeugt. Wichtig ist es, dass bei der Komplexität der Programme allediese Vorgänge automatisiert werden können. Wir wollen hier nur eine stark vereinfachteSicht dieser Schritte vortragen und nur die direkte Umsetzung in den Maschinencodeanschauen.

Compiler verarbeiten die Deklarationsteile der Programme, indem sie eine Symboltabelleerzeugen. Hier sind die Bezeichner und ihre Typattribute eingetragen. Darunter verstehenwir die Angabe, ob es sich um eine ConstDecl, VarDecl oder ProcDecl handelt sowie dieBlockschachtelungstiefe der Definitionsstelle sowie die mit dem Bezeichner verbundeneAdresse.

Diese Tabelle muss weiterhin verwaltet werden. In der folgenden Definition wollen wirden Zugriff auf diese Tabelle erläutern.

Definition 9.4.5Die folgenden Funktionen seien für die Konstantenbezeichner c, die Variablenbezeichnerv und die Prozedurbezeichner p definiert und beziehen sich auf die zur Zeit ihres Aufrufssichtbaren Definitionen.

• level (v), level (p) Blockschachtelungstiefe der Definition

• offset (v) Relative Position von v innerhalb des Aktivierungsblocks

• value (c) Wert von c

135

Page 136: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• adr (p) Anfangsadresse von p im Programmspeicher

• size (p) Anzahl der lokalen Variablen von p

• lv Blockschachtelungstiefe des gerade übersetzten Programmstücks.

Nunmehr werden Funktionen Ecomp, SComp, PComp zur Übersetzung von Ausdrücken,Anweisungen und Programmen definiert, die SM–Programmteile liefern.

Definition 9.4.6Hier beschreiben wir die Übersetzung von Ausdrücken:

1. EComp(ident) :=

CONST value(ident),falls constIdent=ident

LOAD lv− level(ident), offset(ident), sonst2. EComp(number) := CONST number

3. EComp(f1 ∗ f2) :=

EComp (f1)EComp (f2)

MUL

4. EComp(f1 div f2) :=

EComp (f1)EComp (f2)

DIV

5. EComp(f1 + f2) :=

EComp (f1)EComp (f2)

ADD

6. EComp(f1 − f2) :=

EComp (f1)EComp (f2)

SUB

7. EComp(+t) := EComp(t)

8. EComp(− f2) :=

CONST 0

EComp (f2)SUB

9. EComp((e)) := EComp(e),e Ausdruck.

Definition 9.4.7Nun zu den Anweisungen:

1. SComp (i := e) :=

EComp(e)

STORE lv− level(i), offset(i)

2. SComp (p) := CALL lv− level(p), adr(p), size(p)

3. SComp (begin α1; ...;αn end) :=

SComp (α1)...

SComp (αn)

4. if (e1 e2) then α für ∈ =, <,>,<>,<=, >,>=

136

Page 137: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

wird wie folgt übersetzt: Wir erzeugen nun ein Stück Programmcode τ , welches einZahl auf dem Datenstapel hinterlässt, die durch ihre Vorzeichen die Größenverhältnissezwischen e1 und e2 angibt:

EComp(e1)

EComp(e2)

SUB

Bezeichne nun x die Adresse des ersten Befehls hinter der Übersetzung des Blocks α imthen–Zweig. Wir übersetzen dann in Abhängigkeit vom Vergleichsoperator wie folgt

τAAA x

SComp (α)

mit der folgenden Korrespondenz für den SM–Befehl AAA

= ←→ JNE, <>←→ JE, <←→ JGE,

<= ←→ JG, >←→ JLE, >=←→ JL.

5. while (e1 e2) do α für ∈ =, <,>,<>,<=, >,>=

wird analog zur If–Anweisung übersetzt. Hier bezeichnet x die Adresse des zweitenBefehls hinter dem SM–Code für den Do–Zweig α.

An diesen wird grundsätzlich ein Rücksprungbefehl JMP y angehängt, wobei y die Adressedes ersten Befehls im Codesegment τ ist. Dadurch ist gewährleistet, dass die Sequenz αsooft ausgeführt wird, wie die Bedingung (e1 e2) erfüllt ist.

Bei echten Compilern wird zur Behandlung der Vorwärtssprünge das Sprungziel meistin einem zweiten Durchlauf beim Übersetzungvorgang eingetragen. In unserer Mini–Pascalübersetzung haben die Anlage und Freigabe der lokalen Variablen bei der Über-setzung von Blöcken die SM–Befehle CALL und RET übernommen.

Definition 9.4.8Sei β = ΘΦΠ1...Πmα ein syntaktisch korrekter Block in der Sprache Mini–Pascal. Dabeisei

Θ ∈ constDecl, Φ ∈ varDecl, Πi ∈ procDecl, i = 1, ...,m,

und α eine syntaktisch korrekte Anweisungsfolge. Zu den Prozedurdeklarationen mögendie Blöcke βi, i = 1, ...,m gehören. Dann definieren wir

BComp(β) := BComp(β1)...

BComp(βm)SComp(α)

RET

137

Page 138: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Da im Hauptprogramm noch Ein– und Ausgaben vorgenommen werden, müssen wir dieÜbersetzung von Programmen leicht modifizieren:

Definition 9.4.9Sei pg ein syntaktisch korrektes Mini–Pascalprogramm mit dem Hauptblock β wie inder vorangegangenen Definition und einer auf α folgenden Anweisung WriteLn(e). Danndefinieren wir

PComp(pg) := BComp(β1)...

BComp(βm)SComp(α)EComp(e)

RET

Dadurch verbleibt nach Abarbeitung des Programmrumpfs α der Wert e auf dem Da-tenstapel.

Definition 9.4.10Sei psm : N→ I ein SM–Programm. Die Einzelschrittfunktion ∆[psm] ist eine Zustands-transformation

∆[psm] : U → U

mit∆[psm] (δ, π, z) := i [psm (z)] (δ, π, z) .

Sie beschreibt die Zustandsänderung auf den Stapeln und im Instruktionszeiger, die durcheinen Programmschritt bewirkt wird. Die gesamte Interpretation des Programmes erhal-ten wir durch eine Iteration der Einzelschrittfunktion

∆[psm]∞ : U∗→ U.

Für ein n ∈ N ist die n–stellige Eingabefunktion der SM erklärt als

in(n) : Zn → U

in(n) (z1, ..., zn) := (δ0, π0, z0) .

Dabei wird sukzessive für i = 1, ..., n der Wert zi der Variablen vi mit einer dem CONST–Befehl analogen Instruktion von einem Peripheriegerät auf den Datenstapel geschriebenund der Prozedurstapel manipuliert, d.h. den Variablen v1, ..., vn, die an den Offsetstellen1, ...n des ersten Aktivierungsblocks liegen, die Werte z1, ..., zn mit dem STORE–Befehlzugeordnet. Sodann wird zur ersten Instruktion z0 des Programmrumpfs α verzweigt.Der entstandene Prozedurstapel ist π0, der Datenstapel δ0 ist leer.

Die Ausgabefunktion der SM ist erklärt als

out : U → Z

out (δ, π, z) := d1.

138

Page 139: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Sie überträgt den obersten Wert des Datenstapels an ein Peripheriegerät.

Es ergibt sich damit das folgende Programm

Pr [pg] (z1, ..., zn) := out

(∆[Pcomp (pg)]∞

(in(n) (z1, ..., zn)

)).

Schließlich müsste noch gezeigt werden, dass unsere Übersetzung tatsächlich die Semantikder Mini–Pascalprogramme korrekt widerspiegelt.

(Literatur: Herbert Klaeren: Vom Problem zum Programm. Teubner, Stuttgart 1991)

139

Page 140: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

10 Von Mini–Pascal zu Pascal

Einige Vorbemerkungen

Die Sprache Pascal wurde seit den siebziger Jahren gerade im Bildungssektor häufigangewandt um in die Programmierung einzuführen. Ursprünglich war die Sprache demParadigma der strukturierten Programmierung verhaftet. Sie wurde im Laufe der Zeitauch um objektorientierte Konzepte erweitert.

Einen wesentlichen Beitrag zur Popularität von Pascal hat die Firma Borland geleistet,die mit ihren Turbo–Pascal (TP) – und Delphi–Compilern weitverbreitet ist. TP wur-de vor allem durch die Verbindung von Editor und Compiler einschließlich Debuggingbekannt.

Pascal wurde immer wieder erweitert. Mit dem Einzug des Unit–Konzepts, das modularesProgrammieren zulässt, dem Einbezug von verallgemeinerten Datentypen, wie Objekt,sowie einer Reihe von Bibliotheken die die Programmierung grafischer Benutzungsober-flächen zulassen.

Inzwischen ist es etwas ruhiger um die Sprache Pascal geworden. Das liegt darin be-gründet, dass die Sprache in großen Softwareprojekten nur bedingt eingesetzt wird. AufUNIX–Rechnerplattformen ist Pascal praktisch bedeutungslos, durch die Vorrangstel-lung von C. Mit dem Auftreten von neuen netzorientierten Sprachen wie Java ist ihr eineernste Konkurrenz erwachsen.

Mit dem Free Pascal Compiler (FPC)1 ist auch eine freie Variante des kommerziellenTP verfügbar, die zudem weitgehend plattformunabhängig ist. Daneben gibt es auch denGNU Pascal Compiler (GPC)2, ebenfalls freie Software. FPC unterstützt den BorlandPascal dialect Borland und implementiert die Delphi Object Pascal language. GPC hatallerdings noch offene Punkte auf der TO-DO-Liste, die von FPC bereits implementiertwerden, z.B. Strings, Units und objektorientierte Programmierung.3

10.1 Datentypen von Pascal

Eine Menge von Werten heißt Typ. Eine Typdeklaration weist einem Typ einen Namenzu und definiert ihn, dies kann z.B. durch Angabe einer Wertemenge geschehen.

1http://www.freepascal.org2http://www.gnu-pascal.de3Siehe dazu Planned Features auf der GPC Seite http://www.gnu-pascal.de/gpc/Planned-Features.html

140

Page 141: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Variablen sind Wörter, die einen nicht festgelegten Wert aus einer Menge von Werteneines Typs vertreten. Sie haben damit einen Typ. Sie können jeden Wert dieses Typsannehmen. Man darf sich den Wert abgelegt auf einem Werteplatz, der für Werte diesesspeziellen Typs geeignet ist, vorstellen. Zugang erfolgt über eine Referenz zum Werte-platz, der durch den Variablennamen symbolisiert wird. Mit der Zuweisung eines Werteszu einer Variablen wird dieser Wert auf dem zur Variablen gehörigen Werteplatz abgelegt.Pascal kennt die folgenden einfachen Datentypen:

• Integer: Der semantische Bereich ist ein gewisser implementierungsabhängigerAusschnitt aus den ganzen Zahlen. Die größte Zahl dieses Ausschnitts ist die vor-definierte Konstante MaxInt. Verwandte Typen aus Pascaldialekten sind byte, dasdie Zahlen 0..255 umfasst, word, das dieselbe Mächtigkeit wie Integer hat, sichaber auf nichtnegative Zahlen beschränkt, und LongInt, das die doppelte Speicher-kapazität von Integer benötigt, meistens 4 Byte, ShortInt nur die Hälfte.

• Boolean: Der semantische Bereich ist die Menge der Wahrheitswerte, die True undFalse heißen.

• Char: Der semantische Bereich ist die Menge der darstellbaren Zeichen der Refe-renzmaschine und damit implementierungsabhängig. Meist umfassen sie die Zei-chen mit ASCII–Code 32 bis 127. Konstanten von diesem Typ werden als ’A’ etc.geschrieben.

• Real: Hier ist der semantische Bereich eine Menge rationaler Zahlen, die wie-derum implementierungsabhängig ist. Die ähnlichen Typen single, double undextended folgen der IEEE–Norm aus Kapitel 2.1.2, wobei extended über 80 Bitverfügt.Hier ist die Syntax einer Real–Zahl gegeben durch

RealZahl = ["+" | "-"] Ziffernfolge

("." Ziffernfolge | Skalierungsfaktor|

"." Ziffernfolge Skalierungsfaktor).

Skalierungsfaktor = ("E" | "e") ["+" | "-"] Ziffernfolge.

Die hier aufgeführten Typenbezeichner stehen für Standardtypen, sind keine Schlüssel-wörter und können daher umdefiniert werden. Die ersten drei Typen sind Ordinaltypen,da die Elemente ihrer Mengen total geordnet sind und mit einer Vorgänger– und Nachfol-gerfunktion pred und succ, den Standardprozeduren Inc und Dec zur Inkrementierungund Dekrementierung, sowie Ord, das die Ordinalzahl des Arguments liefert, versehensind.

Neben den einfachen Typen gibt es andere vordefinierte Typen, wie

• strukturierte Typen: Array (Vektor), Record (Verbund), Objekt (Klasse, Erweite-rung des Sprachstandards), Set (Mengen) und File–Typen (Datei),

141

Page 142: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• String–Typ (Zeichenketten zwischen ’ ’),

• Zeiger–Typ (z.B. Zeiger auf Adressen von einem vorgegebenen Typ oder untypisiertPointer),

• Prozedur–Typen als Erweiterung des Sprachstandards.

Strukturierte Typen, wie Record, betreffen kartesische Produkte verschiedener MengenA1 × . . .×An, der Mengentyp

Mengentyp = "SET" "OF" OrdinalerTyp.

betrifft in den meisten Implementierungen leider nur Mengen mit höchstens 256 Ele-menten. Dagegen sind die Mengenoperatoren + (entspricht der Vereinigung), - (Komple-mentbildung), * (Durchschnitt) und in (für ∈) definiert. Für boolesche Typen sind dieBooleschen Operatoren not, and, or und xor erklärt, die auch ihre bitweisen Äquiva-lente für Integer–Operandentypen haben.

Zeigertypen haben das EBNF–Diagramm

ZeigerTyp = "^"Typbezeichner.

Variablen diesen Types enthalten die Speicheradresse eines Objekts vom Typ Typbe-

zeichner erweitert um den Wert nil, der zu jedem Pointertyp gehört und auf keineVariable zeigt. Es gilt die folgende Korrespondenz:

^T ist ein Zeigertyp, Variablen dieses Typs zeigen auf Variablen vom Typ T,v vom Typ ^T enthält die Adresse von v^ vom Typ T.

Mit Hilfe einer Typ–Deklaration zwischen den Konstanten– und Variablendeklarationenkönnen eigene Typen definiert werden. Ein Beispiel hierfür sind Unterbereichstypen.

• Sind c und d Konstanten eines Ordinaltyps T und gilt c ≤ d, so kann ein Unterbe-reichstyp

type U = c..d;

deklariert werden. Der entsprechende semantische Bereich ist die Menge aller u ∈ Tmit c ≤ u ≤ d.

Durch die Aufzählung frei wählbarer Namen kann ein Wertebereich und damit ein Typdefiniert werden. Dadurch werden die Werte dieses Typs gleichzeitig linear angeordnet.

type himmelsrichtung = (nord, sued, west, ost);

type farbe = (rot, gelb, gruen, schwarz);

142

Page 143: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Ist O ein Ordinaltyp, in der Regel ein Unterbereichstyp, und T ein beliebiger Typ, so kannein Datentyp von Feldern (Arrays) über T deklariert werden durch

type A = array [O] of T;

Dieser Ansatz kann als Abkürzung von

type matrix = array [O_1] of array ... of array [O_n] of T;

zu

type matrix = array [O_1,...,O_n] of T;

d.h. zu einem mehrdimensionalen Feld erweitert werden.

Einer Deklaration mit n = 3, T = real, O_1 = O_2 = O_3 = 0..10 und

var m : matrix;

kann dann eine Zuweisung m[i, j, k] := 3.5 folgen.

Beispiel 10.1.1Wir wollen die Typdeklarationen für einen Kompass, ein Histogramm für die Häufigkeitvon Kleinbuchstaben und eine Codetabelle zum Übersetzen eines Zeichensatzes in einenanderen angeben:

kompass = array [1..3] of himmelsrichtung;

Histogramm = array [’a’ .. ’z’] of Integer;

Codetabelle = array[char] of char;

Mittels eines Arrays über einem Teilbereichstyp von Charakters char kann auch einString–Typ definiert werden. Die Mächtigkeit des Teilbereichstyps bestimmt dabei dieWortlängen. Das Short–Strings in Turbo–Pascal erlaubt eine maximale Länge von 255Zeichen. Die Ansi–Strings in Delphi und FreePascal erlauben beliebig lange Zeichenket-ten. Es gibt Operatoren (’+’) , die zwei Strings verschmelzen zu einem neuen String,Funtionen, die einen Teilstring ab einer Position einer gewissen Länge auswählen, die dieLänge einer Zeichenkette und die aktuelle Position eines Zeichens in einem String ange-ben. Prozeduren erlauben das Löschen innerhalb eines Strings, das Einfügen eines Stringsin einen anderen, sowie die Umwandlung von Strings in ganze bzw. Gleitkommazahlenund ihre Umkehrung.

Während Felder Ansammlungen von Mengen gleichen Typs sind und damit dem karte-sischen Produkt An entsprechen, leisten Verbünde dies für verschiedene Mengen.

Sind T1, . . . , Tn Typen und v1, . . . , vn Bezeichner, so kann ein Verbund über T1× . . .×Tnals Datentyp durch

143

Page 144: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

verbund = record

v1 : T1;

v2 : T2;

......

vn : Tn;

end;

deklariert werden. Für eine Variable v vom Typ T bezeichnet man die i–te Komponen-te mit v.vi als qualifizierter Ausdruck. Ersetzt man eine Anweisung alpha mit einemqualifizierten Bezeichner durch eine Anweisung

with Recordvariablenliste do alpha ;

so kann man direkt auf vi zugreifen und spart Schreibarbeit.

Beispiel 10.1.2Datum = record

Tag : 1..31;

Monat : (Jan, Feb, Mar, Apr, Mai, Jun,

Jul, Aug, Sep, Okt, Nov, Dez);

Jahr : Integer

end;

In der Folge wollen wir folgende Vereinbarung treffen:

Konstante = ConstIdent | RealZahl | Zahl.

Konstantenliste = Konstante "," Konstante.

Bereichsliste = Konstante .. Konstante "," Konstante .. Konstante.

Eine interessante Erweiterung der bedingten Anweisungen zu einer Auswahlanweisungmit der Syntax

Auswahl = "case" expression öf"

Konstantenliste | Bereichsliste

"," Konstantenliste | Bereichsliste

":" statement ";"

Konstantenliste | Bereichsliste

"," Konstantenliste | Bereichsliste

":" statement ";"

["else" statement] "end".

spielt eine wichtige Rolle bei der Erweiterung des Verbundtyps. Seine Semantik ist intuitivverständlich. Stimmt expression mit einem Wert in der Konstantenliste überein oderliegt der Wert von expression innerhalb einer der Bereiche, so wird das darauf folgende

144

Page 145: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Statement ausgeführt. Der else–Zweig ist fakultativ und wird nur dann durchlaufen,wenn keine der Fälle der Auswahlliste eintritt.

Zur Erweiterung des Verbundtyps wird die Datensatzliste nach dem Schlüsselwort recorddurch die folgende Konstruktion, den sog. varianten Teil, ergänzt:

"case" selektorIdent ":" Selektortyp öf"

Konstantenliste ":" "(" Datensatzliste ")" ";"

Konstantenliste ":" "(" Datensatzliste ")" ";".

Beispiel 10.1.3type Stand = (verh, verw, gesch, ledig);

Person = record

name : record vorname, nachname : string end;

Versnr : integer;

Geschlecht : (Mann, Frau);

Geburt : Datum;

Kinder : integer;

case stkomp : Stand of

verh, verw : (vd : Datum);

gesch : (gd : Datum);

ledig : (unabh : boolean);

end;

Felder und Verbünde sind statische Datenstrukturen; ihre Größe wird zur Übersetzungs-zeit festgelegt und bleibt unveränderlich. So ist es auch nicht möglich, ein array miteinem variablen Indexbereich zu vereinbaren. Zur Spezifikation dynamischer Datenstruk-turen, deren Umfang während des Programmablaufes veränderlich ist, werden die Zeigergenutzt.

Eine Kombination mit einem Verbund führt zum Aufbau einer Liste.

Zur Erinnerung: Eine Variable v vom Zeigertyp ^T enthält die Adresse v^ vom Typ T.Durch die Anweisung new(v) wird freier Speicher vom Typ T angefordert und dessenAdresse der Variablen v zugewiesen. Der vorige Inhalt von v^ bleibt erhalten, ist aberüber v nicht mehr zugänglich. Durch dispose(v) wird der v zugeordnete Speicher wiederfreigegeben, v ist anschließend nil.

Wir wollen ein ausführlicheres Beispiel studieren:

Beispiel 10.1.4type Liste = ^Eintrag;

Eintrag = record

Elem : Integer;

Nachfolger : Liste

end;

var : anfang, neu, p, q : liste;

145

Page 146: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

begin

anfang := nil; (* leere Liste erzeugen *)

new(anfang);

(* Es wird eine Variable anfang^ vom Typ Eintrag erzeugt *)

anfang^.Elem := 2;

anfang^.Nachfolger := nil;

(* nun hat die Liste ein Element *)

(* der Listenanfang ist erzeugt *)

end.

Dann wollen wir am Anfang ein neues Element anfügen:

p := anfang;

new(anfang);

anfang^.Nachfolger := p;

anfang^.Elem := 5;

Damit wurde die bisherige Liste an das neue Element angehängt.

Als nächste Aufgabe wollen wir die Liste durchlaufen und am Ende ein Element anhän-gen.

p := Anfang;

while p^.Nachfolger <> nil do

p := p^.Nachfolger;

new(q);

q^.Nachfolger := nil;

q^.Elem := y;

p^.Nachfolger := q; (* Eintrag an alte Liste gehaengt *)

Suchen eines Elementes elem = x in einer nichtleeren Liste:

p := Anfang; found := false; leer := false;

while not found and not leer do begin

if (p <> nil) then begin

if p^.Elem = x then

found := true

else

p := p^.Nachfolger

end

else leer := true;

if found then

writeln(’gefunden’)

else

146

Page 147: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

if leer then writeln(’nicht vorhanden’);

end; (* while *)

Einfügen und Streichen von Elementen sind weitere wichtige Aufgaben. Da wir am An-fang und am Ende schon ein Element eingefügt haben, muss nur noch der Fall des Einfü-gens nach einem inneren Element q mit q^.elem = nach untersucht werden. Dabei mussder Zeiger dieses Elements auf das neue Element, und dessen Zeiger auf das Nachfolge-element gerichtet werden.

Einfügen eines Elementes nb nach dem Element nach:

p := anfang; found := false; leer := false;

while not found and not leer do begin

if (p <> nil) then begin

if p^.Elem = nach then

found := true

else

p := p^.Nachfolger

end

else leer := true;

if found then

writeln(’gefunden’)

else

if leer then writeln(’nb nicht vorhanden’);

end; (* while *)

if found then begin

new(q);

q^.Nachfolger := p^.Nachfolger;

q^.Elem := nb;

p^.Nachfolger := q

end;

Beim Löschen ist darauf zu achten, dass die Verknüpfungen nicht verloren gehen. So istder Vorgänger des zu löschenden Elements mit dessen Nachfolger zu verknüpfen. Dasverlangt allerdings beim Löschen des ersten wieder eine Sonderbehandlung. Dann kanndas Listenelement mit dispose freigegeben werden.

Dateitypen bestehen aus einer linearen Folge von Komponenten eines wohldefinierteTyps:

Dateityp = "file" ["of" Typ].

Wird der Komponententyp weggelassen, so handelt es sich um einen untypisierten Fi-le. Der Standarddateityp text bezeichnet eine Datei mit zeilenweise angeordneten Zei-chenfolgen. In Standardpascal wird die Datei mittels der Standardprozedur rewrite für

147

Page 148: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

den Schreibvorgang initialisiert, die Standardprozedur put dient zum Beschreiben deraktuellen Komponente der Filevariablen mit dem Wert einer Puffervariablen, mit derbooleschen Variablen eof(input) kann abgefragt werden, ob das Ende des Inputs er-reicht ist. Mit reset(F) wird der Lesevorgang initialisiert und mit get(F) die einzelnenKomponenten der Filevariablen F gelesen und der Übergang zur nächsten Komponentebewirkt, bis eof(F)=true. Bei der Arbeit mit Dateien muss in Turbo–Pascal zunächstdie Prozedur Assign(var F; S : String) aufgerufen werden. Sie ordnet dem internenPascal–Filenamen F den externen Dateinamen S zu, so wie er z.B. auf der Festplatte ab-gespeichert ist. Nach der Arbeit sollte die Datei mit einem Close(F) geschlossen werden.

10.2 Kontrollstrukturen, Prozeduren und Funktionen

Neben den bereits für Mini–Pascal vorgestellten Kontrollstrukturen if - then und while

- do besitzt Pascal die iterativen Strukturen

Repeat-Anweisung = "Repeat" statement ";" statement

üntil" expression.

For-Anweisung = "for" Laufvariable ":=" expression ("to" | "downto")

expression "do" statement.

Zur Semantik ist zu sagen, dass im Gegensatz zur while–Anweisung bei der repeat–Anweisung die Gültigkeit des Booleschen Ausdrucks am Ende geprüft wird und die Itera-tionsstruktur bei Nichterfüllung verlassen wird. Die Anweisungsliste wird also mindestenseinmal ausgeführt.

Bei der For–Anweisung muss die Laufvariable ordinalen Typs sein, ebenso der Ausdruckdes Anfangs– und des Endwertes. Zu Beginn wird die Laufvariable auf den Anfangswertgesetzt und dann sukzessive um eins erhöht oder erniedrigt. Ist im ersten Fall der Endwerte größer oder gleich dem Anfangswert a, so wird die Anweisung e-a+1 mal ausgeführt.Innerhalb des Statements kann der Wert der Laufvariable nicht geändert werden, amEnde der Struktur ist er undefiniert.

Die If - then Anweisung ist in Pascal erweitert:

If-Anweisung = "if" expression "then" statement

["else" statement].

Diese Anweisung ermöglicht die Auswahl einer von zwei Anweisungen in Abhängigkeitvon einer Bedingung. Bei Schachtelungen dieser Anweisungen können Mehrdeutigkeitenmit dem else–Zweig entstehen: Ein else–Zweig wird dem nächstdavorstehenden If zu-geordnet, wenn dieses noch kein else hat und nicht in einer Verbundanweisung oderrepeat–Schleife steht.

Zusätzlich zu den Prozeduren in Mini–Pascal sind in Pascal Deklarationen der Form

procedure P(π1;π2; . . . ;πn);

148

Page 149: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

wobei jedes πi entweder die Form i : T oder var i : T hat, erlaubt. Die Bezeichner iheißen formale Parameter und sind vom Typ T. Im ersten Fall sind es Wertparameter,im zweiten Referenzparameter. Eine derartige Prozedur kann durch die Anweisung

P(t1, . . . , tn)

aufgerufen werden, wobei ti im ersten Fall ein beliebiger Ausdruck des Types T ist. Imzweiten Fall muss es dagegen eine Variable vom zugehörigen Typ sein. Die ti heißenhier aktuelle Parameter. Bei Werteparametern wird der Prozedur der zuvor ausgewerteteAusdruckswert als Kopie, bei Referenzparametern die Adresse der Variablen (, die zuvorschon deklariert wurde,) an die Prozedur übergeben. Im letzten Fall kann der Wert derVariablen innerhalb der Prozedur verändert werden, Referenzvariablen können damitals Eingabe– und Ausgabevariablen für die Prozedur dienen, im ersten Fall ist keinebleibende "Anderung des Wertes der Variablen möglich. Hier ist der Wertparameter miteiner neuen lokalen Variable zu vergleichen, die jedoch schon initialisiert ist.

Eine Prozedurdeklaration, bei der anstelle eines Vereinbarungs– und Anweisungsteils dasreservierte Wort forward folgt, verlangt, dass der eigentliche Prozedurblock zu einem spä-teren Zeitpunkt definiert wird. Diese Definition gibt den Kopf der Prozedur noch einmalwieder. Dazwischen ist die Deklaration weiterer Prozeduren und Funktionen möglich.

Beispiel 10.2.1Program abwechselnd;

var i, h : integer;

procedure q (var k : integer); forward;

procedure p (var i, j : integer);

begin

inc(i);

j := i+5;

if j < 10 then begin write(’ q:’, j); q(j) end;

end;

procedure q;

begin

inc(k);

write(’ p:’, k);

p(i, k)

end;

begin

i := 0; h := 1;

write (’ p:’, h);

p(i, h);

writeln(’ p:’, h)

149

Page 150: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

end.

(* Ergebnis: p:1 q:6 p:7 q:7 p:8 q:8 p:9 q:9 p:10 p:10 *)

Über einen Typ T, der entweder einfach, ein Aufzählungs–, Unterbereichs– oder Zeigertypist, wird eine Funktion mit Werten vom Typ T durch

function f(π1;π2; . . . , πn): T;

deklariert. In einigen Pascal–Versionen sind auch String–Typen als Ausgabetyp erlaubt.Bezüglich der Parameterliste gelten die gleichen Aussagen wie für die Prozeduren. Siekann auch leer sein mit der Deklaration

function f: T;

Innerhalb des Funktionsrumpfes von f wird die Festlegung des Rückgabewertes durcheine Pseudozuweisung auf den Namen der Funktion getroffen,

f := t; (* t vom Typ T *)

wobei t ein Ausdruck ist. Diese Festlegung muss erfolgen, sie kann sogar mehrmals vor-genommen werden. Eine deklarierte Funktion wird durch den Ausdruck

f(t1, . . . , tn)

aufgerufen, der die Funktion eines factors innerhalb einer expression hat. Im Funkti-onsrumpf kann die Funktion wiederum auch rekursiv aufgerufen werden.

function rfak(n : word) : word;

begin

if n=0 then rfak := 1 else rfak := rfak(n-1) * n

end;

Bei der Ausführung eines Pascal–Programms werden zu Beginn die Eingabegrößen vomStandardeingabefile Input, d.h. nach automatischer interner Zuordnung von der Tasta-tur mit dem Befehl read eingelesen und mit dem Befehl write in das Standardausga-befile output, d.h. auf dem Bildschirm ausgegeben. Getrennt werden die Daten durchLeerzeichen oder durch Zeilenendezeichen. Dabei bedeutet read(x1,...,xn), dass vomStandardfile Input die nächsten n Daten eingelesen werden.

readln dagegen übergeht den Rest der laufenden Zeile, es wird dann vom Anfang dernächsten Zeile gelesen. Dabei müssen die Eingabegrößen dem Typ der Variablen xi ent-sprechen. readln(x1,...,xn) simuliert Begin read(x1,...,xn) ; readln end; eineandere Eingabedatei kann durch Benennung einer Filevariablen F vor der Ausdruckslisteangesprochen werden.

150

Page 151: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Für die Ausgabeprozedur write gilt ähnliches; eine andere Ausgabedatei kann durchBenennung einer Filevariablen F vor der Ausdrucksliste angesprochen werden. Statt derVariablen dürfen auch gültige Stringausdrücke gesendet werden. Weiterhin ist es mög-lich, durch Formatanweisung die Ausgabe zu beeinflussen, beispielsweise gibt der Befehlwriteln(Summe: 9: 2, ’ DM’); den Real–Ausdruck Summe mit insgesamt 9 Stellen ein-schließlich Dezimalpunkt, davon 2 Nachkommastellen gerundet, und der BezeichnungDM, getrennt durch eine Leerstelle, aus.

10.3 Unit–Konzepte und objektorientierte Programmierung

10.3.1 Allgemeiner Programmaufbau

Wir geben die EBNF–Notation für den allgemeinen Programmaufbau in leicht gestraffterForm an:

Programm = Programmkopf ";"

[UsesAnweisung] Programmblock.

Programmkopf = "Program" ProcIdent["(" Identliste ")"].

Programmblock = Deklarationsteil "begin" statement

";" statement "end".

Deklarationsteil = [ConstDecl] [TypeDecl] [VarDecl]

ProcDecl | FuncDecl .

UsesAnweisung = üses" UnitIdent ";" UnitIdent ";".

Unit = Unitkopf ";" "interface" Interfaceteil

"implementation" Implementationsteil

"begin" Initialisierungsteil "end" ";".

Unitkopf = "Unit" UnitIdent.

Dabei erlaubt dieser Ansatz den Einsatz vorher kompilierter Module, der Units, die inder Uses–Anweisung mit ihrem Namen in das Hauptprogramm integriert werden. Stan-dardmodule wie SYSTEM mit der Laufzeitbibliothek, das immer eingebunden ist, oder zurBildschirm– und Tastatursteuerung CRT, zum Betriebssystem DOS, WINDOS, zur Graphik-ausgabe GRAPH, zum Auslagern von Programmteilen OVERLAY und zur DruckerausgabePRINTER können mit eingebunden werden. Das Programmierhandbuch der Firma BOR-LAND TURBO–PASCAL x.x enthält nähere Einzelheiten.

Der Interfaceteil kann wieder mit einer Uses–Anweisung beginnen und enthält die nurnach außen sichtbaren Konstanten–, Typ– und Variablen–Befehlsteile, bei den Unterpro-grammen stehen hier nur die Köpfe. Der Implementationsteil enthält die lokalen und pri-vaten Größen, die nur im Modul benutzt werden. Hier werden auch die Deklarationen undAnweisungen der Unterprogramme nachgetragen, Prozedur– und Funktionsblöcke, derenKöpfe schon im Interface–Teil deklariert wurden, werden nur noch durch ihre Bezeichner

151

Page 152: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

eingeleitet. Im Initialisierungsteil werden dann Variablen und andere Größen des Modulsinitialisiert. Der Modul wird wie ein normales Programm übersetzt und kann dann mitder Endung .tpu eingebunden werden. Dabei muss auch die Versionsnummer des Com-pilers beachtet werden. Die Unit Graph.tpu stellt ein vollständiges 2D–Graphikinterfacezur Verfügung, das allerdings die nicht standardisierten XGA–Modi der Graphikkartenaußer acht lässt. Hier sind die Windows–basierten Delphi–Systeme deutlich vorzuziehen.

10.3.2 Objektorientierte Programmierung

Im Datentyp Objekt (bzw. CLASS in anderen Programmiersprachen) werden Daten undMethoden, d.h. Unterprogramme zur Bearbeitung der Daten gemeinsam in einer Record–ähnlichen Struktur der Form

Object Daten; Methoden end;

abgespeichert.

Objekte werden in Hierarchien verwaltet und können sich untereinander Botschaftenzusenden.

In der Objektvereinbarung werden zunächst nur die Köpfe der Methoden angegeben,die Rümpfe folgen anschließend vergleichbar einer forward–Vereinbarung. Im Rumpfstehen dann alle Daten des Objektes zur Verfügung; sie brauchen nicht mehr in derParameterliste übergeben werden.

Auch die Methoden werden in der üblichen Record–Notation

ObjectIdent.MethodIdent

bezeichnet, was durch die with–Anweisung verkürzt werden kann. Durch die AngabeObject (ObjectTypIdent) kann ein neuer Objekttyp von einem bereits deklarierten ab-geleitet werden. Dabei erbt er alle Komponenten, bestehend aus der Datenfeld– undMethodenliste, von seinem Vorgänger. Die Vererbung führt zu einem kompakteren undbesser wartbaren Code, da Stammethoden auf abgeleitete Klassen von Objekten an-wendbar sind. Werden in verschiedenen abgeleiteten Objekten gleichnamige Methodenvereinbart, so erfolgt bei statischen, d.h. nicht mit virtual gekennzeichneten Methodendie Auswahl aufgrund der Typvereinbarung der aktuellen Argumente. Dabei kann es zuProblemen kommen, wenn innerhalb einer Methode ein Aufruf einer anderen Methode inder Hierarchie zurückverfolgt werden muss. Dann sollten diese Methoden auch virtual

sein, weil sie erst zur Laufzeit des Programms anhand der aktuellen Argumente passendin ihrem Kontext ausgewählt werden. Polymorphismus besagt in diesem Zusammenhang,dass eine Methode unter gleichem Namen mit unterschiedlichen Implementierungen inverschiedenen Objekten (Klassen) einer Klassenhierarchie vorkommt. Erst zur Laufzeitwird in Abhängigkeit vom aktuellen Typ entschieden, welche Implementierung aufgerufen

152

Page 153: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

wird. Enthält eine Objektvereinbarung virtuelle Methoden, so muss sie eine Konstruk-tormethode, eingeleitet mit constructor, enthalten, die vor dem ersten Aufruf Initiali-sierungen durchführt. Die Methode muss dann auch in allen abgeleiteten Objekten alsvirtuell gekennzeichnet sein. Objekte können mit den Prozeduren new und dispose dyna-misch erzeugt, initialisiert und vernichtet werden. Daher gibt es neben der constructor–auch eine destructor–Methode. Weiterhin kann eine Datenkapselung mit private er-folgen, d.h., die auf private folgenden Methoden und Variablen werden vor dem Zugrifffremder Objektmethoden geschützt.

Wir wollen nun als Beispiel eine Klassenhierarchie für geometrische Primitive der Compu-tergraphik entwerfen. Dabei sollen die Merkmale der Klassen so ausgewählt werden, dassÄhnlichkeiten zwischen den Klassen erkennbar sind und Vererbungsmechanismen An-wendung finden. Dazu definieren wir eine Klasse GrObjekt, die Methode und Variablenenthält, die für alle Graphikobjekte gültig sind. Hieraus wollen wir dann die konkretenGraphikobjekte ableiten.

Als Methoden aller Klassen wollen wir den Constructor Init, Methoden Zeichne, Sicht-bar, Anzeigen, Loeschen und Verschiebe einführen. Für alle Klassen außer der KlassePunkt kommt die Methode Skaliere hinzu und für alle Objekte, die Flächen repräsentie-ren, soll der Flächeninhalt durch eine Methode Flaeche berechnet werden. Damit sindzunächst die Ähnlichkeiten von Methoden zwischen den Klassen der unterschiedlichengeometrischen Figuren analysiert.

Die Ähnlichkeit von Merkmalen ergibt sich aus einer Untersuchung insbesondere derobjektspezifischen geometrischen Merkmale:

• Die Klasse GrObjekt soll ein Flag sichtbar enthalten, sowie die Methoden zumVerschieben, Anzeigen, Löschen und Zeichnen definieren. Die Methode zum Zeich-nen ist dabei abhängig vom jeweiligen Objekt und muss daher virtuell definiertwerden, d.h. sie ist in jeder abgeleiteten Klasse an das konkrete Objekt anzupas-sen.Alle Graphikobjekte besitzen einen Bezugspunkt im Weltkoordinatensystem, andem ein lokales Koordinatensystem liegt, in dem das Objekt beschrieben ist. DieVerschiebung eines Objektes kann realisiert werden, indem nur der Bezugspunkt(Nullpunkt des lokalen Koordinatensystems) verschoben wird. Daher kann die Me-thode zum Verschieben von Objekten schon in der Klasse GrObjekt implementiertwerden. Eine weitere Variable für alle Objekte ist damit ein Punkt im Weltkoordi-natensystem als Bezugspunkt.

• Das Graphikobjekt Punkt kann durch einen X– und einen Y–Koordinatenwert ineinem zweidimensionalen Koordinatensystem definiert werden. Das Objekt wirdaus GrObjekt abgeleitet und liegt im Bezugspunkt.

• Strecken werden durch zwei Punkte definiert. Deshalb enthält eine Klasse Strecke

neben dem Bezugspunkt einen Variable vom Typ Punkt. Die Merkmale von Strecke

werden also um einen Punkt bzw. dessen beiden Koordinatenwerte erweitert.

153

Page 154: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• Polygonzüge werden durch ihre Eckpunkte und deren Reihenfolge definiert. AlsMerkmale einer Klasse Polygonzug sind deshalb alle Eckpunktkoordinaten bzw.eine Liste von Eckpunktkoordinaten, die auch eine Reihenfolge festlegt, sowie ihreAnzahl aufzunehmen. In der Klassenhierarchie soll sowohl eine Klasse mit einerstatischen als auch eine Klasse mit einer dynamischen Implementierung der Eck-punktliste enthalten sein (PolygonzugS bzw. PolygonzugD).

• Ein Polygon ist ein geschlossener Polygonzug, bei dem Anfangs– und Endpunktgleich sind. Ein Polygon kann somit von einem Polygonzug (statisch und dyna-misch) abgeleitet werden. (Polygon ist insbesondere ein Polygonzug)

• Dreiecke sind spezielle Polygone und können als eine Unterklasse von Polygon ge-bildet werden.

• Beliebige Rechtecke, die im Bezugspunkt des Graphikobjektes liegen, sollen durchdie Angabe von zwei Längen und einem Winkel festgelegt sein. Der Winkel wirddabei durch die X–Achse des lokalen Koordinatensystems und der Seite, die imGegenuhrzeigersinn die erste ist, gebildet. Diese Seite soll die Länge Breite besitz-ten. Die zweite, im Nullpunkt des lokalen Systems darauf im Gegenuhrzeigersinnsenkrecht stehende Seite soll die Länge Hoehe besitzen. Wir müssen also bei der Ab-leitung aus der Klasse GrObjekt weitere Merkmale für den Winkel und die beidenLängen hinzufügen.

• Quadrate können, da sie ein Spezialfall von Rechtecken sind, als Unterklasse derKlasse Rechtecke eingeführt werden. Für ihre Definition genügt – unter den glei-chen Voraussetzungen wie bei Rechtecken – die Angabe einer Länge.

• Ellipsen sollen ähnlich wie Rechtecke durch zwei Längen, die Längen der beidenHalbachsen, und einem Winkel spezifiziert werden. Die Ellipse hat ihren Mittel-punkt im Bezugspunkt (Nullpunkt des lokalen Systems), der Winkel wird analogzum Rechteck durch die X–Achse und die im Gegenuhrzeigersinn erste Halbachseeingeschlossen. Die zweite Halbachse steht auf dieser im Gegenuhrzeigersinn senk-recht. Die Klasse Ellipse hat wie ein Rechteck die zusätzlichen Merkmale einesWinkels und zweier Längen, wird aber trotzdem direkt aus GrObjekt abgeleitet,da Rechteck und Ellipse in keiner semantischen Beziehung zueinander stehen.

• Kreise sind spezielle Ellipsen, bei denen der Winkel keine Rolle spielt und dieLängen der Halbachsen gleich sind. Deshalb können wir eine Klasse Kreis als Un-terklasse der Klasse Ellipse implementieren.

Eine mögliche Klassenhierarchie für geometrische Figuren zeigt unser Bild:

154

Page 155: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

10.3.3 Implementierung

In einem Definitionsmodul werden die Merkmale und Methoden der verschiedenen Klas-sen exakt definiert und gleichzeitig die Hierarchiebeziehungen zwischen den Klassen fest-gelegt.

Program Geometrische_Figuren;

Type PunktRec = Record

x, y : Longint;

End;

GrObjekt = CLASS

(* In Borland Pascal muss das Schluesselwort *)

(* CLASS durch OBJECT ersetzt werden *)

Bezugspunkt : PunktRec;

Sichtbar : Boolean;

Constructor Init (x, y : Longint);

Destructor Done ;

Procedure Verschiebe(dx, dy : Longint);

Procedure Zeichne; Virtual;

Procedure Anzeigen;

Procedure Loesche;

End; (* GrObjekt *);

Punkt = CLASS(GrObjekt)

Constructor Init (x, y : Longint);

Destructor Done ;

Procedure Zeichne; Virtual;

End; (* Punkt *)

155

Page 156: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Strecke = CLASS(GrObjekt)

P1 : Punkt;

Constructor Init (xa, ya, xb, yb : Longint);

Destructor Done ;

Procedure Skaliere (F : Real);

Procedure Zeichne; Virtual;

End; (* Strecke *)

Const MaxPunkte = 100;

Type PktVektor = Array[0..MaxPunkte] Of PunktRec;

PolygonzugS = CLASS(GrObjekt)

Vek : PktVektor;

Constructor Init (PV : PktVektor);

Destructor Done ;

Procedure Skaliere (F : Real);

Procedure Zeichne; Virtual;

End; (* PolygonzugS *)

PolygonS = CLASS(PolygonzugS)

Constructor Init (PV : PktVektor);

Destructor Done ;

Procedure Zeichne; Virtual;

Function Flaeche : Real;

End; (* PolygonS *)

Dreieck = CLASS(PolygonS)

Constructor Init (xa, ya, xb, yb, xc, yc : Longint);

Destructor Done ;

Procedure Zeichne; Virtual;

Function Flaeche : Real;

End; (* Dreieck *)

Punktpointer = ^EinPunkt;

EinPunkt = Record

P : PunktRec;

Z : Punktpointer;

End;

PolygonzugD = CLASS(GrObjekt)

Vek : Punktpointer;

156

Page 157: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Constructor Init (PP : Punktpointer);

Destructor Done ;

Procedure Skaliere (F : Real);

Procedure Zeichne; Virtual;

End; (* PolygonzugD *)

PolygonD = CLASS(PolygonzugD)

Constructor Init (PP : Punktpointer);

Destructor Done ;

Procedure Skaliere (F : Real);

Procedure Zeichne; Virtual;

Function Flaeche : Real;

End; (* PolygonS *)

Rechteck = CLASS(GrObjekt)

Winkel : Real;

Breite, Hoehe : Real;

Constructor Init (xa, ya : Longint; W, B, H : Real);

Destructor Done ;

Procedure Skaliere (F : Real);

Procedure Zeichne; Virtual;

Function Flaeche : Real;

End; (* Rechteck *)

Quadrat = CLASS(Rechteck)

Constructor Init (xa, ya : Longint; W, B: Real);

Destructor Done ;

Procedure Zeichne; Virtual;

End; (* Quadrat *)

Ellipse = CLASS(GrObjekt)

Winkel : Real;

RadiusA, RadiusB : Real;

Constructor Init (xa, ya : Longint; W, Ra, Rb : Real);

Destructor Done ;

Procedure Skaliere (F : Real);

Procedure Zeichne; Virtual;

Function Flaeche : Real;

End; (* Ellipse *)

Kreis = CLASS(Ellipse)

157

Page 158: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Constructor Init (xa, ya : Longint; Ra: Real);

Destructor Done ;

Procedure Zeichne; Virtual;

End; (* Kreis *)

........

END. (* GeomFiguren *)

Die Implementation der Methoden geschieht außerhalb der Definition der Klassen in einerseparaten Prozedur– oder Funktionsdeklaration im Modul Implementation.

Dieser enthält z.B. den Konstruktor für die Methode Punkt.Init:

Constructor Punkt.Init (x, y : Longint);

Begin

Bezugspunkt.x := x;

Bezugspunkt.y := y;

Sichtbar := true

End; (* Punkt.Init *)

oder auch

Procedure GrObjekt.Verschiebe (dx, dy: LONGINT);

Begin

If sichtbar Then Begin

Loesche;

Bezugspunkt.x := Bezugspunkt.x + dx;

Bezugspunkt.y := Bezugspunkt.y + dy;

Zeichne

End;

End; (* Verschiebe *)

Die Namen der anderen Methoden sind praktisch selbsterklärend. Löschen und Anzeigenwerden auf Zeichnen mit verschiedenen Farben zurückgeführt. Bei der Prozedur loeschemuss allerdings darauf geachtet werden, dass bei der Implementation für den Kreis derMittelpunkt nicht gelöscht wird. Dem wird durch die virtuelle Prozedur zeichne Rech-nung getragen.

Literatur:

G. Bohlender, E. Kaucher, R. Klatte, Ch. Ullrich: Einstieg in die Informatik mit Pascal.BI–Wissenschaftsverlag, Mannheim 1993

Free Pascal Online Documentation: http://www.freepascal.org/docs.html

158

Page 159: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

10.4 C versus Pascal

Die Programmiersprache C ist für das Betriebssystem UNIX konzipiert worden. Dochaufgrund der großen Effizienz und der überaus günstigen Portabilität findet man dieseSprache auf fast jedem Rechner. C zeichnet sich durch hohe Ausführungsgeschwindigkeitund kompakten maschinennahen Code aus. Während das Betriebssystem des Macintoshin Pascal geschrieben wurde, war C bei der Entwicklung dafür gedacht, ein Betriebssystem(UNIX) nicht wie damals üblich in Assembler, sondern in einer Hochsprache zu schreiben.So entwickelte M. Ritchie 1972 aus der Sprache B(cpl) von Ken Thompson die Sprache C.Sie besitzt nur einen geringen Satz an Schlüsselwörtern, dafür aber eine große Anzahl vonBibliotheksfunktionen. Der Programmierer gibt C wie Pascal mit einem beliebigen Editorein und speichert die Programme als ASCII–Datei. Diese Dateien übersetzt anschließendder Compiler. Mit Hilfe eines Linkers entsteht ein ausführbares Programm. Da es keineFormatierungsregeln gibt, kann der Programmierer den Quelltext des Programmes durchLeerzeichen und Zeilenumbrüche beliebig strukturieren.

Das Pascal–Programm beginnt mit dem Schlüsselwort PROGRAM gefolgt vom Programm-namen, dann werden im modernen TP ab Version 4 mit USES die Units eingebunden,daran schließt sich der Deklarationsteil an, in dem Konstanten, Typen und Variablen de-klariert werden. Diese Deklarationen sind mit den Schlüsselwörtern CONST, TYPE, VAR

eingeleitet. Im Programmblock steht sodann das Hauptprogramm als Verbundanweisungeingeschlossen von BEGIN ... END. Prozeduren und Funktionen nehmen die Unterpro-gramme auf. Letztere liefern einfache Ergebnistypen zurück. Dadurch sind Wertzuwei-sungen der Form

ergebnis := funktion(wert)

möglich.

C–Programme lassen sich ebenso in kleinere Unterprogramme aufteilen. Die Funktionmain hat dabei die Bedeutung, als erste Funktion aufgerufen zu werden. Das minimaleC–Programm hat die Form main() . In C gibt es keine Prozeduren.

In den runden Klammern können Argumente der Funktion main stehen, die geschweif-ten Klammern dienen generell zur Strukturierung und enthalten hier die Anweisungender Funktion main. Eine Besonderheit von C ist der Präprozessor. Er bearbeitet denQuelltext vor dem Compiler. Seine Aufgabe ist es, Konstanten zu ersetzen und Ma-kros einzufügen. Präprozessorbefehle sind Steueranweisungen und beginnen mit #. DieVerwendung der Anweisung #include dateiname erlaubt die Einbeziehung von Stan-dardbibliotheken, wie stdio.h, die für die Ein– und Ausgabefunktionen in Datei undString zuständig sind. (In Pascal gibt es dazu die Unit system.) Makroanweisungen zurDefinition von Konstanten und Unterprogrammen beginnen mit #define. #define ENDE

1000 veranlasst den Präprozessor, alle im Programmtext folgenden ENDE durch 1000 zuersetzen.

Eine Makroanweisung

159

Page 160: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

#define MAX(a, b) ((a>b) ? a : b)

definiert die Funktion MAX mit den zwei Parametern a und b. Der Operator ? : hat diegleiche Bedeutung wie eine If - Then - Else–Alternativanweisung in PASCAL und isteine Abkürzung für

if (Bedingung) Anweisung1 else Anweisung2;

Der Präprozessor ersetzt den Funktionsaufruf MAX innerhalb eines Programmes wiederdurch den Text des Makros mit angepassten Parametern. Datentypen spielen dabei keineRolle. Erst der Compiler erzeugt vom Datentyp abhängigen Assemblercode.

Merkmal beider Sprachen ist die Blockstruktur. Die Funktion von

Begin ... End

nehmen in C die geschweiften Klammern ein. Das Semikolon als Abgrenzungssymbolist beiden Sprachen gemeinsam. Die Gebrauch von goto und Labeln wie die Regelnfür die Bildung von Bezeichnern sind weitgehend identisch, es existieren vergleichbareDatentypen. Allerdings sind die Namen oft verschieden: So entsprechen sich in C undPascal

C Pascalchar Char

int Integer

enum Word

long Longint

float Single

double Double

allerdings ist in C auf die Klein– und Großschreibung zu achten: gehalt und Gehalt

sind verschiedene Bezeichner. Der C–Typ char ist allerdings nicht standardisiert undeventuell vorzeichenbehaftet.

Bei der Deklaration von Typen zieht man in C den Typ vor:

double summe; /* Var summe : Double; */

const float pi = 3.1415926;

Mit signed und unsigned kann festgelegt werden, ob Variablen Werte mit oder ohneVorzeichen aufnehmen können.

Die vier Grundoperatorzeichen +, -, ., /, sind identisch. Allerdings hat man die folgendenUnterschiede:

160

Page 161: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

C Pascal% Mod

» Shr

« Shl

Für die Ganzzahldivision gibt es keine direkte Entsprechung: Falls Dividend d und Divisorr ganzzahlig sind, so ist d/r die Ganzzahldivision. Ist der Dividend summe ein float, sohängt es vom Typ der Variablen erg ab, ob ein Typecasting erforderlich ist. Ist erg vomTyp int, so kann es unterbleiben. Ansonsten schreiben wir in C

erg=(int) (summe/n);

anstelle von erg := summe Div n;. Mit dem Castoperator wird eine float–Zahl summe/nin den Typ int überführt.

In Pascal ist eine ähnliche Konstruktion möglich mit Typbezeichner (Variablenbezug),d.h.: Integer(erg).

Hier merkt man bereits einen weiteren wesentlichen Unterschied in der Zuweisung. C ver-wendet ein einfaches Gleichheitszeichen, muss dann allerdings in der Abfrage auf Gleich-heit ein doppeltes Gleichheitszeichen ansetzen. Dazu ist in C der Ungleichheitsoperatordurch != anstelle von <> ausgedrückt: Das !–Zeichen steht für nicht. Der Inkrement-operator in C ist ++, der Dekrementoperator -- .

C Pascal++beta Inc(beta)

–beta Dec(beta)

Hier spielt die Reihenfolge ++beta != beta++ eine Rolle, denn im ersten Fall wird derWert des Operanden vor der Auswertung (z.B. einer Ausgabe) und im zweiten Fall nachder Auswertung erhöht.

Eine erweiterte Variante des In– und Dekrementoperators sind die Zuweisungsoperatorenin C. Sie erlauben, eine Variable um einen beliebigen Wert zu ändern.

zahl += 5; /* zahl = zahl + 5 */

Analog existieren die Operationen -=, *=, /=, %=.

Beispiel:

anzahl = 3; wert = 5; summe = 7;

ergibt für

summe += wert * ++anzahl;

161

Page 162: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

den Wert 27, da ++ stärker als * bindet. Andererseits führt

summe += wert * (anzahl++)

auf summe = 22 und anzahl = 4.

In Pascal haben wir

inc(anzahl); summe := summe + anzahl * wert;

bzw.

summe := summe + anzahl * wert; inc(anzahl);

Kommen wir auf die logischen Operatoren zu sprechen: hier hat man bei boolesche Ope-ratoren die Entsprechungen

C Pascal! Not

&& And

|| Or

^ Xor

Beispiel: 10 && 2 = 1, da in C jeder Wert ungleich Null als True interpretiert wird, nurdie Null liefert den Wert False.

Bei den binären Verknüpfungen gelten die folgenden Korrespondenzen:

C Pascal^ Not

& And

| Or

Beispiel: 10 & 6 = 2. (Hier wird der UND–Operator bitweise angewandt.)

Auch die Binäroperatoren führen zu verallgemeinerten Zuweisungen.

Mit Type erzeugt man in Pascal–Programmen benutzereigene Datentypen. In C verwen-det man dafür den Befehl typedef. Zusammengesetzte Datentypen führen in Pascal zurRecordstruktur, in C heißen sie Struktur.

Type Pkw = Record typedef struct

bezeichnung : String[40];

ccm : Word; char bezeichnung[41];

neupreis : double; unsigned int ccm;

End; double neupreis;

pkw;

Var auto : Pkw; pkw auto;

(* Zugriff: *)

auto.ccm := 2476; auto.ccm = 2476;

162

Page 163: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Auch in der Behandlung varianter Records unterscheiden sich beide Sprachen leicht.

Eine Tabelle mit sechs Zeilen und zehn Spalten von double–Zahlen wird in C übrigensmit double tabelle[6][10] verabredet, Bitfelder sind ebenfalls möglich, die Anzahl derBits werden durch Doppelpunkt getrennt hinter dem Namen angegeben.

In Pascal dient das Caret ^ als Kennzeichen eines Zeigers. Ein Zeigertyp definiert eineMenge von Werten, die auf dynamische Variablen eines Grundtyps zeigen. Eine Variabledieses Typs enthält die Speicheradresse eines Objekts vom Grundtyp. Mit der ProzedurNew wird der dynamischen Variablen ein Speicherbereich zugewiesen und die Adressedieses Bereichs in der Zeigervariablen abgelegt. Schließlich kann nun der durch zeiger

beschriebene Bereich mit einem Wert belegt werden. Benötigt man den Speicherbereichnicht mehr, so kann er mit Dispose wieder freigegeben werden. Beispiel:

Var zeiger : ^Integer;

...

New(zeiger);

zeiger^ := 12;

Dispose(zeiger);

In C läuft dies Verfahren identisch ab: das Kennzeichen ist hier das Zeichen *.

Mit int *zeiger wird ein Zeiger auf den Typ int installiert: zeiger weist auf eineInteger–Zahl. Mit dem Memory allocate–Befehl wird implementationsabhängig Speicher-platz entsprechend der Typspeichergröße von int von 2 Bytes zugewiesen, beschriebenund wieder freigegeben:

zeiger = malloc(sizeof(*zeiger));

*zeiger = 12;

free(zeiger);

Während der Adressoperator in Pascal mit @ bezeichnet wird, benutzt C das Zeichen &.

Beispiel:

Var zeiger: pointer; zahl: double; double zahl, *pointer;

zahl := 2.78; zahl = 2.78;

zeiger := @zahl; pointer = &zahl;

Ein auf einen strukturierten Typ weisender Zeiger wird dann mit struct pkw *pfeil

deklariert und pfeil = &auto; lässt jetzt einen Zugriff mit

(*pfeil).ccm = 3276; /* bzw. */

pfeil->ccm = 3276;

163

Page 164: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

zu.

Kommen wir zur Behandlung der Kontrollstrukturen, von denen wir die Alternative schonbesprochen haben. In Verallgemeinerung dazu haben wir die Case–Anweisung:

Case Ausdruck Of switch (Ausdruck)

Werte : Vbanweisung; case Konstante: Vbanweisung; [break;]

......; ......

Else Vbanweisung; default: Vbanweisung;

End;

Lässt man das break weg, so werden alle folgenden Alternativen ebenfalls bearbeitet, dadie Konstante nur als Sprunglabel zählt.

Eine Bemerkung zu den iterativen Konstrukten

For Varia := Startwert To for(Varia=Startwert;

Endwert Do Varia <= Endwert; Varia++)

Vbanweisung; Vbanweisung;

(Hier steht Vbanweisung für Verbundanweisung und Varia für Variable). Allerdings istdie Syntax der For–Anweisung in C wesentlich leistungsfähiger und von der Syntax:

for (Anweisung; Ausdruck; Anweisung) Vbanweisung;

Die erste Anweisung dient der Initialisierung, der Ausdruck zur Terminierung, die zweiteAnweisung zur In– bzw. Dekrementierung, die Verbundanweisung ist bei erfüllter Bedin-gung auszuführen.

Beispiel:

for(puts("[A]betaetigen!"); getchar() != ’A’; putchar(’*’)) ... ;

Weiterhin hat man die Konstrukte:

do Anweisung while(Bedingung);

Anweisung;

while(Bedingung) Anweisung;

die der Repeat– bzw. While–Anweisung entsprechen.

In Pascal unterscheiden sich die beiden Unterprogrammtypen nur durch eine eventuelleAusgabe; wird ein Wert an die aufrufende Funktionsroutine zurückgegeben, handelt essich um eine Funktion, sonst um eine Prozedur.

164

Page 165: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

In C genügt die Funktion allen Anforderungen. Für den Fall, dass sie kein Ergebniszurückgibt, erhält sie den Datentyp void zugewiesen. Routinen, die keine Parameter be-nötigen, dürfen in Pascal ohne runde Klammern aufgerufen werden, in C ist dies jedochnicht der Fall, da hier Funktionsnamen als Adresse betrachtet werden, an der das Un-terprogramm abgelegt ist. Die runden Klammern signalisieren, dass die Routine an derAdresse aufgerufen werden soll.

Kommen wir zu Ein– und Ausgaberoutinen. Die Standardroutinen in Pascal lautenWriteLn und ReadLn.

Beispiel:

WriteLn(’Die Summe ist ’, summe,’ DM’);

Bei Fließkommazahlen sind zusätzlich Formatierungshinweise zulässig:

r := 49349857.99; Writeln(’R: ’, r:10:4, ’ DM’);

erzeugt

R: 49349857.9900 DM

Auch beim Einlesen von Daten mit der ReadLn–Prozedur werden die Datentypen au-tomatisch beachtet. Bei Eingabetyp Integer führt das Lesen von Buchstaben zu einerFehlermeldung.

In C sind zwei universelle Ein– und Ausgabefunktionen vorhanden: scanf und printf.Die Endung f deutet auf formatierte Ein– und Aufgabe hin. Welche Art von Daten mitprintf ausgegeben werden, bestimmt der sogenannte Formatstring, in dem mit Steuer-zeichen % und Folgezeichen der Datentyp festgelegt wird

c : char,

d : integer,

ld: longinteger,

h : short,

u : unsigned,

f : float,

e : wissenschaftliche Fliesskommazahl,

x, X : hex-Zahl,

o : octal,

s : string.

Ebenso können Steuerzeichen eingegeben werden:

165

Page 166: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

\a: Signalton,

\b: backspace,

\f: Seitenvorschub,

\n: neue Zeile,

\r: Wagenruecklauf,

\0: Endkennung Zeichenkette,

\’: Anfuehrungszeichen,

\\: Backslash.

Beispiel: printf("Bruch: %f DM \n", 1.0/3.0);

Bei der Funktion scanf werden die Eingabezeichen analog formatiert und auf Variablen,die hinter dem Formatstring durch ihre Adressen angegeben sind, verteilt.

Zur Bearbeitung von Dateien weist Pascal einer Variablen den Dateinamen mit demAssign–Befehl zu:

Assign(handle, dateiname);

Mit der Deklaration der Variablen handle wird auch gleichzeitig der Datentyp der Dateifestgelegt:

Var: handle: File Of Char;

Alle weiteren Lese– oder Schreiboperationen erfordern zur Identifizierung nur noch denBezeichner handle:

Read(handle, zeichen);

Die Ein– und Ausgabefunktionen von C sind kein integrierter Bestandteil der Program-miersprache, sondern in Bibliotheken abgelegt, die im Lieferumfang des C–Compilersenthalten sind.

Die Standard–I/O–Funktionen arbeiten zeichenorientiert mit sogenannten Streams, Strö-men von Zeichen, die entweder gelesen oder geschrieben werden. Die Verwaltung derStreams läuft über einen Handle, der die ständige Verbindung zur Datei oder zum Gerätherstellt. Vor dem ersten Zugriff auf eine Datei wird diese per Funktionsaufruf geöffnet.Die Funktion liefert dabei einen Handle für die Datei zurück. Alle weiteren Zugriffeerfolgen mit Hilfe des Handles. Einige Handles stehen immer ohne extra Öffnung zurVerfügung:stdin: Standardeingabe per Tastaturstdout: Standardausgabe (auf Bildschirm)stdprn: Standardausgabe (auf Drucker)stderr: Ausgabe von Fehlermeldungen.Alle Handles sind umleitbar. Die zugehörigen Routinen sind in der include–Datei stdio.hdeklariert. Die zur Verwaltung benötigte Struktur FILE hat folgendes Aussehen:

166

Page 167: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

typedef struct

short level;

unsigned flags;

char fd;

unsigned charhold;

short bsize;

unsigned char *buffer;

unsigned char *curp;

unsigned istemp;

short token;

FILE;

Zuerst erhält das Programm von der Fopen–Funktion ein File–handle, das vom Typ FILE

ist. Dann kann zum Lesen geöffnet werden.

FILE *handle;

char *dateiname;

handle = fopen(dateiname, "r");

/* Oeffnen zum lesen */

Für das Lesen und Schreiben werden dann die Funktionen fprintf() und fscanf()

herangezogen:Beispiel: fscanf(handle, "%ld", &eingabe);

167

Page 168: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

11 Logiksprachen und Prolog

11.1 Was ist logische Programmierung?

Nach unseren Vorbereitungen in unserem Einführungskapitel machen wir einen erstenVersuch, diese Frage zu beantworten: Es handelt sich um einen Berechnungsformalismus,der die folgenden Prinzipien vereinigt:

• Er benutzt Logik als Sprache zur Formulierung von Sachverhalten.

• Er benutzt Inferenz–Prozeduren, um dieses Wissen zu befragen, zu benutzen undzu vermehren.

In der logischen Programmierung benutzt man deklarativ repräsentiertes Wissen in Formeiner Wissensbasis unabhängig vom auszuführenden Programm. Eine Frage wird in Formeiner Behauptung gestellt und dann die Wissensbasis durchsucht, bis ein Objekt gefundenist, das die Aussage erfüllt. Dabei werden Formeln der Prädikatenlogik als Anweisungender Programmiersprache benutzt. Es spielen die Hornklauseln die entscheidende Rolle.

L. Sterling und E. Shapiro sagen:

Ein Logik–Programm ist eine Menge von Axiomen oder Regeln, die Relationen zwi-schen Objekten definiert. Die Berechnung des logischen Programms ist eine deduktiveFolgerung des Programms. Ein Programm definiert eine Menge von Folgerungen, welchegleichzeitig sein Inhalt ist. Die Kunst der logischen Programmierung ist es, kurze undelegante Programme zu konstruieren, die die geforderte Bedeutung haben.

Den zentralen Bestandteil bildet eine applikationsunabhängige Inferenz–Maschine. Diesevirtuelle Maschine ist eine Herleitungsprozedur, die nach einer vorgegebenen Suchstra-tegie die Wissensbasis durchsucht, um eine Schlussfolgerung auf eine Anfrage mit Hilfeder Deduktion zu ziehen, und die vorgibt, nach welcher Methode Folgerungen gezogenwerden sollen, damit die gestellte Frage als richtig oder falsch beantwortet werden kann.Inferenzregeln sind die Resolution oder der Modus Ponens, als Suchstrategien werdenStandard–Suchverfahren in einem geeigneten Graphen genutzt.

Auf den Benutzer kommen zwei Aufgaben zu. Als erstes stellt er dem System Anfragen,um den Wahrheitswert gewisser Aussagen oder Relationen zu finden. Allerdings muss einProgrammierer vorher Wissen zu dem Problem in der Wissensbasis abgelegt haben.

Wir geben ein Beispiel:

168

Page 169: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

mensch (sokrates) // Faktum - Aussage

sterblich (X) wenn mensch (X) // Regel - Aussage ueber Aussage

? sterblich (sokrates)? // Anfrage

yes // Antwort

Mittels einer Funktion ”Erklärung” kann sich der Benutzer alle benutzten Fakten undRegeln bei der Entscheidungsfindung aufzeigen lassen.

Wir fassen noch einmal die wichtigsten Punkte zu Logiksprachen zusammen:

• Anwendungsgebiete: Expertensysteme

• Programmiersprachen: PROLOG, LISP, PL/1

• Vorteile: Explizite Darstellung von Wissen; große formale Ausdruckskraft; Herlei-tung neuen Wissens; Verwendung mathematischer Prinzipien; gut definierte Syntaxund Semantik; Inkrementelle Programmentwicklung; Warum – Warum nicht – Er-klärung

• Nachteile: Ungewohnte Denkweise; sehr komplexe Systeme; problematische Schnitt-stellen bei der Datenmanipulation; Reihenfolge der Definitionen, der Klauseln undder Literale innerhalb von Klauseln hat Einfluss auf die Abarbeitung eines Lo-gikprogrammes wie Prolog; Darstellung der Negation durch Negation as FailureRegel führt bei der von Prolog verfolgten Suchstrategie bisweilen zu inkorrektenAntworten; die alleinige Fixierung auf die Prädikatenlogik ist kritikwürdig.

11.2 Von Logik zu Prolog

Die Prädikatenlogik erster Stufe bildet die Basis für Prolog. Ihre Formeln werden in Klau-selform überführt. Diese sind auf Hornklauseln, also auf Klauseln mit höchstens einempositiven Literal beschränkt. Prolog stellt sich dann als ein Beweiser für Hornklausel–Logik dar.

Zur Erinnerung (vgl. Kapitel 1.4.3): Hornklauseln werden in der Form

P : −Q1, Q2, . . . , Qn (Prozedurklausel)

notiert. Dies steht für

Q1 ∧Q2 ∧ . . . ∧Qn → P bzw. äquivalent ¬Q1 ∨ ¬Q2 ∨ . . . ∨ ¬Qn ∨ P.

Dabei steht das Komma für die Konjunktion. Hornklauseln enthalten nur eine Atom-formel als Konklusion im Kopf auf der linken Seite und mehrere durch ∧ verknüpfteAtomformeln als Prämissen im Rumpf auf der rechten Seite. Sie stellen in Prolog dieRegeln dar und können als Prozeduren abgearbeitet werden. Hornklauseln ohne Rumpf

169

Page 170: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

(Prämissen) stellen Fakten dar. Diese beiden Typen von Klauseln machen ein Prologpro-gramm aus und heißen daher Programmklauseln. Hornklauseln ohne Kopf (Konklusion)sind die Anfragen, die zu beweisenden Aussagen; leere Hornklauseln sind immer falschund entsprechen in Prolog dem Fail.

Damit bestehen Prolog–Programme aus Aussagen und Tatsachen, nicht aus Algorith-men, Datentypen, Verzweigungen, Iterationen und Wertzuweisungen, wie sie von derimperativen Programmierung her bekannt sind. Während hier das Schema

Programm = Algorithmus + Datenstruktur gilt, heißt es für PrologProgramm = Logik + Steuerung.

Es existieren für den Programmierer keine Kontrollstrukturen im eigentlichen Sinn. Ermuss sie durch Rekursion nachbilden und kann lediglich durch die Reihenfolge seinerdeklarierten Regeln Einfluss auf die Lösungsfindung durch den Interpreter/Compiler fin-den.

Das Prolog–System kann bei Erfolg oder Scheitern von Anfragen alle Berechnungen rück-gängig machen und nach einer Alternative suchen. Somit kann das Prolog–System durchEinsatz der Inferenzkomponente feststellen, ob eine in einer Anfrage enthaltene Aussa-ge Bestandteil der Wissensbasis (ableitbar) ist oder nicht (Closed world assumption).Nichtableitbarkeit bedeutet allerdings nicht Falschheit.

Prolog–Programme bestehen aus

Fakten (über Objekte und deren Beziehungen)Regeln (Beziehungen zwischen Aussagen)Anfragen (über Objekte, die in den Fakten und Regeln vorkommen)

Fakten

Hans ist Großvater von Anne über seinen Sohn Peter.

Prädikat : grossvater-über (hans, peter, anne).

• Die Namen von Prädikaten und konstanten Argumenten müssen mit Kleinbuchsta-ben beginnen.

• Die Namen variabler Argumente sind durch einen Großbuchstaben an erster Posi-tion gekennzeichnet.

• An der ersten Stelle eines Fakts steht stets der Prädikatenname, danach folgen inrunde Klammern gesetzt die Argumente.

• Die Anzahl der Argumente ist beliebig und kann auch Null sein.

• Die einzelnen Argumente sind durch Kommata voneinander getrennt.

• Die Reihenfolge der Argumente eines Prädikates ist von Bedeutung und nicht ver-änderbar.

170

Page 171: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• Prädikate gleichen Namens aber unterschiedlicher Stelligkeit stellen verschieden-artige Fakten dar.

• Jedes Fakt muss mit einem Punkt abgeschlossen werden.

Regeln

Prolog kann mit Hilfe von Regeln aus bereits bestehenden Fakten neue Fakten ableiten.Eine Regel bewirkt, dass der Wahrheitswert eines Faktums von denen eines anderen odermehrerer abhängig ist.

Wir geben ein Beispiel:

elternteil (hans, peter), elternteil (peter, anne).

Regel:

grosselternteil (hans, anne) :-

elternteil (hans, peter), elternteil (peter, anne).

Es handelt sich hier um eine Wenn–Dann–Regel.

Der abgeleitete Fakt steht auf der linken Seite vor :-, das Komma auf der rechten Seitesteht für ein logisches Und. (Ein Semikolon würde ein logisches Oder bedeuten.) JedeRegel muss mit einem Punkt abgeschlossen werden.

Der eigentliche Sinn einer Regel besteht jedoch in einer mehr allgemeinen Beschreibung.Dazu werden die konstanten Objekte durch Platzhalter ersetzt.

grosselternteil (Person, Enkel) :-

elternteil (Person, Kind), elternteil (Kind, Enkel).

Der Regelkopf grosselternteil (Person, Enkel) ist genau dann für eine konkrete Be-legung der Variablen Person (z.B. hans) und Enkel (z.B. durch anne) ableitbar, wennsich jedes der beiden durch , verknüpften Prädikate elternteil (Person, Kind) undelternteil (Kind, Enkel) des Regelrumpfs aus der Wissensbasis ableiten lässt. Diesist der Fall, wenn es eine Besetzung der Variablen Kind gibt, so dass für die gewähltenKonstanten sowohl elternteil (Person, Kind) (also elternteil (hans, peter)) alsauch elternteil (Kind, Enkel) (also elternteil (peter, anne)) in der Wissensba-sis vorhanden sind.

Wir notieren ein ausführliches Beispiel:

elternteil (hans, peter).

elternteil (karin, peter).

elternteil (otto, susi).

elternteil (frieda, susi).

elternteil (peter, thomas).

elternteil (peter, anne).

elternteil (peter, gabi).

171

Page 172: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

elternteil (susi, thomas).

elternteil (susi, anne).

elternteil (susi, gabi).

elternteil (gabi, klaus).

grosselternteil (Person, Enkel) :-

elternteil (Person, Kind), elternteil (Kind, Enkel).

Anfragen

Nachdem nun die Wissens– und Regelbasis übergeben ist, können Anfragen, die mit demZeichen ?- beginnen und mit einem Punkt enden, an das System gestellt werden. Es wirdnach dem Wahrheitswert eines Faktums gefragt.

Beispiel: ?- elternteil (susi, gabi).

Die Inferenzkomponente durchsucht nun die Wissensbasis, ob das Prädikat dem Prolog–System bekannt ist oder sich ableiten lässt. Es wird dazu ein gleichnamiges Prädikat inder Wissensbasis gesucht. Bei Erfolg werden die Argumente des Goal–Prädikats sowieihre Anzahl miteinander verglichen (Pattern–Matching). Sind die Argumente identisch, sowird die Antwort Yes, anderenfalls, bei Nichtauffindung des Goalprädikats, die AntwortNo ausgegeben.

Die angegebene Anfrage wird mit Yes beantwortet, die Anfrage?- elternteil (hans, susi).

jedoch mit No und?- elternteil (hans, peter), elternteil (otto, susi).

ebenfalls mit Yes.

Dies ist die einfachste Form der Benutzung einer Wissensbasis. Eine zweite Form ermög-licht die Ausgabe von Objekten. Will man z.B. alle Eltern des Kindes anne ermitteln,so wird eine Anfrage der Form ?- elternteil (Person, anne). gestellt. Die Antwortwäre dann

Person = peter

Person = susi

Hier ist Person eine Variable. Sie wird durch alle Objekte aus Fakten der Wissensbasisersetzt, die auf die Anfrage passen. Der Prozess des Ersetzens von Variablen (Gleich-machen von Termen durch Ersetzen von Variablen durch andere Argumente (Objekte))heißt Unifikation.

Genauso ist es möglich, alle Eltern–Kind–Beziehungen mit der Anfrage?-elternteil (Person_1, Person_2).

auszugeben.

Wenn die Anfragen als Argumente nur Variablen enthalten, so heißen sie unqualifiziert,in den vorher aufgeführten Beispielen teilqualifiziert bzw. qualifiziert.

172

Page 173: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Eine anonyme Variable wird durch eine einzelnen Unterstrich _ dargestellt. An dieserStelle kann ein beliebiges Objekt ohne Bedeutung für die weitere Bearbeitung eingesetztwerden.

Nun soll die Anfrage nach einer Grosselternbeziehung gestellt werden:

?- elternteil (peter, Person), elternteil (Person, klaus).

Die Inferenzkomponente durchsucht die Wissensbasis nach einem Prädikat, das zum ers-ten Teil elternteil (peter, Person) der obigen Anfrage passt. Findet sie ein Muster,welches zum ersten Teil der Anfrage passt, z.B. Person = thomas, so wird die Varia-ble Person mit dem Objekt thomas gebunden (diesen Vorgang nennt man Instanziie-ren). Letzterer Vorgang muss eindeutig für alle Vorkommen dieser Variablen geschehen.Nun versucht die Inferenzkomponente, das zweite Teilziel mit der Suche von elternteil

(thomas, klaus) in der Wissensbasis nachzuweisen. Da dieses Teilziel nicht erreichbarist, setzt das Prolog–System einen Mechanismus in Gang, der Backtracking heißt. Die In-ferenzkomponente geht in der Folge der zu bearbeiteten Teilziele einen Schritt zurück undversucht, elternteil (peter, Person) anders zu beweisen. Die Bindung der VariablenPerson an thomas wird dabei wieder gelöst.

Nun wird ein weiteres Muster gesucht und das bereits gefundene verworfen. Dafür kommtPerson = anne infrage. Auch hier scheitert nach Instanziierung Person = anne der Nach-weis der Wahrheit des zweiten Teilziels. Ein nochmaliges Backtracking führt auf Person= gabi. Mit dieser Bindung wird für das zweite Teilziel die Wissensbasis durchsucht undelternteil (gabi, klaus) aufgefunden. Da beide Teilziele bewiesen sind, ist das Goal

elternteil (peter, Person), elternteil (Person, klaus)

erfüllt.

Noch eine Abschlussbemerkung:

In der neuen Regel

nachfahre (Person_1, Person_2) :- elternteil (Person_2, Person_1).

nachfahre (Person_1, Person_2) :- elternteil (Person_2, Kind),

nachfahre (Person_1, Kind).

haben wir die Möglichkeit einer rekursiv definierten Regel kennengelernt.

11.3 Syntax und Semantik von Prolog–Programmen

Syntax bezeichnet die Gesamtheit der grammatischen Regeln einer Sprache, die darüberentscheiden, ob die Sätze eines Textes richtig geformt sind.

173

Page 174: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Dabei bleibt die Bedeutung außer acht. Beispiel: Gestern werden wir nach Hause gehen.

Dagegen ist Semantik die Lehre von der Bedeutung der Elemente von Zeichensystemen.

Ergebnis des Abbildungsprozesses der realen Welt auf ein Prolog–Programm sind Objekteund Prädikate.

Einfache Objekte sind Konstanten und Variablen, komplexere Objekte sind Listen oderBäume, die auch Terme genannt werden. Die Objekte sind über Relationen miteinanderverknüpft, die in Fakten und Regeln abgebildet werden. Die Prädikate werden durch ihrenPrädikatsnamen (Funktor) eindeutig bezeichnet und haben eine wohldefinierte Stelligkeit.

Die folgenden Zeichen sind die Prolog definiert:

• Großbuchstaben A, B, ... , Z

• Kleinbuchstaben a, b, ... , z

• Ziffern 0, 1, 2, ..., 9

• Sonderzeichen:! | # | $ | % | ^ | & | * | ( | ) | _ | + | ~ | | | : |

" | < | > | ? | - | = | ’ | [ | ] | ; | ‘ | , | | | . | /

Atome sind Prädikate, Operatoren oder Objekte und setzen sich aus Buchstaben, Ziffern,Bindestrich und Unterstrich oder Sonderzeichen zusammen. Ihre Namen beginnen miteinem kleinen Buchstaben und dürfen auch in Hochkommata eingeschlossen werden. Indiesem Fall sind beliebige Zeichen erlaubt. Operatorennamen sind im Allgemeinen ausSonderzeichen aufgebaut. Zahlen werden im Allgemeinen als Atome betrachtet.

Konstanten haben im Gegensatz zu Variablen feste Werte und erscheinen als Atome oderZahlen.

Variablen dienen zur Bezeichnung von nicht instanziierten Argumenten von Prädikatenoder Funktoren. Sie unterscheiden sich von Atomen in folgenden Punkten:

Sie beginnen mit einem Großbuchstaben oder Unterstrich.

Der Bindestrich ist nicht zulässig, da er als arithmetische Subtraktion interpretiert werdenkönnte.

Eine anonyme Variable (Dummi, Platzhalter) wird mit einem Unterstrich bezeichnet undbei der Lösungsfindung nicht instanziiert. Bei der Verwendung mehrerer Platzhalter sinddiese völlig unabhängig voneinander.

Variablen gelten als gebunden, wenn gleiche Variablen in einer konjugierten Klausel be-nutzt werden. Wurde einer mehrfach verwendeten Variablen noch kein Wert zugewiesen,so geschieht das zeitweise während der Beweisprozedur durch das System (Instanziie-rung), um Referenzwerte für andere Aussagen zu haben:

aussage (X) :- aussage1 (X), aussage2 (X).

174

Page 175: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

heißt konkret, dass die Variable X innerhalb der Klausel immer den gleichen Wert hat.

Steht an der Stelle des Kommas ein Semikolon, so ist die Variable X nicht gebunden. Siemuss in diesem Fall nicht unifiziert werden.

Unter einer Struktur versteht man einerseits Prädikate, durch Kommata getrennte Terme,d.h. Konstanten, Variablen oder Funktionssymbole, oder auch komplexe Objekte wieBäume oder Listen.

Aus Termen lassen sich in Prolog Sprachkonstrukte bilden wie Konstanten, Variablen,Strukturen, Listen, die wir später noch ausführlicher behandeln.

In Prolog existieren verschiedene Operatoren, um Werte von Variablen zu ändern, gleich-zusetzen oder zu vergleichen. Sie können nur in sinnvollen Zusammenhängen (nach Wert-zuweisung oder bei Vergleichbarkeit) Anwendung finden.

Die Relation Gleichheit prüft, ob Terme gleichgemacht (unifiziert) werden können, d.h.freie Variablen Werte annehmen können, so dass die Terme gleich sind. Daher gelten zweiTerme für das System als gleich, wenn sie gleich sind oder unifiziert werden können.

Konstanten sind gleich, wenn sie identisch sind (gleicher Name, zeichenweise).

Variablen sind gleich, wenn sie den gleichen Namen haben, sie die gleichen Werte habenoder beide frei sind, bzw. falls eine gebunden ist, die freie zur belegten unifiziert werdenkann. Sie sind gleich zu Konstanten, wenn sie ihren Inhalt haben oder dazu unifiziertwerden können.

Strukturen sind gleich, wenn sie den gleichen Funktor und die gleiche Stelligkeit habensowie die Argumente gleich sind.

Das Matching hat die Wertveränderung einer Variablen zur Folge, wenn sie bei Unifika-tion instanziiert werden muss.

Wir geben zwei Beispiele:

datum (Tag, dezember, 1993) = datum (31, Monat, Jahr).

Die Aussagen werden unifiziert durch Instanziierung von:

Tag auf 31, Monat auf dezember, Jahr auf 1993.

dreieck (punkt (1,1), A, punkt (2,3)) =

dreieck (X, punkt (4,Y), punkt (2,Z)).

führt zur Instanziierung: X = punkt (1,1), A = punkt (4,Y), Z = 3 und anschließendesMatching.

175

Page 176: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

11.4 Rekursive Regeln

In Prolog ist eine Regel rekursiv, wenn in ihr das Prädikat, durch das die Regel definiertwird, wieder aufgerufen wird.

Rekursive Problemlösungen dienen häufig der effizienten Programmierung, wie man anvielen Beispielen sieht, die mit dynamischen Strukturen (Liste, Baum, Graph) zu tunhaben.

Wir hatten bereits ein Beispiel angegeben und schließen sogleich ein weiteres an:

Regel1: vorfahr (X, Z) :- elternteil (X, Z).

Regel2: vorfahr (X, Z) :- elternteil (X, Y), vorfahr (Y, Z).

Wissensbasis:

elternteil (heike, robert).

elternteil (thomas, robert).

elternteil (thomas, lisa).

elternteil (robert, anna).

elternteil (robert, petra).

elternteil (petra, jakob).

?- vorfahr (heike, Z).

Lösungen:

Z = robert, Z = anna, Z = petra, Z = jakob

?- vorfahr (thomas, petra).

Yes

?- elternteil (thomas, petra).

No

Hier ist die Closed–World Annahme interessant: Jede Feststellung q über eine Beziehungist genau dann wahr, wenn das Programm P q impliziert, ansonsten ist q unwahr.

Wir gehen also davon aus, dass wir alle wahren Aussagen für die Vorfahren von heike

hergeleitet haben. Anders ausgedrückt:

Wenn man schließen kann, dass A nicht aus P folgt, so gilt ¬A, wobei A ein Grundatomist.

Durch den Ablauf der Ableitbarkeitsprüfung wird die prozedurale Bedeutung eines Prolog–Programmes bestimmt. Da der Inferenz–Algorithmus bei einer veränderten Reihenfolgeder Klausel oft auch einen anderen Verlauf nimmt, kann sich die prozedurale Bedeutungdes Programmes ändern.

176

Page 177: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Die deklarative Bedeutung betrifft die Prädikate in den Klauseln. Sie besteht in derFragestellung, ob ein Goal und nicht wie ein Goal ableitbar ist.

Wir wollen diese Betrachtungen an einigen Beispielen illustrieren und stellen die Ab-bruchbedingung im Programm an die zweite Stelle. Nun gibt es Goals, bei denen dieAbleitungsprüfung nicht zu Ende führt:

Wissensbasis:

elternteil (heike, robert).

elternteil (thomas, robert).

elternteil (thomas, lisa).

elternteil (robert, anna).

elternteil (robert, petra).

elternteil (petra, jakob).

Beispiel:

?- vorfahr (thomas, petra).

vorfahr (X, Z):- elternteil (X, Z).

vorfahr (X, Z) :- elternteil (X, Y), vorfahr (Y, Z).

Regelwerk 1

Der obige Ableitungsbaum zeigt die Gültigkeit.

177

Page 178: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

vorfahr (X, Z) :- elternteil (X, Z).

vorfahr (X, Z) :- vorfahr (Y, Z), elternteil (X, Y).

Dieses Regelwerk führt ebenfalls zur Anwort True.

Der Lösungsweg sieht etwa folgendermaßen aus:

?- elternteil (thomas, petra). false – Instanziierung misslingt

Versuche zweite Regel durch Setzung Y = robert. Eingesetzt lautet sie

vorfahr (thomas, petra) :- vorfahr (robert, petra),

elternteil (thomas, robert).

Also sind zwei Teilgoals zu erbringen, das zweite ist erfüllt, wir fragen nach dem ersten.

Es ist vorfahr (robert, petra) nachzuweisen aus der Wissensbasis.

Dies folgt jedoch aus Regel 1.

Stellt man jedoch die beiden Regeln um, so führt dies in eine Endlosschleife, da immerwieder ein neues Subgoal vorfahr (Y

′...′, petra) erzeugt wird. Dieses Subgoal muss

178

Page 179: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

auf der Wissensbasis mit einem Regelkopf verglichen werden. Dazu werden immer neueVariablen eingeführt.

Ein Kreisschluss lässt sich dadurch vermeiden, dass man die Klausel mit Abbruchkrite-rium (nicht–rekursive Regel) an die Spitze stellt.

11.5 Listen

Eine Liste ist eine geordnete endliche Folge von Objekten eines Typs.

In Prolog setzen sich die Komponenten einer Liste zusammen aus dem Kopf (erstes Ele-ment) und dem Rumpf (Rest der Liste). Trennzeichen ist in diesem Fall das Zeichen |.Man kann eine Liste auch angeben, indem man die Elemente aufzählt und durch Kom-mata trennt. Wir haben die Darstellungsmöglichkeiten:

[a, b, c, d]; [a | [b | [c | [d | []]]]];

a.b.c.d.nil; *(a,*(b,*(c,*(d,[])))).

Neben den beiden ersten Notationen gibt es zwei weitere, die Punkt– und die Funktorno-tation. Letztere ist gekennzeichnet durch das Funktorsymbol * vor der runden Klammer,Trennung der Listenelemente durch Komma und Abschluss mit der leeren Liste [].

In Prolog kann man nur auf das erste Element einer Liste und dann auf den Listenrumpfzugreifen. Die Verarbeitung von Listen erfolgt rekursiv.

Während des Matching werden die Listen mit Konstanten belegt.

Nun werden einige Anfragen diskutiert. Die einfachste ist die nach der Mitgliedschaft ineiner Liste:

mitglied (X, [X | _]). // Fakt

mitglied (X, [_ | Rumpf]) :- mitglied (X, Rumpf). // Regel

Kommt X nicht im Kopf vor, so wird der Rumpf rekursiv abgebaut.

Bei der Bearbeitung der Liste wird X entweder unifiziert, oder X ist nicht Mitglied derListe.

Weitere Beispiele:

?- mitglied (kirsche, [apfel, birne, kirsche, ananas]). Yes

?- mitglied (ananas, [apfel, [kirsche, birne, ananas]]). No

Die Antwort auf die zweite Anfrage ist so zu verstehen: Die eingegebene Liste besteht auszwei Elementen, getrennt durch ein Komma. Das erste Element dieser Liste ist der Ein-trag apfel, das zweite Element ist wiederum eine Liste, gekennzeichnet durch die eckigen

179

Page 180: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Klammern, die selbst aus den drei Elementen kirsche, birne und ananas besteht. Beider Frage, ob ananas Mitglied der Liste ist, erfolgt der Vergleich mit den beiden Elemen-ten. Da ananas weder mit dem ersten Element noch mit der Liste als zweites Elementübereinstimmt, erfolgt die Antwort No.

Operationen auf Listen:Zunächst erklären wir das Anhängen einer Liste an eine andere.

append ([], Liste, Liste).

append ([Kopf | Alte_Liste], Liste, [Kopf | Neue_Liste ]) :-

append (Alte_Liste, Liste, Neue_Liste).

Anfrage:

?- append ([a, b], [c, d], [a, b, c, d]). Yes

?- append ([X, a, b, c], [d], Gesamtliste).

Gesamtliste = [X, a, b, c, d].

?- append ([X | [a, b, c]], [d], Gesamtliste).

Gesamtliste = [X, a, b, c, d].

Im dritten Beispiel ist die Liste [X, a, b, c] nur komplizierter aufgeschrieben: DerKopf der Liste steht links vom Trennzeichen |, rechts davon der Rest der Liste. Da derKopf nur ein einzelnes Element ist, und der Rumpf eine einfache Liste ist, besteht dieGesamtliste gerade aus den Elementen X, a, b und c.

Wir erklären die Arbeitsweise an einem Beispiel:

append ([apfel, birne, kirsche], [ananas], obst).

Zunächst wird die zweite Klausel angewandt und der Listenkopf nacheinander mit denWerten apfel, birne, kirsche instanziiert. Ist dann die erste Liste leer, so kann dieerste Klausel angewandt werden, und wir erreichen Obst = [ananas]. Dann erfolgen dieRücksprünge und die zweiten Klauseln können ausgewertet werden und die Köpfe werdennacheinander an Obst gebunden, so dass letztlich Obst = [apfel, birne, kirsche,

ananas] entsteht.

mitglied (X, Liste) :- append (Liste1, [X | Liste2], Liste).

ist eine alternative Definition der Mitgliedschaft.

Dabei wird die Liste so aufgespalten, dass X in der Mitte steht.

Weitere Operationen sind:

hinzufuegen (X, Liste, [X | Liste]).

180

Page 181: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

und Löschen. Das spezielle Element X wird beim ersten Auftreten gelöscht:

loesche (_ , [], []).

loesche (X, [X | Rumpf], Rumpf) :- !.

loesche (X, [Y | Rumpf], [Y | Rumpf1]) :- loesche (X, Rumpf, Rumpf1).

Die erste Zeile dient dem Abbruch, wenn die Liste leer ist, in der zweiten Zeile wird X ge-löscht, wenn es im Kopf der Liste gefunden wird. Das Programm wird über ! abgebrochenmit Abwicklung der Rücksprünge.

In der dritten Zeile wird rekursiv weitergeschoben.

Man kann das Prädikat so modifizieren, dass alle spezifischen X–Elemente gelöscht wer-den.

Teil– und Inversliste

sublist (S, L) :- append (Liste1, Liste2, L), append (S, Liste3, Liste2).

Dann gibt

?- sublist (S, [a, b, c]).

alle Teillisten aus [a, b, c] aus.

Bilden wir noch ein Prädikat zur Invertierung:

invert ([], []).

invert ([Erst | Rest], Liste) :- invert (Rest, Zwischen_Liste),

append (Zwischen_Liste, [Erst], Liste).

Hier werden zunächst alle Kopfelemente abgeschnitten durch Neuaufruf von invert aufder rechten Seite der Regel. Sodann werden die append–Prädikate wirksam und dieinvert können von innen nach außen ausgeführt werden.

Gibt man als Ziel

?- invert ([a, b, c], Liste).

ein, so erhält man

invert ([], []).

invert ([c], [c]).

invert ([b, c], [c, b]).

invert ([a, b, c], [c, b, a]).

181

Page 182: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Es werden sukzessive die Zwischenlisten ZL2 = [], ZL1 = [c], ZL = [c, b] und Liste

= [c, b, a] erzeugt. Wir wollen dieses Problem genauer studieren und lassen das fol-gende Prolog–Programm laufen:

trace

domains

symbolslist = symbol*

predicates

append(symbolslist, symbolslist, symbolslist)

sublist(symbolslist, symbolslist).

invert(symbolslist, symbolslist).

clauses

sublist(S, L) :- append(_, List2, L), append(S, _, List2).

append([], B_list, B_List).

append([Head | Tail_old], B_List, [Head|Tail_new])

:- append ( Tail_old, B_List, Tail_new).

invert ([], []).

invert ( [Erst | Rest], Liste )

:- invert (Rest, Zliste), append (Zliste, [Erst], Liste).

Die folgenden Bilder zeigen die Anfangsphase und den Ergebnisbildschirm; die trace–Operation bewirkt, dass jede Anwendung einer Teilregel einzeln dokumentiert ist. Dieeinzelnen Schritte sind in einer kleinen Animation wiedergegeben.

182

Page 183: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

11.6 Fail und Cut–Operatoren

Die Operatoren fail und cut gehören zu den sogenannten Ausführungs–Kontroll-Ope-ratoren. Durch sie ist es möglich, den Ablauf eines Prolog–Programmes zu steuern. Mitdem Fail–Operator wird ein Backtracking erzwungen, der Cut–Operator erzwingt einenAbbruch des Backtrackings, die Suche nach weiteren Möglichkeiten wird unterbunden.

Backtracking

Ziel des Backtracking ist es, eine gefundene Teillösung eines gestellten Problems zu einerGesamtlösung auszubauen. Dabei wird versucht, die Teillösung Schritt um Schritt zu er-weitern. Gelingt dies nicht, so befindet man sich in einer Sackgasse, und es ist notwendig,einen oder mehrere Teilschritte rückgängig zu machen.

Strategie bei der Lösungssuche:

• Prolog versucht, die Sub–Goals von links nach rechts zu beantworten. Erst wenn

183

Page 184: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

ein Sub–Goal mit Yes beantwortet ist, wird zum nächsten Subgoal übergegangen.

• Wird eine Variable bei der Untersuchung eines Sub–Goals instanziiert, so ist dieVariable auch in den restlichen Sub–Goals an den Wert gebunden.

• Die Suche nach einem einzelnen Sub–Goal geschieht in der Wissensbasis von obennach unten.

• Kann ein Sub–Goal nicht mit Yes beantwortet werden, wird das Verfahren desBacktrackings angewendet. Das bedeutet, dass Prolog zum vorhergehenden Sub–Goal zurückkehrt, bei der Beantwortung dieses Sub–Goals instanziierte Variablenwieder zurückinstanziiert und nach einer anderen Lösung für dieses Sub–Goal sucht.Existiert diese, so wird wieder versucht, dass nächste Sub–Goal zu beweisen. An-derenfalls wird ein weiterer Schritt des Backtrackings durchgeführt.

• Bei manchen Systemen wird nach der Lösungsfindung nach einer Anfrage mit Va-riablen und der Ausgabe des Ergebnisses ein weiterer Lauf gestartet. Das entsprichteinem initiierten Backtracking vom letzten Sub–Goal der Anfrage aus. (Dies kannz.B. durch Eingabe eines Semikolons am Ende der erfolgten Ausgabe geschehen.)

Wir untersuchen ein Beispiel:

1. student (heinz).

2. student (tina).

3. student (marc).

4. wohnt (tina, leipzig).

5. wohnt (heinz, stuttgart).

6. wohnt (marc, stuttgart).

?- student (X), wohnt (X, stuttgart).

Bei der Lösung geht Prolog folgendermaßen vor:

Es wird versucht, das erste Sub–Goal student (X) abzuleiten. Als erstes Fakt findetProlog

1. student (heinz)

und instanziiert die Variable X auf heinz.

Nun wird das zweite Sub–Goal auf Wahrheit untersucht.

Das Fakt 5. wohnt (heinz, stuttgart). erlaubt es, die gestellte Anfage mit X = heinz

zu beantworten.

Durch Eingabe eines Semikolons kann nach dieser Ausgabe erreicht werden, dass Prolognun nach weiteren Möglichkeiten sucht, das zweite Sub–Goal zu erfüllen, um bei Existenzweitere Lösungen herauszugeben.

184

Page 185: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Dabei wird die Wissensbasis ab 5. weiter durchlaufen (und von Beginn an zyklisch).

Es wird keine weitere Lösung gefunden. Daher wird ein Backtracking–Schritt gemachtund die Variable X freigegeben.

Die zweite Regel setzt X auf tina. Beim Versuch, das zweite Sub–Goal zu erfüllen, schei-tert Prolog, und es erfolgt ein erneutes Backtracking mit Freigabe und Instanziierung X

= marc nach Regel 3. Damit kann unter Benutzung der Regel 6 auch das zweite Subgoalerfüllt werden. Alle weiteren Backtracking–Schritte führen zu keiner neuen Lösung.

Die Lösungssuche kann auch über einen UND–ODER–Baum dargestellt werden. Wiruntersuchen ein weiteres Beispiel mit den folgenden Fakten und Regeln:

mag (heinz, computer).

mag (heinz, tiere).

mag (tina, tiere).

mag (tina, biologie).

interessiert (Jemand, biologie) :-

mag (Jemand, tiere), mag (Jemand, biologie).

?- interessiert (Jemand, biologie).

185

Page 186: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Der erste UND–ODER Baum zeigt die Instanziierungen zur Erfüllung des ersten Sub–Goals und das Scheitern der Erfüllung des zweiten. Die oberen beiden Kanten bezeichnendie UND–Verknüpfung, die Knoten der Fakten sind als ODER–Knoten gezeichnet.

Die Instanziierung mit Jemand = heinz führt jedoch nicht zu einer Herleitung des zwei-ten Teilgoals. Nach dem 8. Schritt tritt ein Backtracking ein. Nun wird Jemand befreitund mit dem dritten Fakt tina instanziiert. Die Unifizierung gelingt dann schließlich im15. Schritt. Prolog antwortet mit Jemand = tina.

186

Page 187: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Mit dem Fail–Operator kann Backtracking erzwungen werden. Wir erläutern dies aneinem Beispiel:

mag (heinz, computer).

mag (marc, sport).

mag (tina, tiere).

mag (hermann, biologie).

mag (lili, mathematik).

interessiert :- mag (Person, Fakt),

write(’Student: ’), display(Person), nl,

write(’interessiert: ’), display(Fakt), nl.

?- interessiert

liefert

Student: heinz

interessiert: computer

Diese Lösung wird sofort gefunden, so dass weitere mögliche Kombinationen nicht aus-gegeben werden. Wenn man die Prüfung des Prädikats allerdings so beeinflussen könnte,dass als Ergebnis false herausgegeben würde, so würden alle Kombinationen ausgege-ben.

Diese Möglichkeit ist nun durch den Fail–Operator gegeben. Durch Anhängen des Ope-rators an den rechten Teil der Regel

interessiert :- mag (Person, Fakt),

write(’Student: ’), display(Person), nl,

write(’interessiert: ’), display(Fakt), nl, fail.

Die Überprüfung des Prädikats schließt damit immer mit fail ab, Prolog sucht nach wei-teren Lösungen. Dadurch werden alle Kombinationen ausgegeben. Bei Auftreten von fail

wird die Regel immer als falsch gewertet, jeder Instanziierungs– und Unifizierungsversuchschlägt fehl.

Kommen wir zum Cut–Operator:

Er wird durch ein Ausrufungszeichen ! notiert und gehört zu den Ausführungs–Kontroll-Operatoren.

Wird der Cut–Operator das erste Mal als zu beweisendes Teilziel angetroffen, so gilter als bewiesen, hinterlässt aber auf dem das Backtracking kontrollierenden Stack einenVermerk mit einem Verweis auf das Teilziel, das zur Aktivierung der Klausel mit demCut–Operator geführt hat. Sollte später eine Fehlschlagsfolge über den Stack im Back-tracking diesen Vermerk antreffen, so löst dies einen Fehlschlag des vermerkten Teilzielsaus.

187

Page 188: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Alternativen für das Teilziel werden für dieses und die davor liegenden nicht mehr be-trachtet.

Der Cut–Operator kann an drei verschiedenen Stellen eingesetzt werden.

• 1. Steht er am Ende einer Anfrage oder Regel, dann erfolgt die Instanziierung derVariablen nur einmal, die Lösungssuche bricht ab, es findet kein weiteres Back-tracking statt.

Beispiel 11.6.1mag (heinz, computer).

mag (marc, sport).

mag (tina, tiere).

mag (hermann, biologie).

mag (lili, mathematik).

?- mag (Person, Fakt), !.

Antwort: Person = heinz, Fakt = computer

Die Suche nach anderen Möglichkeiten wird unterbunden. Die Variableninstanziie-rungen werden nicht aufgehoben, der Abbruch ist erzwungen.

Beispiel 11.6.2mag (heinz, computer).

mag (marc, sport).

mag (tina, tiere).

mag (hermann, biologie).

mag (lili, mathematik).

interessiert (Person, Fakt) :- mag (Person, Fakt),

write(’Student: ’), display(Person), nl,

write(’interessiert: ’), display(Fakt), nl, !.

?- interessiert (Person, Fakt)

liefert auch hier

Student: heinz

interessiert: computer

• 2. Steht der cut am Anfang des Prämissenteils einer Regel, so werden bei Erfül-lung der gesamten Cut–Klausel für eine Instanziierung der Variablen keine weiterenRegeln untersucht.

Beispiel 11.6.3mag (heinz, computer).

mag (marc, sport).

mag (tina, tiere).

mag (lili, mthematik).

188

Page 189: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

interessiert (Person, Fakt) :- !, mag (Person, Fakt), junge (Person).

interessiert (Person, Fakt) :- mag (Person, Fakt).

junge (heinz).

junge (marc).

?- interessiert (Person, Fakt).

liefert

Person: heinz interessiert: computer.

und

Person: marc interessiert: sport.

Da die zweite Regel nicht mehr in Betracht gezogen werden, fallen die weiterenLösungen für tina und lili weg.

• 3. Steht der Cut zwischen den Prämissen einer Anfrage oder einer Regel, dannerfolgt das Backtracking innerhalb der Anfrage oder Regel nur bis zum Cut. Au-ßerdem wird die Entscheidung, welches Fakt für das Prädikat geprüft werden soll,eingefroren. Alternative Regeln und Fakten werden nicht mehr geprüft.

Beispiel 11.6.4student (heinz).

student (tina).

mag (tiere).

mag (biologie).

?- student (X), !, mag (Y).

Antwort:

student = heinz mag = tiere

student = heinz mag = biologie

Durch die Anfrage an das System muss Prolog drei Subgoals erfüllen, wobei dasmittlere beim Antreffen als bewiesen gilt. Einmal getroffene Instanziierungen fürVariablen zum Beweis des ersten Teilziels (hier X) werden eingefroren und nichtmehr aufgehoben. Die Variable Y wird auf tiere instanziiert, im Backtrackingfreigegeben und dann mit biologie instanziiert. Der dann folgende Schritt derFreigabe von X ist durch den Cut–Operator verhindert.

Zusammenfassung der Funktionen zum Cut–Operator

• Der Cut–Operator kommt nur im Prämissenteil einer Regel vor (im Rumpf).

189

Page 190: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• Der Cut–Operator in einer Regel zwischen mit und verbundenen Aussagen wirkttrennend. Schlägt die Prüfung eines weiteren Prädikats nach dem Cut–Operatorfehl, so findet das Backtracking nur noch bis zum Cut–Operator statt. Ein Back-tracking zu einem Prädikat, das links vom Cut–Operator steht, ist unmöglich.

• Der Cut–Operator als eingebauter Operator ist immer bewiesen. Gelingt der Be-weis also bis zu ihm, so gilt die Klausel als bewiesen. Daraus folgt, dass keineAlternativregel für die Instanziierung einer Variablen mehr untersucht wird, wenndie Instanziierung dieser Variablen in einer Regel die Cut–Klausel erfüllt.

Damit dient der Cut–Operator zur Erreichung eines Abbruches bei initiiertem Back-tracking in Suchaktionen.

Dies ist nötig, wenn die einzig mögliche Lösung gefunden ist, die richtige Regel gefundenist oder eine negative Abbruchbedingung (zusammen mit Fail) erfüllt wird.

Durch den Einsatz des Cut–Operators kann die deklarative Bedeutung eines Prolog–Programmes geändert werden. Der Cut–Operator verstärkt die Unvollständigkeitsten-denz von Prolog, da ja erfolgreiche Pfade, die in einem Cut von der Alternativensucheausgeschlossenen Teil des Beweisbaums vorkommen, für die Wiederholungsprozedurenunerreicht bleiben. Das soll an einem einfachen Beispiel erklärt werden:

P :- a, b.

P :- c.

Damit ist die Alternative in Aussagenlogik zu schreiben als P = (a ∧ b) ∨ c.

Dabei kommt es auf die Reihenfolge in der Alternative nicht an. Wird nun die ersteKlausel in

P :- a, !, b.

verändert, so entspricht diesem P = (a ∧ b) ∨ (¬a ∧ c).

Erst wenn a nicht ableitbar ist, wird die zweite Alternative untersucht. Stellt man dieKlauseln um, so wird die richtige Entsprechung erreicht.

Die beiden Operatoren ! und fail bewirken, dass ein Beweis nicht nur abgebrochenwird, sondern auf alle Fälle misslingt, was auch mit Negation as Failure bezeichnet wird.

Wir schließen mit einem Beispiel ab. Hier werden Tiere über die Eigenschaften definiert,dass sie Lebewesen, aber keine Blumen sind:

1. lebewesen (rose).

2. lebewesen (hund).

3. blume (rose).

4. tiere (X) :- blume (X), !, fail.

190

Page 191: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

5. tiere (X) :- lebewesen (X).

?- tiere (rose).

No

?- tiere (X).

No

?- tiere (hund).

Yes.

Nur wenn Prolog nicht über das cut in der ersten Regel läuft, die mit einem fail endet,und das ist in der dritten Anfrage der Fall, wird die zweite Regel benutzt, und die Antwortlautet Yes.

11.7 Standard–Prädikate (Built–in Predicates)

Das Prolog–System stellt einige vordefinierte Standard–Prädikate zur Verfügung, die vomProgrammierer genauso genutzt werden können wie diejenigen, die er sich selbst geschaf-fen hat. Durch Verwendung dieser Prädikate sieht er z.B. Ein– und Ausgabe–Funktionenauf alle Prolog–Systeme gleich, unabhängig von der verwendeten Hardware.

Oftmals sind diese Prädikate auch nicht durch die reine Logik zu beschreiben; z.B. arith-metische Prädikate sind ohne die Kenntnisse der Zahlen nicht beschreibbar. Die Zahlenwiederum sind vom System, der Hardware, abhängig (Wertebereiche). Es ist somit nichtmöglich, die Zahlen durch Prolog zu beschreiben. Die Standard–Prädikate erstreckensich über viele Bereiche des Prolog–Systems. Sie lassen sich je nach Funktion in mehrereKlassen unterteilen:

• I/O–Prädikate: Mittels der I/O Prädikate kann Prolog auf die Peripherie zugreifen.Es werden z.B. Eingaben über die Tastatur und Zugriffe auf die externen Speichermöglich.

• Arithmetische Prädikate: Diese Prädikate können für arithmetische Operationen inProlog benutzt werden, z.B. die Ausführung der Grundrechenarten.

• Wissensbasis–Prädikate: Durch diese Prädikate–Gruppe kann die Wissensbasis vonProlog zur Laufzeit verändert werden, etwa durch Hinzufügen oder Entfernen vonFakten.

• Strukturprädikate: Mit dieser Art von Prädikaten lassen sich die Datenstrukturenvon Prolog untersuchen. Strukturprädikate erlauben z.B. das Testen von Atomen.

• Ausführungs–Kontroll–Prädikate: Diese Prädikate erlauben eine Beeinflussung desAblaufs der Bearbeitung innerhalb eines Prolog–Programms, z.B. cut und fail.

191

Page 192: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• Sonstige Prädikate: Prädikate mit verschiedenen sonstigen Funktionen, z.B. Ver-gleichsprädikate. Diese Prädikate bilden Quasi–Standards für Prolog–Systeme, kön-nen sich jedoch bei den verschiedenen Prolog–Dialekten in Syntax oder Semantikunterscheiden. Daher empfiehlt es sich, im Handbuch des Prolog–Systems nachzu-schauen. Die hier angesprochenen Prädikate bilden nur eine Auswahl der heutzutageverfügbaren Prädikate auf Prolog–Systemen. Sie bilden sozusagen den kleinsten ge-meinsamen Nenner. Wir wollen es daher bei dieser allgemeinen Übersicht belassen.

11.8 Literatur

H. Kleine Büning und S. Schmitgen: PROLOG. Teubner, Stuttgart 1988

U. Schöning: Logik für Informatiker. Spektrum–Verlag Mannheim 2000

R. Yasdi: Logik und Programmieren in Logik. Prentice Hall, München 1995

192

Page 193: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

12 Funktionale Programmierung

12.1 Einführung in funktionale Programmiersprachen

Nachdem wir in den vorigen Kapiteln bereits imperative und logische Programmier-sprachen kennengelernt haben, wollen wir uns nun einer wichtigen dritten Gruppe, denfunktionalen Programmiersprachen widmen. Zu dieser Familie gehören Lisp, Scheme, ML(Meta language), Gofer, Haskell, Hope. Miranda ist eine der bekanntesten funktionalenProgrammiersprachen, die in Bildung und Forschung eingesetzt wird. Es handelt sichbei Miranda um eine Programmierumgebung für allgemeine Zwecke, bei der ein Com-piler in einem interaktiven System eingebettet ist (unter UNIX), das Zugriff auf einenBildschirm–Editor, ein Online–Handbuch und eine Schnittstelle zum Betriebssystem bie-tet. Auch verteilte Compilierung wird unterstützt. Während diese Sprache zu Beginn derneunziger Jahre einen großen Stellenwert genoss, ist nun Haskell mehr in den Vorder-grund getreten. Viele Informationen zu diesen Programmiersprachen befinden sich aufder Home–Page des Lehrstuhls Informatik II (Prof. Indermark) an der RWTH–Aachen.Eine kleine Sprache ist Gofer, die auch auf DOS–PCs läuft.

Zunächst soll ein Überblick über den Sprachumfang einer funktionalen Programmierspra-che gegeben werden. Wir wählen hier exemplarisch Miranda, wie auch im Werk von Birdund Wadler, markieren aber die Unterschiede zu Gofer und Haskell.

Der grundlegende Unterschied zwischen funktionalen und imperativen Sprachen bestehtdarin, dass funktionale Sprachen sich mit der funktionalen Beschreibung einer Lösung fürein Problem befassen, während imperative Sprachen sich mit der Erteilung von Befehlenan einen Rechner beschäftigen.

Bei letzteren listet das Programm detailliert eine Folge von Aktionen auf, die der Com-puter ausführen muss. Dazu muss der Programmierer die Problemlösung beschreiben, siein einen sequentiellen Befehlssatz umsetzen und auch die Speicherverwaltung mittels derTypdefinitionen übernehmen. Den Variablen werden Werte zugewiesen, die sich ändernkönnen. Über dynamische Variablen werden Listen definiert. Hier sind Plätze zu reser-vieren und wieder freizugeben. All dies ist fehleranfällig. Die Verwendung von globalenund lokalen Variablen, ein oft eingeschränkter Funktionsbegriff ziehen weitere Problemenach sich.

Funktionale Sprachen sind Beispiele eines deklarativen Programmierstils, in dem ein Pro-gramm eine Beschreibung eines zu lösenden Problems und verschiedener dafür geltenderBeziehungen liefert. Die Umwandlung in eine Liste von Anweisungen, die auf dem Rech-ner ablaufen, liegt in der Verantwortung der Sprachimplementation.

193

Page 194: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• Der Programmierer braucht sich nicht um die Speicherbelegung zu kümmern. Esgibt keine Zuordnungsanweisung von Werten an Speicherstellen, sondern Wertenwerden von Ausdrücken Namen zugeordnet. Diese Namen werden dann in anderenAusdrücken benutzt oder als Parameter an Funktionen übergeben.

• Name oder Ausdruck haben damit einen eindeutigen Wert, die referentielle Trans-parenz, der sich niemals ändert. Berechnungen mit denselben Argumenten ergebenimmer dasselbe Ergebnis. Der Code ist sicherer und auch im gleichen Kontext wie-derbenutzbar.

• Funktionen und Werte werden als mathematische Objekte behandelt, die mathe-matisch–logischen Regeln gehorchen.

Syntax und Semantik funktionaler Sprachen tendieren zur Einfachheit, führen zu knap-pen und effizienten Programmen.

Wir wollen ein kleines Beispiel in einer imperativen Sprache anführen:

j:= 1;

for i:= 1 to LineLength do

if line[i] < 10 then

begin newline[j]:= line[i]; j:= j+1 end;

Dieses Programmfragment wählt aus einer Liste alle Zahlen mit dem Wert kleiner 10 aus.

Würde man dynamische Listen vorsehen, wäre der Aufwand noch höher.

Das entsprechende Programm einer funktionalen Programmiersprache lautet:

filter (lessthan 10) number_sequence

Hier wird kein Speicher verwaltet, keine Liste über Arrays definiert, Initialisierung undInkrementierung fallen weg, der funktionale Stil ist auch flexibler:

Dasselbe Programm zur Auswahl aller Elemente ungleich "fred" aus einer Zeichenkette:

filter (notequal "fred") string_sequence

Funktionale Sprachen basieren auch nicht auf einem speziellen Hardwaremodell. Daserleichtert Realisierungen fern von der von–Neumann–Architektur, wie Multiprozessorenund Parallel–Verarbeitung. Sie sind auch der Mathematik näher.

Wir wollen uns hier auf eine rein funktionale Sprachdefinition ohne imperative Merkmalebeschränken. Solche Sprachen haben eine einfache und klare Semantik und werden in denBereichen Beweissysteme und Spezifikationen benutzt.

Alle diese Sprachen behandeln Funktionen und Daten gleich, bieten automatische Spei-cherverwaltung, Typflexibilität, Polymorphismus, Mustererkennung, Listenbeschreibung,partielle Funktionen, kontextabhängig verzögerte Auswertung von Funktionen.

194

Page 195: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

12.2 Operatoren, Bezeichner, Typen

Funktionale Programmiersprachen wie Miranda werden mittels eines Schlüsselwortes(hier: mira) im Editor gestartet und können durch Eingabe eines Kürzels (/quit bzw./q) verlassen werden. Bei Gofer rufen wir das Programm auf und sehen uns vor einerEingabeaufforderung mit dem Kürzel ?.

Es wird interaktiv eine Eingabe in Form eines zu berechnenden Ausdruckes oder einesBefehls /... (bzw. :... in Gofer) erwartet. Alle Ausdrücke und Befehle, die auf die Eingabe-aufforderung hin eingegeben werden, müssen durch ein <return> abgeschlossen werden.

Ausdrücke müssen syntaktisch korrekt sein, ansonsten gibt es eine Fehlermeldung.

Dabei gilt die übliche Syntax für Ausdrücke, die wir schon von der ProgrammiersprachePASCAL her kennen.

Beispiele:

MIRANDA (2+3)*5

25

MIRANDA 3 > 4

False

Gofer benutzt hier zum Beispiel auch die Schreibweise(*) ( (+) 2 3 ) 5

Miranda hat den üblichen Satz festeingebauter Funktionen zur Verfügung, wie sqrt. BeiGofer sind je nach benutztem Prelude, das ist eine Datei, in der die Standardfunktionenund Operatoren vereinbart werden (vergleichbar einer Unit in PASCAL), unterschiedli-che Operationen möglich. So unterstützt es beim einfachen Prelude keine floating–pointOperationen, beim Standard–Prelude können +, -, *, /, PrimPlusFloat etc. verwendetwerden.

Programme werden im Editor (/e, bzw. :e) in Form von Skriptdateien erstellt. Hier istes möglich, einem syntaktisch korrekten Ausdruck einen Namen zu geben:

days = ((4*30) + (7*31)+ 28)

hours = 24

Miranda days

365

(Wir übergeben days an die Eingabeaufforderung, innerhalb einer gültigen Skriptdateidefiniert.)

Der Standardname von Programmfiles in Miranda ist script.m. In Gofer wird die imEditor erstellte Datei script per :l script geladen.

Jeder gültige Ausdruck kann mittels Bezeichner = Ausdruck an einen Bezeichner ge-bunden werden. Derselbe Name darf aber nicht zweimal verschiedene Verwendung finden.

195

Page 196: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Bezeichner beginnen mit einem Buchstaben, können dann Zahlen, Unter– und einfacheAnführungsstriche enthalten. Bezeichner mit beginnenden Großbuchstaben sind in GoferKonstruktoren vorbehalten.

hours_in_year = (days*hours)

Wir verfügen über die klassischen einfachen Datentypen, wie boolesche Werte (bool) undZahltypen (num).

Miranda integer 3.0

False

Typen können aber auch durch Aufzählung ihrer Elemente neu definiert werden.

Funktionale Programmiersprachen sind streng typisiert, falsche Matchings werden ange-zeigt:

Miranda 2 div True

type error in expression

cannot unify bool with num

Ein Typchecking findet während des Compiliervorgangs statt. Dabei werden Syntax– undTypfehler angezeigt.

Korrekte Programme können ausgeführt werden.

Wir können Integer– und Realzahlen, sowie die Operationen +, -, *, /, div, mod, ^

verwenden.

Miranda 4.0^-1

0.25.

Miranda 365 div 7.0

Program error: fractional number where integer expected (div)

In Gofer schreibt man ? 4 / 3 oder ? div 4 3 oder ? 4 ‘div‘ 3 (Akzent gravis).

Für die Verknüpfungsreihenfolge der Operatoren gelten die üblichen Regeln der Hierar-chie, Klammern können diese präzisieren oder ändern, gewisse Operatoren sind assoziativoder kommutativ.

Operatoren können überladen werden, wie < für Zahlen und Zeichenketten, allerdingssind Mischungen verschiedener Operatoren nicht möglich.

Funktionale Programmiersprachen unterscheiden zwischen Zeichen und Zeichenketten:

Ein Zeichen ist ein einzelnes Eingabesymbol, gekennzeichnet durch den Typnamen char.Es gibt jedoch kein leeres Zeichen.

196

Page 197: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Zeichen sind die ASCII–Zeichen wie ’A’ oder Sonderzeichen \n für Newline, \’ für Anfüh-rungszeichen, \065 für A. Miranda besitzt Typumwandlungsfunktionen code und decode

von Zeichen zu Zahlen und umgekehrt, in Gofer sind es die schon aus Pascal her bekann-ten ord und chr.

Zeichenketten werden durch eine beliebige Folge von Zeichen dargestellt, die von doppel-ten Anführungszeichen eingeschlossen und durch den Typ [char] gekennzeichnet sind.Daher gibt es einen Unterschied zwischen ’a’ und ä".

Operationen und Funktionen mit Zeichenketten sind ++ (Konkatenation), -- (Subtrak-tion, Gofer:), # (Länge, length in Gofer), ! x (Indizierung, d.h. es wird, bei Null beginnend, dasTeilzeichen mit der angegebenen Nummer x extrahiert; Gofer benutzt !! x).

Miranda äaa" ++ "bbbb" ++ ""

aaabbbb

Miranda äabcd" -- "bacr"

|| Die geloeschten Zeichen sind die rechts stehenden

ad

Miranda # "blabla"

6

Miranda äbc" ! 1 || Gofer benutzt !!

’b’

shownum || wandelt Zahlen in Zeichenketten um

Kommentare in Gofer beginnen mit --, in Miranda mit ||.

Boolesche Werte ergeben sich als Resultate relationaler Operatoren und können die WerteTrue oder False haben.

Ferner finden die logischen Operatoren ~(nicht), & (Konjunktion), ∨ (Disjunktion) Ver-wendung, Gleichheit wird mit = geprüft.

In Gofer treten an diese Stelle not, &&, ||. Das Prüfen auf Gleichheit geschieht mit ==.

Miranda (2<3)=(~(~True))

Zeichenketten können bezüglich lexikographischer Ordnung verglichen werden:

Miranda "B" < "Al"

False

Steht in einer Skriptdatei folgende Vereinbarung:

x = 0.0

y = 0.2

197

Page 198: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

so ergibt sich

Miranda abs(x-y) < 0.4

True

Miranda (x=0) v ((y div x) > 23)

True

oder in Gofer:

? abs(x-y) > 2 where x = 5; y = 2

True

Den Typ eines Ausdruckes kann man in Miranda mittels nachgestelltem :: bestimmen.

Komplexere Datentypen werden mit Tupeln eingeführt: date = (13, "April", 1066).

Tupel stellen das kartesische Produkt der zugrundeliegenden Komponenten dar.

Die Menge der Werte, die ein Typ haben kann, wird als seine Domäne gekennzeichnet.Theoretisch ist die Domäne des Typs int die Menge der ganzen Zahlen, praktisch istsie durch den Prozessortyp eingeschränkt. Liefert eine Berechnung ein Ergebnis durchBereichsüberschreitung außerhalb der Domäne, so wird es durch das spezielle Zeichen⊥, den bottom, repräsentiert. Unser Beispiel betrifft die Domäne (num, [char], num).Zur Vereinfachung kann dieser Typ auch neu benannt werden: Datum == (num, [char],

num).

Tupel können auf Gleichheit (Äquivalenz) und Ungleichheit geprüft werden.

Miranda date = (abs(-13), "April", 1065 + 15 mod 7)

True.

Das zusammengesetzte Format eines Tupels kann auf der linken Seite einer Bezeichner-definition benutzt werden:

(day, month, year) = (13, "April", 1066)

So können day, month und year als einzelne Bezeichner verwendet werden. Weiterhindürfen Tupel geschachtelt werden.

12.3 Funktionen

Miranda verfügt über eingebaute und vordefinierte Funktionen, erlaubt aber auch dieDefinition eigener Funktionen. Wichtige Werkzeuge sind Mustererkennung und Rekursi-on.

Funktionen werden innerhalb der Skriptdatei definiert. Das geschieht nach folgendemSchema:

198

Page 199: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

funktion_name parameter_name = funktion_rumpf.

Dabei besteht die linke Seite aus zwei Bezeichnern. Hier unterscheidet man wie bei PAS-CAL zwischen formalem und aktuellem Parameter. Funktionsrümpfe sind gültige Aus-drücke, Konstanten, Funktionsaufrufe etc.

twice x = x*2

Miranda twice 2

4

Miranda twice ::

num -> num

Miranda twice

<function>

Letzteres sagt aus, dass twice eine Funktion und kein Wertbezeichner oder gar nochnicht definiert ist.

Funktionen können umbenannt werden per tw = twice. Weitere Beispiele sind

isupper c = (c >= ’A’) & (c <= ’Z’)

toupper c = decode ((code c) - (code ’a’) + (code ’A’))

Für sehr lange Funktionsdefinitionen gibt es eine Abseitsregel für die rechte Seite nachdem Gleichheitszeichen:

Start der Aktion in der ersten Zeile,nächste Zeile darf nicht links vom Start der Aktion in der ersten Zeile stehen.

Bei der Auswertung geschachtelter Funktionen kommt es entscheidend auf die Klam-mersetzung an. Dazu wird zur Auswertung des Funktionsrumpfes eine neue Kopie desFunktionskörpers erstellt, in der die formalen Parameter durch eine Kopie der aktuellenParameter ersetzt sind.

Der Wert der aktuellen Parameter wird nicht beeinflusst.

Das Argument der Funktion wird nur berechnet, wenn es erforderlich ist (call–by–name).Wenn darüber hinaus ein Argument mehr als einmal innerhalb eines Funktionskörpersbenutzt wird, wird es höchstens einmal berechnet (call by need, lazy–evaluation).

Im Allgemeinen kann die Berechnung von Funktionsauswertungen als eine Folge vonErsetzungen und Vereinfachungen betrachtet werden.

Das Problem der Funktionen mit mehreren Parametern wird von Miranda mit zwei An-sätzen gelöst, Curryfunktionen und Tupel. Wir behandeln zunächst Tupel und beginnenmit einem Beispiel.

ismybirthday date = (date = (1, "April", 1956))

Miranda ismybirthday ::

(num, [char], num) -> bool

199

Page 200: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Der formale Parameter ist ein Aggregattyp. In Gofer heißen die Typen Int, Bool und[Char].

timestamp (time, message) = message ++ üm" ++ (show time) ++ "Uhr"

Damit ist die Quelldomaine (num, [char]), die Zieldomäne [char].

Im Gegensatz zu PASCAL kann auch der Zieltyp ein Aggregattyp sein:

quotrem (x, y) = ((x div y), (x mod y))

Miranda quotrem ::

(num, num) -> (num, num)

Oder in Gofer:

? quotrem (37, 13) where quotrem (x ’div’ y, x ’mod’ y)

(2, 11)

Parameterlose Funktionen werden oft mit einem expliziten Nullparameter () angegeben.

Kommen wir zu polymorphen Funktionen:

Sie definieren Operationen mit Tupeln ohne Berücksichtigung der Typen ihrer Kompo-nenten.

myfst (x, y) = x

mysnd (x, y) = y

Der Typ von myfst unterscheidet sich von den vorher definierten Funktionen dadurch,dass er ein Wertepaar mit zwei beliebigen Typen übernimmt und den Wert des erstenTyps zurückgibt. (myfst ist identisch mit der eingebauten Funktion fst.) Daher gibt dasSystem an:

mysnd ::

(*, **) -> **

Dabei wird eine Funktion polymorph in den Teilen der Eingabe bezeichnet, in denen sienicht auswertet und damit für beliebige Typen arbeitet.

g (x, y) = (-y, x)

Miranda g ::

(*, num) -> (num, *).

Eine etwas andere Sicht haben wir bei der folgenden Funktionsdefinition, hier zur Ab-wechslung aus Gofer:

200

Page 201: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

(+): Int -> Int -> Int.

In anderen Worten, (+) ist eine Funktion, die ein ganzzahliges Argument nimmt, undeinen Wert vom Typ (Int -> Int) zurückgibt. Zum Beispiel ist (+) 5 eine Funktion,die einen ganzzahligen Wert n nimmt und 5+n zurückgibt. Daher ist (+) 5 4 äquivalentzu 5 + 4 und ergibt 9.

12.3.1 Mustererkennung

Mustererkennung ermöglicht alternative Definitionen von Funktionen in Abhängigkeitvom Format des aktuellen Parameters (vergleichbar dem varianten Rekord in PASCAL).

Die allgemeine Schablone zur Mustererkennung ist:

funktion_name muster_1 = funktion_rumpf_1

...........................

funktion_name muster_N = funktion_rumpf_N

Wenn die Funktion auf ein Argument angewandt wird, liest Miranda die Definitionensequentiell von oben beginnend, bis der tatsächliche Parameter auf eines der Musterpasst.

Wir notieren weitere Beispiele:

not True = False

not False = True

Stundenplan "Montag" = "Arbeiten"

Stundenplan "Dienstag" = "Uebungen"

Stundenplan "Sonntag" = "Ausruhen"

Stundenplan jedentag = "Sonstetwas"

Muster dürfen aus Konstanten, Tupeln, booleschen Variablen oder formalen Parameter-namen bestehen.

decrement (n+1) = n

both_equal (x, x) = True

twenty_four_hour (x, ä.m") = x

twenty_four_hour (x, "p.m") = x + 12

Mustererkennung kann durch Wächter mit den Schlüsselwörtern leistungsfähiger gemachtwerden:

whichsign n = "Positive", if n > 0

= "Zero", if n=0

= "Negative", if n < 0

= "Negative", otherwise

201

Page 202: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Die allgemeine Syntax in Gofer lautet folgendermaßen:

f x1 x2 ... xn | condition1 = e1

| condition2 = e2

...........

| conditionm = em

Die Semantik einleuchtend: Wenn eine Bedingung conditioni wahr ist, nimmt f denAusdruck ei an. Otherwise als Bedingung ist am Ende ebenfalls erlaubt und tritt ein,wenn die Bedingungen vorher alle nicht erfüllt sind.

Für alle bisherigen Beispiele konnte Miranda die Typen der Funktionsparameter aus demKontext erschließen. Bei der internen Funktion show zur Umwandlung in Zeichenkettenwird dieser jedoch überladen, und so ist eine Deklaration

wrong_echo x = (show x) ++ (show x)

mit einem Typfehler verbunden.

Abhilfe bringt eine Typdefinition:

funktion_name :: parameter_typ -> ziel_typ

right_echo :: num -> [char]

right_echo x = (show x) ++ (show x)

Auch ein Polytyp * kann zur Typeinschränkung benutzt werden:

third_same :: (*, *, *) -> *

third_same (x, y, z) = z

third_any :: (*, **, ***) -> ***

third_any (x, y, z) = z

Miranda third_any (1, 3, ’e’)

’e’

(In Gofer werden die *–Bezeichner durch kleine Buchstaben ersetzt.)

12.3.2 Einfache rekursive Funktionen

Wenn eine Funktion auf ein Argument angewandt wird, verursacht das Vorkommen einesFunktionsnamens im Funktionskörper, dass eine neue Kopie der Funktion erstellt wirdund auf ein neues Argument angewendet wird. Dabei wird die gleichzeitige Auswertungeines Ausdruckes solange aufgeschoben, bis die Anwendung des rekursiven Aufrufes einenFunktionswert zurückgibt.

202

Page 203: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Wichtig bei der Rekursion ist die Abbruchbedingung. Ansonsten identifiziert man meh-rere Stile, z.B. Stack– oder akkumulierende Rekursion.

Bei ersterer werden die Argumente aller Berechnungen gestapelt, bis das Abbruchmusterauftritt.

printdots :: num -> [char]

printdots 0 = ""

printdots n = "." ++ (printdots (n-1)), if n > 0

= error printdots : "negative Eingabe", otherwise

In Gofer sieht die Definition etwas anders aus:

pd 5 where pd :: Int -> [Char]; pd n

| n==0 = []

| n > 0 = "." ++ (pd(n-1))

Hier nun ein Beispiel für die akkumulierende rekursive Funktion x + y:

plus :: (num, num) -> num

plus (x, 0) = x

plus (x, y) = plus (x+1, y-1)

Es wird auf den Stack verzichtet. Stattdessen wird in einem Register so lange akkumuliert,bis eine Abbruchbedingung erfüllt ist. Im vorigen Beispiel enthält das Register dann sogardas Ergebnis.

Wir wollen die Fakultätsfunktion noch auf verschiedene Weisen in Gofer formulieren:

fact1 n = if n==0 then 1 else n * fact1 (n-1)

fact2 n | n==0 = 1

| otherwise = n * fact2 (n-1)

fact3 0 = 1

fact3 n = n * fact3 (n-1)

Einige abschließende Bemerkungen:

Funktionale Programmiersprachen haben keinen Zuweisungsoperator, der den Wert einerSpeicherstelle ändern kann. Demgegenüber sind Zuweisungen in imperativen Program-miersprachen sehr wichtig: Sie dienen der

• Steuerung von Ein– und Ausgabewerten (Puffer)

203

Page 204: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

• Steuerung von Schleifen und Iterationen

• Speicherung von Zwischenergebnissen

• Steuerung der Statusregister – Historie – Prozessorzustand

Demgegenüber stellt die Eingabe eines Programmes hier den aktuellen Parameter derobersten Funktion dar. Die Ausgabe ist das Ergebnis der obersten Funktion. Die Wie-derholungssteuerung wird durch Rekursion erledigt.

Die Ergebnisse von Zwischenrechnungen müssen nicht gespeichert werden. Der Mecha-nismus der Speicherverwaltung von Miranda verbirgt Speicherbelegung und –freigabe vordem Programmierer:

Wir geben ein Beispiel zur Vertauschung zweier Werte. Als erstes ist die Realisation inC mit einer Hilfsvariable, dann die Realisation in C mit einem strukturiertem Typ undzum Schluss dieselbe Funktion in Miranda angegeben:Erste Realisiation in C:

void swap(int *xp, int *yp)

int t;

t = *xp;

*xp = *yp;

*yp = t;

Zweite Realisiation in C:

typedef struct

int x ; int y;

PAIR;

PAIR swap(PAIR arg)

PAIR result;

result.x = arg.y;

result.y = arg.x;

return result;

Realisation in Miranda:

swap(x, y) = (y, x)

204

Page 205: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Die Programmsteuerung geschieht also folgendermaßen:

• Iteration durch Rekursion

• Auswahl durch Mustermatching

• Sequenzierung durch: Verschachtelung von Funktionsaufrufen

Ein Beispiel zu einem kleinen Projekt zur Zahl– in Stringumsetzung:

string == [char]

int_to_string :: num -> string

int_to_string n = "-" ++ pos_to_string (abs n), if n < 0

= pos_to_string n, otherwise

pos_to_string :: num -> string

pos_to_string n = int_to_char n, if n < 10

= pos_to_string (n div 10)

++ (int_to_char (n mod 10)), otherwise

int_to_char :: num -> string

int_to_char n = show (decode (n + code ’0’))

12.4 Listen

Die bisher untersuchten Datentypen num, char und bool haben nur einen skalaren Wert,während zusammengesetzte Typen wie Tupel und Zeichenkette Aggregattypen sind.

Wir wollen nun den Typ Liste vorstellen auf der Basis einer rekursiven Definition. Funk-tionen, die Listen bearbeiten, sind von Natur aus rekursiv. Dabei sollen fünf gebräuchlicheMethoden analysiert werden.

Man kann sich eine Liste als eine ineinander geschachtelte Folge von Kisten vorstellen miteiner zentralen leeren Kiste, während die umgebenden Kisten jeweils ein Datenelementund eine kleinere Kiste enthalten.

Um das erste Element zu untersuchen, muss die äußere Kiste geöffnet werden. Darin istneben diesem Element auch der Rest der Liste in einer weiteren Kiste enthalten.

Man benötigt nun Operationen zum Erzeugen einer Liste, zur Zerlegung derselben undzum Erkennen der leeren Kiste [], der der Polytyp [*] zugewiesen wird.

Eine Liste ist daher entweder leer oder ein Element eines gegebenen Typs zusammen miteiner Liste von Elementen desselben Typs.

Listen können im Aggregatformat und im Konstruktformat notiert werden, d.h. einzelneElemente werden durch Kommata getrennt in eckigen Klammern angegeben oder wirhaben die Darstellung kopf : rest, wobei kopf ein einzelnes Element und rest eine

205

Page 206: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Liste ist. Die leere Liste wird durch ein Paar eckiger Klammern dargestellt []. Bei Listenmit einem oder mehreren Elementen ist die leere Kiste im Aggregatformat enthalten, nurDatenelemente werden explizit angegeben: [1]. Weitere Beispiele:

right = [1, 2, 3, 4]

Miranda [(1, 2), (3, 1), (6, 5)] ::

[(num, num)]

Miranda ["Blabla", "Sieh da"] ::

[[char]]

Listen mit Elementen verschiedener Typen sind unzulässig.

Zulässig ist aber leer = [[], [], []] vom Typ [[*]].

Miranda stellt eine Reihe eingebauter Listenfunktionen zur Verfügung, die zum Aufbau,zur Zerlegung, zur Kombination, zur Listenlänge und zur Indizierung dienen.

Wir geben nun Beispiele von Skriptdateien:

aliste = ä" : []

bliste = "fed" : "cb" : aliste

cliste = "cc" : ["c", äb"]

(erstes_element : weitere_elemente) = [3, 2, 1]

|| Mustervergleich

Miranda erstes_element

3

Miranda weitere_elemente

[2, 1]

Miranda 4 : 3 : 2 : []

[4, 3, 2]

Das letzte Beispiel ist interessant, weil die Liste mit dem Operator : im Konstruktformatzur Verfügung gestellt ist, jedoch im Aggregatformat ausgegeben wird. Wichtig ist, dassdie Elemente vom Typ num in eine Liste [] vom Polytyp [*] eingebracht werden undalle Listenelemente den gleichen Typ haben.

Weitere Beispiele sind:

list1 = 1 : [] || ergibt [1]

list2 = 1 : [1] || ergibt [1, 1]

list3 = [1] : [1] : []

|| ergibt [1] : [[1]] = [[1],[1]], da : rechtsassoziativ ist.

Der Typ von : ist

Miranda : ::

* -> [*] -> [*]

206

Page 207: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Dies ist im Sinne der Linksverknüpfung zu lesen (*, [*]) -> * und stellt einen Abbil-dungstyp dar, der im nächsten Kapitel über Currying besprochen wird.

Kommen wir zum Append–Operator ++:

Miranda ++ ::

[*] -> [*] -> [*]

Letzteres ist im Sinne der Linksverknüpfung zu lesen ([*], [*]) -> [*].

Miranda [1] ++ [2, 3]

[1, 2, 3]

Es können nur Listen gleichen Typs miteinander verknüpft werden. Der Operator ++ istkein Konstruktor, da er keine eindeutige Darstellung einer Liste liefert:

["A", "A"] = ersteListe ++ zweiteListe.

Hier kann ersteListe sowohl [], ["A"] oder auch ["A", "A"] sein.

Zum Zerlegen von Listen hat Miranda die Operationen hd (head) und tl (tail):

Miranda hd [1, 2, 3, 4]

1

Miranda tl [1, 2, 3, 4]

[2, 3, 4]

Der hd–Operator ist vom Typ [*] -> *, der tl–Operator vom Typ [*] -> [*].

Miranda (tl [1])

[]

Miranda (tl [1]) ::

[num]

Wir notieren noch: (hd anylist) : (tl anylist) = anylist, hd und tl auf die leereListe angewendet erzeugen Fehler.

Weitere Listenoperationen sind die Subtraktion -, # (Listenlänge) und ! (Indizierung):[1, 2, 3] ! 2 liefert das Ergebnis 3.

Bei der Indizierung wird nach ! die Nummer des gesuchten Elements angegeben, wobeidie Numerierung bei Null beginnt.

Die Eingabe von Listen kann durch die Punkt–Punkt Schreibweise vereinfacht werden,die schon von PASCAL her bekannt ist:

207

Page 208: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Miranda [-2 .. 2]

[-2, -1, 0, 1, 2]

Miranda [1, 7 .. 49]

[1, 7, 13, 19, 25, 31, 37, 43, 49]

Miranda [-2 .. -4]

[]

Miranda [3, 1 .. -5]

[3, 1, -1, -3, -5]

Miranda hd (tl (tl (tl [3, 1 .. -5])))

-3

Interessant sind Listen von Zeichen, vorher schon als Zeichenketten eingeführt:

Miranda [’s’ : ’a’ : ’m’ : ’p’ : ’l’ : ’e’ : []) ::

[char]

Eine äquivalente Schreibweise ist

[’s’, ’a’, ’m’, ’p’, ’l’, ’e’].

Listen und Tupel sind aggregierte Datenstrukturen. Alle Elemente einer Liste müssen da-her demselben Typ angehören. Tupelelemente können demgegenüber verschiedene Typenhaben. Listen und Tupel können gemischt werden:

richtige_tupel = ([1, 2, 3], [True, False])

richtige_liste = [(12, "Juni", 1793), (13, "April", 1066)]

Eine Liste ist rekursiv definiert, ein Tupel nicht.

Tupel können bei einer Äquivalenzprüfung nur gegen Tupel mit gleich zusammengesetz-tem Typ abgeglichen werden, Listen verschiedener Länge können verglichen werden.

12.4.1 Einfache Listenfunktionen

Die folgenden Listenfunktionen sind einfach zu definieren:

isempty :: [*] -> bool

isnotempty :: [*] -> bool

isempty anylist = false

Andere Listenfunktionen sind rekursiv, weil die Liste selbst eine rekursive Struktur ist.

Die Konstruktion geht nach folgendem Schema vor:

template [] = Endwerttemplate (front : rest) = etwas mit ”front” tun und das Ergebnis mit einem rekursivenAufruf auf dem Restargument kombinieren

Wir geben ein Beispiel:

208

Page 209: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

nlist == [num]

sumlist :: nlist -> num

sumlist [] = 0

sumlist (front : rest) = front + sumlist rest

Nun notieren wir eine Schablone für eine akkumulierende rekursive Funktion:

main [] = Abbruchbedingung oder Fehlermeldungmain any = aux (any, (Anfangswert des Akkumulators))aux ([], accumulator) = accumulatoraux ((front : rest), accumulator) = aux (rest, etwas mit ”front” und ”accumulator” tun))

Das folgende Beispiel ist akkumulierend rekursiv, nutzt aber statt eines expliziten Akku-mulators den Anfang der Liste, um das aktuelle Maximum zu speichern.

numlist == [num]

listmax :: numlist -> num

listmax [] = error "listmax - empty list"

listmax (front : []) = front

listmax (front : next : rest)

= listmax (front : rest), if front > next

= listmax (next : rest), otherwise

Nun zwei polymorphe Funktionen mit Listen:

length :: [*] -> num

length [] = 0

length (front : rest) = 1 + length rest

mymember :: ([*], *) -> bool

mymember ([], item) = False

mymember ((item : rest), item) = True

mymember ((front : rest), item) = mymember (rest, item)

Man bemerkt, dass sich der Miranda–Code direkt aus der mathematischen Spezifikationergibt. Weiterhin wird eine Definition der Funktion benötigt für die

• [] leere Liste

• die Standardliste (front : rest)

• eventuell spezielle n–elementige Liste, wie bei listmax im Fall n = 1 (front :

[])

Manchmal sind diese Funktionen jedoch erst nach einem längeren Analyseprozess hinzu-schreiben:

Wir geben ein Beispiel einer Neudefinition der Standardfunktion reverse:

209

Page 210: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

myreverse :: [*] -> [*]

myreverse [] = []

myreverse (front : rest) = myreverse (rest) ++ [front]

startswith erkennt, ob eine Liste Unterliste einer anderen ist:

startswith :: ([*], [*]) -> bool

startswith ([], anylist) = True

startswith (anylist, []) = False

startswith ((front1 : rest1), (front2 : rest2))

= startswith(rest1, rest2) & (front1 = front 2)

Wir wollen nun noch ein Sortierbeispiel mit Miranda lösen, und zwar Insert–Sort:

Man beginnt mit einer unsortierten und einer leeren Liste, die als sortiert gelten kann,und fügt nacheinander die Elemente der unsortierten Liste in die sortierte ein, ohne dieSortierung zu zerstören.

nlist == [num]

isort :: nlist -> nlist

isort anylist = xsort (anylist, [])

xsort :: (nlist, nlist) -> nlist

xsort([], sortedlist) = sortedlist || Abbruchbedingung

xsort (front : rest, sortedlist)

= xsort(rest, insert (front, sortedlist))

insert :: (num, nlist) -> nlist

insert (item, []) = [item]

insert (item, front : rest)

= (item : front : rest), if item <= front

= front : insert (item, rest), otherwise

Man beachte, dass man im Prelude von Gofer vergebene Namen nicht noch einmal ver-wendet. Dazu gehören zum Beispiel insert und qsort.

12.4.2 Rekursion

Stackrekursive Funktionen haben ein wachsendes Stadium des Stacks, wo die Berechnungzurückgestellt wird, und ein abnehmendes, in der das Endergebnis berechnet wird.

Occurs zählt zum Beispiel das Auftreten eines Elements in einer Liste.

occurs :: ([*], *) -> num

occurs ([], item) = 0

occurs((item : rest), item) = 1 + occurs (rest, item)

occurs ((front : rest), item) = 0 + occurs (rest, item)

210

Page 211: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Diese Lösung ist nicht sehr elegant, weil sie für das Nichtauftreten des Elementes künstlichden Wert 0 auf den Stack legt. Günstiger ist die filterrekursive Variante, die ähnlich lautetwie die obige, aber die Null weglässt. Dabei ist klar, dass im Falle des Nichtauftretensnur eine Enddefinition mit der Null und der leeren Liste erfolgt.

Eine andere Version führt zusätzlich einen Akkumulator ein:

occurs :: ([*], *) -> num

occurs (anylist, item) = xoccurs (anylist, item, 0)

xoccurs :: ([*], *, num) -> num

xoccurs ([], item, total) = total

xoccurs ((front : rest), item, total)

= xoccurs (rest, item, total + 1), if front = item

= xoccurs (rest, item, total), otherwise

Kommen wir zu endrekursiven Funktionen:

Hier wird das Beispiel der endrekursiven Funktion mylast gezeigt, bei der die Berechnungniemals ausgesetzt ist. Die Funktion berechnet das letzte Element einer Liste.

mylast [*] -> *

mylast [] = error mylast : "leere Liste"

mylst (front: []) = front

mylast (front : rest) = mylast rest

Betrachten wir schließlich wechselseitige Rekursion:

string == [char]

nasty_skipbrackets :: string -> string

nasty_skipbrackets [] = []

nasty_skipbrackets (’(’ : rest)

= nasty_inbrackets rest

nasty_skipbrackets (front : rest)

= front : nasty_skipbrackets rest

nasty_inbrackets :: string -> string

nasty_inbrackets []

= error "text ends inside a bracket pair"

nasty_inbrackets (’)’ : rest)

= nasty_skipbrackets rest

nasty_inbrackets (front : rest)

= nasty_inbrackets rest

Diese Funktion löscht jeden Text in Klammern aus einer gegebenen Zeichenkette. Fehltdie schließende Klammer, wird der Text bis zum Ende gelöscht. Der Aufruf

211

Page 212: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

nasty_skipbrackets (ä(b)cd(e)")

liefert acd als Ergebnis.

Hier handelt es sich um eine wechselseitige Rekursion. Will man diese vermeiden, sogewährleistet das die folgende Version:

string == [char]

skipbrackets :: string -> string

skipbrackets [] = []

skipbrackets (’(’: rest)

= skipbrackets (inbrackets rest)

skipbrackets (front : rest)

= front : skipbrackets rest

inbrackets :: string -> string

inbrackets []

= error "text ends inside a bracket pair"

inbrackets (’)’ : rest)

= rest

inbrackets (front : rest)

= inbrackets rest

12.5 Curryfunktionen und Funktionen höherer Ordnung

Dieser Abschnitt verdeutlicht, dass Funktionen sogar als Parameter an andere Funktio-nen übergeben werden können; eine Funktion, die eine andere Funktion als Argumentakzeptiert oder eine Funktion als Ergebnis hat, wird als Funktion höherer Ordnung ak-zeptiert.

Durch Currying

curry :: ((a, b) -> c) -> a -> b -> c

curry f a b = f (a, b)

können Funktionen mit mehreren Parametern definiert werden, ohne ein Tupel zu ver-wenden. Eine Curryfunktion besitzt zudem die Eigenschaft, nicht auf alle Parametergleichzeitig angewendet werden zu müssen. Man kann sie partiell anwenden, um eineneue Funktion zu bilden, die dann als Parameter an eine Funktion höherer Ordnungübergeben werden kann. Die Funktionskomposition wird dazu verwendet, partielle Funk-tionen zu verketten.

Wir geben ein Beispiel:

get_nth any [] = error "get_nth"

get_nth 1 (front : any) = front

get_nth n (front : rest) = get_nth (n-1) rest

212

Page 213: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Die Typindikation ist dann

Miranda get_nth ::

num -> [*] -> *

Letztere Typindikation ist als num -> ([*] -> *) zu lesen. Dies führt zur Anwendungneuer Funktionen, wie

getsecond = get_nth 2.

Miranda getsecond ::

[*] -> *

Miranda getsecond [ä", "AA", "xD", "SxS"]

AA

Ein weiteres Beispiel ist:

plus :: num -> num -> num

plus x y = x+y

inc = plus 1

Ein anderer Ansatz wäre der Mechanismus der Operatorsektion: (+)1 2 steht für 1+2

und ergibt die neue Funktion inc = (+) 1 und analog twice = (*) 2

Die folgenden Namen für die Präfix–Curryfunktionen der Operatoren machen die An-wendung verständlicher:

plus (+)

minus (-)

times (*)

divide (/)

and (&)

or (v)

append (++)

cons (:)

equal (=)

notequal (~=)

greaterthan (>)

lessthan (<)

Der eingebaute Punktoperator . (compose) nimmt zwei Funktionen als Parameter auf:quad x = (twice . twice) x. Ein weiteres Beispiel ist die Verwendung eines Operatorsin einer Funktionskomposition.

Miranda ((+2) . (*3)) 3

11

213

Page 214: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Wir wollen nun eine Funktion angeben, die jede dyadische Funktion, also jede Funktion,die von zwei Argumenten abhängt, in das Curryformat umwandelt:

make_curried :: ((*, **) -> ***) -> * -> ** -> ***

make_curried ff x y = ff (x, y)

maxnum :: (num, num) -> num

maxnum (x, y) = x, if x>y

= y, otherwise

newmaxnum = make_curried maxnum

newmaxnum 2 3 = ((newmaxnum 2) 3)

Für die Lösung umfassender Programmierübungen muss der Programmierer einen mo-dularen Ansatz verwenden.

Die Organisation von Umgebungen mit Hilfe des where–Mechanismus in Miranda unddurch Listenvergleiche stellt für den Programmierer ein Mittel zur Gruppierung voneinan-der abhängiger Funktionen und Namen zu einem zusammengehörenden Programmblockdar. Der Programmierer kann die Sichtbarkeit von Funktionsnamen definieren. Dadurchkann er Teile des Programmes bestimmen, in denen diese Namen verwendet werden kön-nen. Hierbei handelt es sich um eine Form der Kapselung. Dazu kommt die Möglichkeitder verzögerten Auswertung und unendlicher Listen.

Wir definieren die unendliche Liste der Quadrate ungerader natürlicher Zahlen

oddsquares = [x*x | x <- [1, 3 .. ]].

Dann ergibt

Miranda take 3 oddsquares

[1, 9, 25]

Ein weiteres Beispiel mit unendlichen Listen ist die folgende rekursive Definition:

oddsquares = xodds 1 where xodds n = (n*n) : xodds (n+2)

Wird eine Skriptdatei übersetzt, so erstellt das System eine Umgebung, die alle Beschrei-bungen der Skriptdatei sowie alle Definitionen der Standardumgebung umfasst. Durchjede Definition wird ein Wert an einen Bezeichner gebunden. Wird ein Objekt definiert,so hat es Zugriff auf alle anderen Bindungen der Umgebung. Bei jeder späteren Über-arbeitung wird dieses Verfahren wiederholt; die alten Definitionen werden entfernt, eineneue Umgebung mit den neuen und den Standarddefinitionen erstellt.

Bei der Definition einer Funktion besitzt ihr Rumpf eine neue Umgebung, der aus deroben definierten Umgebung (der geerbten) und den Namen der formalen Parameter derFunktion besteht (, die nur beim Aufruf mit tatsächlichen Werten verknüpft werden).

214

Page 215: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Diese neue Umgebung gilt nur für den Rumpf und beeinflusst nachfolgende Funktions-aufrufe nicht.

Um den Wert eines Bezeichners zu bestimmen, der im Rumpf einer Funktion erscheint,wird eine von zwei Regeln angewendet.

• Jeder formale Parametername bezieht seinen Wert von dem eigentlichen Parameter,auf den die Funktion angewendet wird. Mit anderen Worten, die lokale Umgebungignoriert etwaige vorausgegangene Bindungen an diesen Bezeichner in der geerbtenUmgebung. Der Name wird in die Funktion eingebunden.

• Jeder andere im Funktionskörper auftretende Name bezieht seinen Wert aus dergeerbten Umgebung. Er ist in dieser Funktion frei.

Der Bereich des Programmtextes, in dem eine Bindung gilt, wird als deren Gültig-keitsbereich bezeichnet. Die oben gezeigten Regeln sind daher auch als Gültigkeitsbereichs-regeln bekannt.

Außerhalb des Gültigkeitsbereiches einer bestimmten Bindung besitzt der Name dieserBindung keinen Wert und jede Referenz auf diesen Bezeichner führt zu einem Fehler. Wirdein Bezeichner in einer Umgebung eines Funktionsrumpfs wiederverwendet, so besitzt dereinen Wert, der aber von Werten außerhalb abweichen kann. Wir geben ein Beispiel:

ten = 10

both_bound xy = x+y+1 || Gueltigkeitsbereich von x und y

not_bound x = x + y + 1 || ündefined name y"

also_both_bound x = x + ten + 1 || Gueltigkeitsbereich von x

Die Fehlermeldung bedeutet, dass Miranda in der Umgebung der Funktion not_bound

keine Bedeutung für y finden kann, obwohl es in der lokalen Umgebung einer anderenFunktion eine Bedeutung für y gibt. Im obigen Fall war ein Wert mit dem Bezeichner yverknüpft, aber in der Funktion both_bound gebunden.

Reservierte Namen können nicht erneut gebunden werden, Funktions– und Typennamensollten aus Stilgründen nicht erneut gebunden werden.

Die Notwendigkeit, den Gültigkeitsbereich von Funktionen und Bezeichnern einzuschrän-ken, hilft Namenskonflikte zu vermeiden und eng verwandte Funktionen zu verbinden.

Ersterer Punkt wird einsichtig, wenn mehrere Personen an einem Programm arbeiten.

Wir zeigen hier das Beispiel qsort:

qsort :: (* -> * -> bool) -> [*] -> [*]

qsort order []

= []

qsort order (front : rest)

215

Page 216: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

= qsort order low ++ [front] ++ qsort order high

where

(low, high) = split(order front) rest

where

split pred []

= ([], [])

split pred (front : rest)

= (front : low, high), if pred front

= (low, front : high), otherwise

where

(low, high) = split pred rest

Hierbei ist order die gewünschte Sortierung, meistens gilt order ∈ <,>. Es sindmehrere where–Blöcke geschachtelt (Gofer benutzt hier zur Strukturierung where ..),und low sowie high sind in verschiedenen Zusammenhängen gebraucht.

Abschließend wollen wir noch ein weiteres Beispiel–Programm, diesmal in Gofer, betrach-ten. Es löst das sogenannte Acht–Damen–Problem, welches die Aufgabe stellt, auf einemSchachbrett acht Damen so zu verteilen, dass sie sich gegenseitig nicht schlagen können.Eine Lösung kann in Form einer Permutation der Ziffern 1 – 8 angegeben werden. Hierbeibezeichnet die Ziffer an der n–ten Stelle (1 ≤ n ≤ 8) die Spalte des Schachbretts, an derin der n–ten Zeile eine Dame steht.

Die Graphik zeigt eine Lösung und zwar 1 5 8 6 3 7 2 4, eine weitere ist 6 4 7 1 8

2 5 3.

Das folgende Programm ermittelt alle 92 Lösungen:

216

Page 217: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

queens 0 = [[]]

queens (m+1) = [ p++[n] | p<-queens m, n<-[1..8], safe p n ]

safe p n = all not [ check (i,j) (m,n) | (i,j) <- zip [1..] p ]

where m = 1 + length p

check (i,j) (m,n) = j==n || (i+j==m+n) || (i-j==m-n)

Hierbei nimmt die Funktion zip aus den beiden Listen [1,..] und p ein Element undmacht daraus ein Paar.Ersetzen wir die zweite Zeile durch

queens (m+1) = [ p++[n] | p<-queens m, n<-[1..8]\\p, safe p n ]

so benötigen wir 40 % weniger Ressourcen, da bereits gefundene Positionen nicht mehrberücksichtigt zu werden brauchen.

12.6 Literatur

R. Bird, Ph. Wadler: Introduction to Functional Programming. Prentice Hall, New York1988

Ch. Clack, C. Myers, E. Poon: Programmieren in Miranda. Prentice Hall, München 1996

P. Thiemann: Grundlagen der funktionalen Programmierung. Teubner, Stuttgart 1994

217

Page 218: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

Literaturverzeichnis

[1] Gert Böhme: Einstieg in die Mathematische Logik. Hanser, München 1981

[2] Volker Claus und Andreas Schwill: Schülerduden Informatik. Dudenverlag, Mann-heim 1997

[3] Herbert Klaeren: Vom Problem zum Programm. Teubner, Stuttgart 1991

[4] Uwe Schöning: Logik für Informatiker. 4. Auflage. Spektrum-Verlag, Mannheim 2000

[5] Ramin Yasdi: Logik und Programmieren in Logik. Prentice Hall, München 1995

[6] Wolfgang Coy: Aufbau und Arbeitsweise von Rechenanlagen. Vieweg, Braun-schweig 1992

[7] Walter Oberschelp und Gottfried Vossen: Rechneraufbau und Rechnerstrukturen.8. Auflage. Oldenbourg Verlag, M"unchen 2000

[8] G. Bohlender, E. Kaucher, R. Klatte, Ch. Ullrich: Einstieg in die Informatik mitPascal. BI–Wissenschaftsverlag, Mannheim 1993

[9] Kathleen Jensen, Niklaus Wirth: PASCAL user manual and report : revised for theISO Pascal standard. 3. Auflage. Springer, New York [u.a.] 1985

[10] Herbert Klaeren: Vom Problem zum Programm. Teubner, Stuttgart 1991

[11] Michael Sonnenschein: Programmieren in PASCAL : Sprachstandard und Turbo-PASCAL. 2. Auflage. H"uthig, Heidelberg 1989.

[12] Turbo Pascal 6.0 Dokumentation: Benutzerhandbuch, Programmierhandbuch, Re-ferenzhandbuch. Borland 1990

[13] Hans Kleine B"uning und Stefan Schmitgen: PROLOG. Teubner, Stuttgart 1988

[14] Uwe Schöning: Logik für Informatiker. 4. Auflage. Spektrum-Verlag, Mannheim 1996

[15] Ramin Yasdi: Logik und Programmieren in Logik. Prentice Hall, München 1995

[16] Richard Bird, Philip Wadler: Introduction to Functional Programming. PrenticeHall, New York 1988

218

Page 219: Informatik A: Rechnerstrukturen und Programmierparadigmen · 2003. 4. 22. · Informatik A: Rechnerstrukturen und Programmierparadigmen Prof. Dr. Norbert Fuhr SS 2003 Universität

[17] Chris Clack, Colin Myers, Ellen Poon: Programmieren in Miranda. Prentice Hall,München 1996

[18] Peter Thiemann: Grundlagen der funktionalen Programmierung. Teubner, Stutt-gart1994

219