Wenn ich den Ausdruck "symbolische Programmierung" höre, fallen mir sofort LISP, Prolog und (ja) Mathematica ein. Ich würde eine symbolische Programmierumgebung als eine charakterisieren, in der die Ausdrücke, die zur Darstellung von Programmtext verwendet werden, zufällig auch die primäre Datenstruktur sind. Infolgedessen wird es sehr einfach, Abstraktionen auf Abstraktionen aufzubauen, da Daten leicht in Code umgewandelt werden können und umgekehrt.
Mathematica nutzt diese Fähigkeit stark aus. Noch schwerer als LISP und Prolog (IMHO).
Betrachten Sie als Beispiel für symbolische Programmierung die folgende Abfolge von Ereignissen. Ich habe eine CSV-Datei, die so aussieht:
r,1,2
g,3,4
Ich habe diese Datei gelesen in:
Import["somefile.csv"]
--> {{r,1,2},{g,3,4}}
Sind die Ergebnisdaten oder Code? Es ist beides. Es sind die Daten, die sich aus dem Lesen der Datei ergeben, aber es ist auch der Ausdruck, der diese Daten erstellt. Im Code ist dieser Ausdruck jedoch inert, da das Ergebnis seiner Auswertung einfach selbst ist.
Jetzt wende ich eine Transformation auf das Ergebnis an:
% /. {c_, x_, y_} :> {c, Disk[{x, y}]}
--> {{r,Disk[{1,2}]},{g,Disk[{3,4}]}}
Ohne auf die Details einzugehen, ist alles, was passiert ist, dass Disk[{...}]
die letzten beiden Zahlen aus jeder Eingabezeile umbrochen wurden. Das Ergebnis ist immer noch Daten / Code, aber immer noch inert. Eine weitere Transformation:
% /. {"r" -> Red, "g" -> Green}
--> {{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}
Ja, immer noch träge. Durch einen bemerkenswerten Zufall ist dieses letzte Ergebnis jedoch zufällig eine Liste gültiger Anweisungen in Mathematicas integrierter domänenspezifischer Sprache für Grafiken. Eine letzte Transformation, und die Dinge beginnen zu geschehen:
% /. x_ :> Graphics[x]
--> Graphics[{{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}]
Eigentlich würden Sie das letzte Ergebnis nicht sehen. In einer epischen Darstellung von syntaktischem Zucker würde Mathematica dieses Bild von roten und grünen Kreisen zeigen:
Aber der Spaß hört hier nicht auf. Unter all dem syntaktischen Zucker haben wir immer noch einen symbolischen Ausdruck. Ich kann eine andere Transformationsregel anwenden:
% /. Red -> Black
Presto! Der rote Kreis wurde schwarz.
Es ist diese Art des "Symbolschiebens", die die symbolische Programmierung kennzeichnet. Ein Großteil der Mathematica-Programmierung ist von dieser Art.
Funktional vs. symbolisch
Ich werde die Unterschiede zwischen symbolischer und funktionaler Programmierung nicht im Detail ansprechen, aber ich werde einige Anmerkungen machen.
Man könnte symbolische Programmierung als Antwort auf die Frage betrachten: "Was würde passieren, wenn ich versuchen würde, alles nur mit Ausdruckstransformationen zu modellieren?" Im Gegensatz dazu kann funktionale Programmierung als Antwort auf folgende Frage gesehen werden: "Was würde passieren, wenn ich versuchen würde, alles nur mit Funktionen zu modellieren?" Genau wie bei der symbolischen Programmierung erleichtert die funktionale Programmierung das schnelle Aufbauen von Abstraktionsebenen. Das Beispiel, das ich hier gegeben habe, könnte beispielsweise in Haskell unter Verwendung eines funktionalen reaktiven Animationsansatzes leicht reproduziert werden. Bei der funktionalen Programmierung dreht sich alles um Funktionszusammensetzung, übergeordnete Funktionen, Kombinatoren - all die raffinierten Dinge, die Sie mit Funktionen tun können.
Mathematica ist eindeutig für die symbolische Programmierung optimiert. Es ist möglich, Code im funktionalen Stil zu schreiben, aber die funktionalen Merkmale in Mathematica sind eigentlich nur ein dünnes Furnier über Transformationen (und eine undichte Abstraktion dazu, siehe Fußnote unten).
Haskell ist eindeutig für die funktionale Programmierung optimiert. Es ist möglich, Code im symbolischen Stil zu schreiben, aber ich würde darüber streiten, dass die syntaktische Darstellung von Programmen und Daten sehr unterschiedlich ist, was die Erfahrung suboptimal macht.
Abschließende Bemerkungen
Abschließend befürworte ich, dass zwischen funktionaler Programmierung (wie von Haskell verkörpert) und symbolischer Programmierung (wie von Mathematica verkörpert) unterschieden wird. Ich denke, wenn man beide studiert, lernt man wesentlich mehr als nur einen - den ultimativen Test der Unterscheidbarkeit.
Undichte funktionale Abstraktion in Mathematica?
Ja, undicht. Versuchen Sie dies zum Beispiel:
f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]
Ordnungsgemäß der WRI gemeldet und von dieser anerkannt. Die Antwort: Vermeiden Sie die Verwendung von Function[var, body]
( Function[body]
ist in Ordnung).