Programmiersprache
Eine Programmiersprache ist eine künstlich geschaffene Sprache (formale Sprache) zur Darstellung von Berechnungen. Programmiersprachen sollen Berechnungen sowohl in einer für einen Computer, als auch in einer für den Menschen lesbaren und verständlichen Form ausdrücken. Sie sind notwendig, da die natürlichen Sprachen für eine genügend detaillierte und präzise Beschreibung von Computerberechnungen zu vieldeutig sind.
Die durch eine Programmiersprache ausgedrückte, von einem Menschen lesbare Beschreibung heißt Quelltext. Den Arbeitsprozess diese Computerprogramme zu erstellen, nennt man Programmieren. Er wird durch Programmierer vorgenommen. Der entstandene Programmcode muss schließlich in eine Anweisungsfolge für den Computer, die Maschinensprache übersetzt werden. Dies ist vergleichbar mit dem Übersetzen von natürlichen Sprachen, jedoch ist dieser Arbeitsschritt durch die Verwendung von Programmiersprachen mittels eines Übersetzungsprogramms automatisierbar. Das Endprodukt dieser Übersetzung nennt man Computerprogramm. Je nachdem, ob diese Übersetzung vor oder während der Ausführung des Computerprogramms erfolgt, unterscheidet man zwischen kompilierenden oder interpretierenden Übersetzungsprogrammen.
Es existieren verschiedene Meinungen, welche Eigenschaften eine Programmiersprache besitzen sollte. Allgemein wird jedoch akzeptiert, dass zumindest die grundlegende mathematische Arithmetik ausgedrückt werden können sollte. Oft erscheint der von der Programmiersprache vorgegebene Programmierstil und die Zweckgebundenheit der Programmiersprache wichtig. Eine theoretische Erkenntnis ist die notwendige Eigenschaft der Turing-Vollständigkeit, falls sie die volle Funktionalität des Computers ausnutzen soll; dies kann bis hin zum sich selbst verändernden Programm dienen.
Aspekte einer Programmiersprache
Form und Funktion
Die äußere Form, in der sich eine Programmiersprache dem Programmierer repräsentiert, bezeichnet man als Syntax. Die meisten Programmiersprachen sind textbasiert. Der Quelltext besteht aus Wörtern und Trennzeichen, ganz ähnlich zu geschriebenen natürlichen Sprachen. Es sind jedoch auch andere Repräsentationen von Programmiersprachen denkbar. So werden häufig zur Gestaltung von graphischen Benutzerschnittstellen visuelle Symbole verwendet. Ähnlich zu einem Notenblatt in der Musik ergibt sich aus den einzelnen Symbolen mit ihren individuellen Bedeutungen ein Dokument, das eine Gesamtheit beschreibt.
Die Bedeutung eines speziellen Symbols in einer Programmiersprache nennt man dessen Semantik. Syntax und Semantik kann man der Spezifikation, teilweise auch der Dokumentation der Programmiersprache entnehmen. Die syntaktische Definition einer Sprache wird meist in der formalen Notation Backus-Naur-Form angegeben. Eine vollständige semantische Spezifikation einer Programmiersprache in einem einzigen Kalkül ist gegenwärtig Forschungsgegenstand.
Die Intention des Programmierers über die Gesamtheit des Computerprogramms nennt man Pragmatik. Mit dem Analogon des Notenblatts beschrieben, beabsichtigte der Kompositeur beispielsweise eine Hymne oder einen Popsong zu schaffen. Die Softwaretechnik hilft dabei dem Programmierer diesen Zweck eines Computerprogramms zu realisieren.
Siehe dazu auch: Semiotik.
Die derzeit am häufigsten verwendeten Programmiersprachen halten eine spezielle Unterscheidung in Syntax und Semantik für Befehle (oft auch Anweisungen genannt) und Daten und deren Wiederverwendung bereit. Dies hat sich historisch entwickelt, da prinzipiell ein Computer diese Strukturen nicht unterscheiden kann.
Befehle
Konzeptionell ist nämlich ein Computer weit mehr als eine Sammlung von Daten und auch keine starre Rechenmaschine. Vielmehr wird angegeben, wie der Computer mit variablen internen oder externen Daten zu verfahren hat. Elementare Anweisungen geben über Schlüsselwörter (key words) an, WANN WAS WIE geändert werden soll. Jede Sprache enthält eine WENN-DANN-artige Anweisung, die letztlich zusammen mit dem Sprungbefehl (Goto) die Universalität bereitstellt, um allgemein auf vielfältigste Fälle reagierende Programme schreiben zu können. Falls Konstrukte für Schleifen mit Bedingungen (Bedingung zuerst, dann Anweisungsgruppe, oder: erst Anweisungsgruppe und dann Bedingung) zur Verfügung stehen, kann auf den Sprungbefehl vollständig verzichtet werden. Niklaus Wirth hat dies mit Modula-2 gezeigt.
Die meisten Befehle lassen sich somit in folgende Kategorien einteilen:
- Eingabe (input)
- Daten von der Tastatur, von einer Datei oder aus anderen Quellen übernehmen.
- Ausgabe (output)
- Daten ausgeben: auf den Monitor, auf einen Drucker oder in eine Datei
- Mathematische Berechnung
- Eine mathematische Berechnung, wie Addition, Multiplikation oder Ähnliches, durchführen.
- Bedingte Verzweigung
- Bestimmte Bedingungen überprüfen und anhand des Ergebnisses entscheiden, welche Befehle als nächste ausgeführt werden.
- Wiederholung oder Schleifen
- Eine Befehlsfolge wiederholt durchführen, meist mit einer gewissen Variation.
- Wiederholen von Programmteilen abhängig von Bedingungen
- Umgang mit nicht mathematischen Elementen
- Blockbildung
- Zusammenfassung mehrerer Befehle, zum Beispiel einer Schleife, in einem Unterprogramm
- Arbeiten mit Variablen
- Zuweisen, Auslesen und Ändern von Speicherinhalten
Daten
Um die üblichen Arten von Information im Computer abzubilden, müssen Möglichkeiten zur Definition von Daten oder Datenstrukturen bereitstehen, die auch als Datentyp bezeichnet werden. Hierbei kann zwischen typisierten (zum Beispiel C++ oder Java) und typenlosen Sprachen (zum Beispiel Tcl oder Prolog) unterschieden werden. Bei typisierten Sprachen sind dies entweder vordefinierte Einheiten für einzelne Zahlen (Byte, Integer, Word, etc.) und Zeichen (Char) oder auch zusammengesetzte für Daten, Wörter, Text, sensorische Information und so weiter (Strukturen, Klassen). Zumeist besteht auch die Möglichkeit, zusammengesetzte Objekte oder Strukturen aufzubauen, und als neuen Typ zu vereinbaren (etwa Arrays, Listen, Stacks, ganze Dateien). Die typenlosen Sprachen behandeln oftmals alle Einheiten als Zeichenketten und kennen für zusammengesetzte Daten eine allgemeine Liste (zum Beispiel Perl).
Bei den typisierten Sprachen gibt es solche mit Typprüfungen zur Übersetzungszeit (statisch typisiert) und solche in denen Typprüfungen primär zur Laufzeit stattfinden (dynamisch typisiert, etwa Smalltalk, Ruby). Werden Typfehler spätestens zur Laufzeit erkannt, spricht man von typsicheren Sprachen. Oft wird fälschlicherweise die statische Typprüfung wegen des angenommenen qualitativen Vorteils gegenüber der dynamischen Typprüfung als „sicher“ bezeichnet.
Es kann keine allgemeine Aussage über die Tauglichkeit beider Formen der Typprüfung getroffen werden – bei statischer Typprüfung ist der Programmierer versucht, diese zu umgehen, beziehungsweise sie wird erst gar nicht vollständig durchgesetzt (zum jetzigen Stand der Technik muss es in jeder statischen Sprache eine Möglichkeit geben, „typlose“ Daten zu erzeugen oder zwischen Typen zu wechseln – etwa wenn Daten vom Massenspeicher gelesen werden), in Sprachen mit dynamischer Typprüfung werden manche Typfehler erst gefunden, wenn es zu spät ist. Bei dynamischer Typprüfung wird jedoch der Programmcode meist sehr viel einfacher.
Oft kann an den „Bürgern erster Klasse“ („First Class Citizens“ – FCCs) einer Programmiersprache – also den Formen von Daten, die direkt verwendet werden können – erkannt werden, welchem Paradigma die Sprache gehorcht. In Java (objektorientiert) zum Beispiel sind Objekte FCCs, in LISP (funktional) ist jedes Stück Programm FCC, in Perl sind es Zeichenketten, Arrays und Hashes.
Bezugnahmemechanismen und Wiederverwendung
Der Kern der Bezugnahme ist die Benennung von Speicherplatz als so genannte Variable. Weitere Bezugnahmen sind Zeiger auf solche Variablen oder Variablengruppen. Auch Befehlsgruppen werden im Programm per Namensaufruf als Prozedur oder Funktionsaufruf verfügbar gemacht. Auf diese Weise wird durch Symbole einerseits eine große Variabilität erreicht, und durch Referenz auf vorhandene Programm- oder Datenteile andererseits ein hohes Maß an Wiederverwendbarkeit erreicht. Viele anwendungsbezogene Sprachen integrieren typische Aufgaben als aufrufbare Befehle.
Geschichte
Anfänge
- „Erstes Programm“ (Ada Lovelace), ~1840
- Lambda-Kalkül (Stephen Cole Kleene und Alonzo Church), ~1935
- Die natürlichen Zahlen (Alan Turing, Kurt Gödel et al., siehe Gödelnummerierung, Standardnummerierung, Berechenbarkeit), ~1936
- Plankalkül (Konrad Zuse), ~1945
Erste Arbeiten im Bereich der Programmierung stammen von Ada Lovelace (1815–1852, einer Assistentin von Charles Babbage), die als erste das Prinzip der variablen Programmierbarkeit erkannte. Da Babbages Rechenmaschine aber auf dem damaligen Stand der Mechanik nie zu Ende gebaut werden konnte, sah Lovelace ihre Ideen noch nicht im praktischen Einsatz.
Dies entwickelte sich erst mit Entwicklung der ersten elektronischen Rechenmaschinen und der Verwendung der booleschen Algebra spürbar weiter. Hervorzuheben sind diesbezüglich etwa 1937 die Patente von Konrad Zuse, welche eine Architektur beschreiben, die später als Von-Neumann-Maschine bekannt wird sowie in den 40er Jahren die Fertigstellung des Plankalküls durch Zuse.
Die 50er Jahre: Die ersten modernen Programmiersprachen
In den 50er Jahren des letzten Jahrhunderts wurden in den USA die ersten drei weiter verbreiteten, praktisch eingesetzten höheren Programmiersprachen entwickelt:
- LISP (LISt Processor) von John McCarthy et al., 1959
- FORTRAN (FORmula TRANslator) von John Backus et al., 1954
- COBOL (COmmon Business Oriented Language) von Grace Hopper et al., 1959
Die vorgenannten Sprachen existieren mit ihren Nachfolgern bis heute. Vor allem LISP beeinflusste die später an amerikanischen Universitäten entwickelte Programmiersprachen stark.
Der nächste grössere Meilenstein wurde zwischen 1958 und 1960 gesetzt, als ein internationales Komittee während einer Tagungsreihe eine „neue Sprache für Algorithmen“ entwarf. Dies mündete in die Entwicklung von Algol 60 (ALGOrithmic Language). In einem Tagungsbericht wurden viele Ideen, die zu dieser Zeit in der Fachgemeinschaft kursierten, aufgenommen und ebenso zwei Neuerungen: Zum einen die Verwendung der Backus-Naur Form (BNF) zur Beschreibung der Syntax der Programmiersprache. Nahezu alle folgenden Programmiersprachen benuzten die BNF, um die Syntax als kontextfreie Grammatik darzustellen. Zum anderen war die Einführung von Gültigkeitsbereichen neu.
Obwohl Algol 60 sich in Nordamerika nicht durchsetzte, hauptsächlich aus politischen Gründen aufgrund der Einführung von IBMs PL/I, teilweise aber auch aus der Entscheidung die Ein- und Ausgabe nicht Teil der Sprachdefinition zu machen, wurde Algol in der Folgezeit zum Standard in der (west)europäischen Welt. Sie beeinflusste die Ausbildung einer ganzen Generation von Informatikern und das Design späterer Sprachen, insbesondere von Simula, Pascal, Scheme und Modula.
Die 60er und 70er Jahre: Entwicklung neuer Paradigmen
- BASIC (Kemeny, Kurtz), 1960
- Simula (Dahl, Nygaard), 1965
- Pascal (Niklaus Wirth, Kathleen Jensen), 1971
- C (Brian W. Kernighan, Dennis Ritchie), 1972
- Prolog (Colmerauer et. al.), 1975
Darauf aufbauend wurde eine riesige Zahl von Programmiersprachen entwickelt, von denen aber die meisten nur Nischenprodukte sind. Es gibt gewissermaßen Sprachfamilien, z. B. C-artige Sprachen, die letztlich auf Algol zurückgehen. In neueren Generationen von spezifischen Sprachen werden erfolgreiche neue Konzepte aus anderen Sprachen eingebaut (Strukturierte Programmierung). Die aktuelle Fortran-Variante ist im Vergleich zur Urvariante stark gewandelt.
Die Geschichte der Programmierung (vor allem die Entwicklung von Unix) hat dazu geführt, dass sich imperative Sprachen (allen voran C mit der Erweiterung C++) für allgemeine Anwendungen durchgesetzt haben. Große Bedeutung erlangte das objektorientierte Programmieren, das Daten-, Prozedur- und Referenzaspekte in dem einzigen Konzept des Objekts vereinigt.
- Smalltalk (Alan Kay), 1980
- Ada, 1980
- C++ (Bjarne Stroustrup), 1983
Die 90er Jahre bis heute: Das Internetzeitalter
Das schnelle Wachstum des Internets war eine neue Herausforderung. Allen voran die Hypertext Markup Language (HTML) ermöglichte dabei als Auszeichnungssprache das Aussehen des Internet zu gestalten. Das Internet bildete eine völlig neue Grundlage für die Erstellung von Softwaresystemen und damit auch für die Entwicklung neuartiger Programmiersprachen. Dass sie sich schon früh in Webbrowsern integrierte, zeichnet die Programmiersprache Java aus und begründete ihre Popularität. Auch setzten sich verschiedenste Skriptsprachen für die Entwicklung von Webserver-Anwendungen durch. Obwohl keine der Sprachen fundamentale Neuerungen im Sprachdesign mit sich brachte, wurden nun Aspekte wie automatische Speicherbereinigung oder starke und statische Typisierung stärker berücksichtigt. Immer größere Beachtung fand auch die Codesicherheit und die Portabilität des Programmcodes, dies führte zur Entwicklung von virtuellen Maschinen als Laufzeitumgebungen.
Der Siegeszug der objektorientierten Programmierung setzte sich nun neben der Erstellung von Anwendungsprogrammen auch bei den Betriebssystemen fort. In diesem Zusammenhang ist die für das objektorientierte Programmieren entworfene graphische Notationsformen der Unified Modeling Language (UML) zu nennen. Absicht ist durch die meist visuellen Modelle der UML Softwarentwicklung zu einem geordneten Prozess zu machen. Dabei kann man feststellen, dass bisher lediglich der Abstraktionsgrad erhöht wurde. UML wird jedoch mit ihrer zunehmenden Spezifizierung immer mehr zur eigenen Programmiersprache werden.
Neuere integrierte, visuelle Entwicklungsumgebungen haben deutliche Fortschritte gebracht, hinsichtlich des Aufwands an Zeit, Kosten und Nerven. Bedienoberflächen lassen sich nun meist visuell gestalten, Codefragmente sind per Klick direkt erreichbar. Dokumentationen zu anderen Programmteilen und Bibliotheken sind direkt einsehbar. Meist gibt es sogar eine so bezeichnete „Look-Up“-Funktionalität, die noch während des Schreibens herausfindet, welche Symbole an einer bestimmten Stelle erlaubt sind und entsprechende Vorschläge macht.
Siehe dazu auch: Zeittafel der Programmiersprachen.
Klassifizierungen
Programmiersprachen lassen sich in verschiedener Hinsicht klassifizieren. Häufig ist die Unterteilung in zugrundeliegende Programmierparadigmen. Eine andere Möglichkeit ist, wie hier exemplarisch dargestellt, Programmiersprachen nach Generationen oder Anwendungsgebieten einzuteilen.
Programmierparadigmen
Siehe dazu: Programmierparadigma.
Vorlage:Paradigmen in Programmiersprachen
„Esoterische“ Programmiersprachen Neben den Klassikern gibt es auch noch eine Reihe eher esoterischer Programmiersprachen, die als anspruchsvolle Scherze gedacht sind. Beispiele sind Brainfuck, False, Shakespeare, Befunge, Whitespace, INTERCAL, Liste esoterischer Programmiersprachen
Sprachgenerationen
Programmiersprachen werden in verschiedene Generationen eingeteilt, um damit zum einen die geschichtliche Entwicklung nachzuzeichnen. Andererseits ist dies ein Ansatz, die Sprachgenerationen als semantische Level zu verstehen. Damit wird der Abstraktionsgrad von der zugrunde liegenden Technik mit jeder Generation erhöht. Die 1. Generation erfordert damit genaueste Kenntnis über die Funktionsweise der zu programmierenden Maschine, während man sich in den höheren Generationen immer mehr der natürlichen Sprache annähert. Dies ist bisher noch nicht vollständig realisiert, da die eindeutige Übersetzung für den Computer gewährleistet sein muss.
1. Generation: Maschinensprache sind die direkt auf einem Prozessor ausführbaren binäre Zahlencodes, die die Befehle darstellen. Die Eingabe erfolgt direkt in binärer 0-1-Form. Die direkte Programmierung in einer Maschinensprache wird heute kaum noch verwendet.
- Beispiel: Es soll die Addition „3 + 4“ durchgeführt werden. Der Prozessor hat für die Operation „addiere“ den festgelegten Code
00011010
. 0011 und 0100 ist die Codierung der Operanden 3 und 4 im Dualsystem. Damit weist die folgende Folge in Maschinensprache den Rechner an, die Addition auszuführen:00011010 0011 0100
.
2. Generation: Assembler ersetzen die Zahlencodes der Maschinensprache durch symbolische Bezeichner (Mnemonics). Eine Assembleranweisung wird in genau einen Maschinenbefehl umgesetzt. Der Anteil der Assemblerprogrammierung ist sehr gering.
- Beispiel: Es soll die Addition „3 + 4“ durchgeführt werden. Der für den Prozessor geeignete Assembler hat den Bezeicher
ADD
für die Operation „addiere“ festgelegt.R0
undR1
sind die Speicherzellen in der die Operanden 3 und 4 stehen. Damit weist der folgende Befehl im Assembler den Rechner an, die Addition auszuführen:ADD R0 R1
.
3. Generation: Höhere Programmiersprachen (High Level Languages) führen Konzepte, wie Variablen ein, um leichter verständlichen Quelltext schreiben zu können. Sprachen der 3. Generation sind weitgehend maschinenunabhängig. Die meisten praktisch eingesetzen Programmiersprachen sind höhere Programmiersprachen.
- Beispiel: Es soll die Addition „3 + 4“ durchgeführt werden. In der höheren Programmiersprache C wird direkt die mathematische Arithmetik für die Operation „addiere“ unterstützt. Es ist dafür eine Variable
Summe
vom Datentypint
nötig. Damit weist der folgende Programmcode in C den Rechner an, die Addition auszuführen:int Summe; Summe = 3 + 4
.
Objektorientierte Sprachen sind ebenfalls Sprachen der 3. Generation. Um jedoch ihre konzeptionelle Sonderrolle zu betonen, werden sie in der Literatur oft als „OO-Generation“ bezeichnet.
4. Generation: Fourth Generation Language (4GL) Sprachen der 4. Generation sind anwendungsbezogene (applikative) Sprachen. Sie stellen u. a. die wichtigsten Gestaltungsmittel von Sprachen der 3. Generation zur Verfügung, zusätzlich jedoch Sprachmittel zur Auslösung von relativ komplexen, anwendungsbezogenen Operationen, beispielsweise zum Zugriff auf Datenbanken (meist per SQL) und zur Gestaltung von Benutzeroberflächen (GUI).
Im Ansatz, die Sprachgenerationen als semantischen Level zu verstehen, sind 4GL-Sprachen deskriptive (beschreibende) Sprachen. Die Programmierung ist einen weiteren Schritt von der Technik entfernt. Als Beispiel für eine beschreibende Sprache der vierten Generation kann man SQL heranziehen, eine Datenbankabfragesprache. Hier wird z. B. der Befehl übermittelt: „Gib mir alle Kunden im Ort Hamburg“. Wie das Ergebnis erzielt wird (etwa Öffnen von Dateien, Positionieren eines Zeigers, etc.), ist im Gegensatz zu 3GL-Sprachen nicht mehr zu beschreiben.
Leider wurde im Marketing einiger Hersteller von Entwicklungsumgebungen fälschlich der Begriff 4GL benutzt, um das Produkt als vermeintlich moderner darzustellen. Dies ist allerdings falsch. Der semantische Level einer Sprache ändert sich nicht durch die eingesetzte Entwicklungsumgebung und deren Fähigkeiten.
5. Generation: (Very High Level Language, VHLL) Sprachen der 5.Generation gestatten das Beschreiben von Sachverhalten und Problemen. Sie kommen vor allem im Bereich der KI (künstliche Intelligenz) zum Einsatz. Die Wahl des Problemlösungsweges kann (entsprechend dem Sprachkonzept) dem jeweiligen System (weitgehend) überlassen werden. Bekanntestes Beispiel für eine Sprache der 5. Generation ist PROLOG.
Anwendungsgebiete
Assemblersprachen Assemblersprachen gehören immer direkt zu ihrer entsprechenden Maschinensprache. Die Maschinenbefehle werden jedoch in einer menschenlesbaren Form geschrieben. Weiter wird mittels Symbolen adressiert, die absoluten Adressen werden vom Assembler errechnet. Auch können symbolische Konstanten benutzt und Makros, die häufig wiederkehrende Befehlsfolgen repräsentieren, definiert werden.
Datenstrukturen
LISP verwendet als konzeptionelle Hauptstruktur Listen. Auch das Programm ist eine Liste von Befehlen, die andere Listen verändern. FORTH verwendet als konzeptionelle Hauptstruktur Stacks und Stackoperationen.
siehe Datenstruktur
Visuelle Programmierumgebungen Visuelle Programmierumgebungen erleichtern die Programmierung, da zwischen Eigenschaften von Objekten, der Lage von Objekten auf dem Bildschirm und dem eigentlichen Code unterschieden wird.
Beispiele: Visual Basic, Gambas, Visual C++, Borland C++, Delphi (Sprache: Object Pascal), Kylix (Delphi/C++ für Linux), KDevelop (Sprache: C++), Microsoft Visual Studio, ClickEXE.
Ein sehr mächtiges Beispiel ist auch das Open Source-Projekt Eclipse, das sich konzeptionell nicht auf eine bestimmte Sprache festlegt.
Daneben wird der Begriff Visuelle Programmierung auch für Programmiersprachen benutzt, bei denen graphisch in Diagrammen (in der Regel Statechart-Dialekte) programmiert wird. Beispiele hierfür: Esterel (mit Esterel Studio, http://www.esterel-technologies.com ), Statemate (http://www.ilogix.com/statemate/statemate.cfm ), MATLAB mit Simulink, LabVIEW, BlueJ, Lava und Agilent VEE (ehemals HP-VEE).
Anwendungsbezogene Sprachen und Systeme Datenbanksysteme: dBase, Clipper, Xbase, Paradox. EASY
Authoringprogramme: Macromedia Director
Siehe auch
Weblinks
Vorlage:Wiktionary1 Vorlage:Wikibooks1
- Geschichte der Programmiersprachen von Horst Zuse
- Virtual Museum of Computing (Linksammlung)
- 99 Bottles of Beer: Ein Programm in 700 Programmiersprachen bzw. Dialekten
- Éric Lévénez: Computer Languages History, mit Zeittafel und Linkverzeichnis (englisch)