Mathematica: Was ist symbolische Programmierung?

78

Ich bin ein großer Fan von Stephen Wolfram, aber er scheut sich definitiv nicht, sein eigenes Horn zu betätigen. In vielen Referenzen lobt er Mathematica als ein anderes symbolisches Programmierparadigma. Ich bin kein Mathematica-Benutzer.

Meine Fragen sind: Was ist diese symbolische Programmierung? Und wie ist es mit funktionalen Sprachen (wie Haskell) zu vergleichen?

Zenna
quelle

Antworten:

74

Sie können sich die symbolische Programmierung von Mathematica als ein Such- und Ersetzungssystem vorstellen, in dem Sie programmieren, indem Sie Such- und Ersetzungsregeln angeben.

Zum Beispiel könnten Sie die folgende Regel angeben

area := Pi*radius^2;

Bei der nächsten Verwendung areawird es durch ersetzt Pi*radius^2. Angenommen, Sie definieren eine neue Regel

radius:=5

Wenn Sie radiuses jetzt verwenden , wird es neu geschrieben 5. Wenn Sie auswerten area, wird es neu geschrieben, Pi*radius^2wodurch die Umschreiberegel für ausgelöst wird, radiusund Sie erhalten Pi*5^2als Zwischenergebnis. Dieses neue Formular löst eine integrierte Umschreiberegel für den ^Vorgang aus, sodass der Ausdruck weiter umgeschrieben wird Pi*25. An diesem Punkt wird das Umschreiben gestoppt, da keine anwendbaren Regeln vorhanden sind.

Sie können die funktionale Programmierung emulieren, indem Sie Ihre Ersetzungsregeln als Funktion verwenden. Wenn Sie beispielsweise eine Funktion definieren möchten, die hinzugefügt wird, können Sie dies tun

add[a_,b_]:=a+b

Wird jetzt add[x,y]umgeschrieben x+y. Wenn Sie hinzufügen möchten, dass nur die Zahlen a, b gelten, können Sie dies stattdessen tun

add[a_?NumericQ, b_?NumericQ] := a + b

Jetzt add[2,3]wird die 2+3Verwendung Ihrer Regel und die 5Verwendung der integrierten Regel für umgeschrieben +, während sie add[test1,test2]unverändert bleibt.

Hier ist ein Beispiel für eine interaktive Ersetzungsregel

a := ChoiceDialog["Pick one", {1, 2, 3, 4}]
a+1

Hier awird durch ersetzt ChoiceDialog, das dann durch die Nummer ersetzt wird, die der Benutzer im daraufhin angezeigten Dialogfeld ausgewählt hat. Dadurch werden beide Mengen numerisch und es wird eine Ersetzungsregel für ausgelöst +. Hier ChoiceDialogals integrierte Ersetzungsregel nach dem Motto "Ersetzen Sie ChoiceDialog [einige Dinge] durch den Wert der Schaltfläche, auf die der Benutzer geklickt hat".

Regeln können unter Verwendung von Bedingungen definiert werden, die selbst ein Umschreiben von Regeln durchlaufen müssen, um Trueoder zu erzeugen False. Angenommen, Sie haben eine neue Gleichungslösungsmethode erfunden, aber Sie denken, dass sie nur funktioniert, wenn das Endergebnis Ihrer Methode positiv ist. Sie können die folgende Regel ausführen

 solve[x + 5 == b_] := (result = b - 5; result /; result > 0)

Hier solve[x+5==20]wird durch 15 ersetzt, solve[x + 5 == -20]bleibt aber unverändert, da keine Regel gilt. Die Bedingung, die verhindert, dass diese Regel angewendet wird, ist /;result>0. Der Evaluator prüft im Wesentlichen die potenzielle Ausgabe der Regelanwendung, um zu entscheiden, ob er fortfahren soll.

Der Evaluator von Mathematica schreibt jedes Muster gierig mit einer der Regeln neu, die für dieses Symbol gelten. Manchmal möchten Sie eine genauere Kontrolle haben, und in diesem Fall können Sie Ihre eigenen Regeln definieren und diese manuell anwenden

myrules={area->Pi radius^2,radius->5}
area//.myrules

