Buscar este blog

lunes, 28 de octubre de 2013

Paso de parámetros (II)

El paso de parámetros no siempre es por valor (también conocido "por copia"). El problema consiste en que los tipos de datos más complejos, como los vectores y matrices, tardan mucho en ser copiados. Así, no es lo mismo pasar un número entero, que un vector de 200 numeros enteros.

Por eso, el paso de vectores se hace por referencia. No hace falta marcar nada, simplemente sucede. Se le llama "por referencia" porque se pasa un número entero que permite localizar al vector, y referirse a él.

La implicación de todo esto es que, cuando se cambian las posiciones de un vector, los cambios realizados se mantienen después de volver de la función que lo llamó.

Veamos un ejemplo:


/** @name   nombres
  * @brief  Escribe un nombre propio formateado en pantalla
  * @author jbgarcia@uvigo.es
  * @date   2013-10-28
  */

import std.io;
import std.util;
import std.charType;

void cnvtNombre(char[] s)
{
 s[ 0 ] = toUpperCase( s[ 0 ] );
 
 for(def i = 1; i < size( s ); ++i) {
  s[ i ] = toLowerCase( s[ i ] );
 }
 
 return;
}

char[] nombre = readln( "Dame tu nombre: " );
cnvtNombre( nombre );
println( nombre );

Este programa toma un nombre, y lo convierte, de forma que la inicial pasa a estar en mayúsculas, y el resto de las letras del nombre pasado, en minúsculas. Esto se hace en la función cnvtNombre(), y dado que el vector se pasa por referencia (es el vector de caracteres s en cnvtNombre()), los cambios repercuten en el programa principal (donde el vector se llama nombre), con lo que se puede visualizar el resultado sin problema.

martes, 15 de octubre de 2013

Vectores

Los vectores son tipos complejos de datos. Esto es así porque se crean a partir de datos simples: en concreto, se trata de colecciones de datos de tipo simple, de tal forma que el tamaño de la colección siempre es fijo. El tamaño de cualquier vector se puede obtener utilizando la función size().

Los tipos de los vectores se crean simplemente añadiendo "[]" al tipo base del vector (es decir, el tipo de cada elemento del mismo). Para acceder a un elemento del vector, se indica el nombre del vector, y a continuación, entre corchetes, la posición a la que se quiere acceder. Es necesario tener en cuenta que la posición del primer elemento es 0, y que la última posición de un vector v es size( v ) - 1.

TipoExplicación
int El tipo es int[]. Se trata de un vector de números enteros.
double El tipo es double[]. Se trata de un vector de números reales.
char El tipo es char[]. Se trata de un vector de caracteres. Los vectores de caracteres tienen un significado especial: se interpretan como texto, en el que cada posición del vector es un carácter del mismo.

Aunque es posible crear un vector de booleanos, no es con diferencia un vector tan utilizado como los tres tipos anteriores.

Supongamos como ejemplo que se desea calcular la media de un vector de números reales. La creación de un vector se hace utilizando el operador new, que crea un nuevo vector. A continuación, se indica el tipo, y, entre corchetes, el número total de elementos.

double[] v = new double[ 3 ];

v[ 0 ] = 10;
v[ 1 ] = 12;
v[ 2 ] = 14;

def suma = 0.0;
for(def i = 0; i < size( v ); ++i) {
    suma += v[ i ];
}

print( "La media es: " );
println( suma / size( v ) );

Cuando conocemos de antemano los valores que va a guardar un vector, es posible crear el vector sin hacer cada una de las asignaciones a cada una de sus posiciones. Así, el caso anterior es equivalente al programa siguiente:

double[] v = new double[]{ 10, 12, 14 };
def suma = 0.0;
for(def i = 0; i < size( v ); ++i) {
    suma += v[ i ];
}

print( "La media es: " );
println( suma / size( v ) );

Los vectores de caracteres se interpretan como texto o, muy comúnmente llamados, cadenas de texto. Así, por ejemplo, no es necesario crear un bucle para visualizar un vector de caracteres, sino que ya se visualiza directamente mediante print(). También es posible asignar directamente un texto a una variable de tipo vector de caracteres: jC se encargará de crear el vector correspondiente.

