it-swarm-eu.dev

Mathematica: qu'est-ce que la programmation symbolique?

Je suis un grand fan de Stephen Wolfram, mais il est résolument adepte de son talent. Dans de nombreuses références, il vante Mathematica comme un paradigme de programmation symbolique différent. Je ne suis pas un utilisateur de Mathematica. 

Mes questions sont: quelle est cette programmation symbolique? Et comment se compare-t-il aux langages fonctionnels (tels que Haskell)?

77
zenna

Vous pouvez considérer la programmation symbolique de Mathematica comme un système de recherche et remplacement dans lequel vous programmez en spécifiant des règles de recherche et remplacement. 

Par exemple, vous pouvez spécifier la règle suivante

area := Pi*radius^2;

La prochaine fois que vous utiliserez area, il sera remplacé par Pi*radius^2. Maintenant, supposons que vous définissiez une nouvelle règle

radius:=5

Maintenant, chaque fois que vous utilisez radius, il sera réécrit dans 5. Si vous évaluez area, il sera réécrit dans Pi*radius^2, ce qui déclenche la règle de réécriture pour radius et vous obtiendrez Pi*5^2 comme résultat intermédiaire. Ce nouveau formulaire déclenchera une règle de réécriture intégrée pour l'opération ^ afin que l'expression soit réécrite davantage dans Pi*25. À ce stade, la réécriture s’arrête car il n’ya pas de règles applicables.

Vous pouvez émuler une programmation fonctionnelle en utilisant vos règles de remplacement en tant que fonction. Par exemple, si vous voulez définir une fonction qui ajoute, vous pouvez faire 

add[a_,b_]:=a+b

add[x,y] est maintenant réécrit dans x+y. Si vous voulez ajouter uniquement aux valeurs numériques a, b, vous pouvez le faire à la place.

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

Désormais, add[2,3] est réécrit dans 2+3 à l'aide de votre règle, puis dans 5 à l'aide de la règle intégrée pour +, alors que add[test1,test2] reste inchangé.

Voici un exemple de règle de remplacement interactive

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

Ici, a est remplacé par ChoiceDialog, qui est ensuite remplacé par le nombre choisi par l'utilisateur dans la boîte de dialogue qui s'est affichée, ce qui rend les quantités numériques et déclenche la règle de remplacement pour +. Ici, ChoiceDialog en tant que règle de remplacement intégrée dans le sens de "remplacer ChoiceDialog [certains éléments] par la valeur du bouton sur lequel l'utilisateur a cliqué".

Les règles peuvent être définies à l'aide de conditions qui doivent elles-mêmes être réécrites afin de produire True ou False. Supposons par exemple que vous ayez inventé une nouvelle méthode de résolution d’équations, mais que vous pensiez que cela ne fonctionne que lorsque le résultat final de votre méthode est positif. Vous pouvez faire la règle suivante

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

Ici, solve[x+5==20] est remplacé par 15, mais solve[x + 5 == -20] reste inchangé car aucune règle ne s'applique. La condition qui empêche l'application de cette règle est /;result>0. Evaluator examine essentiellement le résultat potentiel de l'application de règles pour décider de l'accepter ou non.

L'évaluateur de Mathematica réécrit goulûment chaque motif en appliquant l'une des règles applicables à ce symbole. Parfois, vous voulez avoir un contrôle plus précis, et dans ce cas, vous pouvez définir vos propres règles et les appliquer manuellement comme ceci 

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

Ceci appliquera les règles définies dans myrules jusqu'à ce que le résultat cesse de changer. Ceci est assez similaire à l'évaluateur par défaut, mais vous pouvez maintenant avoir plusieurs ensembles de règles et les appliquer de manière sélective. Un exemple plus avancé - montre comment créer un évaluateur de type Prolog qui effectue une recherche dans des séquences d'applications de règles.