Dadurch werden Regeln angewendet, die in definiert sind, myrulesbis sich das Ergebnis nicht mehr ändert. Dies ist dem Standardauswerter ziemlich ähnlich, aber jetzt können Sie mehrere Regelsätze haben und diese selektiv anwenden. Ein erweitertes Beispiel zeigt, wie ein Prolog-ähnlicher Evaluator erstellt wird, der Sequenzen von Regelanwendungen durchsucht.

Ein Nachteil der aktuellen Mathematica - Version kommt auf , wenn Sie das Standardauswertungsprogramm verwenden müssen Mathematica (zu nutzen Integrate, Solveusw.) und ändern mögen Standardreihenfolge der Bewertung. Das ist möglich, aber kompliziert , und ich denke gerne, dass eine zukünftige Implementierung der symbolischen Programmierung eine elegantere Möglichkeit haben wird, die Bewertungssequenz zu steuern

Jaroslaw Bulatow
quelle
2
@ Yaro Ich denke, die Sache wird interessanter, wenn Sie Regeln als Ergebnis von Funktionen erhalten (wie in Solve, DSolve usw.)
Dr. belisarius
Sie können sich Solveaber nur einen weiteren Satz von Umschreibregeln vorstellen. Wenn Sie einige Gleichungen angeben, die Mathematica nicht lösen kann, Solve[hard_equations]bleibt dies erhalten, Solve[hard_equations]und Sie können eine benutzerdefinierte SolveRegel definieren , die in diesem Fall gilt. In diesem Fall schätze ich, dass sie / verwenden; Bedingung, um Muster für "jede Gleichung zu definieren, die mit Methoden in Mathematica gelöst werden kann", so dass für harte Gleichungen die eingebaute Regel nicht gilt und Solvein der ursprünglichen Form bleibt
Yaroslav Bulatov
2
Ich denke, das macht es zu kompliziert. Mathematica-Programme sind im Grunde nur Sätze von Ersatzregeln. Die Ausführung ist ein Prozess, bei dem vorhandene Regeln auf Eingaben angewendet werden, bis keine Regel mehr übereinstimmt
Jaroslaw Bulatow,
7
+1 Sehr schöne, nicht gewundene Erklärung ohne Hornblasen. Vielleicht möchte ich nur hinzufügen, dass der Kernel bereits unzählige Regeln und Algorithmen enthält, die fast alle in den meisten Sprachen verfügbaren mathematischen Bibliotheken darstellen, und noch einige mehr.
Dr. Belisarius
3
Simon, Lambda-Kalkül selbst ist nur eines der Umschreibesysteme. Das Umschreiben von Begriffen ist ein allgemeinerer Ansatz als jeder bestimmte TRS.
SK-Logik
74

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:

Alt-Text

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

Alt-Text

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).

WReach
quelle
2
Hat Ihnen die WRI wirklich geraten, dies zu vermeiden Function[var, body]? Das ist seltsam, da es in den Dokumenten empfohlen wird ...
Simon
6
@Simon: Ja, ich habe eine E-Mail von der WRI, die besagt, dass ich es vermeiden sollte, wenn ich mir Sorgen über die Semantik einer Funktionsänderung mache, die davon abhängt, ob ein Anrufer in der "Anrufkette" zufällig ein gleichnamiges Symbol verwendet mit Function[var, body]. Es wurde keine Erklärung dafür angeboten, warum dies nicht behoben werden konnte, aber ich spekuliere, dass Functiones seit 1.0 katastrophal wäre, sein Verhalten so spät im Spiel zu ändern. Das Problem wird in (leicht) näher beschrieben hier .
WReach
5
Angesichts des Expositionsniveaus seiner Interna in mma bin ich mir nicht einmal sicher, ob Functiondies auch im Prinzip geheilt werden könnte - zumindest mit der aktuellen eindringenden Semantik von Ruleund RuleDelayed, die die Bindungen der Konstrukte des inneren Scoping, einschließlich ihrer selbst, nicht berücksichtigen . Dieses Phänomen scheint mir eher mit dieser Eigenschaft von Ruleund verbunden zu sein RuleDelayedals speziell mit Function. Aber so oder so stimme ich zu, dass es jetzt sehr gefährlich ist, dies zu ändern. Schade, denn Function[var,body]sollte nicht verwendet werden - solche Fehler sind in umfangreichen Projekten kaum zu erkennen.
Leonid Shifrin
@WReach Mathics , ein FOSS Mathematica-Klon, hat kein Problem mit undichten Funktionen! Ich habe es gerade versucht.
Mohammad Alaggan
@ M.Alaggan FYI, es sieht so aus, als ob Mathics hierher gezogen ist , es hat die erste Veröffentlichung (1.0) nicht hinter sich gebracht, aber es hat einige ziemlich neue Commits (Nov. 2018)
jrh
9