import std.io;

char[] mensaje = "Hola, mundo";
println( mensaje );

Como siempre, es posible obviar char[] y sustituirlo por def en la segunda línea.

lunes, 30 de septiembre de 2013

Parámetros en procedimientos

En la última entrada, aprendimos a crear procedimientos: consiste en darle un nombre a un código, de manera que podamos invocarlo cuando queramos.

Nuestro procedimiento, en este momento, sirve para crear cuadrados.

void dibujaCuadrado()
{
 final def Lados = 4;
 final def Angulo = 90;
 final def Distancia = 50;
 
 for(def i = 0; i < Lados; ++i) {
  turtle.forward( Distancia );
  turtle.turnRight( Angulo );
 }
}

De acuerdo, pero quizás... podríamos hacer que la distancia (la longitud del lado, representada por la distancia que recorre la tortuga), pudiese cambiar. De esta manera, podríamos crear cuadrados más grandes o más pequeños. Eso se puede conseguir, precisamente, mediante parámetros. Le añadiremos parámetros al procedimiento, de forma que podamos cambiar algunos aspectos. De hecho, si pudiéramos cambiar el número de lados... ¡podríamos dibujar cualquier polígono regular, pues el ángulo se obtiene dividiendo 360 entre el número de lados!

Sintaxis de creación del procedimiento

void nombreProcedimiento(tipo1 id1 [, tipo2 id2 [,... tipon idn]])
{
    instruccion1;
    instruccion2;
    ...
    instruccionn;
}

Sintaxis de llamada al procedimiento

nombreProcedimiento( expresión1 [, expresión2 [,... expresiónn]]);
Los parámetros, tal cual aparecen en la definición del procedimiento, se denominan formales. Los que aparecen cuando llamamos al procedimiento, se denominan reales. Los parámetros reales pueden ser valores literales, variables, cálculos...

Ejemplo

void dibujaPoligono(int lados, int distancia)
{
 def angulo = 360 / lados;
 
 for(def i = 0; i < lados; ++i) {
  turtle.forward( distancia );
  turtle.turnRight( angulo );
 }
}

dibujaPoligono( 4, 50 );

En este ejemplo, estaríamos dibujando un polígono regular de 4 lados, es decir, un cuadrado cuyo lado es de longitud 50.

Ejemplo

Podemos hacer figuras aleatorias utilizando el procedimiento rand() en el módulo math. Veamos un programa completo, que crea cuatro figuras haciendo girar la tortuga cuatro veces.

/** @name   poligonos
  * @brief  Dibuja polígonos al azar
  * @author jbgarcia@uvigo.es
  * @date   2013-9-30
  */

import media.gw;
import media.turtle;
import std.math;

void dibujaPoligono(int lados, int distancia)
{
 def angulo = 360 / lados;
 
 for(def i = 0; i < lados; ++i) {
  turtle.forward( distancia );
  turtle.turnRight( angulo );
 }
}

for(def i = 0; i < 4; ++i) {
 dibujaPoligono( ( rand() * 15 ) + 3, 20 );

 turtle.setPen( false );
 turtle.forward( 60 );
 turtle.turnRight( 90 );
 turtle.setPen( true );
}

domingo, 29 de septiembre de 2013

Procedimientos

Los procedimientos son bloques de código a los que se les da un nombre, para poder utilizarlos más adelante mediante ese nombre.

Los bloques de código se encierran entre llaves { y }. No es nada nuevo, en realidad: los hemos estado utilizando constantemente para indicar el código que se debe ejecutar cuando una condición se cumple (o no), o cuando una repetición se sucede. Por supuesto, tienen muchas más posibilidades, que iremos desgranando poco a poco.

Sintaxis

void nombreProcedimiento()
{
    instruccion1;
    instruccion2;
    ...
    instruccionn;
}

Ejemplo

