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 area
wird es durch ersetzt Pi*radius^2
. Angenommen, Sie definieren eine neue Regel
radius:=5
Wenn Sie radius
es jetzt verwenden , wird es neu geschrieben 5
. Wenn Sie auswerten area
, wird es neu geschrieben, Pi*radius^2
wodurch die Umschreiberegel für ausgelöst wird, radius
und Sie erhalten Pi*5^2
als 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+3
Verwendung Ihrer Regel und die 5
Verwendung 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 a
wird 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 ChoiceDialog
als 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 True
oder 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
Dadurch werden Regeln angewendet, die in definiert sind, myrules
bis 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
, Solve
usw.) 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
Solve
aber 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 benutzerdefinierteSolve
Regel 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 undSolve
in der ursprünglichen Form bleibtWenn 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:
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:
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).quelle
Function[var, body]
? Das ist seltsam, da es in den Dokumenten empfohlen wird ...Function[var, body]
. Es wurde keine Erklärung dafür angeboten, warum dies nicht behoben werden konnte, aber ich spekuliere, dassFunction
es seit 1.0 katastrophal wäre, sein Verhalten so spät im Spiel zu ändern. Das Problem wird in (leicht) näher beschrieben hier .Function
dies auch im Prinzip geheilt werden könnte - zumindest mit der aktuellen eindringenden Semantik vonRule
undRuleDelayed
, die die Bindungen der Konstrukte des inneren Scoping, einschließlich ihrer selbst, nicht berücksichtigen . Dieses Phänomen scheint mir eher mit dieser Eigenschaft vonRule
und verbunden zu seinRuleDelayed
als speziell mitFunction
. Aber so oder so stimme ich zu, dass es jetzt sehr gefährlich ist, dies zu ändern. Schade, dennFunction[var,body]
sollte nicht verwendet werden - solche Fehler sind in umfangreichen Projekten kaum zu erkennen.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
quelle
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.
quelle
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.
quelle