Wie andere hier bereits erwähnt haben, schreibt Mathematica viel um. Vielleicht ist Haskell nicht der beste Vergleich, aber Pure ist eine nette funktionale Sprache zum Umschreiben von Begriffen (die Menschen mit Haskell-Hintergrund vertraut sein sollte). Vielleicht klärt das Lesen ihrer Wiki-Seite über das Umschreiben von Begriffen ein paar Dinge für Sie:

http://code.google.com/p/pure-lang/wiki/Rewriting

Michael Kohl
quelle
1
Semantisch ähnelt Pure eher Scheme (dynamische Typisierung, standardmäßig eifrige Auswertung) oder OCaml (hat explizite Referenzzellen). Aber die Syntax und die Bibliotheken spiegeln die von Haskell wider.
Dubiousjim
6

Mathematica verwendet das Umschreiben von Begriffen stark. Die Sprache bietet eine spezielle Syntax für verschiedene Formen des Umschreibens sowie eine spezielle Unterstützung für Regeln und Strategien. Das Paradigma ist nicht so "neu" und natürlich nicht einzigartig, aber sie sind definitiv auf dem neuesten Stand dieser "symbolischen Programmierung", zusammen mit den anderen starken Spielern wie Axiom.

Was den Vergleich mit Haskell angeht, so könnten Sie dort mit ein wenig Hilfe beim Verschrotten Ihrer Boilerplate-Bibliothek umschreiben, aber es ist bei weitem nicht so einfach wie in einer dynamisch typisierten Mathematica.

SK-Logik
quelle
0

Symbolisch sollte nicht mit funktional kontrastiert werden, sondern mit numerischer Programmierung. Betrachten Sie als Beispiel MatLab vs Mathematica. Angenommen, ich möchte das charakteristische Polynom einer Matrix. Wenn ich das in Mathematica machen wollte, könnte ich eine Identitätsmatrix (I) und die Matrix (A) selbst in Mathematica bekommen, dann mache ich Folgendes:

Det[A-lambda*I]

Und ich würde das charakteristische Polynom erhalten (egal, dass es wahrscheinlich eine charakteristische Polynomfunktion gibt), andererseits, wenn ich in MatLab wäre, könnte ich es nicht mit Basis-MatLab machen, weil Basis-MatLab (egal, dass es wahrscheinlich ein charakteristisches Polynom gibt Funktion) ist nur gut darin, Zahlen mit endlicher Genauigkeit zu berechnen, nicht Dinge, in denen zufällige Lambdas (unser Symbol) enthalten sind. Sie müssten lediglich das Add-On Symbolab kaufen und dann Lambda als eigene Codezeile definieren und diese dann ausschreiben (wobei Ihre A-Matrix in eine Matrix rationaler Zahlen anstatt in Dezimalstellen mit endlicher Genauigkeit umgewandelt wird). und während der Leistungsunterschied für einen kleinen Fall wie diesen wahrscheinlich nicht wahrnehmbar wäre, würde er dies in Bezug auf die relative Geschwindigkeit wahrscheinlich viel langsamer als Mathematica tun.

Das ist also der Unterschied: Symbolische Sprachen sind daran interessiert, Berechnungen mit perfekter Genauigkeit durchzuführen (häufig mit rationalen Zahlen im Gegensatz zu numerischen), und numerische Programmiersprachen sind dagegen bei der überwiegenden Mehrheit der Berechnungen, die Sie durchführen müssten, sehr gut und tendieren dazu um schneller bei den numerischen Operationen zu sein, für die sie gedacht sind (MatLab ist in dieser Hinsicht für höhere Sprachen - ausgenommen C ++ usw. - nahezu unübertroffen) und eine Pisse, die bei symbolischen Operationen schlecht ist.

William Bell
quelle