Joan Soler-Adillon || Programació: Conceptes
Despatx 234 || 935 421 484
----------------------------------------------------------------
----------------------------------------------------------------
PROGRAMACIÓN: CONCEPTOS || MASTER EN ARTES DIGITALES
Sección 9: Funciones
1. Funciones
2. Parámetros
3. Retorno
4. Más ejemplos
Las funciones nos sirven para organizar el código y poderlo reciclar luego. Una función es una estructura que en un momento dado en el flujo del programa es invocada. Cuando esto pasa, las instrucciones que estén contenida en la función se van a ejecutar.
Podemos volver al ejemplo de la luz del segundo día, donde el programa decidía si era yo o un alumno, según la distancia de cada uno hacia la puerta, quien debía ir a cerrarla. Una vez tomada la decisión, no basta, de hecho, para que la puerta sea cerrada. Ir a cerrar la puerta es una acción compleja que consta de varias acciones concretas, que cuando estamos hablando de programación habrá que explicitar:
Estas acciones se pueden explicitar en el código principal, lo que en Processing sería el DRAW o bién puede crearse una función que las englobe (abrirLaPuerta), y que en el código principal sólamente tenga que invocarse esta función cada vez que se tenga que cerrar una puerta.
Entre otras ventajas, la funciones nos permiten organizar mejor el código Así, podemos por ejemplo crear un dibujo que requiere mucho código con una sola línea en el draw, y todo el código del dibujo contenido en una sola función, de manera que el DRAW nos queda así:
Source code: bitxo
Aquí, drawTheBitxo(); invoca una función que hemos creado para dibujar el animalito. Cuando llamamos la función, lo hacemos por su nombre, seguido de paréntesis. En el caso de una función que no recibe parámetros (como este), simplemente habrimos y cerramos paréntesis sin más para indicar a java que nos estamos refiriendo a una función.
Para crear la función, hay que tener en cuenta dos cosas. Si hay retorno (si devuelve o no algún valor), y si tiene parámetros. Como dedicamos a éstas dos cosas sendos apartados en esta sesión, de momento nos centraremos en funciones que no retornan nada y no reciben parámetros.
También sería el caso siguiente, donde utilizamos una función para hacer otro dibujo:
void setup(){ size(200,200); } void draw(){ background(0); //invocamos la función, que definimos más adelante: dibujaUnSputnik(); } //He aquí una función que dibuja un SPUTNIK: void dibujaUnSputnik(){ strokeWeight(3); stroke(255); fill(255,0,0); line(70,70,130,130); line(70,130,130,70); ellipse(100,100,35,35); }
Aquí podemos ver cómo declaramos una función:
El void debería sonar del SETUP y el DRAW. Es la palabra clave que indica que la función no retorna nada. De la misma manera, los paréntesis en blanco indican que la función no recibe parámetros.
Lo que estas dos cosas significan es que una función así se invocará de la siguiente manera:
Simplemente llamando la función por su nombre y los paréntesis en blanco provocaremos que en el punto en el código donde la función es invocada, el flujo de acciones salta hacia la llave donde empiezan las instrucciones de la función, sigue hasta que la función se cierra, y continúa luego hacia la línea que sigue la invocación. En el ejemplo, vuelve a empezar el DRAW.
2.- Parámetros
Lo que vimos hasta ahora no pasa de ser una mejor organización del código. Las funciones empiezan a ser mucho más interesantes cuando empezamos a utilizar los parámetros.
Por ejemplo, podemos tener una función que nos dibuje algo allí donde está el mouse
...allí donde clicamos...
Source code: sptnk01
Source code: sptnk02_clic
...o según una variable de posición:
Source code: sptnk03_pos
Aquí la gracia está en que en los tres casos utilizamos exactamente la misma función:
void dibujaUnSputnik(int x_, int y_){ strokeWeight(3); stroke(255); fill(255,0,0); line(x_-30,y_-30,x_+30,y_+30); line(x_-30,y_+30,x_+30,y_-30); ellipse(x_,y_,35,35); }
es decir, la reutilizamos. La misma función exactamente en tres sketch distintos. La diferencia está en el momento en que la función es invocada. En el primer y segundo ejemplos, en el DRAW, y en el tercero, en la función de sistema: MOUSEPRESSED, que se ejecuta cuando se clica el mouse.
Pero aquí la función tiene una característica distinta a las del punto anterior. La función dibujaUnSputnik recibe parámetros. Concretamente recibe dos. Dos enteros. De aquí que al declarar la función no dejemos el paréntesis en blanco:
Lo que está dentro del paréntesis son pues los parámetros, que se reciben en forma de variable. Unas variables que hay que declarar como vemos, y que llamamos en este caso x_ e y_.
Los valores que se asignarán a dichas variables serán los de los parámetros que especifiquemos al asignar la función. Así,
Asignaría a x_ el valor 87 y a y_ el valor 349 en dicha función. En nuestros ejemplos, lo que se les asigna es otra variable, en este caso variable de sistema, pero que no deja de ser un entero: mouseX y mouseY.
3.- Retorno
Otra cosa que pueden hacer las funciones, y aquí, aunque puede parecer que la cosa se complica, es cuando las cosas se vuelven interesantes. Además de poder o no recibir parámetros, una funcion puede o no devolver (retornar) valores. Hasta ahora hemos visto funciones que nunca devuelven nada. Este tipo de funciones simplemente se invocan, con o si parámetros, si más.
¿Pero qué pasa si una función devuelve un valor? ¿A quién se lo devuelve y cómo lo gestionamos? Pues de hecho es bien sencillo. Cuando invocamos una función que devuelve un valor, hemos de asignar éste valor a una variable (o utilizarlo directamente). Es algo que de hecho hemos estado ya haciendo con random.
Por ejemplo, si tenemos una función llamada calculines que nos realiza los dificilísimos calculos de hacer la media entre dos valores, la asignaríamos a una variable llamada resultado:
int resultado; resultado = calculines(32,439);
Y la función en sí sería algo así:
int calculines(int a_, int b_){ int aRetornar; aRetornar = (a_+b_)/2; return aRetornar; }
Otro aspecto muy importante ahora es la declaración de la función. Y aquí por fin cobrará sentido el famoso void: Cuando declaramos una función que devuelve un valor, debemos especificar el tipo de datos que devuelve al declararla. Así, una función que devuelve un entero se declararía por ejemplo así:
int dameUnEnteroBonito(){ //instrucciones }
Y una que nos ha de devolver un float:
float dameUnFlotador(){ //instrucciones }
Y, finalmente, hay que tener SIEMPRE, en una función que retorna algo, una instrucción de retorno al final. Dicha instrucción se llama return y va seguida de un valor que se retorna, valga la redundancia...
float dameUnFlotador(){ //instrucciones donde flotador es una variable del tipo float return flotador; }
Como siempre, mejor con ejemplos:
Imaginaros que tenemos dos equipos de programadores. Unos tienen que hacer un dibujo según lo rápido que se mueva el mouse. Los otros, contar los píxels blancos. Lo típico, vaya...
Pues con funciones cada equipo podría trabajar por separado y al final integrarlo todo en un solo sketch:
Source code: countdraw2
Y otro ejemplo, con envío de parámetros y retorno en una sola función. Podemos utilizar arrays, loops y funciones para hacer cosas como contar la posición media de un grupo de elementos:
Source code: mitja
4.- Más ejemplos
No, no olvidaremos a las pelotitas, pero ahora podemos utilizar una función para hacer que lo que rebota no sea una pelota, sinó el rebicho. Además, mirando el código veréis como en funciones es cuestión de organización.
Source code: bitxorebota
Y finalmente, otro ejemplo del uso de funciones y de paso introduciendo otras ideas de programación más interactiva.
Otro ejemplo del uso de funciones, y de paso de interacción:
Source code: follow
----------------------------------------------------------------------------------------------------