Programmierstile - Verarbeitungsmodelle - Programmiersprachen

 

Programmiermethoden

Methoden sind Mengen von Regeln, die man systematisch verwenden kann, um ein Ziel zu erreichen. Programmiermethoden leiten uns bei der Programmerstellung. Die bekannte Methode der Strukturierten Programmierung etwa besagt, daß man zuerst \begin{quote} das Programm top-down zu entwerfen habe, bis man auf das Maschinen- (oder Programmiersprachen-) Niveau gelangt ist \end{quote}\begin{quote} und dann, auf dem Rückwege, die Datenstrukturen synthetisieren muß (indem man die konkreten Datenstrukturen der untersten Ebenen einbringt in die abstrakteren der höheren Ebenen). (R.Floyd 1979, [k2-5])\end{quote} Einem fertigen Programm sieht man i.a. die Methode seiner Herstellung nicht an. Was man ihm jedoch ansieht, das ist der Programmierstil, in dem es geschrieben wurde.

Die KI hat sich bisher nicht tief mit Programmiermethoden beschäftigt. C.Hewitt hat wohl das typische Verfahren des KI-Programmierers geschildert,als er schrieb: ``Einige Autoren haben eine Programmierung von oben nach unten befürwortet. Wir finden, daß unser eigener Programmierstil (besser wohl: Programmiermethode -- H.S.) genauer als `aus der Mitte' beschrieben werden kann. Typischerweise starten wir mit der Spezifikation für eine große Aufgabe und würden sie gern in ein Programm umsetzen. Wir verfeinern diese Spezifikationen, um ein Programm so schnell wie möglich zu erzeugen. Dieser Anfangsversuch, die Spezifikationen zu befolgen, hat den Effekt, uns zu veranlassen, die Spezifikationen auf zwei Arten zu ändern: erstens, mehr Spezifikationen zur Definition der Aufgabe hinzuzufügen (nämlich Elemente, von denen wir ursprünglich nicht ahnten, daß sie relevant sind) und zweitens, die Spezifikationen zu verallgemeinern und neu zu kombinieren, um eine Aufgabe zu beschreiben, die einfacher implementierbar ist und unseren eigentlichen Bedürfnissen mehr entspricht." [k2-8]

Mit dieser Ansicht wird nicht jeder übereinstimmen können. Die in der Einleitung erwähnten Spötter werden witzeln, gerade solche Aussagen bewiesen die Unseriosität der KI. Es dürfte klar sein, daß hier noch viel zu leisten ist, wenn Ergebnisse der KI angewendet werden sollen.

An anderer Stelle [k2-H] hat Hewitt, bezogen auf die ``Aktor Semantik'', folgende Methodik (zunächst als ``evolutionary behavioral programming methodology'', später als ``actor programming methodology'' bezeichnet) beschrieben:

  1. Entscheide über die natürlichen Objektarten, die man im Bereich (engl. domain) haben sollte.
  2. Entscheide für jede Art von Objekten, welche Art von Nachrichten sie empfangen sollte.
  3. Entscheide für jede Art von Objekten, was sie jeweils tun sollte, wenn sie Nachrichten der verschiedenen Arten erhält.

So für sich genommen, kann dies kaum als Programmiermethode akzeptiert werden. Denn in seiner Aktor-Sprache (vgl. Kap. 16) gibt es den Begriff ``Objektart'' nicht -- so daß die Methode schlicht die Auswahl der Aktoren und die Bestimmung ihrer Funktionalität vorschreibt, Handlungen, die das Programmieren selbst naturgemäß mit sich bringt, und die durch ihre Auflistung allein noch keine Methode konstituieren. Es bestehen aber interessante Verwandtschaften zu H.Wedekinds Programmentwicklungsmodell[k2-W]. Dort wird insbesondere versucht, die Frage zu beantworten, auf welcheWeise der Programmierer zu den ``natürlichen'' Objektarten (Wedekindspricht von ``Objekttypen'') kommt.

 

Programmierstile

In ihrem Buch [k2-10] über stilvolle FORTRAN-Programmierung verwenden \linebreak B.Kernighan und P.Plauger den Begriff Programmierstil, um eine Bewertungskategorie für Programme zu haben. Sie sagen, daß ein Programmierer einen ``schlechten Programmierstil'' habe, wenn er etwa

oder

Ähnliches dekretieren andere Autoren (etwa: [k2-14]). Diese Verwendung desBegriffes Programmierstil scheint uns wenig fruchtbar zu sein.

In der Umgangssprache wird das Wort ``Stil'' ähnlich verwendet, wenn wir etwa ausdrücken, daß jemand einen schlechten Stil hat, weil er mit den Türen wirft oder üble Nachrede übt hinter dem Rücken der Betroffenen. Im Gegensatz dazu aber verwenden wir das Wort Stil auch, um systematische Verhaltensweisen oder Erscheinungsformen zu klassifizieren. In der Kunst etwa dient der Begriff ``Baustil'' dazu, Gemeinsamkeiten bei verschiedensten Bauwerken hervorzuheben. Wir sprechen nicht davon, daß eine Kirche in einem schlechten Stil gebaut sei, sondern sagen, sie sei im {\em gotischen Stil} erbaut. Dies schließt nicht aus, daß Puristen Baustile, die nicht ihrem Lieblingsstil entsprechen, als schlecht abtun könnten.Wenn wir einem Bauwerk einen Stil zuordnen, meinen wir keineswegs die Methode der Herstellung -- obwohl sicher in dieser Hinsicht auch gewisse Rückwirkungen bestehen -- sondern wir verstehen diese Bemerkung so, daß die Kirche sichtbare Anzeichen des betreffenden Stils hat.

Wenn wir den Begriff Programmierstil in diesem Sinne verwenden wollen, dann müssen wir Stilelemente bestimmen, die sich an fertigen Programmen nachweisen lassen.

Folgendes Gedankenbeispiel möge dies illustrieren: Selbst in FORTRAN könnte ein Programm weitgehend ohne Zuweisungen geschrieben werden, nur unter Verwendung von Funktionsaufrufen. Wir bezeichnen dies dann als funktions-orientierten Programmierstil. (Diese Verfahrensweise geht in FORTRAN allerdings nicht sehr gut, weil Bedingungen nur mit Anweisungen abgefragt und weil nur numerische Datentypen und Zeichenketten als Werte übergeben werden können.) Andererseits kann man auf jede Verschachtelung verzichten und simple Anweisungen, die allenfalls einen arithmetischen Operator enthalten, hintereinander schreiben. Dann gebraucht man FORTRAN als ``höhere Assemblersprache'' und pflegt den anweisungs-orientierten Programmierstil in Reinkultur. D.Knuth hat 1970 [k2-11] festgestellt, daß dies die vorherrschende Art der FORTRAN-Verwendung ist.

Beide Stilrichtungen können wir leicht an fertigen FORTRAN-Programmen nachweisen, weil die Stilelemente unschwer beschreibbar und abprüfbar sind.

Wir haben nun gar nichts dagegen, daß die Benutzung des Wortes ``Stil'' eine Unbestimmtheit in dem Sinne ausdrückt, daß die Wahl des Programmierstils in gewisser Weise offen ist: Daß neben objektiven Gründen (die Kirche ist kein Bürgerhaus) auch ein ganz wesentlich subjektiver Antrieb diese Wahl bestimmt. Diese Unbestimmtheit ist vielmehr ganz in unserem Sinne, und wir werden sie durch die Bezeichnungsweise ``Stil'' betonen.

Wir haben in Bezug auf die Subjektivität zu konstatieren, daß es in der Wissenschaft Moden gibt, wie auch im gesellschaftlichen Leben, daß diese Moden vielfältige Ursachen haben, und daß die Informatik von diesem Wechsel der Moden keineswegs ausgenommen ist. Mußte man Ende der fünfziger Jahre unbedingt einen Compiler ``gebaut'' haben, so war es kurz darauf die Listenverarbeitung, mit der man die Programmierer hinter dem Ofen hervorlocken konnte. Es folgten die formalen Sprachen, dann die ``integrierten Informationssysteme'', kurz darauf wurde man nicht mehr akzeptiert, wenn man nicht strukturiert programmierte -- dann mußte man unbedingt Programme verifizieren, eine relationale Datenbank verwenden -- und heute ist die Reputation vernichtet, wenn man nicht Expertensysteme entwickelt (oder wenigstens ``wissensbasierte'' Programme).

Nicht viel anders ist es mit den Programmierstilen: Bis mindestens zum Ende der sechziger Jahre war Programmieren gleichbedeutend mit der Planung von sequentiellen Anweisungsfolgen. Man konnte sich schlechterdings nichts anderes vorstellen -- das war die naturgegebene Art zu programmieren. Zwar gab es da um 1960 herum einige ``irregeleitete'' Theoretiker, die meinten, man könne mit Funktionen so programmieren, daß Elemente der Ablaufsteuerung bzw.Speicherverwaltung nicht mehr auftauchten, aber die wurden zum Ende der sechziger Jahre überwunden, belehrt bzw.ignoriert. ``Ketzerische'' Ansichten, wie die C.Greens [k2-7], daß ein Theorembeweiser als Interpreter einer Programmiersprache, des Prädikatenkalküls, anzusehen sei, wurden nicht zur Kenntnis genommen. Die mit LISP [k2-27] oder APL [k2-9] mögliche funktionale (funktions-orientierte) Programmierung war weitgehend bestimmt von Denkweisen der konventionellen Programmierung. Hewitt's Sprache PLANNER [k2-8], die den Zielbegriff in die Programmierung einbrachte, wurde selbst von Insidern als unbrauchbar denunziert: ``Was wir brauchen, ist eine Programmiersprache und kein Theorembeweiser'' (G.Sussman, 1971 [k2-28]). Im Verlauf der siebziger Jahre wurden dann in -- man ist versucht zu sagen, geheimen -- abgegrenzten Zirkeln neue Programmierstile begründet: PROLOG wurde definiert [k2-2] und die logik-orientierte Programmierung popularisiert [k2-12], SmallTalk wurde entwickelt [k2-6] und objekt-orientiert programmiert, die funktions-orientierte Programmierung wurde in LISP neu belebt [k2-29] und wurde populärer (noch nicht zur Mode) durch Backus' Turing Lecture [k2-1],das Expertensystem MYCIN zeigte die Verwendbarkeit einer Mischform von Regeln und Implikationen [k2-21], das Expertensystem R1/XCON [k2-18] machte Produktionsregeln fast zur Mode und heute sind diese Programmierstile verwendbar, ohne daß die ``Modepäpste'' den auf diese abartige Weise Programmierenden ``exkommunizieren'' können.

Ja man hat bisweilen den Eindruck, daß es schon zum guten Ton gehört, ein regel-orientiertes Programm -- unter dem typisch modische Ursachenspiegelnden Namen {\bf Expertensystem} -- geschrieben zu haben.

In der KI besteht noch nicht ganz Einigkeit, ob man in diesen Fällen von ``Wissensrepräsentation'' oder von ``Programmierung'' sprechen sollte. Letzteres wird sicher weniger oft gemacht: Einerseits des vordergründigen Effekts wegen, den das Wort ``Wissensrepräsentation'' hat, andererseits deswegen, weil -- trotz der langen Erfahrung mit der funktions-orientierten Programmierung auch in diesen Kreisen -- Programmieren immer noch mit dem In-Abarbeitungsreihenfolge-Bringen von Aktionen verwechselt wird.

Immerhin sollte aus diesen Anmerkungen klar geworden sein, daß wir mit dem Begriff ``Programmierstil'' nicht den Prozeß der Programmentwicklung charakterisieren wollen, denn dazu dient bereits der Begriff ``Programmiermethoden''. Es könnte zwar sein, daß es Stile von Programmiermethoden gibt, aber das soll uns hier nicht weiter beschäftigen.

Wir akzeptieren demgegenüber gerne, daß ein Programmierstil nurfür oder vor allem für gewisse Phasen des Programmierprozesses angemessen sein kann. So könnte man sich gut vorstellen, daß funktions-orientierte Programmierung in frühen Etappen der Programmentwicklung sehr geeignet ist, während die Optimierung eines fertigen Programms in Bezug auf einen realen (von-Neumannschen) Rechner durch anweisungs-orientiertes Programmieren zu erledigen ist.

Wenn diese Bemerkungen den Eindruck erweckt haben, daß ein neuer Programmierstil an eine neue Programmiersprache gebunden sein muß, dann erinnern wir an unser Beispiel mit FORTRAN: Es zeigt, {\em daß man sich mit ein und derselben Sprache ganz unterschiedlich ausdrücken kann}.

LISP ist ein schönes Beispiel für diese Unabhängigkeit der Programmierstile von der Programmiersprache. Konzipiert als Erweiterung von FORTRAN unter Betonung der Funktionen und Terme [k2-15], wurde die Sprache überraschend durch McCarthy's Bemühen um einen Formalismus fürrekursive Funktionen und S.Russell's Interpreterimplementierung zu einer rein funktions-basierten Sprache [k2-17]. Die Programmierer des Jahres 1959 kamen damit nicht zurecht und mißbrauchten bestimmte Sprachelemente (die bedingten Ausdrücke) für die anweisungs-orientierte Programmierung, die damals Mode war, und die sie gewöhnt waren. McCarthy bequemte sich, Sprachelemente einzubauen, die, statt zum Mißbrauch, zur Symbiose von Anweisungen und Funktionen (Termen) führten. (Erhellend der Titel des Memos [k2-16] ``Programs in LISP'', so als seien vorher keine Programme geschrieben worden!) Die Implementation der Datenstrukturen -- insbesondere die automatische Verwaltung der Listenobjekte -- brachte es mit sich, daß die Variablen nicht mehr als Speicherplätze angesehen wurden, in die Daten zu ``transportieren'' waren, sondern eher als Namen für rechnerinterne Objekte. Die Begrenztheit der Speicher zwang die Programmierer, alte Objekte zu modifizieren. Dies paßte nicht gut in das Funktionsdenkmodell, barg aber in sich die Möglichkeit weiterer Entwicklung. Mindestens einmal hat man angesetzt, durch ein spezielles Speicherverwaltungsschema dies unnötig zu machen. Die Programmierer verwendeten LISP aber für mindestens zehn Jahre als eine Art erweitertes FORTRAN, dachten und programmierten in konventioneller Weise. (Viele tun dies noch heute!)

Nachdem J.Weizenbaum [k2-30] und J.Moses [k2-19] Ende der sechziger Jahre bzw.Anfang der siebziger Jahre auf das Variablenbindungsproblem aufmerksam gemacht und E.Sandewall[k2-20]die Verwendungsmöglichkeiten einer im Sinne der funktions-orientierten Programmierung korrekten Lösung aufgezeigt hatte, begann ein langsamer Revitalisierungsprozeß der funktions-orientierten Programmierung. Dieser führte zu saubereren LISP-Implementationen bzw.-Dialekten (Sussman's SCHEME [k2-29]) und wurde begleitet von einem in der gesamten Informatik spürbaren Trend zur funktions-orientierten Programmierung. War diese Rückbesinnung auf die mit LISP begründete funktions-orientierte Programmierung noch vorhersagbar, so führte A.Kay's originelleAufnahme von SIMULA [k2-6] und die dabei mitvollzogene Verwendung von Grundsätzen des Arbeitens mit abstrakten Datentypen, die in der objekt-orientierten Programmierung Gestalt annahm, zu der Entdeckung, daß ähnliche objekt-orientierte Elemente auch in LISP angelegt waren. Man entwickelte aus den Ansätzen der daten-gesteuerten Programmierung den objekt-orientierten Programmierstil auch in LISP, stellte fest, daß -- in Gestalt der Closures [k2-22,k2-23,k2-29] -- ideale Konstrukte für die Implementierung von Objekten vorhanden waren und suchte nach einer systematischen Methode, neue Objektklassen einzuführen. Diese wurde durch Erweiterung von LISP durch die Flavors (in MacLISP) oder durch direkte Übernahme von Grundkonzepten von SmallTalk (in InterLISP) gefunden. Keiner der Lösungsvorschläge kann schon als endgültig gelten, dennoch wird heute in LISP durchaus objekt-orientiert gedacht und programmiert [k2-27].

Wir schließen daraus, daß eine Programmiersprache sich zum Programmieren in verschiedenen Stilen eignet. Vielleicht kann man sogar sagen, daß eine Programmiersprache umso erfolgreicher ist, je mehr Programmierstile sie ermöglicht. Natürlich kann man, wenn man einen neuen Programmierstil im Auge hat, eine ihm besonders angemessene Programmiersprache entwickeln. Wie das aber mit formalen Systemen so ist, kann diese Sprache oft nicht nur in der antizipierten Weise verwendet werden. Je nach Einstellung kann man vom Mißbrauch oder von der schöpferischen Aneignung sprechen. Wir glauben, daß z.B.die Verwendung von PROLOG zur Programmierung von rein sequentiellen Ein/Ausgabeprogrammen durch FORTRAN-ähnliche Anweisungsfolgen, die man durch die Verwendung des berüchtigten Cut erreichen kann, ein Mißbrauch dieser Sprache ist.

Es sollte aber keineswegs verwundern, daß nicht jede Programmiersprache \linebreak gleich flexibel umgedeutet werden kann. Die Ursachen dieser verschiedenen Grade der Umdeutbarkeit haben gewöhnlich aber wenig mit der Konzentration auf den beabsichtigten Stil zu tun: Hätte FORTRAN ein größeres Spektrum an Datentypen, und könnten diese als Werte von Funktionen übergeben werden, so wäre zum Beispiel objekt-orientierte Programmierung möglich. Die funktions-orientierte Programmierung wäre dann immer noch beeinträchtigt durch den Mangel an bedingten Ausdrücken. Die sind wieder in ALGOL-60 vorhanden -- dort istreine funktions-orientierte Programmierung möglich -- sogar rekursive Funktionen sind erlaubt. (Allerdings können komplexe Datentypen kaum frei verwendet werden.)

 

Verarbeitungsmodelle

Die Frage ist nun natürlich, worin das Wesen eines Programmierstils beruht -- oder, deutlicher formuliert, welche Überlegungen einen Programmierer zu einem Stil führen. Woran hat man zu denken, wenn man den Programmierstilwechseln will?

Unsere Hauptthese zu dieser Frage ist:

\vspace{3mm}

\noindent{\em Ein Programmierstil basiert auf einer (möglicherweise spekulativen) Vorstellung von einem ``Rechner'' (einem ``Verarbeitungsmodell''), der die Programme abarbeiten soll.}

\vspace{3mm}

Dieser ``Rechner'' kann ein völlig theoretischer (selbst nicht-effektiver) Kalkül (Maschine, Mechanismus, Gerät) sein, der -- im Extremfall -- prinzipiell nicht realisierbar ist und auch nicht als Basis für ein einsatzfähiges Verarbeitungsmodell (oder eine Algorithmentheorie bzw.eine Theorie berechenbarer Funktionen) dienen kann.

So kann man sich einen universellen Beweiser, das ist ein Pogramm, das jedes Theorem der Prädikatenlogik in vernachlässigbarer Zeit beweisen kann, gut vorstellen -- allerdings kann es ein derartiges Programm nicht geben.

Beispiele für solche Modelle sind: \begin{description} \item[(a)] Das konventionelle Verarbeitungsmodell ({\em von-Neumann Maschine}). Es verarbeitet Folgen von intern repräsentierten Anweisungen durch schrittweises Auslösen von Aktionen, die den Maschinenzustand verändern. Die Maschine verfügt über einen Datenspeicher, auf den die Aktionen wirken: Durch Transport, Verknüpfung, Erzeugung, Vernichtung und Vergleich von Daten.

\item[(b)] Das allgemeine funktionale Verarbeitungsmodell. Es geht vom mathematischen Funktionsbegriff aus. Funktionen in diesem Sinne sind eindeutige Abbildungen von einem Definitionsbereich in einen Wertebereich. Sowohl Definitions- als auch Wertebereich existieren abstrakt -- unabhängig davon, in welcher Reihenfolge etwa Paare, die die funktionale Beziehung erfüllen, ausgewählt werden. Der Mathematiker sieht die Funktion extensional, d.h.als Menge aller dieser Paare, abstrakt gegeben an. Dem ``Rechner'' werden z.B.Funktionsnamen mit Argumenten übermittelt. Er liefert Werte zurück. Die konkrete Realisierung einer derartigen ``Funktionsanwendung'' ist nicht von Interesse: Im Idealfall ist der Wert unmittelbar gegeben.

Dieses Modell ist nur vorstellbar, solange man sich nicht zu genau in die Einzelheiten einläßt: Der ``Rechner'' hat sicher in der einen oder anderen Form Beschreibungen von Funktion und Argumenten zu verarbeiten. Nun kann man mit Namen nur abzählbar viele Funktionen ansprechen; nur endlich viele, wenn man den Menschen einbezieht und eine maximale Länge vorgibt. Es gibt aber überabzählbar viele Funktionen. Von dem ``Rechner'' ist also nur ein kleiner Teil benutzbar -- der überaus größte Teil liegt immer brach. Es scheint nicht unpassend, die Kapazität des ``Rechners'' gleich auf die abzählbar vielen berechenbaren Funktionen einzuschränken.

\item[(c)] Das allgemeine relationale Verarbeitungsmodell. Es geht vom mathematischen Relationsbegriff aus. Relationen in diesem Sinne sind Teilmengen von Produkten aus mehreren Mengen (die natürlich selbst Mengenprodukte sein können), d.h.Mengen von Tupeln. Alle beteiligten Mengen existieren abstrakt -- unabhängig davon, in welcher Reihenfolge etwa Tupel, die in der Relation liegen, ausgewählt werden. Der Mathematiker sieht die Relation extensional, d.h. als Menge aller dieser Tupel, abstrakt gegeben an. Dem ``Rechner'' werden z.B. Relationsnamen mit Argumenten übermittelt. Er liefert einen bestimmten Wert (für den Wahrheitswert ``wahr'') zurück, wenn das Tupel in der Relation liegt.\footnote{Prinzipiell müßte dazu die Relation entscheidbar sein; wir wollen aber hier annehmen, daß die fiktive Maschine jede Relation so behandeln kann.} Eine andere Verwendung besteht in der Übermittlung von Relationsnamen mit nur teilweise spezifizierten Argumenttupeln. In diesem Fall wird nach Tupeln gesucht, die die spezifizierten Argumente enthalten. Gibt es solche, dann werden die in ihnen stehenden weiteren Werte als Werte der freigehaltenen Tupelkomponenten angesehen und eventuell in der einen oder anderen Form ausgegeben.

\item[(d)] Das Beweisermodell. Es basiert auf der logischen Deduktion und behandelt logische Formeln. Eine Menge von Formeln werden als Axiome verwendet und mit ihnen wird eine weitere Formel, die wir als Theoremkandidat ansehen können, bewiesen oder widerlegt. Wenn die Formel, die ähnlich wie ein Term im Falle des funktionalen Verarbeitungsmodells das Beweisermodell aktiviert, noch freie Variablen enthält -- sie stellt dann keine Aussage im eigentlichenSinne dar, sondern ein Aussagenschema --, dann werden Werte bestimmt, fürdie die Formel beweisbar ist. Ähnliches wäre auch denkbar für Formeln mit Existenzquantor, für die konkrete Objekte bestimmt werden könnten, so daß entsprechende Terme, die in die Formel ohne Existenzquantor für die gebundenen Variablen eingesetzt werden, die Formel beweisbar machen.

Wir sehen, daß wir schnell bereits sehr technische Fragen ansprechen, die in der Übersicht eigentlich keinen Platz haben. Soviel sollten wir aber schon anmerken, daß das Beweisermodell nur bei bestimmten Eigenschaften der logischen ``Programmier''-Sprache realisierbar ist: Liegt eine Prädikatenlogik erster Stufe vor, so kann es keinen universellen Beweiser geben, der sowohl die Deduzierbarkeit der Theoreme und die Widerlegbarkeit der Formeln, die keine Theoreme sind, bestimmen kann. Auch die Axiomensysteme -- d.h. die Programme -- haben erheblichen Einfluß auf die Leistungsfähigkeit des Verarbeitungsmodells, wie man seit Gödel weiß.

Abgesehen von den prinzipiellen Schranken haben die konkreten Modellrealisierungen unterschiedliche Eigenschaften, die sich z.B. in verschiedener Effizienz bezüglich gleicher Formelmengen (Programme) zeigen.

\item[(e)] Der allgemeine Problemlöser. Der allgemeine Problemlöser verarbeitet Problemspezifikationen. Ein Problem wird in einer bestimmten Art - die wir hier offenlassen - beschrieben, und der Problemlöser produziert die (oder eine) Lösung. Die in der KI bisher betrachteten Problemlöser akzeptierten Beschreibungen (einfacher) Probleme, die durch Angabe einer Anfangssituation, eines Zielprädikats und erlaubter Operatoren beschrieben wurden.

Man kann gut das gesamte Ziel der KI (oder wenigstens einen guten Teil davon) dadurch zusammenfassend beschreiben, daß man sagt, der KI gehe es um die Realisierung solcher Problemlöser-Verarbeitungsmodelle. Deshalb ist die Vielfalt der in der KI entwickelten Verarbeitungsmodelle nicht zufällig: Sie sind bei der Annäherung an das Problemlösermodell entwikkelt worden. \end{description}

Wie man sieht, besteht ein Unterschied zwischen den Modellen (a) - (c) und (e): Während die ersten mathematische Modelle sind, deren Äquivalenz die mathematische Logik bewiesen hat, handelt es sich bei letzterem nicht unbedingt um ein ähnlich umfassendes Modell. Es ist für unsere Überlegungen immer wichtig, wenn festgehalten werden kann, daß ein Modell universell ist, d.h.alle berechenbaren Funktionen realisiert. Doch sind wir auch an eingeschränkten Verarbeitungsmodellen interessiert, die für einen Anwendungsbereich besonders einfach und angepaßt sind.

Weitere Verarbeitungsmodelle werden in dem vorliegenden Buch besprochen. Viele von ihnen sind mit mathematischen Ansätzen zur Bestimmung des Bereichs der berechenbaren Funktionen verbunden.

 

Programmiersprachen

Programmiersprachen sind formale Systeme (Kalküle), die ein bestimmtesVerarbeitungsmodell widerspiegeln. Sie bieten Notationsmittel für die Grundelemente eines Verarbeitungsmodells an, sowie {\em Mittel zur Komposition} dieser Elemente zu ganzen Programme. Dabei sind diese Programme höchst unterschiedliche Gebilde.

So haben anweisungs-basierte Programmiersprachen, die sich auf das konventionelle Verarbeitungsmodell beziehen, Mittel anzubieten, mit denen Programme als sequentielle Pläne für Aktionenfolgen beschrieben werden können. Da solche Rechner zyklische Abarbeitung von Anweisungsstücken ermöglichen, muß die Programmiersprache Ausdrucksmittel dazu bereitstellen. Häufig wiederkehrende Muster von Aktionsfolgen werden zu sog.Prozeduren (Unterprogrammen) zusammengefaßt. Diese Zusammenfassung ermöglicht die Abstraktion durch Betonung der Äquivalenz des Ein-/Ausgabeverhaltens dieser Prozeduren: Verschiedene Prozeduren, die gleiches Ein-/Ausgabeverhalten haben, werden in eine Äquivalenzklasse angeordnet, unabhängig davon, welche konkrete Folge von Anweisungen zur Realisierung dieses Verhaltens dient [k2-27]. Dazu muß die Programmiersprache Ausdrucksmittel enthalten.Die bekannten Programmiersprachen bieten keine Mittel zur Beschreibung der Abstraktion (d.h.der Ein-/Ausgabebedingungen), sondern erlauben nur dieFormulierung einer Prozedur aus dieser Klasse. Um dennoch die Zufälligkeit der Auswahl dieser Prozedur (Funktionalität ist wichtig, nicht Implementation) sichtbar zu machen, wählt man gewöhnlich Prozedurnamen, die die geltenden Ein-/Ausgabebedingungen suggerieren.

Weiterhin muß es Zeichenfolgen zum Ansprechen der {\em primitiven Operationen} geben (Datenmanipulation), zum Einlesen von Daten und Ausgeben der Ergebnisse.

Der sich auf dieses Verarbeitungsmodell beziehende Programmierer benötigt in seiner anweisungs-basierten Programmiersprache

Im Falle des funktionalen ``Rechners'' steht die Frage nach der Funktionsbeschreibung im Mittelpunkt. Eine verwendbare Programmierspracheenthält nicht lediglich ein umfängliches Verzeichnis verwendbarer Funktionen (obwohl in einzelnen Fällen 40000 Funktionen und mehr so aufgesucht werden können), sondern Hilfsmittel zur {\em Konstruktion neuer Funktionsbezeichnungen}. Gewöhnlich versteht der Programmierer dieses Hilfsmittel so, als ob es ihn zur Definition neuer Funktionen befähige. Für Mathematiker dagegen existiert die Funktion jedoch schon immer. So wäre es besser zu sagen, daß nicht neue Funktionen definiert, sondern neue Beschreibungen für gewisse Funktionen konstruiert werden.

Gemäß dem mathematischen Begriff hat dieses Konstruktionshilfsmittel zwei \linebreak Komponenten zu enthalten: Eine zur Konstruktion von Mengenbeschreibungen und eine zur Erzeugung neuer Vorschriften für Abbildungen. Letztere könnte als Paarmenge beschrieben werden. Angemessener erscheint ein Vorgehen, bei dem man die Abbildungsvorschrift dadurch beschreibt, indem man Grundausdrucksmittel bereitstellt (Namen von gewissen vorgegebenen Grundfunktionen) und Konstruktionsmittel (Termverschachtelung). Mannimmt gewöhnlich an, daß die beteiligten Mengen (Definitions- und Wertebereich) beim Aufbau der Abbildungsvorschrift implizit mit beschrieben werden.

Oft kann dieselbe Funktion mit verschieden konstruierten Abbildungsvorschriften beschrieben werden. Man möchte jedoch wieder die Äquivalenz des Ein-/Ausgabeverhaltens zur Abstraktion nutzen. Um diese beabsichtigte Abstraktion zu unterstützen, sollte eine funktions-basierte Sprache Ausdrucksmittel bereitstellen. Diese können auch zur Einführung von Kurzbezeichnungen für Konstruktionsvorschriften dienen.

Programme für den funktionalen ``Rechner'' sind Funktionsbeschreibungen (und eventuell Beschreibungen von Definitions- und Wertebereich). Wenn der Wert einer Funktion für ein bestimmtes Element des Definitionsbereichs ermittelt werden soll, so muß man die Funktion auf dieses Element anwenden. Dafür notiert man gewöhnlich Terme, d.h.Ausdrücke aus Funktions- und Argumentbeschreibung.

Die durch die Programmiersprache bestimmte Notation für solche Funktionsanwendungen, oft ``Ausdruck'' (auch ``Applikation'') genannt, repräsentiert für den in diesem Modell denkenden Programmierer den Wert und {\bf nicht} einen Prozeß, der zur Ermittlung des Wertes führt.

Der diesem Verarbeitungsmodell folgende Programmierer wird konsequent einen Stil verfolgen, in dem die konkrete Reihenfolge der Aktionen, die eine konventionelle Maschine durchführt, um den Wert zu berechnen, keine Rolle spielt. Er benötigt deshalb eine Programmiersprache, die folgendes bereitstellt:

Dieser ``Programmier''-Stil ohne jede Bezugnahme auf Abarbeitung und Operation stellt natürlich einen Extremfall dar. Dennoch läßt sich so programmieren, und es gibt entsprechende Programmiersprachen (J.Backus [k2-1],M.Broy [k2-4]).\newpage

Übersicht

Wir verwenden also drei Begriffe aufeinander bezogen: {\em Programmierstil, Programmiersprache} und Verarbeitungsmodell. Dabei begründet die Vorstellung von einem Verarbeitungsmodell den Programmierstil und dieser kann durch Programmiersprachen unterstützt bzw.behindert werden. In der KI wirdzur Beschreibung der entsprechenden Zusammenhänge meist der Terminus ``Programmierparadigma'' [k2-3] (engl. programming paradigm) benutzt. Dieser Terminus hilft aber nicht, die Zusammenhänge zu erklären, er bereichert nicht \ldots , er bleibt nur an der Oberfläche. Verbunden mit dem T.Kuhn'schen Begriff des wissenschaftlichen Paradigmas\footnote{Kuhn hat in einer Theorie des Wissenschaftsfortschritts die Auffassung dargelegt, die Wissenschaft folge gewissen Denkmodellen, solange die Autorität der Vertreter dieser Denkmodells groß genug sei, und solange das Denkmodell geeignet erscheint. Im ``Untergrund'' werde von Konkurrenten der Wortführer ein neues Denkmodell erdacht, vorgeschlagen und erprobt. In einem revolutionären Akt verdränge es das alte Modell, wenn dieses (wissenschaftlich und ``politisch'') ``angeschlagen'' ist. Die Wissenschaft schreite so von Revolution zu Revolution.} [k2-13] hat er darüber hinaus auch den Nachteil, daß er die bezeichneteSache zu einer erstrebenswerten macht: Wer möchte nicht eine wissenschaftliche Revolution auslösen? Es wundert nicht, wenn demzufolge die Paradigmen willkürlich etabliert werden, und wenn ihre Zahl recht groß ist. Dies wiederum ist ein sicheres Anzeichen dafür, daß die Qualität dieser Paradigmen von den von Kuhn gemeinten sehr verschieden ist. Eine Bezeichnungsweise, die diese Gefahr vermeidet und deutlich eine besondere Eigenart der Programmierung beschreibt, erscheint hier hilfreich.

Darüber hinaus hat Floyd in seiner Turing-Lecture [k2-5] den Begriff Programmierparadigma inflationär entwertet, indem er ihn auf alles anwandte, was denkbar (d.h.benennbar) ist. Die Lektüre dieser Arbeit sollte jedermann davon abhalten, den Terminus ``Programmierparadigma'' zu verwenden. Wir schlagen vor, das Wort ``Paradigma'' nur dann zu verwenden, wenn wirklich ein einzigartiges Denkmodell -- oder ein Gedankenmodell, das eine umfassende wissenschaftliche Problematik spiegelt -- gemeint ist.

Wir wollen systematisch bei der Benennung eines Programmierstils das Beiwort orientiert verwenden. Dies hat sich einerseits längst eingebürgertund unterstreicht andererseits eine sonst übersehene Eigenart eines Stils: Daß es sich hier immer um Tendenzen, um Annäherungen an ein Optimum, um Leitenlassen zu einem Ideal handelt. Hier kommt auch wieder der schon erwähnte subjektive Charakter ins Spiel. Bei der Benennung der entsprechenden Programmiersprachen und der Verarbeitungsmodelle wollen wir das Beiwort basiert einsetzen, um auf die wesentliche Rolle des Konstrukts hinzuweisen,das dem Modell und der Sprache den Namen gibt.

Für die Dreiheit der Begriffe bedienen wir uns immer typischer Elemente oder Konstrukte, deren Verwendung für den Stil bezeichnend ist:

\vspace{3mm}

Anweisung -- Funktion -- Relation -- Regel -- Objekt -- Plan -- ...

\vspace{3mm}

Nur im Falle von PROLOG und verwandten Sprachen wollen wir die ganze Klasse der Formalismen ansprechen (aus Traditionsgründen). Wir verwenden in diesem Falle die Bezeichnung logik-basierte Sprache.\begin{description} \item[Anweisungs-basierte Sprachen] sind alle sog.``algorithmischen'' Sprachen.\item[Funktions-basierte Sprachen] sind LISP und APL. \item[Regel-basierte Sprachen] sind die OPS-Sprachen. \item[Objekt-basierte Sprachen] sind SmallTalk, Act-1 und ObjTalk. \item[Relation-basierte Sprachen] liegen den verschiedenen ``Constraint''-Systemen zu Grunde. \end{description}

Wenn wir die Verarbeitungsmodelle entsprechend wachsender Entfernung von der von-Neumann'schen Maschine (d.h.mit kleiner werdendem Einfluß derAblaufsteuerung) in ein Schaubild bringen, entsteht etwa folgendes Bild:

\vspace{5mm}

\begin{tabular}{|l|l|l|l|l|} math. & math. & Prädikaten- & & Problem- \\ Funktionen- & Relationen- & Logik (univ. & & löser \\ konzept & konzept & Beweiser) & & \\ Backus' & & reine & &\\ Modell & & Ziel-orient. & &\\ & & Modelle & Post'sche &\\ & & & Produk- & \\ & & & tionen &\\ Term- & Constraint- & PROLOG & Markov- & GPS \\ Reduktions- & Systeme & (Resolution- & Algorith- &\\ Kalküle & & Beweiser) & men &\\ & & PROLOG & Produk- &\\ & & (prozedur. & tionen &\\ & & Modell) & Systeme &\\hline\multicolumn{4}{c}{von-Neumann Maschine, (menschl. Rechner) etc.}\\ \end{tabular}