Buscar este blog

martes, 26 de noviembre de 2013

Mezclando consola y gráficos

La consola (entrada y salida de texto) y la salida gráfica no son incompatibles. jC es capaz de mezclar ambas cosas, para poder realizar aplicaciones más interesantes.

En este caso, la tarea a realizar será retomar la creación de polígonos, aunque tomando los datos (distancia y lados) de la consola, de tal manera que sea el usuario el que pueda elegir qué polígono representar.

Ya conocemos la función necesaria para poder dibujar un polígono, como se vió en una entrada anterior. Ya que lo que hace que la figura a representar varíe es el número de lados y la distancia a recorrer para cada lado, esos serán los parámetros de la función:

void dibujaPoligono(int lados, int distancia)
{
 def angulo = 360 / lados;

 for(def i = 0; i < lados; ++i) {
  turtle.turnRight( angulo );
  turtle.forward( distancia );
 }
}

En realidad, conociendo esto, sólo es necesario pedir los datos (lados y distancia), y llamar a la función adecuada. El código aparece a continuación.


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

import std.io;
import std.string;
import media.gw;
import media.turtle;

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

gw.setTitle( "Poly" );
def lados = strToInt( readln( "Introduzca num. de lados: " ) );
def distancia = strToInt( readln( "Introduzca distancia de lado: " ) );
dibujaPoligono( lados, distancia );

viernes, 22 de noviembre de 2013

La consola

Aunque ya la hemos estado utilizando en muchos ejercicios, vamos a estudiar un poco más en profundidad cómo funciona la consola en jC.

Además de pintar, jC también es capaz de pedir e imprimir texto. Esto se denomina "consola", pues es la forma estándar de comunicarse con un ordenador: introduciendo órdenes por el teclado y observando el resultado por la pantalla.

    Las funciones que tenemos a nuestra disposición para manejarnos con la consola son precisamente dos:
  • print( <expr> ): Permite mostrar cualquier valor por la pantalla. Hay dos variantes: print() y println(), la segunda realiza un salto de línea tras imprimir.

    Ejemplo

    print( "Tu nombre es: " );
    println( nombre );
    
  • readln( <msg> ): Devuelve la cadena de texto introducida por el usuario. El mensaje permite que el usuario conozca qué datos se le están preguntando.

    Ejemplo

    def nombre = readln( "Introduce tu nombre: " );
    print( "Hola, " );
    println( nombre );
    
    Dado que lo que devuelve readln() es una cadena, el código anterior es equivalente a:

    Ejemplo

    char[] nombre = readln( "Introduce tu nombre: " );
    print( "Hola, " );
    println( nombre );
    

No siempre querremos obtener cadenas de texto por parte del usuario. En ocasiones, querremos preguntarle datos numéricos, como por ejemplo, una edad o una altura. Aunque sólo es posible leer cadenas de texto de la consola, siempre es posible convertirlas a un número, bien sea este entero (int) o real (double). En la librería estándar std.string, tenemos a nuestra disposición las funciones strToInt() y strToDbl(), que convierten una cadena pasada por parámetro en un número, entero y real, respectivamente.
    Funciones de conversión de cadena a número:
  • strToInt( <cadena de texto> ): Esta función acepta una cadena de texto como parámetro, y devuelve el número entero que contiene.

    Ejemplo

    int edad = strToInt( cadenaDeTexto );
    println( edad );
    
  • strToDbl( <cadena de texto> ): Esta función acepta una cadena de texto como parámetro, y devuelve el número real que contiene.

    Ejemplo

    int altura = strToDbl( cadenaDeTexto );
    println( altura );
    

Realizaremos ahora un programa completo, que permita convertir grados celsius en farenheit, y viceversa. Para convertir los grados celsius (c) en farenheit (f), es necesario aplicar la fórmula:

f = ( c * 1,8 ) + 32
Para realizar la conversión inversa, de farenheit (f) a celsius (c):
c = ( f - 32 ) / 1,8

Es muy fácil escribir, por tanto dos funciones que hagan las conversiones respectivas. Dado que los grados llevan posiciones decimales, es necesario que las variables a utilizar sean de tipo número real (double).
double cnvtGradosCelsiusFarenheit(double x)
{
 return ( ( x * 1.8 ) + 32 );
}

double cnvtGradosFarenheitCelsius(double x)
{
 return ( ( x - 32 ) / 1.8 );
}

Sólo resta, por tanto, pedir una cadena de texto, y convertirla a un número real (double), esto puede hacerse de manera muy sencilla:

def grados = strToDbl( readln( "Introduce unos grados: " ) );
Una vez que se ha llamado a las funciones anteriores con la variable grados, sólo es necesario imprimir el resultado. El programa completo aparece a continuación:

/** @name   conversorGrados
  * @brief  Convierte los grados celsius a farenheit y vicecersa.
  * @author jbgarcia@uvigo.es
  * @date   2013-11-21
  */

import std.io;
import std.string;

double cnvtGradosCelsiusFarenheit(double x)
{
 return ( ( x * 1.8 ) + 32 );
}

double cnvtGradosFarenheitCelsius(double x)
{
 return ( ( x - 32 ) / 1.8 );
}

def grados = strToDbl( readln( "Introduce unos grados: " ) );

print( "Los grados celsius introducidos son grados farenheit: " );
println( cnvtGradosCelsiusFarenheit( grados ) );

print( "Los grados farenheit introducidos son grados celsius: " );
println( cnvtGradosFarenheitCelsius( grados ) );

viernes, 15 de noviembre de 2013

Funciones

Las funciones son como procedimientos, pero en lugar de no devolver nada, devuelven un valor, que se marca con return. El tipo de la devolución del valor se indica en el lugar en el que en un procedimiento se indica void.

Recordemos por tanto un procedimiento:

void imprimeConAsteriscos(char[] msg)
{
    print( "*** " );
    println( msg );
}

Una función se utiliza para realizar algún tipo de cálculo o proceso, cuyo resultado hay que devolver. Una función muy sencilla se puede ver a continuación:

double alCuadrado(double x)
{
    return ( x * x );
}

Una vez creada la función, es posible utilizarla en cualquier otra función o procedimiento, de igual forma que se utiliza un procedimiento (indicando su nombre y parámetros, estos entre paréntesis), pero con la peculiaridad de que es necesario guardar el resultado en una variable o utilizarlo en otro cálculo. No es necesario que la función a utilizar haya sido creado antes de la función que la utiliza. Por ejemplo, la hipotenusa de un triángulo rectángulo se calcula haciendo la raiz cuadrada de la suma del cuadrado de las hipotenusas. El programa completo aparece a continuación.

import std.math;
import std.io;

double calculaHipotenusa(double cateto1, double cateto2)
{
    return sqrt( alCuadrado( cateto1 ) + alCuadrado( cateto2 ) );
}

double alCuadrado(double x)
{
    return ( x * x );
}

print( "Hipotenusa para un triángulo de lados 5 y 6:" );
println( calculaHipotenusa( 5, 6 ) );

Un programa anterior transformaba un nombre, convirtiéndolo a minúsculas, a excepción de la primera letra, que siempre se pone en mayúsculas. Para hacerlo, se utilizaba un procedimiento y se aprovechaba el hecho de que los vectores se pasan por referencia.

/** @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 );

En lugar de crear un procedimiento para realizar esta tarea, es posible escribir una función que cree una copia de la cadena de texto, y devuelva una cadena nueva. Una ventaja de esto es que la cadena original no se modifica, si bien por el contrario la desventaja es que se utiliza el doble de memoria. El programa completo aparece a continuación.

/** @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;

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

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