La version actuelle de Mathematica présente un inconvénient lorsque vous devez utiliser l'évaluateur par défaut de Mathematica (pour utiliser Integrate, Solve, etc.) et souhaitez modifier la séquence d'évaluation par défaut. C'est possible mais compliqué , et j'aime bien penser qu'une mise en oeuvre future de la programmation symbolique aura un moyen plus élégant de contrôler la séquence d'évaluation

72
Yaroslav Bulatov

Lorsque j'entends l'expression "programmation symbolique", LISP, Prolog et (oui) Mathematica me viennent immédiatement à l'esprit. Je qualifierais un environnement de programmation symbolique de celui dans lequel les expressions utilisées pour représenter le texte du programme se trouvent également être la structure de données primaire. En conséquence, il devient très facile de construire des abstractions sur des abstractions puisque les données peuvent facilement être transformées en code et inversement.

Mathematica exploite énormément cette capacité. Encore plus lourdement que LISP et Prolog (IMHO).

Comme exemple de programmation symbolique, considérons la séquence d'événements suivante. J'ai un fichier CSV qui ressemble à ceci:

r,1,2
g,3,4

J'ai lu ce fichier dans:

Import["somefile.csv"]
--> {{r,1,2},{g,3,4}}

Le résultat est-il une donnée ou un code? C'est les deux. Ce sont les données qui résultent de la lecture du fichier, mais il s’agit également de l’expression qui construira ces données. Cependant, en tant que code, cette expression est inerte car le résultat de son évaluation est simplement lui-même.

Alors maintenant, j'applique une transformation au résultat:

% /. {c_, x_, y_} :> {c, Disk[{x, y}]}
--> {{r,Disk[{1,2}]},{g,Disk[{3,4}]}}

Sans s'attarder sur les détails, tout ce qui s'est passé est que Disk[{...}] a été encapsulé autour des deux derniers chiffres de chaque ligne d'entrée. Le résultat est toujours data/code, mais toujours inerte. Une autre transformation:

% /. {"r" -> Red, "g" -> Green}
--> {{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}

Oui, toujours inerte. Cependant, par une coïncidence remarquable, ce dernier résultat est en réalité une liste de directives valides dans le langage incorporé spécifique au domaine de Mathematica pour les graphiques. Une dernière transformation, et les choses commencent à arriver:

% /. x_ :> Graphics[x]
--> Graphics[{{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}]

En fait, vous ne verriez pas ce dernier résultat. Dans un affichage épique de sucre syntaxique, Mathematica montrerait cette image de cercles rouges et verts:

alt text

Mais le plaisir ne s'arrête pas là. Sous tout ce sucre syntaxique, nous avons encore une expression symbolique. Je peux appliquer une autre règle de transformation:

% /. Red -> Black

alt text

Presto! Le cercle rouge est devenu noir.

C'est ce genre de "poussée de symbole" qui caractérise la programmation symbolique. Une grande majorité de la programmation de Mathematica est de cette nature.

Fonctionnel vs symbolique

Je ne traiterai pas en détail des différences entre programmation symbolique et fonctionnelle, mais je ferai quelques remarques.

On pourrait considérer la programmation symbolique comme une réponse à la question: "Que se passerait-il si j'essayais de tout modéliser en utilisant uniquement des transformations d'expression?" La programmation fonctionnelle, en revanche, peut être considérée comme une réponse à: "Que se passerait-il si j'essayais de tout modéliser en utilisant uniquement des fonctions?" Tout comme la programmation symbolique, la programmation fonctionnelle facilite la création rapide de couches d'abstractions. L’exemple que j’ai donné ici pourrait être facilement reproduit dans, disons, Haskell en utilisant une approche d’animation réactive fonctionnelle. La programmation fonctionnelle concerne la composition des fonctions, les fonctions de niveau supérieur, les combinateurs - toutes les choses astucieuses que vous pouvez faire avec les fonctions.

Mathematica est clairement optimisé pour la programmation symbolique. Il est possible d'écrire du code dans un style fonctionnel, mais les fonctionnalités de Mathematica ne sont en réalité qu'un mince placage sur les transformations (et une abstraction qui fuit, voir la note ci-dessous).

Haskell est clairement optimisé pour la programmation fonctionnelle. Il est possible d'écrire du code dans un style symbolique, mais je dirais même que la représentation syntaxique des programmes et des données est assez distincte, ce qui rend l'expérience sous-optimale.

Remarques finales

En conclusion, je soutiens qu'il existe une distinction entre la programmation fonctionnelle (telle qu'illustrée par Haskell) et la programmation symbolique (telle que décrite par Mathematica). Je pense que si on étudie les deux, on en apprendra beaucoup plus que d'étudier un seul - le test ultime de la distinction.


Abstraction fonctionnelle qui fuit dans Mathematica?

Ouais, qui fuit. Essayez ceci, par exemple:

f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]

