Inicio / Wikis / Tutoriales / Introducción informal a Matlab y Octave - MATLAB (II)

Introducción informal a Matlab y Octave - MATLAB (II)

(13 opiniones)
Tutorial creado por Guillem Borrell i Nogueras. Extraido de: http://torroja.dmt.upm.es/%7Eguillem/matlab/
05 de Noviembre de 2006
Encriptación

5 - MATLAB (II)

2.6  Operadores

Cuando operamos elementos en Matlab, como cuando lo hacemos en cualquier lenguaje de programación, debemos tener en cuenta la dicotomía entre variable y argumento. Es tan importante porque debemos comprender perfectamente la sutileza de que los operadores no operan variables sino argumentos, en el caso de Matlab, matrices.

La consecuencia directa es que todos los operadores de Matlab son matriciales. En algunos casos, como la suma o la resta, los operadores matriciales son equivalentes a lo escalares (elementwise9 o elemento a elemento); en cambio la multiplicación y la potencia generan resultados completamente distintos.

Por ejemplo, si tomamos dos matrices cualquiera:
>> A=[1 2 3;4 5 6;7 8 9];
 >> B=[9 8 7;6 5 4;3 2 1];
 
Su multiplicación directa va a dar como resultado precisamente la multiplicación matricial:
>> A*B
 ans =
    30   24   18
    84   69   54
   138  114   90
 
¿Y si queremos una operación escalar? Todos los operadores de Matlab pasan de matriciales a escalares añadiendo un punto justo antes del símbolo del operador. En el caso de las matrices anteriores:
>> A.*B
 ans =
    9  16  21
   24  25  24
   21  16   9
 
La potencia es la operación que más errores provoca cuando se es principiante con el Matlab. Cuando uno piensa en la operación de la potencia nunca piensa en ella como una operación matricial; es muy poco intuitivo. No pensamos que la operación:
>> A^2
 
Sea equivalente a:
>> A*A;
 
Operación que es matricialmente del todo lógica. Este comportamiento provoca dos errores típicos.
  1. El operador se negará a elevar a un exponente matrices que no sean cuadradas.
  2. Cuando el exponente es fraccionario da resultados que aparentemente no tienen ninguna lógica
Un ejemplo claro es lo que nos encontramos cuando elevamos la anterior matriz a una burda aproximación del número π:
>> A^3.14
 ans =
    691.22 -    0.43i   850.20 -    0.12i  1009.17 +    0.20i
   1567.33 -    0.05i  1925.90 -    0.01i  2284.47 +    0.02i
   2443.44 +    0.34i  3001.60 +    0.09i  3559.76 -    0.15i
 
¿Números complejos?

El problema es que en Matlab cualquier argumento puede ser una matriz; olvidarnos el punto antes de utilizar la potencia...
>> A.^3.14
 ans =
     1.0000    8.8152   31.4891
    77.7085  156.5906  277.5843
   450.4098  685.0189  991.5657
 
es uno de los errores más comunes. Por suerte los errores suelen ser tan evidentes que la sangre raramente llega al río. El problema llega cuando elevamos al cuadrado matrices cuadradas. Los resultados obtenidos con el operador matricial y escalar son parecidos y del mismo orden de magnitud con lo que pueden ser un gran escollo en el proceso de depuración.

2.6.1  Operadores aritméticos

