Definición y uso de funciones
Edit me

Introducción

En octave, las funciones pueden recibir cero, uno o varios parámetros y devolver cero, uno o varios resultados. Todos estos elementos son por defecto, matrices (o vectores), aunque tabién pueden ser strings o cualquier otra tipo de datos que soporte Octave.

Definición de Funciones

Una forma fácil de comenzar a escribir una función, es usando el comando edit seguido por el nombre de la función, acompañada de la extensión .m.

Por ejemplo, si queremos armar la función obtener_numero, ejecutamos el comando edit obtener_numero.m en la consola. Pueden pasar dos cosas:

  • Si la función ya existe, octave mostrará el código. Si queríamos implementar una función nueva, deberíamos elegir un nombre distinto.
  • Si la función no existe, preguntará si queremos crear el archivo obtener_numero.m en la carpeta de trabajo actual. A esto respondemos sí.

Al hacer esto, veremos en el editor un montón de comentarios (los cuales podríamos borrar si queremos) seguido por la definición de la función en sí. Algo como esto:

## Copyright (C) 2018 ferna
## 
## This program is free software: you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
## 
## This program is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
## 
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see
## <https://www.gnu.org/licenses/>.

## -*- texinfo -*- 
## @deftypefn {} {@var{retval} =} obtener_numero (@var{input1}, @var{input2})
##
## @seealso{}
## @end deftypefn

## Author: ferna <ferna@HR14>
## Created: 2018-09-06

function retval = obtener_numero (input1, input2)

endfunction

De lo cual, nos importa esta parte:

function retval = obtener_numero (input1, input2)
# Tu código va aquí
endfunction

Funcionamiento de las funciones

Las funciones empiezan con esta linea, donde se decalara los valores que devuelve, el nombre de la función (que generalmente debe coincidir con el nombre del archivo, pero sin el .m) y finalmente los parámetros que toma.

Por ejemplo, esta función no recibe ningún parámetro, y al llamarla como obtener_numero() devuelve el valor 7.

function numero = obtener_numero ()
  numero = 7;
endfunction

Parece un ejemplo muy pavo, pero analicemos este otro:

function numero = obtener_numero ()
  numero = 7;
  numero = 23;
endfunction

Ahora, la función obtener_numero() devolverá 23, dado que siempre devuelve el último valor que contienen las variables a la hora de terminar la función.

Funciones con parametros

Dada una matriz , puede descomponerse como la suma de una matriz simétrica y otra antisimétrica (o hermítica y antihermítica), es decir: , con:

Vamos a implementar una función a la que le pasemos una matriz A y devuelva la componente simétrica de la descomposición, S:

Para ello hacemos: edit descomp_simetrica.m y colocamos el siguiente código:

function S = descomp_simetrica (A)
  S = (A' + A)/2;
endfunction

Y podemos verificar que funciona: descomp_simetrica([1 2 3; 0 2 3; 3 0 1])

Funciones con varias salidas

Sería cómodo que la función descomp_simetrica(A) pueda devolver ambas partes de la descomposición: S y AS. Eso se puede hacer facilmente, listando entre [] los valores que la función devuelve, de esta forma:

function [S, AS] = descomp_simetrica (A)
  S  = (A + A')/2;
  AS = (A - A')/2;
endfunction

Pero si la llamamos, devuelve una sola matriz, igual que antes: descomp_simetrica([1 2 3; 0 2 3; 3 0 1]) ¿Qué paso? Es que para obtener el segundo parámetro, es necesario pedirlo formalmente:

A = [1 2 3; 0 2 3; 3 0 1];

[sim, antisim] = descomp_simetrica([1 2 3; 0 2 3; 3 0 1]);

disp(sim)
disp(antisim)

disp(sim+antisim);

Y vemos que funciona bien. Una forma de verificarlo sería observando que la matriz sim+antisim-A es nula. Puede calcular la norma infinito de la matriz con norm(sim+antisim-A, "inf").

Funciones con varias entradas

De forma análoga, una función puede recibir más de un parámetro:

function hipotenusa = calcular_hipotenusa(a, b)
  hipotenusa = sqrt(a.^2 + b.^2);
endfunction

Funciones anónimas

Muchas veces las funciones son pequeñas y con uso muy específico, lo cual hace que sea engorroso tener que armar un archivo aparte para ella. Por este y otros motivos, es posible definir una función en una sola linea:

calcular_hipotenusa_anonima = @(a,b) sqrt(a.^2 + b.^2);

Y si hacemos whos, podemos ver que hay una función llamada calcular_hipotenusa_anonima ¡como si fuese una variable!. Para los que vienen de C, pueden pensar que es un puntero a función (en Octave se llaman function handle. Resulta que las funciones son objetos válidos, como los números o matrices ¡incluso se pueden armar vectores de funciones! (en realidad, cell structs, que son como vectores… pero divertidos… y se usan con {} en lugar de []).

Las funciones anónimas están piolas para llamarlas recursivamente, o adaptar los parámetros de funciones molestas, o cosas así.

Punteros a funciones

Se puede obtener un puntero (o handler) a una función de carne y hueso colocando el operador @ delante de su nombre. Por ejemplo:

mi_funcion = @cos;

disp(mi_funcion(pi))

Eso permite pasar funciones como argumento a otras funciones, por ejemplo:

fsolve(@cos, 1)

fsolve sirve para hallar la raíz de la función que le pasas como primer parámetro, más cercana al valor que le pasas como segundo parámetro.

Vectores de funciones

Para meter funciones adentro de un vector, se utilizan los {}:

trigonometricas_radianes = {@sin, @cos, @tan}
trigonometricas_grados  = {@sind, @cosd, @tand}

# Uso las funciones guardadas:
disp("Estos dos tendrían que ser iguales");
trigonometricas_radianes{1}(pi/4)
trigonometricas_grados{1}(45)

# A ver si anda:
x = 2*pi*rand;
disp("Si da 1, las trigonometricas en radianes andan");
trigonometricas_radianes{1}(x).^2 + trigonometricas_radianes{2}(x).^2

xd = x*180/pi;
disp("Si da 1, las trigonometricas en grados andan");
trigonometricas_grados{1}(x).^2 + trigonometricas_grados{2}(x).^2

¿Y esto para que me sirve? Bueno, cuando tenes que comparar funciones para ver si todas dan lo mismo, podes meterlas en un struct (el coso con {}) e iterar ese struct con un for (esos suelen ser los únicos for que tienen sentido en octave… siempre y cuando parfor no ande… si no, usa parfor).