Mathematica: ¿que es la programación simbólica?

79

Soy un gran admirador de Stephen Wolfram, pero él definitivamente no es tímido para tocar su propia bocina. En muchas referencias, ensalza Mathematica como un paradigma de programación simbólica diferente. No soy un usuario de Mathematica.

Mis preguntas son: ¿qué es esta programación simbólica? ¿Y cómo se compara con los lenguajes funcionales (como Haskell)?

Zenna
fuente

Respuestas:

74

Puede pensar en la programación simbólica de Mathematica como un sistema de búsqueda y reemplazo en el que se programa especificando reglas de búsqueda y reemplazo.

Por ejemplo, podría especificar la siguiente regla

area := Pi*radius^2;

La próxima vez que lo use area, será reemplazado por Pi*radius^2. Ahora, suponga que define una nueva regla

radius:=5

Ahora, cada vez que lo use radius, se reescribirá 5. Si lo evalúa area, se reescribirá en Pi*radius^2qué desencadenadores de la regla de reescritura radiusy obtendrá Pi*5^2como resultado intermedio. Este nuevo formulario activará una regla de reescritura incorporada para la ^operación, por lo que la expresión se reescribirá aún más Pi*25. En este punto, la reescritura se detiene porque no hay reglas aplicables.

Puede emular la programación funcional utilizando sus reglas de reemplazo como función. Por ejemplo, si desea definir una función que agregue, puede hacer

add[a_,b_]:=a+b

Ahora add[x,y]se reescribe en x+y. Si desea agregar solo para solicitar numérico a, b, en su lugar podría hacer

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

Ahora, add[2,3]se reescribe para 2+3usar su regla y luego para 5usar la regla incorporada para +, mientras que add[test1,test2]permanece sin cambios.

Aquí hay un ejemplo de una regla de reemplazo interactiva

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

Aquí, ase reemplaza con ChoiceDialog, que luego se reemplaza con el número que el usuario eligió en el cuadro de diálogo que apareció, lo que hace que ambas cantidades sean numéricas y activa la regla de reemplazo +. Aquí, ChoiceDialogcomo una regla de reemplazo incorporada en la línea de "reemplazar ChoiceDialog [algunas cosas] con el valor del botón que el usuario hizo clic".

Las reglas se pueden definir utilizando condiciones que, a su vez, deben reescribirse para producir Trueo False. Por ejemplo, suponga que inventó un nuevo método de resolución de ecuaciones, pero cree que solo funciona cuando el resultado final de su método es positivo. Podrías hacer la siguiente regla

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

Aquí, solve[x+5==20]se reemplaza con 15, pero solve[x + 5 == -20]no cambia porque no hay ninguna regla que se aplique. La condición que impide que se aplique esta regla es /;result>0. El evaluador esencialmente busca el resultado potencial de la aplicación de reglas para decidir si seguir adelante con ella.

El evaluador de Mathematica reescribe con avidez cada patrón con una de las reglas que se aplican a ese símbolo. A veces, desea tener un control más preciso y, en tal caso, puede definir sus propias reglas y aplicarlas manualmente de esta manera

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

Esto aplicará las reglas definidas en myruleshasta que el resultado deje de cambiar. Esto es bastante similar al evaluador predeterminado, pero ahora podría tener varios conjuntos de reglas y aplicarlas de forma selectiva. Un ejemplo más avanzado muestra cómo hacer un evaluador similar a Prolog que busque secuencias de aplicaciones de reglas.

Uno de los inconvenientes de la actual versión de Mathematica aparece cuando es necesario utilizar evaluador predeterminado de Mathematica (para hacer uso de Integrate, Solve, etc.) y desea la secuencia de cambio predeterminado de evaluación. Eso es posible pero complicado , y me gusta pensar que alguna implementación futura de programación simbólica tendrá una forma más elegante de controlar la secuencia de evaluación.

Yaroslav Bulatov
fuente
2
@Yaro Supongo que la cosa se vuelve más interesante cuando obtienes reglas como resultado de funciones (como en Solve, DSolve, etc.)
Dr. belisarius
Pero puedes pensar en Solveotro conjunto de reglas de reescritura. Cuando da algunas ecuaciones que Mathematica no puede resolver, Solve[hard_equations]queda Solve[hard_equations]y puede definir una Solveregla personalizada que se aplique en este caso. En este caso, supongo que usan /; condicional para definir un patrón para "cualquier ecuación que se pueda resolver con métodos en Mathematica", por lo que para las ecuaciones rígidas la regla incorporada no se aplica y Solvepermanece en su forma original
Yaroslav Bulatov
2
Creo que eso lo complica demasiado, los programas de Mathematica son básicamente conjuntos de reglas de reemplazo. La ejecución es un proceso de aplicación de reglas existentes a la entrada hasta que ninguna regla coincide
Yaroslav Bulatov
7
+1 Explicación muy agradable sin complicaciones y sin cuernos. Quizás lo único que me gustaría agregar es que ya hay miles de millones de reglas y algoritmos incluidos en el kernel que representan casi todas las bibliotecas matemáticas disponibles en la mayoría de los lenguajes, y luego algunas más.
Dr. belisarius
3
Simon, el cálculo lambda en sí es solo uno de los sistemas de reescritura. La reescritura de términos es un enfoque más general que cualquier TRS en particular.
SK-logic
75

