Was Sie im Wesentlichen fragen, ist der Unterschied zwischen der Rechenkraft und der Ausdruckskraft (oder einfach Ausdruckskraft ) einer Sprache (oder eines Rechensystems).
Rechenleistung
Die Rechenleistung bezieht sich darauf, welche Arten von Problemen die Sprache berechnen kann. Die bekannteste Klasse von Rechenleistung ist diejenige, die einer Universal-Turing-Maschine entspricht . Es gibt viele andere Rechensysteme, wie z. B. Random Access Machines , λ-Kalkül , SK-Kombinator-Kalkül , µ-rekursive Funktionen , WHILE
Programme und viele andere. Und wie sich herausstellt, können sich alle gegenseitig simulieren, was bedeutet, dass sie alle die gleiche Rechenleistung haben.
Daraus entsteht die Church-Turing-These (benannt nach Alonzo Church , der die λ-Rechnung erstellt hat, und Alan Turing , der die Universal Turing Machine erstellt hat). Die Church-Turing-These ist eine Hypothese zur Berechenbarkeit mit zwei Aspekten:
- alle allgemein berechenbaren Rechnersysteme sind gleich leistungsfähig und
- Ein Mensch, der einem Algorithmus folgt, kann genau die Funktionen berechnen, die eine Turing-Maschine (und damit eines der anderen Systeme) berechnen kann.
Die zweite ist jedoch im Bereich der Philosophie des Geistes wichtiger als die Informatik.
Es gibt jedoch zwei Dinge, die die Church-Turing-These nicht sagt und die für Ihre Frage sehr relevant sind:
- wie effizient die verschiedenen Simulationen sind und
- wie bequem die Kodierung eines Problems ist.
Ein einfaches Beispiel für (1): Auf einem Computer mit wahlfreiem Zugriff dauert das Kopieren eines Arrays proportional zur Länge des Arrays. Bei einer Turing-Maschine dauert die Zeit jedoch proportional zum Quadrat der Länge des Arrays, da die Turing-Maschine keinen wahlfreien Speicherzugriff hat und sich nur zellenweise über das Band bewegen kann. Daher muss es sich n- mal über die n Elemente des Arrays bewegen, um sie zu kopieren. Daher können verschiedene Berechnungsmodelle auch im asymptotischen Fall, in dem versucht wird, von Implementierungsdetails abzuweichen, unterschiedliche Leistungsmerkmale aufweisen.
Beispiele für (2) gibt es zuhauf: Sowohl λ-Kalkül als auch Python sind Turing-vollständig. Aber möchten Sie lieber ein Programm in Python oder in λ-Kalkül schreiben?
Es gibt auch eine dritte Falte, die ich bis jetzt umgangen habe: Alle diese ursprünglichen Systeme wurden von Logikern, Philosophen oder Mathematikern entworfen, nicht von Informatikern… einfach, weil Computer und damit Informatik nicht existierten. Diese alle gehen zurück zu den frühen 1930er Jahren, noch vor Konrad Zuse ist sehr erste Experimente (die nicht programmierbar und / oder Turing-complete ohnehin waren). Sie sprechen nur von "berechenbaren Funktionen auf den natürlichen Zahlen".
Nun, wie sich herausstellt, gibt es eine Menge, die Sie als Funktionen auf natürlichen Zahlen ausdrücken können - schließlich kommen unsere modernen Computer sogar mit viel weniger aus (im Grunde 3-4 Funktionen auf den Zahlen 0 und 1, und das war's ), aber welche Funktion berechnet beispielsweise ein Betriebssystem?
Diese Vorstellung von I / O-Nebenwirkungen, die mit der Umgebung interagieren, wird von der Idee der "Funktionen über natürlichen Zahlen" nicht erfasst. Und doch ist es irgendwie wichtig, da, wie Simon Peyton Jones einmal legt es „All eine reine Funktion ohne Nebenwirkungen, ist Ihre CPU heiß machen“ , zu dem ein Zuschauer antwortete „tatsächlich, das ist eine Seite -Effekt auch! "
Edwin Brady , der Designer von Idris , verwendet scherzhaft (ich weiß nicht, ob er es erfunden hat) den Ausdruck "Tetris-vollständig", um diesen Unterschied zwischen "kann jede berechenbare Funktion auf natürlichen Zahlen berechnen" und "kann" auszudrücken verwendet werden, um nicht triviale Programme zu schreiben, die mit der Umgebung interagieren ". Ironischerweise demonstriert er dies durch die Implementierung eines Space Invaders-Klons in Idris , ist jedoch zuversichtlich, dass Tetris sich zu Space Invaders reduziert.
Eine andere Sache, die hervorgehoben werden muss, ist, dass nicht nur die Turing-Äquivalenz nicht unbedingt ausreicht, um über das tatsächliche Schreiben von "nützlichen" Programmen zu sprechen, sondern dass OTOH möglicherweise auch nicht einmal notwendig ist . Zum Beispiel ist SQL erst mit ANSI SQL: 1999 Turing-äquivalent geworden , aber vorher war es noch nützlich. In der Tat könnten einige argumentieren, dass die Herstellung eines Turing-Äquivalents überhaupt nicht zu seiner Nützlichkeit beigetragen hat. Es gibt viele domänenspezifische Sprachen, die nicht Turing-äquivalent sind. Datenbeschreibungssprache ist normalerweise nicht (und sollte es auch nicht sein). Total Languages kann natürlich nicht Turing-äquivalent sein, Sie können jedoch Ereignisschleifen, Webserver oder Betriebssysteme darin schreiben. Es gibt auch Sprachen, die Turing-äquivalent sind, bei denen dies jedoch als Fehler angesehen wird.
Alles in allem ist die Turing-Äquivalenz also nicht besonders interessant, es sei denn, Sie möchten Programme statisch analysieren.
Ausdruckskraft
Unter der Annahme, dass unser Rechensystem leistungsfähig genug ist, um unser Problem überhaupt zu lösen, müssen wir als Nächstes unseren Algorithmus zum Lösen dieses Problems in einer Art formaler Notation für dieses System ausdrücken. Mit anderen Worten: Wir müssen ein Programm in einer Computersprache schreiben. Hier kommt der Begriff der Ausdruckskraft ins Spiel.
Es bezieht sich im Wesentlichen darauf, wie "einfach" oder "angenehm" es ist, unser Programm in unserer speziellen Programmiersprache zu schreiben. Wie Sie sehen können, ist der Begriff ziemlich vage, subjektiv und eher psychologisch als technisch.
Es gibt jedoch Versuche, die Definitionen zu präzisieren. Der bekannteste (und strengste, den ich kenne) stammt von Matthias Felleisen in seinem Aufsatz Über die Ausdruckskraft von Programmiersprachen (die ersten beiden Seiten enthalten eine sanfte Einführung, der Rest des Aufsatzes ist fleischiger).
Die Hauptintuition ist folgende: Wenn Sie ein Programm von einer Sprache in eine andere Sprache übersetzen, sind einige der Änderungen, die Sie vornehmen müssen, lokal enthalten (wie z. B. FOR
Schleifen in WHILE
Schleifen oder Schleifen in bedingte GOTO
s umwandeln), und einige erfordern eine Änderung der globalen Struktur des Programms.
Wenn Sie ein Merkmal einer Sprache nur durch lokale Transformationen durch ein anderes Merkmal einer anderen Sprache ersetzen können, haben diese Merkmale keine Auswirkung auf die Ausdruckskraft. Dies nennt man syntaktischen Zucker .
Wenn es andererseits erforderlich ist, die globale Struktur des Programms zu ändern, kann die Sprache, in die Sie übersetzen, die Funktion nicht ausdrücken. Die Sprache, aus der Sie übersetzen, soll aussagekräftiger sein (in Bezug auf diese Funktion).
Beachten Sie, dass dies eine objektiv messbare Definition der Ausdruckskraft gibt. Beachten Sie auch, dass der Begriff kontextabhängig und vergleichend ist. Also, wenn jedes Programm in der Sprache A kann Sprache übersetzt wird B mit nur lokalen Veränderungen, und es gibt mindestens ein Programm in Sprache B , das kann nicht übersetzt wird A mit nur lokalen Änderungen, dann Sprache B ist streng ausdrucksvoller als Sprache EIN. Das wahrscheinlichere Szenario ist jedoch, dass viele Programme in beiden Sprachen hin und her übersetzt werden können, aber es gibt einige Programme in beiden Sprachen, die nicht in die andere übersetzt werden können. Dies bedeutet, dass keine Sprache strikt aussagekräftiger ist als die andere. Sie verfügen lediglich über unterschiedliche Funktionen, mit denen unterschiedliche Programme auf unterschiedliche Weise ausgedrückt werden können.
Dies gibt eine formale Definition dessen, was es bedeutet, "ausdrucksvoller" zu sein, erfasst aber immer noch nicht die psychologischen Vorstellungen, die hinter dem Phänomen stehen. Beispielsweise erhöht syntaktischer Zucker nach diesem Modell nicht die Ausdruckskraft einer Sprache, da er nur durch lokale Änderungen übersetzt werden kann. Aber wir wissen aus Erfahrung , dass mit FOR
, WHILE
und zur IF
Verfügung, auch wenn sie nur syntaktischer Zucker für die bedingten sind GOTO
Marken zum Ausdruck unserer Absicht leichter .
Tatsache ist, dass verschiedene Sprachen unterschiedliche Merkmale haben, die es leichter oder schwerer machen, unterschiedliche Denkweisen über ein Problem auszudrücken. Und manche Menschen finden vielleicht eine Möglichkeit, ihre Absicht leichter auszudrücken, und andere eine andere.
Ein Beispiel, das ich im Ruby-Tag auf StackOverflow gefunden habe: Viele Benutzer, die dem Ruby-Tag folgen, behaupten, dass Schleifen einfacher zu verstehen sind als Rekursion und Rekursion nur für fortgeschrittene funktionale Programmierer und Schleifen für Neueinsteiger intuitiver komplette Neulinge, die intuitiv Code wie folgt schreiben:
def rock_paper_scissors
get_user_input
determine_outcome
print_winner
rock_paper_scissors # start from the top
end
Was normalerweise dazu führt, dass mehrere Leute kommentieren, dass "das nicht funktioniert" und "sie es falsch machen" und der "richtige Weg" ist:
def rock_paper_scissors
loop do
get_user_input
determine_outcome
print_winner
end
end
Offensichtlich gibt es einige Leute, für die die Schwanzrekursion eine natürlichere Art ist, das Konzept des "Schleifens" auszudrücken als Schleifenkonstrukte.
Zusammenfassung
Die Tatsache, dass zwei Sprachen Turing-äquivalent sind, sagt eins und genau eines aus: Sie können die gleichen Funktionen für natürliche Zahlen berechnen wie eine Turing-Maschine. Das ist es.
Es sagt nichts darüber aus, wie schnell sie diese Funktionen berechnen. Es sagt nichts darüber aus, wie einfach es ist, diese Funktionen auszudrücken. Und es sagt nichts darüber aus, was sie außer der Berechnung von Funktionen für natürliche Zahlen noch tun können (z. B. Verknüpfung mit C-Bibliotheken, Lesen von Benutzereingaben, Schreiben von Ausgaben auf den Bildschirm).
Bedeutet das, dass die Klasse von Problemen, die jede Programmiersprache tatsächlich lösen kann, von Sprache zu Sprache unterschiedlich ist, obwohl diese Sprachen alle vollständig sind?
Ja.
- Es gibt Probleme, die nicht unter den Begriff "Turing-complete" fallen (der sich nur mit Rechenfunktionen für natürliche Zahlen befasst), beispielsweise das Drucken auf dem Bildschirm. Zwei Sprachen können Turing-vollständig sein, aber eine kann das Drucken auf dem Bildschirm ermöglichen und die andere nicht.
- Selbst wenn beide Sprachen die gleichen Probleme lösen können, sagt das nichts darüber aus, wie komplex die Codierung ist und wie einfach es ist, diese Codierung auszudrücken. ZB kann C jedes Problem lösen, das Haskell kann, indem Sie einfach einen Haskell-Interpreter in C schreiben. Sie müssen jedoch zuerst den Haskell-Interpreter schreiben, um ein Problem auf diese Weise zu lösen!