Skip to main content

Metalinguistische Abstraktion

  • Chapter
  • 40 Accesses

Zusammenfassung

Als wir uns mit Programmkonstruktion beschäftigten, stellten wir fest, daß Programmierexperten die Komplexität ihrer Konstruktionen mit denselben allgemeinen Techniken unter Kontrolle halten, wie sie von den Konstrukteuren aller komplexen Systeme verwendet werden. Sie kombinieren elementare Einheiten zu zusammengesetzten Ob jekten, sie abstrahieren zusammengesetzte Ob jekte zu Bausteinen auf einer höheren Ebene und sie erhalten Modularität mit einer entsprechend weitgefaßten Sicht der Systemstruktur. Zur Veranschaulichung dieser Techniken haben wir die Sprache Lisp verwendet, um Prozesse zu beschreiben und um Datenob jekte und Rechenprozesse zu konstruieren, die Modelle komplexer Phänomene in der wirklichen Welt sind. Wenn wir uns jedoch zunehmend komplexen Problemen zuwenden, stellen wir fest, daß Lisp und in der Tat jede festgelegte Programmiersprache für unsere Bedürfnisse nicht ausreicht. Wir müssen uns ständig neuen Sprachen zuwenden, um unsere Ideen wirkungsvoller ausdrücken zu können.

... Die Magie liegt in den Worten — wie Abrakadabra und Sesam öffne dich — aber diese Worte sind in der einen Geschichte magisch und in der anderen nicht. Die wirkliche Magie besteht darin zu wissen, welche Worte die richtigen sind und wofür; der Trick ist, den Trick zu lernen.

... Und diese Worte sind aus den Buchstaben unseres Alphabets gemacht: ein paar Dutzend Schnörkel, die wir mit der Feder zeichnen können. Sie sind der Schlüssel! Und der Schatz, wenn es uns nur gelingt, ihn zu fassen! Es ist als ob — als ob der Schlüssel zu dem Schatz der Schatz selber ist!

John Barth, Chimera

This is a preview of subscription content, log in via an institution.

Buying options

Chapter
USD   29.95
Price excludes VAT (USA)
  • Available as PDF
  • Read on any device
  • Instant download
  • Own it forever
eBook
USD   54.99
Price excludes VAT (USA)
  • Available as PDF
  • Read on any device
  • Instant download
  • Own it forever

Tax calculation will be finalised at checkout

Purchases are for personal use only

Learn about institutional subscriptions

Preview

Unable to display preview. Download preview PDF.

Unable to display preview. Download preview PDF.