Cuando escucho la frase "programación simbólica", inmediatamente me vienen a la mente LISP, Prolog y (sí) Mathematica. Caracterizaría un entorno de programación simbólico como uno en el que las expresiones utilizadas para representar el texto del programa también resultan ser la estructura de datos principal. Como resultado, resulta muy fácil construir abstracciones sobre abstracciones, ya que los datos se pueden transformar fácilmente en código y viceversa.

Mathematica explota esta capacidad en gran medida. Incluso más que LISP y Prolog (en mi humilde opinión).

Como ejemplo de programación simbólica, considere la siguiente secuencia de eventos. Tengo un archivo CSV que se parece a esto:

r,1,2
g,3,4

Leí ese archivo en:

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

¿El resultado es un código o datos? Son ambos. Son los datos que resultan de leer el archivo, pero también es la expresión que construirá esos datos. Sin embargo, según el código, esta expresión es inerte ya que el resultado de evaluarla es simplemente ella misma.

Entonces ahora aplico una transformación al resultado:

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

Sin detenerse en los detalles, todo lo que ha sucedido es que Disk[{...}]se ha envuelto alrededor de los dos últimos números de cada línea de entrada. El resultado sigue siendo datos / código, pero sigue siendo inerte. Otra transformación:

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

Sí, todavía inerte. Sin embargo, por una notable coincidencia, este último resultado resulta ser una lista de directivas válidas en el lenguaje específico de dominio incorporado de Mathematica para gráficos. Una última transformación y empiezan a suceder cosas:

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

En realidad, no verías ese último resultado. En una exhibición épica de azúcar sintáctica, Mathematica mostraría esta imagen de círculos rojos y verdes:

texto alternativo

Pero la diversión no termina ahí. Debajo de todo ese azúcar sintáctico todavía tenemos una expresión simbólica. Puedo aplicar otra regla de transformación:

% /. Red -> Black

texto alternativo

¡Presto! El círculo rojo se volvió negro.

Es este tipo de "empuje de símbolos" lo que caracteriza a la programación simbólica. La gran mayoría de la programación de Mathematica es de esta naturaleza.

Funcional vs simbólico

No abordaré las diferencias entre programación simbólica y funcional en detalle, pero contribuiré con algunas observaciones.

Se podría ver la programación simbólica como una respuesta a la pregunta: "¿Qué pasaría si tratara de modelar todo usando solo transformaciones de expresión?" La programación funcional, por el contrario, puede verse como una respuesta a: "¿Qué pasaría si intentara modelar todo usando solo funciones?" Al igual que la programación simbólica, la programación funcional facilita la creación rápida de capas de abstracciones. El ejemplo que di aquí podría reproducirse fácilmente en, digamos, Haskell utilizando un enfoque de animación reactiva funcional. La programación funcional tiene que ver con la composición de funciones, funciones de nivel superior, combinadores, todas las cosas ingeniosas que puede hacer con funciones.

Mathematica está claramente optimizado para programación simbólica. Es posible escribir código en estilo funcional, pero las características funcionales en Mathematica son en realidad solo una fina capa sobre las transformaciones (y una abstracción con fugas en eso, vea la nota a pie de página a continuación).

Haskell está claramente optimizado para la programación funcional. Es posible escribir código en estilo simbólico, pero yo objetaría que la representación sintáctica de los programas y los datos es bastante distinta, lo que hace que la experiencia sea subóptima.

Observaciones finales

En conclusión, defiendo que existe una distinción entre la programación funcional (como la personifica Haskell) y la programación simbólica (como la personifica Mathematica). Creo que si uno estudia ambos, aprenderá sustancialmente más que estudiar solo uno: la prueba definitiva de distinción.


¿Abstracción funcional con fugas en Mathematica?

Sí, con fugas. Pruebe esto, por ejemplo:

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

Debidamente informado y reconocido por el WRI. La respuesta: evita el uso de Function[var, body]( Function[body]está bien).