void dibujaCuadrado()
{
    for(def i = 0; i < 4; ++i) {
        turtle.forward( 100 );
        turtle.turnRight( 90 );
    }
}

Ahora disponemos de un código que podemos invocar cuando queramos. Por ejemplo, sería posible dibujar cuatro cuadrados seguidos con solo llamar a este procedimiento. Para hacerlo, solo es necesario poner su nombre, y a continuación, la apertura y cierre de paréntesis.

turtle.forward( 25 );
dibujaCuadrado();

turtle.forward( 25 );
dibujaCuadrado();

turtle.forward( 25 );
dibujaCuadrado();

turtle.forward( 25 );
dibujaCuadrado();

Por supuesto, ya sabemos que podemos mejorar un poco este código. Todo lo que suponga repeticiones, es mejor, al fin y al cabo, meterlo dentro de un bucle:

for(def i = 0; i < 4; ++i) {
    turtle.forward( 25 );
    dibujaCuadrado()
}

Hablando de mejorar el código, no es bueno que aparezcan tantas constantes que no sabemos muy bien de dónde salen. Aunque en este momento estamos exagerando, pronto veremos que es muy útil tener estos valores como constantes o variables. Podemos mejorar nuestro primer procedimiento:

void dibujaCuadrado()
{
 final def Lados = 4;
 final def Angulo = 90;
 final def Distancia = 50;
 
 for(def i = 0; i < Lados; ++i) {
  turtle.forward( Distancia );
  turtle.turnRight( Angulo );
 }
}


Programa final

El programa final, que hemos ido desgranando en esta entrada, queda como aparece más abajo. Se ha introducido el uso de setPen(), un procedimiento en el módulo turtle. Lo que hace este procedimiento (sí, son también procedimientos de la librería de jC), es evitar que la tortuga deje rastro o no, según se le pase true o false.


/** @name   Cuadrado4
  * @brief  Dibuja cuatro cuadrados en la pantalla.
  * @author jbgarcia@uvigo.es
  * @date   2013-09-29
  */

import media.gw;
import media.turtle;

void dibujaCuadrado()
{
 final def Lados = 4;
 final def Angulo = 90;
 final def Distancia = 50;
 
 for(def i = 0; i < Lados; ++i) {
  turtle.forward( Distancia );
  turtle.turnRight( Angulo );
 }
}

for(def i = 0; i < 4; ++i) {
 turtle.setPen( false );
 turtle.turnRight( 90 );
 turtle.forward( 25 );
 turtle.setPen( true );
 dibujaCuadrado();
}

viernes, 27 de septiembre de 2013

JavaScript backend

Han sucedido algunas novedades desde que empecé este pequeño blog. En concreto, me estoy refiriendo a la nueva habilidad de jC de compilar a JavaScript.

En la imagen se puede ver el resultado, tras pulsar el botón play con el programa star.jc cargado (publicado recientemente). Tras compilar a un HTML con el código JavaScript, lo abre en el navegador del sistema.

Esto permite cosas muy chulas, como una sección en la web de jC para ejemplos... que son ejecutables por el usuario. Cuando el usuario pulsa el botón de ejecutar, arranca un programa creado con jC.

La idea de crear código para BeanShell, nunca me acabó de convencer. El objetivo era ir teniendo "algo", para más adelante compilar a Java, es decir, a un jar. Sin embargo, creo que la idea de compilar a JavaScript es todavía mucho mejor que compilar a Java, pues hoy en día los navegadores que soportan JavaScript son todavía más ubícuos que los Java Applets, que están desapareciendo de Internet.

En cualquier caso, es posible escoger la plataforma objetivo (BeanShell, o JavaScript) en Edición >> Preferencias.

Las sintaxis de Java (y, por lo tanto, BeanShell) y JavaScript son muy parecidas, así que el mayor esfuerzo consistió en portar las librerías, en realidad.

En fin, ¡a disfrutarlo!

lunes, 9 de septiembre de 2013

Tipos y variables (o constantes)

Hasta ahora, cuando queríamos definir variables y constantes, lo hacíamos así:

final def Lados = 4;    // Constante
def distancia = 2;      // Variable

No es que no se pueda seguir haciendo así (es perfectamente válido, e incluso recomendado), pero es mejor que vayamos teniendo en cuenta qué está sucediendo "por debajo".

Cuando jC detecta la palabra reservada def, lo que hace es fijarse en lo que aparece después del símbolo igual, la asignación, para saber qué tipo debe asignarlo a la variable. Por ejemplo, en el caso de arriba, tanto a Lados como a distancia se le asigna el tipo número entero o int.

Si nos planteamos el siguiente caso:

final def PI = 3.1416;
final def CR = '\n';

En este caso, jC deduce que el tipo para PI es número flotante, que se denomina double (por doble precisión). El tipo para CR es un carácter, o char. La tabla completa de tipos disponibles es la siguiente:

TipoExplicación
booleanSólo puede almacenar dos valores: true(o verdadero) or false (falso). Además, true y false son palabras reservadas en el lenguaje (no se pueden crear variables con esos nombres, por ejemplo). Una condición, como por ejemplo, i > 5, se evalúa a true o false.
charUsado para representar caracteres, según la norma UTF-16.
intNumeros enteros (positivos, el cero, y negativos). Es capaz de representar números de -2 mil millones a +2 mil millones.
doubleNúmeros reales o en coma flotante. Soportan un rango muy grande, aunque precisamente debido a temas de precisión, para realizar comparaciones numéricas entre números enteros, es mejor utilizar int.

Las definiciones que aparecen más arriba se pueden reescribir así:

final int Lados = 4;    // Constante
int distancia = 2;      // Variable
final double PI = 3.1416;
final double CR = '\n';

¡Es el momento de experimentar! En cualquier lugar donde se escribe def, se puede sustituir por su tipo, que ahora ya sabemos cuáles son (al menos los tipos básicos, pronto veremos los tipos complejos).

Un ejemplo:

/** @name   Poly
  * @brief  Dibuja polígonos
  * @author jbgarcia@uvigo.es
  * @date   2013-8-10
  */

import media.gw;
import media.turtle;

final int Lados = 4;
final int Distancia = 35;

double angulo = 360 / Lados;

gw.setTitle( "Poly" );
for(int i = 0; i < Lados; ++i) {
 turtle.turnRight( angulo );
 turtle.forward( Distancia );
}

Y recuerda, no es que no se pueda utilizar def, de hecho es más cómodo, y por tanto aconsejable.

sábado, 10 de agosto de 2013

¡Y más repeticiones!

El hecho de haber avanzado hasta abarcar decisión y repetición nos permite hacer programas cada vez más complicados.

El ejemplo esta vez consistirá en dibujar polígonos regulares de cualquier número de lados. En realidad, es muy sencillo si pensamos en que la circunferencia tiene 360º, y que tenemos que dividir esos grados por el número de lados para saber qué ángulo debemos escoger para cada conjunción de aristas... ¡un polígono regular se circunscribe en una circunferencia!

Así el algoritmo sería, en pseudocódigo:

i <- 0
Lados <- 4
Distancia <- 25

mientras i < Lados:
    gira( 360 / Lados )
    dibuja( Distancia )
    i <- i + 1

El pseudocódigo permite diseñar una aplicación, en un lenguaje natural. Se trata de una notación muy libre, que simplemente nos permita esbozar qué es lo que es necesario hacer para completar el problema.

¡Es el momento de empezar a programar! Lanza jC desde el menú de programas y pulsa en nuevo... escoge un directorio y un nombre para el programa. Escribe, o copia y pega lo que viene a continuación, y... ¡ya está!

/** @name   Poly
  * @brief  Dibuja polígonos
  * @author jbgarcia@uvigo.es
  * @date   2013-8-10
  */

import media.gw;
import media.turtle;

final def Lados = 4;
final def Distancia = 35;

def angulo = 360 / Lados;

gw.setTitle( "Poly" );
for(def i = 0; i < Lados; ++i) {
 turtle.turnRight( angulo );
 turtle.forward( Distancia );
}