Referenzen

  1. Die wichtigsten von diesen sind Mechanismen zur Fehlerbehandlung und die Unterstützung der Fehlersuche. 2Trotzdem werden wichtige Aspekte des Evaluationsprozesses auch mit unserem Evaluator unaufgeklärt bleiben. Die wichtigsten davon sind die detaillierten Mechanismen, mit denen Prozeduren andere Prozeduren aufrufen und Werte an die Aufrufenden zurückgeben. Wir werden uns diesen Fragestellungen in Kapitel 5 zuwenden, wo wir uns den Evaluationsprozeß bei der Implementierung des metazirkulären Evaluators als ganz einfache Maschine genauer betrachten.

    Google Scholar 

  2. Wenn wir die Fähigkeit zur Anwendung von elementaren Prozeduren voraussetzen, was bleibt uns dann noch für den Evaluator zu implementieren? Die Aufgabe des Evaluators besteht nicht so sehr darin, die elementaren Bestandteile der Sprache zu bestimmen, als vielmehr darin, die verbindenden Elemente bereitzustellen — die Mittel zur Kombination und die Mittel zur Abstraktion -, die eine lose Menge von elementaren Bestandteilen zu einer Sprache zusammenfügen. Im einzelnen: — Der Evaluator ermöglicht den Umgang mit verschachtelten Ausdrücken. Obwohl zum Beispiel der einfache Mechanismus von anwenden-elementare-prozedur ausreichen würde, um den Ausdruck (+ 1 6) auszuwerten, kann er mit (+ 1 (* 2 3)) nicht umgehen, weil die Argumente der elementaren Prozedur + Zahlen sein müssen, der Ausdruck (* 2 3) als Argument würde ihr im Hals stecken bleiben. Eine wichtige Rolle spielt der Evaluator bei der Choreographie des Zusammenspiels von Funktionen, so daß (* 2 3) auf 6 reduziert wird, bevor es als Argument an + übergeben wird. — Der Evaluator ermöglicht die Verwendung von Variablen. Zum Beispiel kann die elementare Prozedur + nicht mit Ausdrücken wie (+ x 1) umgehen. Wir brauchen einen Evaluator, der sich die Variablen merkt und sie durch ihre Werte ersetzt, bevor er die elementaren Prozeduren anstößt. — Der Evaluator ermöglicht uns die Definition zusammengesetzter Prozeduren. Dazu muß er sich Prozedurdefinitionen merken, er muß wissen, wie diese Definitionen bei der Auswertung von Ausdrücken verwendet werden, und er muß einen Mechanismus bereitstellen, der den Prozeduren die Annahme von Argumenten erlaubt. — Der Evaluator stellt die Sonderformen zur Verfügung, die anders als Prozeduraufrufe ausgewertet werden müssen.

    Google Scholar 

  3. Wir haben uns hier dafür entschieden, daß der Zuweisungsoperator den neuen Wert für die Zuweisung als Ergebnis liefern soll, obwohl das im Lisp-Dialekt Scheme nicht vereinbart ist, dort wird das Ergebnis einer set !-Operation als nicht bestimmt angesehen.

    Google Scholar 

  4. Mit dieser Implementierung von def ine wird ein subtiles Problem bei der Behandlung interner Definitionen nicht berücksichtigt, sie ist jedoch für die meisten Fälle korrekt. Wir werden in Abschnitt 5.2.5 sehen, worin das Problem besteht und wie es gelöst werden kann.

    Google Scholar 

  5. In vielen Lisp-Implementierungen werden auch die Symbole nil und t als selbstauswertend betrachtet. In Scheme sind nil und t gewöhnliche Symbole, die ganz am Anfang in der globalen Umgebung an entsprechende Werte gebunden werden.

    Google Scholar 

  6. Wie in Abschnitt 2.2.3 erwähnt ist diese erweiterte Form des quote die Form, in der der Evaluator die quotierten Ausdrücke sieht, selbst wenn diese Ausdrücke mit dem Anführungszeichen eingegeben wurden. Den Ausdruck ’a zum Beispiel würde der Evaluator in der Form (quote a) sehen. Siehe Übung 2.30.

    Google Scholar 

  7. Die oben beschriebene Darstellung wird tiefe Bindung genannt. Ihr Nachteil ist, daß der Evaluator eventuell viele Rahmen durchsuchen muß, um die Bindung für eine gegebene Variable zu finden. Eine Möglichkeit zur Vermeidung dieser Ineffizienz ist der Einsatz einer Strategie, die lexikalische Adressierung genannt wird. Wir werden diese in Abschnitt 5.3 erörtern.

    Google Scholar 

  8. Die hier gezeigte Schnittstelle zu den elementaren Prozeduren des zugrundeliegenden Lisp ist zwar sehr direkt, aber äußerst unhandlich. Übungen 4.7 und 4.8 zeigen bessere Möglichkeiten, elementare Prozeduren zu handhaben.

    Google Scholar 

  9. Für manche widerspricht es jeglicher Intuition, daß ein Evaluator, der durch eine relativ einfache Prozedur implementiert ist, Programme emulieren kann, die komplexer sind als der Evaluator selbst. Die Existenz einer universellen Evaluatormaschine ist eine tiefgründige und wunderbare Eigenheit, die die Berechenbarkeitstheorie mit sich gebracht hat. Die Rekursionstheorie, ein Zweig der mathematischen Logik, befaßt sich mit den logischen Grenzen der Berechenbarkeit. Douglas Hofstadters wunderschönes Buch Gödel, Escher, Bach (1979) geht einigen dieser Ideen nach.

    Google Scholar 

  10. Vorsicht: Die elementare Prozedur eval ist nicht identisch mit der Prozedur auswerten, die wir in Abschnitt 4.1 implementierten, denn sie verwendet tatsächliche Scheme-Umgebungen statt der Umgebungsstrukturen, die wir in Abschnitt 4.1.3 als Beispiel erstellten. Diese tatsächlichen Umgebungen können vom Benutzer nicht als gewöhnliche Listen behandelt werden; auf sie kann nur über eval oder andere spezielle Operationen zugegriffen werden. In der Implementierung von Scheme am MIT ist user-initial-environment ein Symbol, das an die Anfangsumgebung gebunden ist, in der die vom Benutzer eingegebenen Ausdrücke ausgewertet werden.

    Google Scholar 

  11. Anm. d. Ü.: Hier wird auf das technische Problem hingewiesen, das sich ergibt, wenn die Prozedur des metazirkulären Evaluators anwenden ebenfalls apply heißt (was im amerikanischen Original der Fall ist). Es wird vorgeschlagen, für diese Prozedur einen anderen Namen zu wählen, so daß sich kein Namenskonflikt mit dem Namen des elementaren Operators ergibt, oder (define apply-in-scheme apply) zu verwenden, um auf die Originalversion von apply unter einem anderen Namen zugreifen zu können, und die metazirkuläre Version apply zu nennen.

    Google Scholar 

  12. Anm. d. Ü.: Im amerikanischen Original wird der Begriff ”snarf” verwendet und in der Fußnote wie folgt erklärt: Snarf: ”Schnappen, insbesondere eines großen Dokuments oder einer Datei, um sie mit oder ohne die Erlaubnis des Besitzers zu verwenden.” Snarf down: ”Snarf, manchmal mit dem Beiklang von absorbieren, verarbeiten oder verstehen.” (Diese Definitionen wurden von Steele u.a. 1983 ge” sn arfed”.)

    Google Scholar 

  13. Das ähnelt der Herangehensweise in der Sprache Algol 60, die beide Arten der Übergabe von Argumenten beinhaltet, den Namensaufruf (entsprechend der normalen Reihenfolge) und den Wertaufruf (entsprechend der applikativen Reihenfolge).

    Google Scholar 

  14. Das Wort Thunk stammt von der Implementierung des Namensaufrufs in Algol 60. Wir kennen den Ursprung dieses Namens nicht, aber wir haben gehört, daß er sich auf den Laut bezieht, den Daten von sich geben, wenn sie in einem laufenden Algol-System in den Keller geschrieben werden.

    Google Scholar 

  15. Diese Art der Optimierung ist als Argumentübergabe mit Bedarfsaufruf (engl. call-by-need) bekannt. Der Namensaufruf verursacht Probleme beim Verständnis von Programmen mit Zuweisungen und bei der Kontrolle der räumlichen Komplexit ät von Programmen, aber das ist die Unschuld selbst im Vergleich zu den theoretischen Problemen, die durch Bedarfsaufruf bei gleichzeitigem Vorhandensein von Zuweisungen verursacht werden. Der hervorragende Artikel von Clinger (1982) versucht, die vielfältigen Dimensionen der Verwirrung zu klären, die hierbei auftreten.

    Google Scholar 

  16. In APL werden freie Variablen ebenfalls dynamisch gebunden. In den meisten anderen Sprachen, zum Beispiel in den von Algol 60 abstammenden, wird statisch gebunden.

    Google Scholar 

  17. In der traditionellen Lisp-Literatur ist dieses Einfangen freier Variablen als Abwärts-”Funarg”-Problem bekannt. Es gibt auch das komplementäre Aufwärts-”Funarg”-Probdem, bei dem Prozeduren, die als Werte zurückgegeben werden, die Bindungen ihrer freien Variablen ”verlieren”. Siehe Übung 4.17 als Beispiel.

    Google Scholar 

  18. In Abschnitt 4.3 werden wir sehen, wie es sich mit Modulen vermeiden läßt, daß solche Variablen global sein müssen.

    Google Scholar 

  19. Wir werden sehen, wofür die lets da sind, wenn wir weiter unten die Module im Detail betrachten.

    Google Scholar 

  20. In Kapitel 2 konstruierten wir komplexe Zahlen aus nicht typisierten Zahlen. Hier verwenden wir Zahlen des Typs reell.

    Google Scholar 

  21. Logikprogrammierung hat eine lange Vorgeschichte in der Erforschung von Methoden zum automatischen Beweisen. Frühe Beweisprogramme konnten nur sehr wenig, weil sie den Bereich der möglichen Beweise erschöpfend durchsuchten. Der größte Durchbruch, der eine solche Suche plausibel machte, wurde mit der Entdeckung des Unifikationsalgorithmnus und des Resolutionsprinzips (Robinson 1965) in den frühen sechziger Jahren erreicht. Die Resolution wurde zum Beispiel von Green und Raphael (1968) (siehe auch Green 1969) als Grundlage für ein deduktives Frage-Antwort-System verwendet. In dieser Zeit konzentrierten sich die Forscher meist auf Algorithmen, die mit Sicherheit einen Beweis finden, falls einer existiert. Solche Algorithmen waren schwer unter Kontrolle zu halten und auf einen Beweis hinzusteuern. Hewitt (1969) erkannte die Möglichkeit, die Kontrollstruktur einer Programmiersprache mit den Operationen eines Logiksystems zu kombinieren. Eine eingeschränkte Version seiner Idee wurde von Sussman, Winograd und Charniak (1971) implementiert und wurde als Basis für zahlreiche bedeutende Problemlösungsprogramme verwendet (siehe z.B. Winograd 1971). Zur selben Zeit entwickelte Colmerauer in Marseille regelbasierte Systeme zur Bearbeitung natürlicher Sprache (siehe Colmerauer u.a. 1973). Er entwickelte eine Programmiersprache mit dem Namen Prolog zur Darstellung dieser Regeln. Kowalski (1973; 1979) in Edinburgh erkannte, daß die Ausführung eines Prologprogramms als Durchführung eines Beweises interpretiert werden kann (mit einer Beweistechnik, die lineare Resolution von Horn-Klauseln genannt wird). Die Verbindung der letzteren beiden Strömungen führte zu der aktuellen Bewegung in der Logikprogrammierung. So haben sich die Franzosen mit der Entwicklung von Prolog an der Universität von Marseille Verdienste um die Entstehung der Logikprogrammierung erworben, während die Briten ihre Arbeit an der Universität von Edinburgh hervorheben können. Nach der Behauptung einiger Leute am MIT wurde die Logikprogrammierung von diesen Gruppen entwickelt, als sie herauszufinden versuchten, wovon Hewitts brillante, aber unergründliche Doktorarbeit eigentlich handelte. Zur Geschichte der Logikprogrammierung siehe Robinson 1983.

    Google Scholar 

  22. Um den Zusammenhang zwischen den Regeln und der Prozedur zu zeigen, entspreche das x (wobei x nicht leer sei) in der Prozedur dem (cons u v) in der Regel. Dann entspricht z in der Regel dem append von (cdr x) und y.

    Google Scholar 

  23. Das löst für den Benutzer sicher nicht das gesamte Problem, wie die Lösung berechnet werden soll. Es gibt viele verschiedene mathematisch äquivalente Regelsätze zur Formulierung der Relation append, von denen nur einige in effektive Mittel zur Berechnung in beliebiger Richtung umgewandelt werden können. Außerdemn gibt ”Was-ist”-Information manchmal keinerlei Hinweis auf das ”Wie-geht-das” der Berechnung einer Antwort. Betrachten Sie zum Beispiel das Problem der Berechnung von y, so daß y 2 = x.

    Google Scholar 

  24. Logikprogrammierung erhielt starken Auftrieb, als die japanische Regierung 1981 mit einem ehrgeizigen Projekt begann, das auf den Bau superschneller Computer abzielt, die für Sprachen der Logikprogrammierung optimiert sein sollen. Die Geschwindigkeit solcher Computer soll in LIPS (Logical Inferences Per Second — logische Ableitungen pro Sekunde) gemessen werden statt der üblichen FLOPS (FLoating-point Operations Per Second — Fließkomma-Operationen pro Sekunde). Die einzigen Sprachen, die die Japaner für die Computer der Zukunft für beachtenswert halten, sind Lisp und Prolog. Das hat den größten Teil der Computerindustrie in den USA und die Mehrheit der U.S.-amerikanischen Computerwissenschaftler etwas aus der Fassung gebracht, sie scheinen sich in dem Pascal-PL/1-Ada-Lager verschanzt zu haben.

    Google Scholar 

  25. Tatsächlich gilt diese Beschreibung von not nur für einfache Fälle. Das eigentliche Verhalten von not ist komplexer. Wir werden die Besonderheiten von not in den Abschnitten 4.4.2 und 4.4.3 untersuchen.

    Google Scholar 

  26. Wir lassen auch Regeln ohne Rumpf zu, und wir interpretieren die Bedeutung solch einer Regel so, daß der Folgerung der Regel beliebige Werte der Variablen entsprechen.

    Google Scholar 

  27. Da der Mustervergleich im allgemeinen sehr teuer ist, würden wir es gerne vermeiden, den gesamten Mustervergleicher auf jedes Element in der Datenbank anzuwenden. Das wird gewöhnlich dadurch erreicht, daß der Vorgang in einen schnellen Grobvergleich und in einen abschließenden Feinvergleich aufgeteilt wird. Der Grobvergleich filtert die Datenbank und liefert dabei eine kleine Menge von Kandidaten für den abschließenden Feinvergleich. Mit einiger Sorgfalt können wir unsere Datenbank so arrangieren, daß ein Teil der Arbeit des Grobvergleichs schon erledigt werden kann, wenn die Datenbank aufgebaut wird, und nicht erst, wenn wir die Kandidaten auswählen. Das wird Indizieren der Datenbank genannt. Ein großer Technologiebereich wurde um Indizierungsschemata für Datenbanken herum aufgebaut. Unsere Imnplemnentierung, beschrieben in Abschnitt 4.5.5, enthält eine einfache Form einer solchen Optimierung.

    Google Scholar 

  28. Es besteht ein subtiler Unterschied zwischen dieser Implementierung des not als Filter und der üblichen Bedeutung des not in der mathematischen Logik. Siehe Abschnitt 4.4.3 weiter unten.

    Google Scholar 

  29. Man kann sich die Unifikation auch so vorstellen, daß sie das allgemeinste Muster erzeugt, das eine Spezialisierung der beiden Eingabemuster ist. Das heißt, die Unifikation von (?x a ?y) und (?y ?z a) ist (a a a). Für unsere Implementierung ist die Vorstellung geeigneter, daß das Ergebnis der Unifikation ein Bindungsrahmen ist, und kein Muster.

    Google Scholar 

  30. Beim einseitigen Mustervergleich sind alle Gleichungen mit Mustervariablen explizit und bereits nach der Unbekannten (der Mustervariablen) aufgelöst.

    Google Scholar 

  31. Da die Unifikation eine Verallgemeinerung des Mustervergleichs ist, könnten wir das System vereinfachen, indem wir beide Ströme mit Hilfe des Unifikators erzeugen. Andererseits erfordert der vollausgebaute Unifikationsalgorithmus viel mehr Aufwand als der Mustervergleicher, unser System wird also effizienter arbeiten, wenn wir den einfachen Mustervergleicher einsetzen, wo immer er ausreicht.

    Google Scholar 

  32. Daß eine bestimmte Methode der Ableitung legitim ist, ist keine triviale Aussage. Man muß beweisen, daß aus wahren Prämissen nur wahre Schlußfolgerungen abgeleitet werden können. Die Ableitungsmethode, die die Regelanwendungen darstellen, ist modus ponens, die vertraute Methode der Ableitung, die besagt: Wenn A wahr ist und aus A folgt B wahr ist, dann können wir schließen, daß B wahr ist.

    Google Scholar 

  33. Wir müssen diese Aussage einschränken und zugeben, daß wir die Terminierung einer Berechnung voraussetzen, wenn wir von der von einem Logikprogramm durchgeführten ”Ableitung” sprechen. Leider ist selbst diese eingeschränkte Aussage für unsere Implementierung der Anfragesprache falsch (und auch für Programme in Prolog und in den meisten anderen aktuellen Sprachen der Logikprogrammierung), weil wir not und lisp-value verwenden. Wie wir weiter unten ausführen werden, ist das in der Anfragesprache implemen- tierte not nicht immer konsistent mit dem not der mathematischen Logik, und lisp-value führt zu zusätzlichen Komplikationen. Wir könnten eine Sprache implementieren, die mit der mathematischen Logik konsistent ist, indem wir einfach not und lisp-value aus der Sprache weglassen, und nur noch Programme mit einfachen Anfragen, and und or schreiben. Das würde jedoch die Ausdruckskraft der Sprache weitgehend einschränken. Ein Hauptaugenmerk bei der Erforschung der Logikprogrammierung liegt auf der Suche nach Wegen, mehr Konsistenz mit der mathematischen Logik zu erreichen, ohne allzu viel Ausdruckskraft zu opfern.

    Google Scholar 

  34. Das ist kein Problem der Logik sondern der von unserem Interpretierer bereitgestellten prozeduralen Interpretation der Logik. Wir knnten einen Interpretierer schreiben, der hier nicht in eine Schleife geraten würde. Zum Beispiel könnten wir alle Beweise von unseren Aussagen und Regeln in einer zuerst in die Breite gehenden Reihenfolge ableiten, statt erst in die Tiefe zu gehen. In einem solchen System ist es jedoch schwieriger, aus der Reihenfolge der Ableitungen in unseren Programmen Nutzen zu ziehen. Ein Versuch, eine anspruchsvolle Steuerung in ein solches Programm einzubauen, ist in deKleer u. a. 1977 beschrieben. Eine andere Technik, die nicht zu so ernsten Steuerungsproblemen führt, besteht darin, spezielles Wissen miteinzubauen, wie Detektoren für bestimmte Arten von Schleifen (Übung 4.38). Es kann jedoch kein allgemeines Schema geben, um ein System zuverlässig davor zu bewahren, bei der Durchführung von Ableitungen unendlichen Pfaden zu folgen. Man stelle sich eine diabolische Regel vor, etwa der Form ”Um zu zeigen, daß P(x) wahr ist, zeige, daß P(f(x)) wahr ist”, für eine passend gewählte Funktion f.

    Google Scholar 

  35. Betrachten wir die Anfrage (not (baseballfan (Bitdiddle Ben))). Das System stellt fest, daß (baseballfan (Bitdiddle Ben)) nicht in der Datenbank ist, also entspricht der leere Bindungsrahmen nicht dem Muster und wird nicht aus dem anfänglichen Strom von Bindungsrahmen herausgefiltert. Das Ergebnis der Anfrage ist somit der leere Rahmen, mit dem die eingegebene Anfrage instantiiert wird zu (not (baseballfan (Bitdiddle Ben))).

    Google Scholar 

  36. Eine Erörterung und Rechtfertigung dieser Behandlung des not ist in dem Artikel von Clark (1978) zu finden.

    Google Scholar 

  37. Nehmen wir zum Beispiel an, wir haben ein Prädikat zweiter-vorname?, das entscheidet, ob eine Person einen zweiten Vornamen hat, indem es die Länge der Liste überprüft, die den Namen darstellt. Die Auswertung von (lisp-value zweiter-vorname? (Bitdiddle Ben)) darf nicht versuchen, das Argument (Bitdiddle Ben) als Lisp-Ausdruck auszuwerten, bevor es an zweiter-vorname? weitergegeben wird.

    Google Scholar 

  38. In den meisten Lisp-Systemen kann der Benutzer die gewöhnliche Leseprozedur read durch die Definition von Lesemnakrozeichen so modifizieren, daß sie solche Umwandlungen durchführt. Quotierte Ausdrücke werden bereits in dieser Weise behandelt: Die Leseprozedur übersetzt automatisch ’ausdruck in (quote ausdruck), bevor der Evaluator es zu sehen bekommt. Wir könnten dafür Sorge tragen, daß auf dieselbe Weise ?ausdruck in (? ausdruck) umgewandelt wird; der Klarheit wegen haben wir hier jedoch die Umwandlungsprozedur explizit angegeben.

    Google Scholar 

Download references

Author information

Authors and Affiliations

Authors

Rights and permissions

Reprints and permissions

Copyright information

© 1993 Springer-Verlag Berlin Heidelberg

About this chapter

Cite this chapter

Abelson, H., Sussman, G.J., Sussman, J. (1993). Metalinguistische Abstraktion. In: Struktur und Interpretation von Computerprogrammen. Springer, Berlin, Heidelberg. https://doi.org/10.1007/978-3-662-01163-8_4

Download citation

  • DOI: https://doi.org/10.1007/978-3-662-01163-8_4

  • Publisher Name: Springer, Berlin, Heidelberg

  • Print ISBN: 978-3-540-56934-3

  • Online ISBN: 978-3-662-01163-8

  • eBook Packages: Springer Book Archive

Publish with us

Policies and ethics