Alcance
fuente
2
¿Realmente el WRI le aconsejó que lo evitara Function[var, body]? Eso es extraño ya que se recomienda en los documentos ...
Simon
6
@Simon: Sí, tengo un correo electrónico de WRI que dice que si me preocupa que la semántica de una función cambie en función de si alguna persona que llama en la "cadena de llamadas" usa un símbolo con el mismo nombre, entonces debería evitar usando Function[var, body]. No se ofreció ninguna explicación sobre por qué esto no se pudo arreglar, pero especulo que, dado que Functionexiste desde la versión 1.0, sería desastroso cambiar su comportamiento tan tarde en el juego. El problema se describe en (un poco) más detalle aquí .
WReach
5
Con el nivel de exposición de sus elementos internos en mma, ni siquiera estoy seguro de que Functionpueda curarse, incluso en principio, al menos con la semántica intrusa actual de Ruley RuleDelayed, que no respetan las vinculaciones de las construcciones de alcance interno, incluidos ellos mismos . Este fenómeno me parece más relacionado con esta propiedad de Ruley RuleDelayed, que específicamente con Function. Pero de cualquier manera, estoy de acuerdo en que cambiar esto es muy peligroso ahora. Lástima, porque Function[var,body]no se debe utilizar; estos errores serán casi imposibles de detectar en proyectos importantes.
Leonid Shifrin
@WReach Mathics , que es un clon de FOSS Mathematica, ¡no tiene el problema de función con fugas! Solo lo probé.
Mohammad Alaggan
@ M.Alaggan FYI, parece que Mathics se ha mudado aquí , no superó la primera versión (1.0) pero tiene algunas confirmaciones bastante recientes (noviembre de 2018)
jrh
10

Como otros ya han mencionado aquí, Mathematica hace muchas reescrituras de términos. Sin embargo, quizás Haskell no sea la mejor comparación, pero Pure es un buen lenguaje funcional para reescribir términos (que debería resultar familiar para las personas con experiencia en Haskell). Tal vez la lectura de su página Wiki sobre la reescritura de términos te aclare algunas cosas:

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

Michael Kohl
fuente
1
Semánticamente, Pure se parece más a Scheme (tipado dinámico, predeterminado en evaluación ansiosa) u OCaml (tiene celdas de referencia explícitas). Pero la sintaxis y las bibliotecas reflejan las de Haskell.
dubiousjim
6

Mathematica está usando mucho la reescritura de términos. El lenguaje proporciona una sintaxis especial para varias formas de reescritura, soporte especial para reglas y estrategias. El paradigma no es tan "nuevo" y, por supuesto, no es único, pero definitivamente están a la vanguardia de esta "programación simbólica", junto con los otros jugadores fuertes como Axiom.

En cuanto a la comparación con Haskell, bueno, podría reescribir allí, con un poco de ayuda de desechar su biblioteca repetitiva, pero no es tan fácil como en Mathematica tipado dinámicamente.

SK-lógica
fuente
1

Lo simbólico no debe contrastarse con lo funcional, debe contrastarse con la programación numérica. Considere como ejemplo MatLab vs Mathematica. Supongamos que quiero el polinomio característico de una matriz. Si quisiera hacer eso en Mathematica, podría obtener una matriz de identidad (I) y la matriz (A) misma en Mathematica, luego haga esto:

Det[A-lambda*I]

Y obtendría el polinomio característico (no importa que probablemente haya una función polinomial característica), por otro lado, si estuviera en MatLab no podría hacerlo con MatLab base porque MatLab base (no importa que probablemente haya un polinomio característico function) solo es bueno para calcular números de precisión finita, no cosas donde hay lambdas aleatorias (nuestro símbolo) allí. Lo que tendría que hacer es comprar el complemento Symbolab, y luego definir lambda como su propia línea de código y luego escribir esto (en el que convertiría su matriz A en una matriz de números racionales en lugar de decimales de precisión finita) , y aunque la diferencia de rendimiento probablemente sería imperceptible para un caso pequeño como este, probablemente lo haría mucho más lento que Mathematica en términos de velocidad relativa.

Entonces esa es la diferencia, los lenguajes simbólicos están interesados ​​en hacer cálculos con perfecta precisión (a menudo usando números racionales en lugar de numéricos) y los lenguajes de programación numérica, por otro lado, son muy buenos en la gran mayoría de los cálculos que necesitaría hacer y tienden a para ser más rápidos en las operaciones numéricas para las que están destinados (MatLab es casi incomparable en este sentido para lenguajes de nivel superior, excluyendo C ++, etc.) y un piss pobre en operaciones simbólicas.

William Bell
fuente