Introducción a la programación en el entorno Matlab
 
------------------------------------------------------------------------------
NOTA: Para que MATLAB encuentre sus programas debe poner en la variable path la ruta hasta el directorio donde esté trabajando. Concretamente, aceptando que su ruta es C:\usuarios\MiDirectorio, debe escribir

path(path,'C:\usuarios\MiDirectorio')

También es posible situarse en el directorio de trabajo mediante la orden cd nombre_de_directorio. Esto nos permitirá ejecutar nuestros programas sin añadir el directorio al path. Además, mediante el comando ls, es posible ver qué archivos se encuentran en el directorio en el que estamos trabajando. El comando edit nos permite abrir con el editor cualquier archivo que se encuentre en el path o en el directorio de trabajo. 

Todas las operaciones mencionadas se pueden realizar directamente  en el entorno de ventanas mediante el ratón. Para añadir un directorio al path, basta con acceder al menú File>Set Path y con la opción Add Folder se puede añadir cualquier directorio. La opción Add with subfolders permite añadir directorios con todos los subdirectorios que estos incluyan. Además, el entorno de ventanas también permite cambiar de directorio de trabajo rápidamente y abrir cualquier archivo con una operativa similar a la de cualquier programa de Windows.
------------------------------------------------------------------------------

De forma similar a la introducción de comandos vía teclado, como hemos visto hasta ahora, podemos escribir las instrucciones en un fichero y, cuando el nombre del fichero sea tecleado en el prompt de la ventana de comandos, las instrucciones contenidas en él se ejecutarán secuencialmente.

Los programas en Matlab constan de dos tipos de entidades:

  1. Scripts, o ficheros de instrucciones tales que al término de la ejecución de las mismas, las variables en ellos definidas permanecen en el espacio de trabajo.
  2. Funciones, o ficheros que contienen una o varias veces la palabra function. Cada función está pensada como una porción de código que recibe un conjunto de parámetros, ejecuta instrucciones, y devuelve un resultado. Sin embargo, todas las variables internas de la función se pierden al salir del cuerpo de la misma y volver a la fracción de código que llamó a tal función. Además, por cada fichero sólo una función es visible desde el exterior del fichero (aquélla cuyo nombre coincide con el nombre del fichero). Otras funciones de ese fichero sólo podrán ser usadas desde el interior del mismo.

Como ejemplo, el fichero denominado MiScript.m consta de la siguiente línea de código

resultado = calculo(2,3)

y el fichero calculo.m consta de

function salida = calculo(parametro1, parametro2)

salida =parametro1 + parametro2; 

IMPORTANTE: A la hora de trabajar con Matlab, es mucho más útil trabajar con scripts que hacerlo directamente en la línea de comandos. Esto permite modificar fácilmente el código y reutilizarlo convenientemente, además de mantenerlo guardado para uso futuro. También es útil y aconsejable el dotar a las funciones y scripts creados de su correspondiente ayuda. Para ello, basta con escribir al comienzo del archivo una serie de líneas comentadas (recordemos el carácter % para comentar las líneas) donde se indique qué hace el programa, qué parámetros recibe y/o devuelve en caso de ser una función, etc... Tras crear la ayuda del script o función, basta con escribir la primera línea sin comentarios (o una simple línea en blanco) para crear el código, incluyendo los comentarios de éste que no aparecerán en la ayuda.

A modo de ejemplo, podemos ver qué ocurre al llamar a la función midiagonal.m: 

function [A B]=midiagonal(num,vect)
%Esta función genera, a partir de un vector y un escalar, una matriz
%diagonal cuyos elementos son los vector que resulta de multiplicar el
%escalar por el vector de entrada. Además, devuelve otro vector cuyos
%elementos son los de la diagonal de la matriz.
%Argumentos de entrada:
%num: escalar que multiplica al vector de entrada.
%vect: vector de entrada
%Resultado:
%A: matriz cuya diagonal es el vector resultante de num*vect
%B: vector cuyos elementos coinciden con la diagonal de la matriz A

A=diag(num*vect);
B=diag(diag(num*vect));

Nótese que las funciones pueden devolver también múltiples valores. Podemos observar su funcionamiento tecleando:

>>[res1 res2]=midiagonal(pi,ones(1,3));

Y obtener ayuda:

>>help midiagonal

Otra característica útil del entorno de trabajo Matlab reside en la posibilidad de guardar variables del espacio de trabajo en ficheros con extensión .mat. Para ello se utiliza la orden save. Esto permite recuperar valores obtenidos en sesiones anteriores mediante la orden load. Supongamos que queremos grabar en un archivo "misesion.mat" todas las variables que se han creado durante la práctica. Para ello sólo tendríamos que escribir:

>>save misesion

Si ahora borramos todas las variables del espacio de trabajo:

>>clear

Podemos comprobar que no hay ninguna variable al teclear whos. Para recuperarlas, simplemente tenemos que hacer uso del comando load:

>>load misesion 

Se deja al alumno la tarea de comprobar qué posibilidades permiten estos comandos para seleccionar las variables a guardar.

Ejercicio: escriba un script que obtenga el producto escalar de un vector por sí mismo (esto es, que calcule la norma del vector). Genere el vector mediante la llamada a una función. El vector debe contener los primeros cincuenta múltiplos de tres. La función asimismo, debe mostrar gráficamente dicho vector en pantalla. Genere la ayuda en línea tanto para el script, como para la función. El script debe también guardar en un archivo la norma del vector como única variable.
 

Control de flujo

MATLAB, al igual que la mayoría de los lenguajes de programación, incluye instrucciones para el control del flujo de sus programas, incrementando de esta forma la potencia de los cálculos realizables.

