¡Saludos, estudiantes!. Vamos a ir concretando lo que sabemos ya sobre programación:
- Ya sabemos (en teoría) declarar y trabajar con variables y con constantes.
- Sabemos trabajar con la sentencia de control IF(condición)THEN (conjunto de instrucciones si se cumple la condición)ELSE (instrucciones si no se cumple la condición).
- En concreto, aprendimos lo que es una variable booleana , esto es, una variable que puede valer 1 (true) o 0 (false).
De hecho, en programación es muy común utilizar operaciones lógicas. Una operación lógica, a diferencia de una operación matemática, no devuelve un valor como combinación de operandos, sino true o false como combinación de condiciones. Esto es, comprueba si se produce una condición, y devuelve 1 si se cumple, o 0 si no se cumple (esto es, el principio básico de la sentencia IF-THEN-ELSE).
Operaciones lógicas
Las operaciones lógicas por excelencia en programación son:
- AND (condición y). Su símbolo es & o &&, depende del programa.
- OR (condición o). Su símbolo es |
- NOT (condición no). Su símbolo es !
A | B | A&&B | A||B | !A |
0 | 0 | 0 | 0 | 1 |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 1 | 1 | 1 | 0 |
En resumen: la operación and devuelve verdadero si se cumple A y se cumple B, la operación or se activa si se cumple A ó B, y la operación not devuelve lo contrario de la condición: si A se cumple, not devuelve falso, y al revés.
Por ejemplo, vamos a reescribir el código del anterior artículo de modo que las condiciones de rebote se recojan en una única sentencia IF para rebote horizontal y otra para condición vertical (a diferencia de las cuatro que había antes):
//Declaramos variables de coordenada y de velocidad
int x=0;
int y=0;
int vx=5;
int vy=5;
void setup(){
size(800,600);
}
void draw(){
//Fondo azul claro, que me gusta mucho
background(0,100,255);
ellipse(x,y,50,50);
//Me salgo por la derecha
//Me salgo por la izquierda o por la derecha
if ((x<0)||(x>width)){
vx=-vx;
}
//Me salgo por abajo o por arriba
if ((y>height)||(y<0)){
vy=-vy;
}
//Cambio a las nuevas coordenadas
x=x+vx;
y=y+vy;
}
NOTA: notad cómo se respetan los paréntesis, esto es: cuando combino dos condiciones, lo hago en un paréntesis general, donde entra cada condición con su propio paréntesis:
if((condicion1)&&(condicion2)) {
//instrucciones;
}
Sentencias de control de flujo. El bucle for.
Hay muchas y muy variadas definiciones para este tipo de comandos en lenguajes de programación. Por definición, una sentencia de control de flujo sería un comando que evalúa una determinada condición o conjunto de condiciones, de modo que dependiendo de si se cumplen (o no) ejecutará o no un bloque de instrucciones de un modo predefinido.
Dado que es una definición bastante ambigua, tratemos de concretar en ejemplos. Como ya habéis trabajado con la sentencia if (condición){}, pasemos al estudio de los bucles.
Un bucle es un conjunto de instrucciones que se repite un número determinado de veces, de acuerdo a una o varias condiciones estipuladas. En Processing, en particular, el comando para bucles es for:
for (valor inicial;condición para repetir el bucle;cambio de la variable){
//Instrucción 1;
//Instrucción 2;
//etc;
}
Veamos un ejemplo: dibujemos un conjunto de 10 circunferencias con una sola orden que se repita 10 veces:
//Declaramos una variable de coordenada horizontal x
int x=40;
void setup(){
size(800,600);
background(255,255,255);
for (int i=0;i<10;i=i+1){
//Dibujamos un circulo de radio 80
//en la parte central de la pantalla
ellipse(x,300,80,80);
x=x+80;
}
}
void draw(){
//No es necesario
}
Como podéis ver, lo que define al bucle se encuentra entre los paréntesis detrás de for(), separados por punto y coma:
- El primer elemento define una variable de tipo int que hemos llamado i, y que empieza siendo 0;
- En el segundo elemento estipulamos la condición que debe cumplirse para que el bucle se repita: si i<10, vuelve a empezar.
- En el tercer elemento ponemos la orden de modificación de i a cada repetición del bucle, en este caso, aumenta en 1 i a cada repetición (esto también se lograría con escribir i++).
La variable i del ejemplo es de tipo local (como la he definido dentro del bucle for, sólo existe dentro de este bucle, y se desconoce en el resto del programa. Las variables locales y globales deberían tratarse en artículo aparte, para cuando progresemos un poco más.
Los bucles pueden repetirse todas las veces que queramos, pues somos nosotros los que imponemos las condiciones de trabajo. En el siguiente ejemplo trabajamos con una variable de tipo float que va a lograr 100 repeticiones:
//Declaramos una variable de coordenada horizontal x
int x=4;
void setup(){
size(800,600);
background(255,255,255);
for (float i=0;i<10;i=i+0.1){
line (x,0,x,600);
x=x+8;
}
}
void draw(){
//No es necesario
}
Notad, por otro lado, que hay que tener mucho cuidado con las condiciones del bucle, pues podemos encontrarnos con casos en los que el bucle se repetiría indefinidamente (o hasta que se produzca un desbordamiento, es decir, hasta que se cuelgue), como en este ejemplo:
for (int i=0;i<9;i=i-1){
text ("Este bucle se producirá indefinidamente",200,200);
}
Y, por el contrario, hay bucles que no llegarían a cumplirse nunca, como éste:
for (int i=5;i<3;i++){
//Este circulo no llegara a dibujarse nunca
ellipse(400,300,50,50);
}
Ejecutar el bucle al menos una vez: do-while()
De un modo parecido, podemos generar bucles con la sentencia do while(condicion), con la única diferencia de que esta sentencia cumple el bucle al menos una vez, aunque no se cumpliera la condición estipulada. Compruébalo con este código:
//Declaramos una variable x=5
int x=5;
void setup(){
size(800,600);
}
void draw(){
background(255,0,0);
//La condición x<3 no se cumple, porque vale 5, pero
//el bucle se ejecuta igualmente
do {
ellipse(400,300,50,50);
}while(x<3);
}
Otra opción para construir bucles es la utilización de while() en solitario:
//Declaramos una variable x=500
int x=500;
void setup(){
size(800,600);
background(255,0,0);
//Este bucle se repetira 10 veces (circulos concentricos)
while(x>0){
ellipse (400,300,x,x);
x=x-50;
}
}
void draw(){
//No es necesario
}
Obteniéndose la siguiente imagen, en la que se pueden apreciar diez círculos concéntricos. En esta ocasión, el progreso de la variable x que hemos utilizado como condición es decreciente (partiendo de 500, vamos bajando de 50 en 50), por dos razones: para que veas otro modo de producir un bucle, y porque queremos empezar por el circulo mas grande para ir dibujando los más pequeños encima; de lo contrario, sólo verías el último círculo, que al ser superior en tamaño taparía a los demás. Una última solución habría sido dibujar circunferencias transparentes con la orden noFill();
Si combinamos estos conceptos con lo que ya sabemos sobre animaciones y movimientos, puedes lograr efectos muy curiosos: por ejemplo, podemos conseguir círculos concéntricos que aumenten su tamaño con el ratón:
//Declaramos una variable x=500
int x=500;
void setup(){
size(800,600);
//Este bucle se repetira 10 veces (circulos concentricos)
}
void draw(){
//Conseguimos efecto de animacion
background(255,0,0);
//reiniciamos x a 500
x=500;
while(x>0){
//Dibujamos 10 circulos concentricos cuyo radio depende
//de mouseX
ellipse (400,300,x+mouseX,x+mouseX);
x=x-50;
}
}
Elegir entre varias posibilidades: switch case
Una sentencia de control de flujo presente en la mayoría de los lenguajes de programación es la de tipo switch case, que pide que se le pase una variable como argumento, y la compara con varias posibilidades. Si encuentra una coincidencia, ejecutará el conjunto de órdenes que hayamos escrito para ese caso.
En Processing la sintaxis para escribir un switch responde a:
switch (condicion){
case caso1:
//Instruccion 1;
//Instruccion 2;
//Etc...
break;
case caso2:
//Instruccion 1;
//Instruccion 2;
//Etc...
break;
//Y así tantos casos como queramos
//El case default se utiliza (opcional) para cuando no hay coincidencias
case default:
//Instruccion 1;
//Instruccion 2;
//Etc...
break;
}
Como esto empieza a complicarse, acabaré este artículo con un ejemplo lo más sencillo posible: vamos a crear un programa que según esté en la parte izquierda, central o derecha de la pantalla me dibuje, respectivamente, un círculo, un cuadrado o un triángulo. Para ello, vamos a aprender dos comandos nuevos en Processing: floor() y ceil():
- floor(x) calcula el valor entero más cercano al valor float (decimal) x que le hayamos dado, es decir, redondea, pero siempre al entero inmediatamente inferior. Por ejemplo, floor(3.1415) nos devolvería un 3.
- ceil(x), por el contrario, nos devuelve el número entero inmediatemente superior a x. En nuestro ejemplo, ceil(3.1415) nos devolvería un 4 (aunque por redondeo nos correspondiera un 3).
- round(x) simplemente nos devolvería x redondeado. round(3.1415) devolvería 3.
Jugando con esos conceptos, crearemos un programita que vaya leyendo la variable de entorno mouseX(),y asigne a una variable x un valor que valdrá 0, 1 ó 2 dependiendo del tercio horizontal de pantalla en que me halle. Para ello dividiré por width/3, y rendondearé al número más bajo, es decir, x=floor(mouseX/(width/3));
Así tendremos tres casos posibles:
- Si estamos en el primer tercio horizontal de la pantalla, x=0, y por tanto dibujaremos el círculo.
- En el tercio central de la pantalla, x=1, y dibujaremos el cuadrado.
- En el tercio derecho de la pantalla, x=2, y dibujaremos el triángulo.
Aquí tenéis el programa completo:
//Declaramos una variable x
int x;
void setup(){
size(800,600);
//Este bucle se repetira 10 veces (circulos concentricos)
}
void draw(){
background(255,0,0);
//Hacemos que x valga 0,1 o 2 dependiendo de si estamos
//en la parte izquierda, central o derecha de la pantalla
x=floor(mouseX/(width/3));
switch(x){
case 0:
ellipse (400,300,50,50);
break;
case 1:
rect (400,300,50,50);
break;
case 2:
triangle (300,400,400,200,500,400);
break;
}
}
Si lo ejecutas, verás que se cumple puntualmente todo lo que he prometido. ¿O es que lo dudabas?. ¡Estudiante de poca fe!. ¡Avergüénzate!. ¡Compruébalo abajo!
Y ya está sonando el timbre...
He intentado hacerte llegar estos conceptos de programación de un modo ameno y cercano, aunque para los aprendices en ocasiones esto puede ser igualmente un proceso denso y aburrido. Ya me haréis llegar vuestra opinión por las redes sociales o a través de los comentarios de la web. Mientras tanto, me despido por ahora. El próximo artículo versará sobre el uso de funciones, con nuevos conceptos (o conceptos que ya estábamos utilizando, pero aún no lo sabíamos) como argumentos o retorno de valores. Mientras tanto, descansad y asentad lo aprendido. ¡Sed felices!. ¡Siempre creciendo!. ¡Siempre aprendiendo!. ¡Cultura maker!.