x+y
Suma. Si ambos operandos son matrices el número de filas y columnas debe ser el mismo. Si uno de los dos operandos es un escalar su valor es sumado a todos los elementos del otro
x-y
Resta. Las consideraciones sobre matrices son las mismas que con el operador suma.
x*y
Multiplicación matricial. Si ambos elementos son matrices el número de filas y de columnas debe ser el mismo. Si uno de los dos operandos es un escalar su valor es multiplicado a todos los elementos del otro.
x.*y
Multiplicación elemento a elemento. Si ambos operandos son matrices el número de filas y de columnas debe coincidir.
x/y
``División de izquierda a derecha''. Esta operación es equivalente a:
( (y⊤)-1x⊤ ) ⊤
con la diferencia que en este caso no se calcula la inversa. En el caso que la matriz y no sea cuadrada se da una solución con la condición de mínimo error10.
x./y
División de izquierda a derecha elementwise. Se divide cada elemento de la matriz x por cada elemento de la matriz y.
xy
División de derecha a izquierda. Esta operación es equivalente a:
x-1y
y sirve para resolver sistemas de ecuaciones lineales. Como en la división anterior no se calcula efectivamente la inversa de la matriz, de modo que en el caso que no sea cuadrada seguirá dando un resultado. Analizaremos este operador con mucha más profundidad en el apartado dedicado a álgebra lineal.
x.y
División de derecha a izquierda elementwise.
xy
x**y
Potencia. Si ambos operadores son escalares el resultado es x elevado a la potencia de y. Si x es un escalar y y es una matriz cuadrada el resultado se calcula a partir de un desarrollo de autovalores. Si x es una matriz cuadrada e y es un entero el resultado es la multiplicación de x por ella misma y veces y si y es un real se calcula el resultado por un desarrollo de autovalores. Cuando tanto x como y son matrices el resultado es error. La notación ** sólo se puede usar en Octave.
x.y
 
