Introducción a Mathematica (en construcción...) Alberto Ruiz <[email protected]> Departamento de Informática y Sistemas Universidad de Murcia, Spain à Preliminares Mathematica es un entorno de cálculo simbólico, compuesto por un lenguaje de programación de alto nivel, una colección extensa de funciones matemáticas y de propósito general, y un sistema para editar interactivamente documentos como éste que contienen texto normal, órdenes sencillas, programas más complejos, gráficos, etc. Los documentos están organizados por "celdas" indicadas en azul a la derecha. Hay varios tipos de celdas: las más importantes son las de texto normal, y las de entrada (input) y salida de información (output), como las siguientes: In[1]:= 2 + 2 Out[1]= 4 El lenguaje de Mathematica es interpretado: no hace falta compilación. Está basado en reglas de transformación de expresiones y aprovecha muchas construcciones de programación funcional. Para ejecutar una orden o evaluar una expresión se edita en una celda tipo "input" y cuando esté terminada se pulsa Shift−Enter (tecla de mayúsculas + tecla de nueva línea). A continuación aparecerá una celda con el resultado. Mathematica se puede usar directamente como una calculadora: In[2]:= 1.5 + 2.5 Out[2]= 4. El espacio en blanco significa multiplicación (también se puede usar *): In[3]:= 2 H5 + 5L Out[3]= 20 La aritmética es exacta: In[4]:= 1000 350 Out[4]= 20 7 y sin límite en el tamaño de los números: In[5]:= 2 ^ 100 Out[5]= 1267650600228229401496703205376 In[6]:= 100 ! Out[6]= 93326215443944152681699238856266700490715968264381621468592963895217599993229 9156089414639761565182862536979208272237582511852109168640000000000000000000 00000 Pero si en las operaciones aparecen números aproximados, el resultado también lo será: In[7]:= 1000.0 350 Out[7]= 2.85714 Siempre se puede convertir un valor exacto en un valor aproximado usando la función N (convertir en un número). En Mathematica los argumentos se encierran entre corchetes: In[8]:= N@1000 350D Out[8]= 2.85714 In[9]:= Π Out[9]= Π In[10]:= N@ΠD Out[10]= 3.14159 Como argumento opcional se puede especificar la precisión del número: In[11]:= N@Π, 100D Out[11]= 3.14159265358979323846264338327950288419716939937510582097494459230781640628 6208998628034825342117068 Disponemos de todas las funciones matemáticas usuales : In[12]:= [email protected] Out[12]= 44.7012 In[13]:= [email protected] Out[13]= 0.992809 In[14]:= Sin@30 DegreeD Out[14]= 1 2 Existen bibliotecas con constantes y unidades físicas. à Principio Fundamental de Mathematica Todo son expresiones que se evalú an mediante la aplicación de reglas de transformación estructural. Expresiones Una expresión es o bien un átomo (número o símbolo) o bien una "cabeza" y "argumentos", que, a su vez, son expresiones. Este tipo de estructura recursiva permite representar cualquier cosa: fórmulas matemáticas, programas, dibujos, etc. Este mismo documento es en realidad una expresión de ese tipo. Las expresiones tienen una representación externa, visualmente conveniente, pero en realidad están constituidas por este tipo de estructuras anidadas. Por ejemplo: a+b es en realidad Plus@a, bD (Los componentes de una expresión (o argumentos de una función) se escriben con corchetes.) Otro ejemplo: a Sin@3 + b xD se representa internamente mediante Times@a, Sin@Plus@3, Times@b, xDDDD La forma interna de cualquier expresión se obtiene mediante la orden FullForm: In[15]:= FullFormAa SinA EE bx+3 !!!!!! Πm Out[15]//FullForm= Times@a, Sin@Times@Power@Power@Pi, mD, Rational@-1, 2DD, Plus@3, Times@b, xDDDDD Los "arrays" se representan mediante la estructura más general de lista, que es una colección de expresiones encerradas entre llaves: 84, a + b, 17, 81, 2, 3<, 5< aunque, por supuesto, internamente son expresiones con cabeza "Lista": List@4, Plus@a, bD, 17, List@1, 2, 3D, 5D Evaluación El funcionamiento de Mathematica consiste en leer una expresión (y pasarla a la forma interna), evaluarla, y mostrar el resultado (en forma externa). La evaluación consiste en la aplicación de ciertas "reglas" que modifican la estructura de la expresión. Estas reglas son de varios tipos: a) reglas que se aplican automáticamente (manipulación matemática usual) b) transformaciones y algoritmos de distinto tipo ya preprogramados, y c) algoritmos definidos por nosotros para manipular las expresiones de acuerdo con nuestros intereses. En primer lugar veremos algunos ejemplos de transformaciones automáticas y de instrucciones que realizan transformaciones útiles: Simplificación: In[16]:= 2 + 3 + 5 x + y + 10 x - 5 Out[16]= 15 x + y Expansión de potencias: In[17]:= Expand@H1 + xL10 H1 - yL5 D Out[17]= 1 + 10 x + 45 x2 + 120 x3 + 210 x4 + 252 x5 + 210 x6 + 120 x7 + 45 x8 + 10 x9 + x10 - 5 y - 50 x y - 225 x2 y - 600 x3 y - 1050 x4 y - 1260 x5 y - 1050 x6 y - 600 x7 y - 225 x8 y 50 x9 y - 5 x10 y + 10 y2 + 100 x y2 + 450 x2 y2 + 1200 x3 y2 + 2100 x4 y2 + 2520 x5 y2 + 2100 x6 y2 + 1200 x7 y2 + 450 x8 y2 + 100 x9 y2 + 10 x10 y2 - 10 y3 - 100 x y3 450 x2 y3 - 1200 x3 y3 - 2100 x4 y3 - 2520 x5 y3 - 2100 x6 y3 - 1200 x7 y3 - 450 x8 y3 100 x9 y3 - 10 x10 y3 + 5 y4 + 50 x y4 + 225 x2 y4 + 600 x3 y4 + 1050 x4 y4 + 1260 x5 y4 + 1050 x6 y4 + 600 x7 y4 + 225 x8 y4 + 50 x9 y4 + 5 x10 y4 - y5 - 10 x y5 - 45 x2 y5 120 x3 y5 - 210 x4 y5 - 252 x5 y5 - 210 x6 y5 - 120 x7 y5 - 45 x8 y5 - 10 x9 y5 - x10 y5 Solución simbólica de sistemas de ecuaciones: In[18]:= Solve@5 x - 8 a 4, xD Out[18]= 99x ® H1 + 2 aL== 4 5 Derivadas simbólicas: In[19]:= D@Sin@Exp@Cos@xDDD, xD Out[19]= -ãCos@xD Cos@ãCos@xD D Sin@xD Integrales simbólicas: In[20]:= à Sin@xD2 â x Out[20]= x 1 - Sin@2 xD 2 4 Desarrollos en serie: In[21]:= Series@Exp@xD, 8x, 0, 5<D x2 2 x3 6 x4 24 x5 120 Out[21]= 1 + x + + + + + O@xD6 Límites: i k 1 y5 x , x ® ¥E 2x{ In[22]:= LimitAj z j1 + z Out[22]= ã52 Representaciones gráficas 2D: In[23]:= Plot@Sin@xD + Sin@10 xD, 8x, 0, 2 Π<D; 2 1 1 -1 -2 2 3 4 5 6 Y 3D: In[24]:= Plot3D@[email protected] Hx x + y yLD, 8x, -3, 3<, 8y, -3, 3<, Mesh ® False, PlotPoints ® 50D ; 1 0.75 0.5 0.25 0 2 0 -2 0 -2 2 La aritmética es exacta: In[25]:= Solve@5 x2 - 8 + 5 x3 4, xD 1 1 !!!!!! 13 1 1 !!!!!! 13 19 M + J I157 + 36 19 MN =, 3 15 3 5 1 1 1 !!!! !!!!!! 13 1 !!!! !!!!!! 13 9x ® - - I1 + ä 3 M I3925 - 900 19 M - I1 - ä 3 M J I157 + 36 19 MN =, 3 30 6 5 1 1 1 !!!! !!!!!! 13 1 !!!! !!!!!! 13 9x ® - - I1 - ä 3 M I3925 - 900 19 M - I1 + ä 3 M J I157 + 36 19 MN == 3 30 6 5 Out[25]= 99x ® - + I3925 - 900 Pero siempre podemos obtener números aproximados. In[26]:= Solve@5 x2 - 8 + 5 x3 4, xD N Out[26]= 88x ® 1.07537<, 8x ® -1.03768 + 1.07471 ä<, 8x ® -1.03768 - 1.07471 ä<< La notación x//f es equivalente a f[x] (se utiliza para ir encadenando o componiendo funciones cómodamente). Se pueden resolver sistemas de ecuaciones polinomiales con varias incógnitas: In[27]:= Solve@8x2 + x y y, x y2 <, 8x, y<D N Out[27]= 88x ® 0., y ® 0.<, 8x ® 0.56984, y ® 0.754878<, 8x ® 0.21508 - 1.30714 ä, y ® -0.877439 + 0.744862 ä<, 8x ® 0.21508 + 1.30714 ä, y ® -0.877439 - 0.744862 ä<< La solución de ecuaciones no lineales debe hacerse numéricamente, indicando el punto de partida en la búsqueda: In[28]:= FindRoot@Sin@Hx - .3L Cos@xDD, 8x, 89 Degree<D Out[28]= 8x ® 1.5708< Factorización de polinomios: In[29]:= Factor@x99 + y99 D Out[29]= Hx + yL Hx2 - x y + y2 L Hx6 - x3 y3 + y6 L Hx10 - x9 y + x8 y2 - x7 y3 + x6 y4 - x5 y5 + x4 y6 - x3 y7 + x2 y8 - x y9 + y10 L Hx20 + x19 y - x17 y3 - x16 y4 + x14 y6 + x13 y7 - x11 y9 x10 y10 - x9 y11 + x7 y13 + x6 y14 - x4 y16 - x3 y17 + x y19 + y20 L 60 Hx + x57 y3 - x51 y9 - x48 y12 + x42 y18 + x39 y21 - x33 y27 - x30 y30 x27 y33 + x21 y39 + x18 y42 - x12 y48 - x9 y51 + x3 y57 + y60 L Y de números enteros: In[30]:= FactorInteger@192492352D Out[30]= 882, 6<, 813, 3<, 837, 2<< In[31]:= 26 133 372 Out[31]= 192492352 Se pueden obtener sumas infinitas: In[32]:= â ¥ 1 n=2 Out[32]= n2 1 H-6 + Π2 L 6 otro ejemplo: In[33]:= â ¥ 1 n2 +5 !!!! !!!! !!!! !!!! !!!! !!!! 5 M I2 ä + 5 M CschA 5 ΠE I-15 Π CoshA 5 ΠE + 8 5 SinhA 5 ΠEMM !!!! !!!! !!!! I45 5 I-ä + 5 M Iä + 5 MM n=2 Out[33]= -II-2 ä + Aunque no siempre convergen: In[34]:= â ¥ n=2 1 n Sum::div : Sum does not converge. Out[34]= â ¥ n=2 1 n Las derivadas se pueden expresar explícitamente: In[35]:= D@Sin@Exp@Cos@a xDDD, xD Out[35]= -a ãCos@a xD Cos@ãCos@a xD D Sin@a xD O con la notación abreviada: In[36]:= Sin ’@xD Out[36]= Cos@xD In[37]:= Tan ’’@xD Out[37]= 2 Sec@xD2 Tan@xD Las integrales definidas se calculan simbólicamente, no numéricamente sumando áreas: In[38]:= à 1 0 4 â x 1+x Out[38]= 4 Log@2D In[39]:= à 1 0 4 â x 1 + x2 Out[39]= Π Lo que nos permite que los extremos sean símbolos: In[40]:= à t 1 1 â x 1+x Out[40]= -Log@2D + Log@1 + tD In[41]:= % Simplify Out[41]= LogA E 1+t 2 (El símbolo % representa el resultado anterior) Si deseamos integrales numéricas hacemos lo siguiente: In[42]:= à 1 0 4 â x N 1+x Out[42]= 2.77259 In[43]:= IntegrateA , 8x, 0, v<E 4 1+x Out[43]= 4 Log@1 + vD Se pueden aplicar transformaciones trigonométricas: In[44]:= Sin@34 xD Out[44]= Sin@34 xD In[45]:= % TrigExpand Out[45]= 34 Cos@xD33 Sin@xD - 5984 Cos@xD31 Sin@xD3 + 278256 Cos@xD29 Sin@xD5 - 5379616 Cos@xD27 Sin@xD7 + 52451256 Cos@xD25 Sin@xD9 286097760 Cos@xD23 Sin@xD11 + 927983760 Cos@xD21 Sin@xD13 1855967520 Cos@xD19 Sin@xD15 + 2333606220 Cos@xD17 Sin@xD17 1855967520 Cos@xD15 Sin@xD19 + 927983760 Cos@xD13 Sin@xD21 286097760 Cos@xD11 Sin@xD23 + 52451256 Cos@xD9 Sin@xD25 - 5379616 Cos@xD7 Sin@xD27 + 278256 Cos@xD5 Sin@xD29 - 5984 Cos@xD3 Sin@xD31 + 34 Cos@xD Sin@xD33 Las reglas de simplificación son muy potentes. Por ejemplo, pueden deshacer la anterior expansión: In[46]:= % Simplify Out[46]= Sin@34 xD La integración simbólica permite abordar la solución de ecuaciones diferenciales: In[47]:= DSolve@f ’@xD k, f@xD, xD Out[47]= 88f@xD ® k x + C@1D<< In[48]:= DSolve@8f ’’@xD w2 f@xD, f@0D 1<, f@xD, xD Out[48]= 88f@xD ® ã-w x H1 - C@2D + ã2 w x C@2DL<< Mathematica dispone de todas las funciones necesarias para efectuar eficientemente cálculo matricial. Por ejemplo, el producto matricial y matriz vector se representa mediante "." (Dot): In[49]:= m1 = Table@i + j, 8i, 3<, 8j, 4<D Out[49]= 882, 3, 4, 5<, 83, 4, 5, 6<, 84, 5, 6, 7<< In[50]:= m1 MatrixForm i2 3 4 5z y j j z j j z j3 4 5 6z z k4 5 6 7{ Out[50]//MatrixForm= In[51]:= Hm2 = Table@i + j2 , 8i, 4<, 8j, 2<DL MatrixForm Hm1.m2L MatrixForm 5y z z 6z z z z z 7z z z 8{ i2 j j j 3 j j j j j j j4 k5 Out[51]//MatrixForm= 54 96 y i j z j z j j z j 68 122 z z k 82 148 { Out[52]//MatrixForm= Es posible automatizar que las matrices se muestren como tales y no como listas de listas... In[53]:= MakeBoxes@x_ ? MatrixQ, StandardFormD := ToBoxes MatrixForm x Off@MatrixQ::argtD In[55]:= m1 i2 3 4 5y z z 4 5 6z z z j k4 5 6 7{ j j Out[55]= j j j3 Al ser un entorno de cálculo simbólico, tiene la ventaja de que los elementos de las matrices pueden ser símbolos: In[56]:= m = J Out[56]= J 1 a N c 7 1 a N c 7 In[57]:= Transpose@mD Out[57]= J 1 c N a 7 In[58]:= Inverse@mD 7 i 7-a c j Out[58]= j j j k - 7-a c c a - z 7-a c y z z z 1 7-a c { No confundamos el producto elemento a elemento (*): In[59]:= m * Inverse@mD 7 i j 7-a c j Out[59]= j j j c2 k - 7-a c a - y 7-a c z z z z z 7 7-a c { 2 Con el producto de matrices (.): In[60]:= m.Inverse@mD 7 ac i 7-a c - 7-a c j Out[60]= j j j k 0 In[61]:= % Simplify Out[61]= J 0 7 7-a c - ac 7-a c y z z z z { 1 0 N 0 1 Muchos más ejemplos pueden consultarse en la ayuda del sistema. Reglas de transformación estructural La descripción de los algoritmos en Mathematica se hace mediante un estilo de programación diferente a C. Aunque es posible usar construcciones de tipo for, while, etc., para controlar la asignación de valores a variables de acuerdo con la lógica del algoritmo (estilo "imperativo"), Mathematica está orientado hacia un estilo de programación llamado "funcional" y, sobre todo, a la descripción de los algoritmos mediante lo que podemos llamar transformación estructural basada en "pattern matching" (reconocimiento de patrones sintácticos, o "esquemas de expresión"). El siguiente ejemplo muetra la aplicación de una regla de transformación muy simple. La notación "/." es un convenio para indicar que la expresión de la izquierda se va a transformar de acuerdo con la regla de la derecha: In[62]:= p + jp . p ® 3 y Out[62]= j3 y + 3 y Aquí solo transformamos el número 3: In[63]:= p3 + jv+3 + 6 . 3 ® 4 Out[63]= 6 + j4+v + p4 Cuando combinamos la aplicación de reglas con pattern matching se obtiene un estilo de programación muy claro y expresivo. Por ejemplo, para indicar que una función es lineal podemos hacer algo como: In[64]:= f@a + g@cD + bD . f@x_ + y_D ® f@xD + f@yD Out[64]= f@aD + f@b + g@cDD Pero la regla se ha aplicado solo una vez. Es mejor: In[65]:= f@a + g@cD + bD . f@x_ + y_D ® f@xD + f@yD Out[65]= f@aD + f@bD + f@g@cDD donde //. significa que la regla se aplica hasta que la expresión no cambie. La parte izquierda de una regla es un patrón: un esquema de expresión donde algunas partes, indicadas con un símbolo subrayado, representan variables o "comodines" que coinciden con cualquier expresión. La parte derecha de la regla indica cómo se construye la expresión resultante recomponiendo trozos de la expresión original (denotados por los símbolos anteriores, ya sin subrayar). La parte izquierda de una regla es un patrón: un esquema de expresión donde algunas partes, indicadas con un símbolo subrayado, representan variables o "comodines" que coinciden con cualquier expresión. La parte derecha de la regla indica cómo se construye la expresión resultante recomponiendo trozos de la expresión original (denotados por los símbolos anteriores, ya sin subrayar). En lugar de aplicar reglas concretas en cada caso, en la práctica resulta más cómodo suministrar reglas que se deberán aplicar siempre que sea posible, en forma de definiciones. Definiciones En lenguajes de programación imperativos, del estilo de C, las variables son contenedores de números (o de códigos de otros objetos), y las funciones se definen para que admitan unos parámetros y devuelvan resultados de un cierto tipo. En Mathematica la filosofía es diferente: la programación consiste en asociar a cada símbolo un conjunto de reglas que se deben aplicar siempre, automáticamente. Cuando evaluamos una asignación del tipo In[66]:= a = 2 Out[66]= 2 Lo que hacemos es crear la regla a ®2 que se aplicará siempre: In[67]:= a + a Out[67]= 4 Una nueva asignación como In[68]:= a = 3 + b Out[68]= 3 + b sustituye la regla a®2 por a®3+b. In[69]:= a2 Out[69]= H3 + bL2 Podemos interpretar lo anterior como que en Mathematica las variables no tienen un tipo predeterminado y fijo (como en C, que deben ser int, double, etc.) sino que pueden tomar como valor cualquier expresión. En realidad lo que hacemos es introducir una regla de transformación automática asociada a ese símbolo. Este concepto se lleva a sus últimas consecuencias en lo que podríamos considerar como la definición de funciones. En este caso, lo que se hace es crear una serie de reglas automáticas para un símbolo, con patrones que hacen referencia a posibles argumentos. Si evaluamos la siguiente asignación: In[70]:= sincos@x_D := Sin@xD + Cos@xD definimos la función sincos, pero lo que estamos haciendo en realidad es crear la regla sincos@x_D ® Sin@xD + Cos@xD y solicitar que sea evaluada siempre que sea posible. Observa el operador de asignación ":=" (dos puntos igual). Más adelante explicaremos la diferencia con "=". Por el momento basta saber que en la mayoría de las situaciones en que definimos funciones usamos ":=",y cuando asignamos variables usamos "=". La función ya está disponible: In[71]:= sincos@3D Out[71]= Cos@3D + Sin@3D In[72]:= sincos@2 ΠD Out[72]= 1 In[73]:= [email protected] Out[73]= -0.848872 In[74]:= Plot@sincos@xD, 8x, 0, 2 Π<D 1 0.5 1 2 3 4 5 6 -0.5 -1 Out[74]= Graphics A veces la definción de una función debe tener en cuenta casos particulares: In[75]:= sinc@0D = 1; Sin@xD sinc@x_D := x Observa que In[77]:= Sin@xD . x ® 0 x General::dbyz : Division by zero. 1 Power::infy : Infinite expression encountered. 0 Out[77]= Indeterminate Y que Sin@xD In[78]:= LimitA , x ® 0E x Out[78]= 1 Las expresiones se evalúan hasta donde sea posible: In[79]:= ? sinc Global‘sinc sinc@0D = 1 Sin@xD sinc@x_D := x In[80]:= sinc@2 + 3 + a + b * bD Out[80]= Sin@8 + b + b2 D 8 + b + b2 En general, las definiciones consisten de varias reglas, para considerar diferentes casos de interés: In[81]:= fact@0D = 1; fact@n_D := n fact@n - 1D En C se utilizaba la construcción "if" para determinar la acción a tomar. En Mathematica también se utiliza, pero muchas veces es más claro dar diferentes reglas para cada tipo de argumento. Diferencia entre = e := La diferencia entre los operadores ":=" y "=" es la siguiente: "=" define una regla cuya parte derecha se evalúa en mismo momento de la definición. ":=" define una regla cuya parte derecha se evalúa en el momento de usar la regla. In[83]:= a = 10100 ; b := 10100 Lo que guardamos es: In[85]:= ? a Global‘a a= 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000 In[86]:= ? b Global‘b b := 10100 El resultado de la evaluación es el mismo In[87]:= a Out[87]= 1000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000 In[88]:= b Out[88]= 1000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000 Pero a ya lo tiene precalculado, y b lo tiene que calcular cada vez que se utilice. En general usaremos := en la definición de funciones y = para definir símbolos como si fueran variables con un valor In[89]:= Remove@"Global‘*"D Patrones avanzados En muchos casos es conveniente especificar en un patrón secuencias de argumentos, condiciones de los elementos de una estructura, etc. Consideremos la anterior definción de fact: In[90]:= fact@0D := 1 fact@n_D := n fact@n - 1D El problema es que fracasa con números negativos o números no enteros: In[92]:= [email protected] $RecursionLimit::reclim : Recursion depth of 256 exceeded. $RecursionLimit::reclim : Recursion depth of 1024 exceeded. Out[92]= 3.4049139272933 ´ 10492 Hold@[email protected] - 1DD Vamos a mejorarla un poco. Primero borramos todas las reglas asociadas a fact: In[93]:= Clear@factD Y damos la nueva definición: In[94]:= fact@0D := 1 fact@n_Integer? PositiveD := n fact@n - 1D La regla sólo "se dispara" en los casos permitidos: In[96]:= [email protected] Out[96]= [email protected] In[97]:= fact@4D Out[97]= 24 In[98]:= fact@-4D Out[98]= fact@-4D (Podríamos añadir una regla que recogiese el resto de los casos indicando un error.) El símbolo ? indica que el elemento de un patrón tiene que cumplir una condición. Por ejemplo x_?OddQ hace coincidencia con un número impar, al que se denota por x. Por ejemplo, la función OddQ detecta números impares: In[99]:= OddQ@5D Out[99]= True In[100]:= OddQ@6D Out[100]= False Esto nos permite definir una función con valores diferentes según sea el argumento par o impar: g@n_ ? EvenQD := n 2 In[101]:= g@n_ ? OddQD := 3 n + 1 Funciona como deseamos: In[103]:= g@3D Out[103]= 10 In[104]:= g@4D Out[104]= 2 Dado cualquier símbolo del sistema (no de los definidos por nosotros, al menos en principio) podemos obtener ayuda sobre él pinchando encima y pulsando F1. P.ej. pruébalo con OddQ. También se puede pedir ayuda mediante la orden "Information" o símplemente con "?": In[105]:= ? OddQ OddQ@exprD gives True if expr is an odd integer, and False otherwise. La interrogación también sirve para mostrar las reglas que hemos asocidado a un símbolo: In[106]:= ? g Global‘g g@n_ ? OddQD := 3 n + 1 n g@n_ ? EvenQD := 2 (se usa el mismo símbolo para las condiciones de los patrones y para pedir información sobre un símbolo) Continuamos con los patrones avanzados. Un símbolo detrás del subrayado indica el tipo de cabeza que tiene que tener la expresión para que haya coincidencia. La siguiente función f está definida para argumentos que sean listas o que sean q[.] In[107]:= f@x_ListD := Reverse@xD f@x_qD := 1 + x Cuando el argumento es un número no hay definición para f: In[109]:= f@3D Out[109]= f@3D Cuando es una lista se dispara primera regla: In[110]:= f@81, 2, 3<D Out[110]= 83, 2, 1< Para lo siguiente tampoco tenemos una definición: In[111]:= f@h@3DD Out[111]= f@h@3DD Pero sí para esto: In[112]:= f@q@3DD Out[112]= 1 + q@3D La siguiente definición usa patrones con secuencias de argumentos: Definimos una regla para s que se dispare cuando el argumento es una lista de 1 ó más elementos, todos ellos numeros. In[113]:= s@a : 8__ ? NumberQ<D := Apply@Plus, aD La función Apply sirve para cambiar la cabeza de una expresión por la que nosotros deseemos. Fíjate que al cambiar a una lista de cosas la cabeza List por la cabeza Plus, fabricamos una expresión en la que se van a sumar los elementos de la lista. In[114]:= s@810, 5, 18<D Out[114]= 33 Observa el proceso de evaluación: In[115]:= Trace@s@810, a, 18<DD ColumnForm 8NumberQ@10D, True< 8NumberQ@aD, False< s@810, a, 18<D Out[115]= s@810, a, 18<D Se comprueba si los argumentos de la lista son todos números, después se fabrica la expresión de suma y se evalúa. Con otro tipo de argumentos el símbolo s se queda sin evaluar: In[116]:= s@3D Out[116]= s@3D In[117]:= s@81, a, 3<D Out[117]= s@81, a, 3<D x_ se refiere a una sola expresión, x_ _ se refiere a una o más expresiones y x _ _ _ se refiere a cero o más expresiones. El resultado es una "secuencia desnuda", que automáticamente desaparece cuando va metida en cualquier otra expresión: In[118]:= r@x__D := Reverse@8x<D In[119]:= r@a, b, cD Out[119]= 8c, b, a< In[120]:= q@x__D := x In[121]:= q@a, b, cD Out[121]= Sequence@a, b, cD In[122]:= f@q@a, b, cDD Out[122]= f@a, b, cD Borramos: In[123]:= Remove@"Global‘*"D à Programación Funcional El estilo de programación convencional, "imperativo", está basado en la modificación de los contenidos de ciertas "variables" mediante construcciones de control de flujo. Las asignaciones no son "igualdades" en el sentido matemático del término y, por tanto, el orden de las instrucciones es fundamental para la corrección del programa. El análisis de un programa no es sencillo, dado que existe un "estado" de las variables, que va cambiando, y que modifica el efecto que tendrá cada instrucción en distintos momentos de un programa. El estilo de "programación funcional" trata de aproximarse a la práctica matemática usual, donde se utilizan "definiciones" de objetos que luego podrían sustituirse en cualquier otro punto del programa sin afectar a su funcionamiento (transparencia referencial). No existe el concepto de "variable" a la que podamos asignar un valor, y la ejecución del programa consiste ú nicamente en la evaluación de funciones que con la misma entrada siempre producirán la misma salida. Se han inventado muchos lenguajes funcionales. Uno particularmente atractivo es Haskell, que posee evaluación "no estricta" y una forma restringida pero muy práctica de pattern matching. La evaluación no estricta (perezosa) permite trabajar de forma muy elegante con estructuras potencialmente infinitas de las que se calcularán sólo los términos necesarios. El pattern matching se usa para expresar las definiciones de "definiciones" de objetos que luego podrían sustituirse en cualquier otro punto del programa sin afectar a su funcionamiento (transparencia referencial). No existe el concepto de "variable" a la que podamos asignar un valor, y la ejecución del programa consiste ú nicamente en la evaluación de funciones que con la misma entrada siempre producirán la misma salida. Se han inventado muchos lenguajes funcionales. Uno particularmente atractivo es Haskell, que posee evaluación "no estricta" y una forma restringida pero muy práctica de pattern matching. La evaluación no estricta (perezosa) permite trabajar de forma muy elegante con estructuras potencialmente infinitas de las que se calcularán sólo los términos necesarios. El pattern matching se usa para expresar las definiciones de una función en diferentes tipos de entrada de manera mucho más clara que usando preguntas explícitas. Este lenguaje y otros de su estilo son muy prácticos para el prototipado rápido de muchos problemas de programación típicos, a veces sin demasiada pérdida de eficiencia. Sin embargo, por ahora no disponen del conjunto de bibliotecas matemáticas necesarias para el cálculo científico ni permiten manipular cómodamente expresiones matemáticas arbitrarias. Mathematica incluye muchas construcciones de programación funcional. En realidad,es el estilo de programación recomendado y eficiente para este sistema (aunque la sintaxis no es siempre tan elegante y concisa como p.ej. la de Haskell). La evaluación es estricta (más próxima a la programación convencional) por lo que las estructuras siempre tienen un tamaño finito concreto y se evalú an completamente aunque sólo necesitemos una parte de ellas (esto solo significa que tenemos que ser un poco más cuidadosos al programar). Por otro lado, el "motor" de pattern matching es tremendamente potente y eficiente, permitiendo patrones de una gran complejidad y poder expresivo, imprescindibles en un entorno de cálculo simbólico. Veamos algunos ejemplos de programación funcional. En primer lugar vamos a definir funciones numéricas sencillas. En todos los casos se sustituye la iteración y la asignación por recursión. Potencias enteras: In[124]:= pot@n_, 0D := 1 pot@n_, m_D := n pot@n, m - 1D Máximo común divisor: In[126]:= mcd@x_, 0D := x mcd@x_, y_D := mcd@y, Mod@x, yDD Cualquier función (no solo el ejemplo típico del factorial) se puede expresar de esta forma. Sin embargo, el enorme poder expresivo de la programación funcional se manifiesta en la manipulación de expresiones estructuradas, especialmente listas. Cualquier operación de manipulación de listas se puede expresar en términos de las primitivas First, Rest, y Prepend (históricamente, "cons"). First devuelve el primer elemento de la lista y Rest la lista sin el primer elemento. Prepend[a,l] añade como cabeza a l. Por cuestiones de eficiencia,dependiendo de la implementación,muchas operaciones de listas se hacen directamente, sin recurrir a las primitivas básicas. Longitud de una lista (por supuesto,disponemos de la primitiva Length): In[128]:= lon@8<D := 0 lon@8a_, b___<D := 1 + lon@8b<D Otra posiblidad: In[130]:= lon2@8<D := 0 lon2@x_D := 1 + lon2@Rest@xDD Suma de los elementos de una lista: In[132]:= sumalist@8<D := 0 sumalist@x_D := First@xD + sumalist@Rest@xDD Una construcción funcional muy importante es Map. Map[f,list] construye una lista que contiene el resultado de aplicar una función f a cada elemento. In[134]:= Map@Sqrt, 81, 4, 9, 100, 144<D Out[134]= 81, 2, 3, 10, 12< Como se usa mucho tiene la abreviatura /@ : In[135]:= Sqrt 81, 4, 9, 100, 144< Out[135]= 81, 2, 3, 10, 12< ¿Cómo definirías Map? Apply es otro ejemplo de construcción funcional. Simplemente cambia la "cabeza" de una expresión. Sirve p.ej. para pasar a una función una lista de argumentos que están en una lista. El símbolo @@ es una abreviatura de Apply: Cálculo de factorial mediante cambio de cabeza a la lista de números: In[136]:= Times Range@5D Out[136]= 120 Definimos una función que calcula la media de los elementos de una lista. Plus x In[137]:= med@x_ListD := Length@xD In[138]:= med@84, 8, 16, 32<D Out[138]= 15 In[139]:= med@810, 20, c<D Out[139]= 30 + c 3 Es posible definir funciones "puras", sin nombre (expresiones de "lambda" cálculo): In[140]:= Function@algo, algo2 + 1D Out[140]= Function@algo, algo2 + 1D A esa función se le pasan argumentos entre corchetes, igual que a las demás: In[141]:= Function@algo, algo2 + 1D@5D Out[141]= 26 Se puede abreviar con la notación & (Function) y # (el argumento): In[142]:= H#2 + 1 &L@5D Out[142]= 26 Range construye una lista desde 1 hasta el valor deseado: In[143]:= Range@5D Out[143]= 81, 2, 3, 4, 5< En el siguiente ejemplo aplicamos una función pura: In[144]:= #2 + 1 & Range@10D Out[144]= 82, 5, 10, 17, 26, 37, 50, 65, 82, 101< (Curiosamente, podemos derivar funciones puras:) In[145]:= Function@x, 2 x2 + 3 Cos@xDD ’ Out[145]= Function@x, 4 x - 3 Sin@xDD In[146]:= Log@#D & ’’ 1 #1 Out[146]= - 2 & Otra construcción funcional importante es Select, que sirve para extraer de una lista los elementos que cumplan un predicado: In[147]:= Select@Range@100D, PrimeQD Out[147]= 82, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97< Una alternativa es usar Cases, que selecciona elementos teniendo en cuenta no un predicado sino un patrón, y adema´s permite transormarlo en el resultado. Es muy función muy potente: In[148]:= Cases@881, 2<, 3, 85, 7<, 8<, x_ListD Out[148]= J 1 2 N 5 7 In[149]:= Cases@881, 2<, 3, 85, 7<, 8<, x_List ¦ Length@xDD Out[149]= 82, 2< Usamos la flecha ¦ (se teclea como :>) para que la parte derecha de la regla se evalúe al usar la regla (análogamente a :=) y no en el momento de definirla. In[150]:= Cases@881, 2<, 3, 83, 4, 5<, 85, 7<, 8<, 8a_, b_< ® b2 D Out[150]= 84, 49< Aunque como tal no se usa mucho en la práctica, la función Fold es una de las herramientas básicas de la programación funcional. La mejor manera de explicar su funcionamiento es pensar que Fold[f,x,list] cambia la "coma" de separación de los elementos de la lista por la función f, y como primer valor usa x: In[151]:= Fold@f, x, 8a, b, c<D Out[151]= f@f@f@x, aD, bD, cD In[152]:= Fold@Plus, 3, 8a, b, c<D Out[152]= 3 + a + b + c Existe el correspondiente FoldList, que devuelve en una lista todos los pasos: In[153]:= FoldList@Plus, x, 8a, b, c<D Out[153]= 8x, a + x, a + b + x, a + b + c + x< Ejercicio: calcular la distribución empírica (acumulada) de un conjunto de observaciones aleatorias. (¿un "one−liner"?mejor no...). Usando variantes de Fold podemos escribir muchísimos algoritmos y funciones típicas. Nest sirve para "anidar" funciones: In[154]:= Nest@h, x, 5D Out[154]= h@h@h@h@h@xDDDDD In[155]:= Nest@1 H1 + #L &, x, 20D Out[155]= 1 1 1 + 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1 1+ 1+x Los puntos fijos de funciones se calculan mediante FixedPoint: In[156]:= FixedPoint@Sqrt, 0.5D Out[156]= 1. Se puede construir una lista de los valores por los que va pasando: In[157]:= FixedPointList@Sqrt, 2.D Out[157]= 82., 1.41421, 1.18921, 1.09051, 1.04427, 1.0219, 1.01089, 1.00543, 1.00271, 1.00135, 1.00068, 1.00034, 1.00017, 1.00008, 1.00004, 1.00002, 1.00001, 1.00001, 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.< Podemos aprovechar para experimentar con la función g que definimos antes: La volvemos a definir (por si acaso la habíamos borrado) g@n_ ? EvenQD := n 2 In[158]:= g@n_ ? OddQD := 3 n + 1 Se cree que esta función aplicada repetidamente desde cualquier número siempre acaba por caer a 1. Vamos a comprobarlo en algunos casos. Primero definimos una función que comprueba la conjetura con un cierto número. NestWhileList sirve para hacer llamadas anidadas, guardando los resultados en una lista, mientras se cumpla una condición: In[160]:= conj@n_D := NestWhileList@g, n, # ¹ 1 &D Probamos la conjetura con algunos valores. Por ejemplo con 11 se regresa pronto al 1: In[161]:= conj@11D Out[161]= 811, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1< In[162]:= conj@11D Length Out[162]= 15 Con 27 tarda algo más en regresar, pero lo hace: In[163]:= conj@27D Out[163]= 827, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1< Vamos a representar gráficamente lo que tardan en regresar los 1000 primeros valores (tarda un poco en calcularlo). Primero construimos una lista con las longitudes: In[164]:= lon = Table@Length@conj@jDD, 8j, 2, 3000<D; Luego la representamos. ListPlot sirve para representar gráficamente los elementos de una lista. In[165]:= ListPlot@lonD 200 150 100 50 500 1000 1500 2000 2500 3000 Out[165]= Graphics Parece que hay cierta textura... Podemos modificar la representación dando opciones de dibujo a la orden ListPlot. En este caso saco los primeros 100 elementos de la lista y los muestro con los puntos unidos por líneas. La opción PlotRange sirve para que aparezcan en el dibujo todos los puntos, incluyendo los más extremos. In[166]:= ListPlot@Take@lon, 100D, PlotJoined ® True, PlotRange ® AllD 120 100 80 60 40 20 20 40 60 80 100 Out[166]= Graphics Borramos: In[167]:= Remove@"Global‘*"D Es conveniente estudiar en la ayuda todas las construcciónes funcionales. Ejercicio: intentar la definición recursiva, funcional, de la transpuesta de una matriz. Construcciones de control de flujo de tipo imperativo Por supuesto, disponemos de las construcciones de programación usuales (for, if, etc.): In[168]:= For@i = 1, i £ 5, i ++, Print@2i DD 2 4 8 16 32 Cuando necesitamos definir variables locales usamos la construcción Module: In[169]:= mcd@n_, m_D := Module@8a, b, r<, If@n > m, a = n; b = m, a = m; b = nD; While@Hr = Mod@a, bDL ¹ 0, a = b; b = rD; Return@bDD In[170]:= mcd@100, 150D Out[170]= 50 Sin embargo muchas veces es mejor usar otras construcciones más naturales del lenguaje. Borramos: In[171]:= Remove@"Global‘*"D à Listas, matrices, dot Los "arrays" (listas) se construyen mediante Table y se accede a sus elementos con doble corchete [[ ]]: In[172]:= m = Table@2i , 8i, 0, 10<D Out[172]= 81, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024< Si las listas contienen únicamente enteros o reales, internamente se usa una representación muy eficiente. In[173]:= m@@4DD Out[173]= 8 Muchas operaciones se realizan sobre todos los elementos de una lista: In[174]:= m + 10 Out[174]= 811, 12, 14, 18, 26, 42, 74, 138, 266, 522, 1034< In[175]:= Attributes@PlusD Out[175]= 8Flat, Listable, NumericFunction, OneIdentity, Orderless, Protected< La suma es asociativa, se mete en las "listas", si acepta números dará números, Plus[1] = 1, conmutativa In[176]:= 2m Out[176]= 82, 4, 16, 256, 65536, 4294967296, 18446744073709551616, 340282366920938463463374607431768211456, 115792089237316195423570985008687907853269984665640564039457584007913129639 936, 134078079299425970995740249982058461274793658205923933777235614437217640300 73546976801874298166903427690031858186486050853753882811946569946433649006 084096, 179769313486231590772930519078902473361797697894230657273430081157732675805 50096313270847732240753602112011387987139335765878976881441662249284743063 94741243777678934248654852763022196012460941194530829520850057688381506823 42462881473913110540827237163350510684586298239947245938479716304835356329 624224137216< In[177]:= Log@2, 2m D Out[177]= 81, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024< El producto de matrices y el producto matriz vector se representa con un punto: (o la función Dot) Creamos dos matrices: In[178]:= m1 = Table@i * j, 8i, 2<, 8j, 3<D Out[178]= J 1 2 3 N 2 4 6 La matriz es una lista de listas, pero también se puede representar como In[179]:= m2 = Table@ij , 8i, 3<, 8j, 4<D i1 1 1 1 z y z 4 8 16 z z z j k 3 9 27 81 { j j Out[179]= j j j2 Calculamos su producto matricial: In[180]:= m1.m2 Out[180]= J 14 36 98 276 N 28 72 196 552 El producto matriz vector también usa el punto: In[181]:= m1.81, 2, 3< Out[181]= 814, 28< Importante: a) No se distingue entre vectores fila o columna. b) Dot funciona para "tensores" matrices multidimensionales de cualquier número de dimensiones... Disponemos de operaciones matriciales usuales: In[182]:= m3 = m2.Transpose@m2D 30 120 y i 4 z z z 340 1554 z j z 120 1554 7380 k { j j Out[182]= j j j 30 In[183]:= Inverse@m3D 27 i j 8 j j j 5 j Out[183]= j j j j 4 j 5 k 24 5 - 4 y z z z 109 z z z - 1164 z z z 115 z 6984 { 5 24 105 194 109 - 1164 In[184]:= Inverse@m3D N -1.25 0.208333 y i 3.375 z z 0.541237 -0.0936426 z z z j -1.25 k 0.208333 -0.0936426 0.0164662 { j j Out[184]= j j j Muchas operaciones matriciales funcionan también con matrices simbólicas: In[185]:= InverseAJ b i -2+b j j Out[185]= j j k - -2+b 1 1 2 NE 1 b 2 - z -2+b y z z z 1 -2+b { Ejemplo de una función Listable... g@x_D := 8x, x< In[186]:= SetAttributes@g, ListableD In[188]:= g@4D Out[188]= 84, 4< Se "mete dentro" de un argumento de tipo lista: In[189]:= g@8a, b, c<D i a az y z bz z z j c c k { j j Out[189]= j j jb Esta función tiene la misma definición, pero al no tener el atributo listable no se distribuye dentro los argumentos de tipo lista: In[190]:= h@x_D := 8x, x< In[191]:= h@4D Out[191]= 84, 4< In[192]:= h@8a, b, c<D Out[192]= J a b c N a b c Si lo deseamos, podemos trabajar con vectores fila y columna explícitos, usando matrices de una sola fila o columna: In[193]:= col = List 8a, b, c< iay j z z j Out[193]= j j z jbz z kc{ In[194]:= fil = 88x, y, z<< Out[194]= H x y zL In[195]:= fil.col Out[195]= H a x + b y + c z L El único problema es que no produce un escalar, sin una matrix 1x1. In[196]:= col.fil iax ay azy z z by bzz z z j kcx cy cz{ j j Out[196]= j j jbx Mejor Outer: In[197]:= Outer@Times, 8a, b, c<, 8x, y, z<D iax ay azy z z by bzz z z j kcx cy cz{ j j Out[197]= j j jbx In[198]:= Outer@h, 8a, b, c<, 8x, y, z<D MatrixForm h@a, xD h@a, yD h@a, zD y i j z j j z j h@b, xD h@b, yD h@b, zD z z j z j z h@c, xD h@c, yD h@c, zD k { Out[198]//MatrixForm= También tenemos Inner, una generalización de Dot: In[199]:= Inner@f, 8a, b, c<, 8x, y, z<, gD Out[199]= g@f@a, xD, f@b, yD, f@c, zDD In[200]:= Inner@Times, 8a, b, c<, 8x, y, z<, PlusD Out[200]= a x + b y + c z Vamos a escribir un algoritmo conciso para generar una permutación aleatoria de números. La idea es pegar a los números del 1 a 10 unos números aleatorios, emparejando las dos listas con Transpose. Se ordenan y se cogen todos los segundos elementos: In[201]:= Last HHTranspose 8Table@Random@D, 810<D, Range@10D<L SortL Out[201]= 82, 9, 8, 5, 4, 10, 1, 3, 7, 6< Veamos el proceso paso a paso: In[202]:= 8Table@Random@D, 810<D, Range@10D< Out[202]= J 0.996618 0.318965 0.697993 0.0535312 0.983696 0.264338 0.268539 0.561153 1 2 3 4 5 6 7 8 In[203]:= Transpose@%D 0.996618 1 y i j z j j z 0.318965 2 z j z j z j z j z j z 0.697993 3 j z j z j z j z j z 0.0535312 4 j z j z j z j z 0.983696 5 j z j z j z Out[203]= j z j z 0.264338 6 j z j z j z j j z 0.268539 7 z j z j z j z j z j z 0.561153 8 j z j z j z j z j z 0.135592 9 j z j z k 0.765438 10 { In[204]:= Sort@%D i 0.0535312 4 z y j j j z 0.135592 9 z j z j z j z j z j z 0.264338 6 j z j z j z j z j z 0.268539 7 j z j z j j z 0.318965 2 z j z j z z Out[204]= j j j z 0.561153 8 z j z j z j z j z j z 0.697993 3 j z j z j z j z j z 0.765438 10 j z j z j z j z j z 0.983696 5 j z j z 0.996618 1 k { In[205]:= Last % Out[205]= 84, 9, 6, 7, 2, 8, 3, 10, 5, 1< (Por supuesto, en una biblioteca tenemos RandomPermutation) Tenemos operaciones matriciales típicas de cálculo científico: valores y vectores propios, valores singulares, etc. Borramos: In[206]:= Remove@"Global‘*"D à Ajuste de mínimo error cuadrático En Mathematica podemos resolver cómodamente ajustes de mínimos cuadrados. In[207]:= datos = Table@8x, x2 + Random@Real, 8-2, 2<D<, 8x, 0, 5, .1<D; Mostramos los primeros: In[208]:= Take@datos, 5D 0 i j j j 0.1 j j j j Out[208]= j 0.2 j j j j j j j 0.3 k 0.4 -1.96865 y z z -1.85859 z z z z z 0.206314 z z z z z -1.61708 z z z 0.575433 { In[209]:= g1 = ListPlot@datosD 25 20 15 10 5 1 2 3 Out[209]= Graphics In[210]:= sol = Fit@datos, 81, x<, 8x<D Out[210]= -4.63811 + 5.09923 x 4 5 In[211]:= g2 = Plot@sol, 8x, -2, 7<D 30 20 10 -2 2 4 6 -10 Out[211]= Graphics Es muy cómodo unir los dos gráficos de manera consistente: In[212]:= Show@g1, g2D 30 20 10 -2 2 4 6 -10 Out[212]= Graphics El ajuste lineal es inadecuado. Vamos a probar con un polinomio de orden mayor: In[213]:= sol2 = Fit@datos, 81, x, x2 , x3 , x4 <, 8x<D Out[213]= -1.49295 + 3.65777 x - 2.03306 x2 + 0.905784 x3 - 0.0877753 x4 In[214]:= g3 = Plot@sol2, 8x, -2, 7<D 30 20 10 -2 2 -10 -20 Out[214]= Graphics 4 6 In[215]:= Show@g1, g3D 30 20 10 -2 2 4 6 -10 -20 Out[215]= Graphics Por supuesto, en este caso el polinomio adecuado es de orden 2, pero esto puede no saberse en situaciones reales. Podemos comparar las dos soluciones: In[216]:= Show@g1, g2, g3D 30 20 10 -2 2 4 6 -10 -20 Out[216]= Graphics El problema también puede resolverse fácilmente programando la solución en el lenguaje de Mathematica. Pero en este caso resulta mucho más conveniente utilizar la función incorporada Fit. Borramos: In[217]:= Remove@"Global‘*"D à Gráficos Son expresiones como las demás, pero hay funciones que, como efecto colateral, las muestran. Las expresiones gráficas combinan en listas anidadas primitivas gráficas (puntos, líneas, etc.) y directivas gráficas (tamaños, colores, ancho, etc.): In[218]:= dibu = 8 8RGBColor@1, 0.5, 0D, [email protected], Point@81, 4<D, Point@8-2, 2<D<, 8RGBColor@1, 0, 1D, AbsoluteThickness@2D, Line@880, 0<, 81, 0<, 81, 1<, 80, 1<, 80, 0<<D< <; In[219]:= Graphics@dibuD Show@#, AspectRatio ® Automatic, Frame ® TrueD &; 4 3 2 1 0 -2 -1.5 -1 -0.5 0 0.5 1 También hay primitivas 3D. (Y visores OpenGL gratuitos.) Se pueden exportar los objetos gráficos a cualquier formato (jpeg, eps, etc.). à Otras cosas Se puede invocar una función de varias maneras: In[220]:= Sqrt@4D Out[220]= 2 In[221]:= Sqrt 4 Out[221]= 2 In[222]:= 4 Sqrt Out[222]= 2 Los corchetes son los más seguros porque no hay problema de ambigüedad. Las dos barras sirven para tomar toda la expresión de la izquierda y aplicar finalmente una función. En una celda podemos poner varias expresiones que se evaluarán una detrás de otra. Si no deseamos mostrar el resultado de una expresión ponemos punto y coma detrás. In[223]:= 2 + 2 3 + 3; 4+4 Out[223]= 4 Out[225]= 8 Cuando estamos corrigiendo la definición de un símbolo que tenga varias reglas asociadas, conviene poner Clear al principio para que cada vez que se evalúe el juego de definiciones, las reglas anteriores, erróneas, se borren. Si no se hace así puede ser que en las pruebas se obtengan resultados incorrectos. In[226]:= f@x_IntegerD := 3 x In[227]:= f@3D Out[227]= 9 Si ahora cambiamos la definición In[228]:= f@x_D := 2 x El resultado no cambia porque toma la regla anterior que es más específica: In[229]:= f@3D Out[229]= 9 Las dos reglas coexisten: In[230]:= [email protected] Out[230]= 6. En estos casos conviene borrar al principio las definiciones del símbolo para que se quede sólo con las definiciones explícitamente evaluadas después. In[231]:= Clear@fD f@x_D := 2 x In[233]:= f@3D Out[233]= 6 Por supuesto, en programas grandes conviene organizar un documento con secciones de definición, de ejemplos de uso, etc. Borramos: In[234]:= Remove@"Global‘*"D