Dûment signalé à, et reconnu par, WRI. La réponse: éviter l'utilisation de Function[var, body] (Function[body] est correct).

73
WReach

Comme d'autres l'ont déjà mentionné, Mathematica fait beaucoup de réécriture de termes. Haskell n'est peut-être pas la meilleure comparaison, mais Pure est un langage de réécriture de termes fonctionnel et agréable (qui devrait sembler familier aux personnes ayant une formation en Haskell). Peut-être que la lecture de leur page Wiki sur la réécriture de termes éclaircira quelques points pour vous:

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

9
Michael Kohl

Mathematica utilise beaucoup le terme réécriture. Le langage fournit une syntaxe spéciale pour diverses formes de réécriture, un support spécial pour les règles et les stratégies. Le paradigme n'est pas "nouveau" et bien sûr, il n'est pas unique, mais ils sont définitivement à la pointe de cette "programmation symbolique", aux côtés d'autres grands acteurs tels qu'Axiom.

Pour ce qui est de la comparaison avec Haskell, eh bien, vous pourriez y réécrire, avec un peu d’aide de votre bibliothèque standard, mais ce n’est pas aussi simple que dans un Mathematica à typage dynamique.

6
SK-logic

Symbolique ne doit pas être opposé à fonctionnel, mais à la programmation numérique. Considérons par exemple MatLab vs Mathematica. Supposons que je veuille le polynôme caractéristique d'une matrice. Si je voulais faire cela dans Mathematica, je pourrais obtenir une matrice d’identité (I) et la matrice (A) elle-même dans Mathematica, puis procédez comme suit:

Det[A-lambda*I]

Et j'obtiendrais le polynôme caractéristique (peu importe qu'il y ait probablement une fonction polynomiale caractéristique), par contre, si j'étais dans MatLab, je ne pourrais pas le faire avec MatLab de base car MatLab de base (peu importe qu'il y ait probablement un polynôme caractéristique fonction) n’est bon que pour calculer des nombres de précision finie, pas des choses où il y a des lambdas aléatoires (notre symbole). Ce que vous devez faire, c'est acheter le complément Symbolab, puis définir lambda comme sa propre ligne de code, puis l'écrire (dans lequel il convertira votre matrice A en une matrice de nombres rationnels plutôt que de décimales à précision finie) et, bien que la différence de performances ne soit probablement pas perceptible pour un cas aussi petit, elle le ferait probablement beaucoup plus lentement que Mathematica en termes de vitesse relative.

C’est la différence. Les langages symboliques sont intéressés par des calculs parfaitement précis (souvent à l’aide de nombres rationnels et non numériques), tandis que les langages de programmation numériques sont très performants dans la grande majorité des calculs que vous devez effectuer. pour être plus rapides dans les opérations numériques auxquelles ils sont destinés (MatLab est pratiquement incomparable à cet égard pour les langages de niveau supérieur - excluant le C++, etc.) et pisse pour les opérations symboliques.

0
William Bell