x.**y
Potencia elementwise. Si los dos operandos son matrices deben ser del mismo tamaño. La notación .** sólo se puede usar en Octave
-x
Negación
x'
Traspuesta compleja conjugada. Si la matriz x está compuesta por números reales el resultado es exactamente el mismo que para la traspuesta. Si hay algún argumento complejo el operador es equivalente a hacer conj(x.').
x.'
Traspuesta 11. Nótese que el uso de la notación usual elementwise no tiene el mismo sentido.

2.6.2  Operadores de comparación

x<y
Verdadero si x es menor que y.
x<=y
Verdadero si x es menor o igual que y.
x==y
Verdadero si x es igual que y.
x>=y
Verdadero si x es mayor o igual que y.
x>y
Verdadero si x es mayor que y.
x!=y
 
x=y
Verdadero si x es distinto que y. En Octave son válidos ambos signos mientras que Matlab sólo soporta el segundo.

2.6.3  Operadores lógicos

Hay dos tipos de operaciones lógicos, los que interactúan con matrices y los que lo hacen con expresiones lógicas como las que nos encontramos en las estructuras if y while. Los del primer tipo son & para ``y'', | para ``o'' y ! para ``no''. Si decimos que operan con matrices es porque aplicados a matrices de condiciones lógicas devuelven una matriz del mismo tamaño, por ejemplo:
>> [1,2;0,1]&[0,1;0,1]
 ans =
   0  1
   0  1
 >> ![0,1;2,0]
 ans =
   1  0
   0  1
 
La expresión entera para condiciones lógicas es 0 para ``falso'' y distinto de 0 para ``verdadero'', es decir, lógica binaria usual.

Para componer expresiones lógicas se usan los símbolos && para ``y'' y || para ``o''. La diferencia entre estos y los anteriores es que el resultado siempre será booleano. Si se aplica a una matriz colapsará sus elementos con la función all para llegar a una expresión única. Como ya hemos dicho antes su aplicación principal se encuentra en las estructuras de control de ejecución como if y while.

En los capítulos posteriores veremos varias aplicaciones de estos operadores.

2.6.4  Operadores de comparación por bits en enteros

Matlab es también capaz de comparar y manipular los bits en enteros. Para eso dispone de las funciones siguientes:
>> bit<TAB>
 bitand    bitmax    bitset    bitxor
 bitcmp    bitget    bitor     bitshift
 >> a=5; %Un numero entero
 >> dec2bin(a) %Que tiene esta representacion decimal
 ans = 101
 >> a=bitset(a,1,0) %Reset del primer bit
 a = 4
 >> a=bitset(a,6,1) %Set del sexto bit
 a = 36
 >> dec2bin(a)
 ans = 100100
 >> b=bitset(1,6) % Una forma alternativa de set
 b = 33
 >> dec2bin(b)
 ans = 100001
 >> bitand(a,b) % y logico
 ans = 32
 >> dec2bin(bitand(a,b)) % En bits...
 ans = 100000
 >> bitor(a,b) % o logico
 ans = 37
 >> dec2bin(bitor(a,b)) % En bits...
 ans = 100101
 >> bitxor(a,b) % o exclusivo logico
 ans = 5
 >> dec2bin(bitxor(a,b)) % En bits...
 ans = 101
 

2.7  Variables

Debemos pensar en ellas como cajas que ocupan memoria, independientemente de lo que lleven dentro. Debe abstraerse la variable del argumento que contenga, en el fondo no es más que un nombre.

Por el hecho de ser un lenguaje de scripting las variables no deben declararse. Esto hace que programar sea mucho más sencillo, se haga con menos errores y en menos tiempo a costa de un mayor tiempo de ejecución. Esto también significa que la cantidad de memoria asignada a una variable es dinámica, podemos ampliar una matriz 12 sin ningún problema con el simple hecho de llenar un elemento que no exista.

Aquello que nosotros asignamos a una variable se llamará argumento13. A cada variable le asignaremos uno o varios argumentos, de modo que la variable no es más que un nombre por el que llamamos su contenido.

Las variables pueden ser cualquier secuencia de letras, no hay limitación en su número, sólo que deben empezar con un carácter tipo letra o con _ . Se distingue entre mayúsculas y minúsculas. Los nombres siguientes serían válidos:
x
 x15
 __hola_que_tal__
 fjalsbdgaoqiwbodj
 
El nombre de una variable es una función en sí misma, llamada sin argumentos sacará por pantalla el valor de la variable. Algunas variables tienen un valor por defecto como pi o ans. Algunas de estas variables son parte de la configuración interna del programa así que es importante conocerlas para no tener sorpresas desagradables.

2.7.1  Acceso a las variables:

Si nos dicen que una variable es local por defecto probablemente no entendamos nada. Saber si una variable es accesible o no por una función no es una tarea sencilla y depende de cómo se hayan declarado las variables. El esquema normal es el de la figura 2.3. Supongamos que somos una variable en el programa principal y en un instante de la ejecución somos el argumento de una función. No nos sucede absolutamente nada. Otra variable va a tomar nuestro valor en la cabecera de la función y su resultado se va a volcar en el programa principal. A no ser que el resultado se nos asigne, cambiando nuestro valor, seguirá sin sucedernos nada. En cambio las variables locales para la función son eliminadas de la memoria cuando termina su ejecución.


Figure 2.3: Comportamiento normal de una variable llamada por una función


Si le damos a una variable el atributo de global con la palabra clave global entonces esta variable podrá ser vista por cualquier unidad de código sin necesidad de llamarla en su cabecera. A estas variables no les importa si están en el programa principal o en una función, su contexto es toda la ejecución; pueden saltar a cualquier hilo como en el esquema de la figura 2.4:


Figure 2.4: Comportamiento de una variable global definida en el programa principal


Al estar definida como una variable global no sólo puede ser vista por la función; si además la función cambia su valor también cambiará en el programa principal. Un ejemplo de ello es el código siguiente:
>> global x=2
 >> x
 x = 2
 >> function f()
 global x
 x=3
 end
 >> f()
 x = 3
 
Como se ve debemos tener cuidado con los nombres de las variables globales, no es difícil estropear el código llamando una variable global en una función sin querer14.

Un tercer atributo es el de persistent
. Cuando dentro de una función indicamos que una variable es persistent estamos imponiendo que esta variable se guarde en memoria para la siguiente vez que se llame la función en vez de hacerla desaparecer, como es normal. El diagrama conceptual es el de la figura 2.5.


Figure 2.5: Propiedades de una variable persistente.


No tiene ningún sentido definir una variable como persistent en el programa principal porque las variables locales para el hilo principal no se destruyen hasta que termina la ejecución.

2.7.2  Funciones dedicadas a variables

Tenemos varias funciones íntimamente relacionadas con el concepto de variable, algunas de ellas son:
  • is_global que devuelve un 1 en el caso que la variable efectivamente lo sea
  • clear que borra el contenido de una o varias variables, funciones o variables globales dependiendo del parámetro que le pasemos
  • who que da una lista de las variables que en aquél mismo momento estén ocupadas por algún argumento. También acepta parámetros para variar su comportamiento.
  • whos , lo mismo que who pero nos da más información.
Importante:
Es necesario entender el concepto de variable local y global para comunicar funciones y scripts de una manera eficiente

2.7.3  Contenedores

La limitación las matrices es que sólo pueden contener elementos de un mismo tipo. Una matriz no puede contener una cadena de caracteres y un número a la vez. Si sumamos mentalmente dos matrices entenderemos facilmente por qué.

Necesitemos entonces definir un tipo
más complejo que un caracter o un escalar. Un tipo derivado es una extensión del concepto de argumento en el que se permite que una variable contenga un argumento múltiple con elementos de distinta naturaleza. Un tipo derivado es lo que permite asignar a una variable un número y una cadena de caracteres a la vez.

La estructura típica de los tipos derivados es la que tiene forma de árbol. Son llamadas estructuras de datos. En ellas, una variable primitiva contiene más variables adicionales que a su vez pueden contener más ramificaciones. Otro tipo es emular el comportamiento de una matriz o un vector y permitir que sus elementos sean de tipos distintos. Obviamente esta ganancia de potencia se pierde en los operadores. Una matriz puede ser sumada a otra matriz, en cambio un tipo derivado no puede ser sumado a otro tipo derivado ya que Matlab no tiene ninguna información de qué contiene cada uno.

Esto hace que la línea divisoria entre el concepto de variable y el de argumento se difumine. Podemos pensar en una matriz como un único argumento donde todos los elementos son del mismo tipo y los tipos derivados como estructuras de variables. Entrar en sutilezas teóricas no es muy conveniente, simplemente debemos centrarnos en la gran utilidad de las estructuras de datos o las celdas de variables ya que permiten agrupar en forma de única variable estructuras más complejas de lo habitual.

2.7.3.1  Estructuras de datos

Octave, al igual que la mayoría de los lenguajes de programación modernos, nos permite agrupar variables en estructuras tipo árbol. Podemos entonces agrupar distintos tipos de argumentos dentro de una variable. Para probar esta posibilidad vamos a escribir un script de prueba que se va a llamar estructura.m.
% estructura.m
 
 % Éste es el script de prueba para las variables de tipo estructura
 foo.num=1.234;   
 foo.string='hola mundo';   
 foo.options.option1=true;   
 foo.options.option2=false;
 
Lo guardamos en el directorio correspondiente y si nos vamos a la consola y tecleamos estructura qué obtenemos? Pues nada en absoluto porque le hemos pedido con los puntos y coma que no nos sacara ninguna información por pantalla. Si queremos que nos diga qué hay dentro de la variable foo debemos teclear foo en la consola y nos aparecerá lo siguiente:
>> estructura
 >> foo   
 foo =   
 {   
   num = 1.2340   
   options =   
   {   
     option1 = 1   
     option2 = 0   
   }   
   string = hola mundo   
 }
 
que es exactamente la estructura de argumentos que hemos introducido 15. Vemos la manera de introducir comentarios en un script de Matlab, con el signo %. Todo lo que escribamos a partir de éste símbolo será ignorado por el intérprete.

Además de muchas otras, la mayor utilidad de las variables de tipo estructura es que nos permiten acortar las cabeceras de las funciones. Pongamos como ejemplo un programa que se configura con unos veinte parámetros. Si una función necesita quince de ellos significa que cada vez que llamemos a la función en nuestro código aparecerá una cabecera con quince variables. Esto haría el código más pesado de escribir y mucho más difícil de leer y mantener. Podemos poner todos nuestros parámetros dentro de una variable que se llame parms, de tal manera que siempre que necesitemos un parámetro, por ejemplo Longitud, en cabecera simplemente llamamos a parms y dentro del programa nos referimos a parms.Longitud.

2.7.3.2  Cell Arrays.

Las celdas son matrices o hiper-matrices de variables. No debemos confundirlas con las matrices usuales que son estructuras de argumentos del mismo tipo. Una celda puede contener matrices, cadenas de texto y argumentos lógicos a la vez siempre que estén en celdas separadas. A diferencia de las estructuras de datos no tendremos que asignar un nombre a cada una de las celdas porque ya se les asignará un grupo de índices.

Podemos construir celdas de dos modos, el primero es declarar una variable como cell y darle una dimensiones. Por ejemplo construiremos una celda de 2 por 2 cuyos elementos parecidos a los contenidos en la estructura de datos foo:
>> foo = cell(2,2)
 foo =
 {
   [1,1] = []
   [2,1] = []
   [1,2] = []
   [2,2] = []
 }
 >> foo{1,1}=1.2340;
 >> foo{1,2}=[1,2;3,4];
 >> foo{2,1}='hola mundo';
 >> foo{2,2}=true;
 >> foo
 foo =
 {
   [1,1] = 1.2340
   [2,1] = hola mundo
   [1,2] =
     1  2
     3  4
   [2,2] = 1
 }
 
Como en el caso de las matrices convencionales podremos ampliar las celdas con sólo llenar una que no tenga ningún argumento asignado; las celdas ``sobrantes'' quedan vacías:
>> foo{3,1}=false
 foo =
 {
   [1,1] = 1.2340
   [2,1] = hola mundo
   [3,1] = 0
   [1,2] =
     1  2
     3  4
   [2,2] = 1
   [3,2] = []
 }
 
El otro modo de iniciar una estructura de celdas es hacer lo mismo que con una matriz pero usando llaves en vez de corchetes. Para iniciar las celdas foo pero de modo abreviado:
>> foo={1.2340,[1,2;3,4];'hola mundo',true}
 foo =
 {
   [1,1] = 1.2340
   [2,1] = hola mundo
   [1,2] =
     1  2
     3  4
   [2,2] = 1
 }
 
Importante:
Las celdas pueden almacenar cualquier tipo de argumento, incluso funciones. Es más fácil y rápido escribirlos utilizando llaves.

2.7.3.3  La necesidad de los cell arrays y los tipos derivados

No todo en un lenguaje de programación son aplicaciones sencillas donde escalares, vectores y matrices bastan. Tampoco es cierto que el dato más complejo suelen ser los argumentos que pasamos a las funciones. Un programa puede requerir variables que contengan tipos de gran complejidad, normalmente obligados por el propio algoritmo.

Supongamos que intentamos una población de datos especialmente compleja, algo usual en una base de datos. Pongamos como ejemplo una descripción de todos los trabajadores de una empresa. Esto es un ejemplo, seguramente no utilizaremos un lenguaje de programación orientado a cálculo numérico para resolver un problema como este pero otras aplicaciones como los algoritmos genéticos pueden requerir tipos derivados especialemnte complejos.

Sigamos con el ejemplo definiendo nuestro tipo Empleado. Cada empleado de la empresa proporciona los siguientes datos:
  • Nombre completo
  • Nacionalidad
  • Documento de identidad
    • Tipo
    • Número
    • Fecha de expedición
    • Fecha de caducidad
  • Dirección de contacto
  • Números de teléfono
    • Número de la dirección de contacto
    • Teléfono movil de empresa
    • Teléfono movil personal
Cuando nuestros datos son muy complejos solemos pensar durante largo tiempo el método más adecuado de almacenar todos los datos. Crear una variable para cada trabajador no es muy recomendable porque es probable que tengamos que iterar sobre el total de ellos. Tampoco parece muy inteligente partir todos los datos en varias matrices y relacionarlas según los índices.

La abstracción llevada a los lenguajes de programación modernos tiene una consecuencia muy importante: debemos ser capaces de seguir uilizando las mismas herramientas de programación sea cual sea el dato con el que estemos trabajando. Si un lenguaje se ha diseñado con el paradigma de la abstracción como prioridad podremos seguir escalando la complejidad de las estructuras de datos tanto como queramos sin perder las herramientas de cálculo. Matlab no es un prodigio en este sentido pero se defiende bien.

El elemento que mantiene la escalabilidad en las estructuras complejas de datos son los cell arrays. Al seguir una estructura matricial pueden encapsularse tantos cell arrays como queramos. Pueden contener tanto los tipos básicos (números y cadenas de texto) como estructuras de datos o otras celdas. De este modo podemos definir la estructura empleado del siguiente modo:
>> Empleado1={'Paquito Palotes Parrondo';'Deaqui';
 ... {'DNI',646843216,12122000,12122000};
 ... 'C Buenaesperanza 12 1,2';
 ... [666251487,555698541]}
 Empleado1 =
 
 {
   [1,1] = Paquito Palotes Parrondo
   [2,1] = Deaqui
   [3,1] =
 
   {
     [1,1] = DNI
     [1,2] = 646843216
     [1,3] = 12122000
     [1,4] = 12122000
   }
 
   [4,1] = C Buenaesperanza 12 1,2
   [5,1] =
 
     666251487  555698541
 
 }
 
Lo novedoso es que mantenemos la escalabilidad de la estructura indefinidamente. Podemos sin ninguna restricción encapsular todos los empleados en la variable Empresa como si fueran elementos de una fila en una celda:
>> Empresa={Empleado1,Empleado2,Empleado3}
Valora este capítulo: (13 opiniones)
Autor y licencia de 'Introducción informal a Matlab y Octave - MATLAB (II)'
Guillem Borrell i Nogueras Extraído de: http://torroja.dmt.upm.es/%7Eguillem/matlab/ CopyLeft
Este contenido ha sido recopilado por el equipo de Wikilearning. Todo el contenido recopilado se ha obtenido respetando y comunicando en nuestro site la licencia de cada fuente.
Wikilearning tiene permiso expreso por escrito de los autores para publicar los contenidos que ha extraído de otras webs, incluyendo su uso comercial.

Opiniona sobre 'Introducción informal a Matlab y Octave - MATLAB (II)' (13)

Tu nombre debe tener tres caracteres como mínimo.
Es necesario que te des de alta con una cuenta de correo válida.
Es necesario que te des de alta con una cuenta de correo válida.
El contenido del título de tu opinión debe tener tres caracteres como mínimo.
Es obligatorio que selecciones una valoración del recurso.
El contenido del comentario de tu opinión debe tener tres caracteres como mínimo.

Opina sobre este tutorial



* Valoración:
* Nombre:
* Correo electrónico:
* Título:
* Comentario:

Wikis relacionados con 'Introducción informal a Matlab y Octave - MATLAB (II)'

Este trabajo ha tenido en cuenta los supuestos teóricos analizados en el artículo “Competencias: Un... Más »
Las fotografias de flores (flora en general) quizas sean las que mejor se dejan enmarcar.... Más »
En la edición anterior, se explicó las bases de Netfilter/IPTables. En esta segunda entrega, se... Más »
Género gramatical y sexo no son, como muchos ingenuos o espontáneos usuarios de la lengua... Más »
En la primera parte se introdujo un estudio transtextual de la obra, además de discutir... Más »
¿Estás seguro de que deseas eliminar este capítulo?