Bucles FOR

Estos bucles permiten la ejecución de un comando o grupo de comandos, un número fijo predeterminado de veces. Por ejemplo:
 

for i=1:n,  x(i)=0, end
 

asigna el valor 0 a los n primeros elementos del vector x. Si n es menor que 1, el comando central no se ejecutará ninguna vez. Si x no existe, o bien tiene menos de n elementos, se reservará memoria adicional de forma automática.

Una práctica común es la utilización de bucles anidados:
 

for i=1:m
        for j=1:m
             A(i,j)=1/(i+j-1) ;
        end
end
A

Es importante no olvidar que cada sentencia for debe concluir con su end correspondiente.
 
  

Bucles WHILE

Los bucles WHILE permiten la ejecución de un comando o grupo de comandos un número indeterminado de veces bajo el control de una condición lógica. Esto es, los comandos se ejecutarán mientras} se verifique dicha condición.

Como ejemplo mostramos los comandos necesarios para averiguar cuál es el primer número entero  cuyo factorial es un número de 100 dígitos:
 

n=1
while prod(1:n)< 1e100, n=n+1; end;
n

Otro ejemplo de utilización de while es el cálculo de la exponencial matricial. Como es sabido, su definición viene dada por la serie de potencias:
 

 expm(A) = I + A + (A^2)/2! + (A^3)/3! + ...
 

Resulta razonable sumar términos de la serie hasta que el resultado no varíe con la inclusión de más de ellos, debido a la precisión finita del ordenador. En el ejemplo siguiente A representa la matriz dada, E la exponencial de dicha matriz, F representa un sumando individual de la serie de potencias y k es el índice de dicho sumando. Las instrucciones centrales se ejecutarán hasta que F sea tan pequeño, que su suma a E no cambie a ésta.

E=zeros(size(A));
F=eye(size(A));
k=1;
while (norm(E+F-E,1)>0)
    E=E+F;
    F=(A^k)/prod(1:k);
    k=k+1;
end
 
Sentencia IF

En su forma más simple, la sentencia if se escribe en la forma siguiente (obsérvese que –a diferencia de C/C++/Java– la condición no va entre paréntesis, aunque se pueden poner si se desea):

if condicion
sentencias
end

Existe también la bifurcación múltiple, en la que pueden concatenarse tantas condiciones como se desee, y que tiene la forma:

if condicion1
bloque1
elseif condicion2
bloque2
else %opción por defecto para cuando no se cumplen las condiciones 1 ni 2
bloque3
end

donde la opción por defecto else puede ser omitida: si no está presente no se hace nada en caso de que no se cumpla ninguna de las condiciones que se han chequeado. Un ejemplo de uso podría ser:

if I == J
A(I,J) = 2;
elseif abs(I-J) == 1
A(I,J) = -1;
else
A(I,J) = 0;
end

Es interesante aquí observar qué obtenemos al ejecutar las sentencias anteriores dentro de dos bucles for anidados. Ejecutando help relop se puede obtener información sobre el funcionamiento de los operadores relacionales utilizados en las sentencias de control.

        Vectorización

Existe la tendencia entre los programadores experimentados de usar Matlab de la misma forma que un lenguaje de alto nivel como C. Sin embargo, esto conduce a una programación muy ineficiente si por ejemplo se usan bucles for para realizar operaciones como sumar los componentes de un vector (en lugar de usar la función sum). Otra forma de evitar los bucles for reside en el uso de operaciones vectoriales. En el ejemplo de la suma, se puede hacer de forma muy eficiente sin más que darse cuenta que la suma de los elementos de un vector fila puede obtenerse multiplicando dicho vector por un vector columna de unos. En efecto, la operación del producto interno calcula la suma.

La ineficiencia que presenta Matlab con los bucles se debe al hecho de ser un lenguaje interpretado. Por eso, los bucles for deben utilizarse sólo cuando es imprescindible. En general, más del 90% de los bucles que se usan en Matlab pueden ser reemplazados por su equivalente en código vectorial, que es más rápido.

Un ámbito donde surgen programas lentos se encuentra en los condicionales. Aparentemente, los tests condicionales nunca deberían vectorizarse, pero no es así. A continuación se presenta un ejemplo para la obtención simple de una señal impulso aprovechando que los resultados lógicos (verdadero o falso) se representan con números:

>>nn = [-20:80];

>>impulso = (nn == 0 );

Pruebe a representar el impulso con la función gráfica stem:

>>stem(nn,impulso)

Esta operativa capta la esencia de la fórmula matemática, ya que el impulso discreto se define como la señal que toma un valor distinto de cero sólo en n=0.

El siguiente ejemplo ilustra la vectorización mediante dos funciones que, de forma diferente, recortan una señal y la dejan dentro de unos límites. En primer lugar la versión lenta:

function y=clip(x,lo,hi)
%RECORTA --- los elementos más grandes y pequeños de la matriz x
%======> VERSIÓN LO MÁS LENTA POSIBLE <======
%
[M,N]= size(x);
for m = 1:M
for n=1:N
if x(m,n)>hi
x(m,n)=hi;
elseif x(m,n)<lo
x(m,n)=lo;
end;end;end;
y=x;

Y la versión vectorizada:

function y=clip(x,lo,hi)
%RECORTA --- los elementos más grandes y pequeños de la matriz x
%======> VERSIÓN RÁPIDA <======
%usando operadores lógicos matriciales sustituyendo a los bucles
y=(x.*[x<=hi])+(hi.*[x>hi]);
y=(y.*[x>=lo])+(lo.*[x<lo]);