31 marzo 2014

Pascal - 2b: booleanos, case

2.2. Variables booleanas

Una variable "Boolean" (llamadas así en honor al matemático George Boole) es una variable lógica, que puede valer TRUE (verdadero) oFALSE (falso), y se pueden usar para hacer que las condiciones resulten más legibles:
(* IF5.PAS, "if" y booleanos             *)
(* Parte de CUPAS5, por Nacho Cabanes    *)
 
program if5;
 
var
    numero: integer;
    esPositivo: boolean;
 
begin
    writeLn('Escriba un numero');
    readLn(numero);
    esPositivo := numero>0;
    if esPositivo then writeLn('El numero es positivo');
end. 
 
(* Ejemplo de ejecucion:
Escriba un numero
2
El numero es positivo
*)
 


2.3. Muchos posibles valores (case)

Cuando queremos comprobar entre varios posibles valores, podemos hacerlo con muchos "if" seguidos, pero puede resultar pesado y hacer que el programa sea poco legible. Hay una alternativa que resulta mucho más cómoda en esas ocasiones: la orden case. Su sintaxis es
case expresion of 
  caso1: sentencia1; 
  caso2: sentencia2; 
  ... 
  casoN: sentenciaN; 
end;
o bien, si queremos indicar también lo que se debe hacer si no coincide con ninguno de los valores que hemos enumerado, añadimos la cláusula opcional else:
case expresion of 
  caso1: sentencia1; 
  caso2: sentencia2; 
  ... 
  casoN: sentenciaN; 
  else
    otraSentencia;</tt> 
end;
(Nota: Esta es la sintaxis empleada por Turbo Pascal y Free Pascal. En Pascal estándar, esta construcción se empleaba con otherwise en lugar de "else" para expresar "en caso contrario".
Veamos un ejemplo:
(* CASE1.PAS, Condiciones múltiples con "case" *)
(* Parte de CUPAS5, por Nacho Cabanes          *)
 
program case1;
 
var
    letra: char;
 
begin
    WriteLn('Escriba un simbolo');
    ReadLn(letra);
    case letra of
        ' ':                 WriteLn('Un espacio');
        'A'..'Z', 'a'..'z':  WriteLn('Una letra');
        '0'..'9':            WriteLn('Un digito');
        '+', '-', '*', '/':  WriteLn('Un operador');
    else  { otherwise en SURPAS y otros compiladores }
        WriteLn('No es espacio, ni letra, ni digito, ni operador');
    end;
end. 
 
(* Ejemplo de ejecucion:
Escriba un simbolo
a
Una letra
*)
 
Una precaución: la "expresión" debe pertenecer a un tipo de datos con un número finito de elementos, como "integer" o "char", pero no "real".
Y como se ve en el ejemplo, los "casos" posibles pueden ser valores únicos, varios valores separados por comas, o un rango de valores separados por .. (como los puntos suspensivos, pero sólo dos).
(Como siempre, puedes ver la versión más actualizada en la página oficial del curso)

29 marzo 2014

Pascal - 2: Condiciones con if

2 - Condiciones

2.1. Si... entonces...

2.1.1. Condiciones básicas

Vamos a ver cómo podemos evaluar condiciones desde Pascal. La primera construcción que trataremos es if ... then, que en español se podría traducir como "si ... entonces", algo que expresa bastante bien lo que podemos hacer con ella:
(* IF1.PAS, primera prueba de "if"    *)
(* Parte de CUPAS5, por Nacho Cabanes *)
 
program if1;
 
var numero: integer;
 
begin
    writeLn('Escriba un numero');
    readLn(numero);
    if numero > 0 then writeLn('El numero es positivo');
end. 
 
(* Ejemplo de ejecucion:
Escriba un numero
26
El numero es positivo
*)
 
Es decir, tras "if" se una indica "condición", que debe dar como resultado "verdadero" o "falso". La "sentencia" que indiquemos tras la palabra "then" se ejecutará si esa condición es "verdadera".
A partir de ahora, el comentario de la cabecera de nuestro fuente será como éste, un poco más detallado para indicar un "nombre del fuente" y la versión del curso a la que pertenece, de modo que sea más fácil saber de qué fuente se está hablando en caso de preguntar dudas en algún foro, como los de AprendeAProgramar.com. Además, los fuentes no usarán acentos ni eñe, para que no se conviertan en "caracteres extraños" si se llevan a un sistema operativo distinto.
En nuestro caso, veíamos si el número era mayor que cero (positivo). Los "operadores de comparación" que podemos emplear son:
OperadorSignificado
>Mayor que
>=Mayor o igual que
<Menor que
<=Menor o igual que
=Igual que
<>Distinto de

2.1.2. Si no se cumple la condición

También podemos indicar lo que queremos que se haga si no se cumple la condición. Para ello tenemos la construcción "if condición then sentencia1 else sentencia2":
(* IF2.PAS, uso de "if" con "else"    *)
(* Parte de CUPAS5, por Nacho Cabanes *)
 
program if2;
 
var
    numero: integer;
 
begin
    writeLn('Escriba un numero');
    readLn(numero);
    if numero<0 span="" style="box-sizing: border-box; color: black; font-weight: bold;">then
writeLn('El numero es negativo.') else writeLn('El numero es positivo o cero.') end.   (* Ejemplo de ejecucion: Escriba un numero 26 El numero es positivo o cero. *)  
El comportamiento no es exactamente el mismo que si escribimos dos "if", con condiciones opuestas, como en este ejemplo:
(* IF2B.PAS, Dos "if" alternativos, sin "else" *)
(* Parte de CUPAS5, por Nacho Cabanes          *)
 
program if2b;
 
var
    numero: integer;
 
begin
    writeLn('Escriba un numero');
    readLn(numero);
    if numero < 0 then
        writeLn('El numero es negativo.')
    if numero >= 0 then
        writeLn('El numero es positivo o cero.')
end. 
 
(* Ejemplo de ejecucion:
Escriba un numero
26
El numero es positivo o cero.
*)
 
En este segundo fuente se analizará la segunda condición, incluso en caso de que se haya cumplido la primera; en caso de usar "else", por el contrario, no se analiza la segunda parte cuando se cumple la condición inicial.
En ambos fuentes hemos vuelto a emplear escritura indentada: las líneas que "dependen" de la anterior (como la orden que sigue a un "if" o a un "else") están un poco más a la derecha. La recomendación más habitual para esta "sangría" adicional en el texto es no usar el símbolo de tabulación, sino espacios en blanco, y la cantidad de espacios que se suele emplear es 4 (casi cualquier editor de texto moderno se podrá configurar para escribir espacios al pulsar la tecla de tabulación, y para que esa cantidad de espacios sea la que nosotros indiquemos).
Un detalle importante que conviene tener en cuenta es que antes del "else" no debe haber un punto y coma, porque eso indicaría el final de la sentencia "if...", y el compilador nos avisaría con un error.

2.1.3. Condiciones y sentencias compuestas

Si queremos hacer varias cosas cuando se cumpla una cierta condición, deberemos encerrar todas ellas entre "begin" y "end", para formar una "sentencia compuesta":
(* IF3.PAS, "if" y sentencias compuestas *)
(* Parte de CUPAS5, por Nacho Cabanes    *)
 
program if3;
 
var
    numero: integer;
 
begin
    writeLn('Escriba un numero');
    readLn(numero);
    if numero < 0 then
    begin
        writeLn('El numero es negativo.  Pulse INTRO para seguir.');
        readLn
    end;
end. 
 
(* Ejemplo de ejecucion:
Escriba un numero
-10
El numero es negativo.  Pulse INTRO para seguir.
 
*)
 
En este ejemplo, si el número es negativo, se ejecutan dos acciones: escribir un mensaje en pantalla y esperar a que el usuario pulse INTRO (o ENTER, o RETURN, o ↵, según sea nuestro teclado), lo que podemos conseguir usando "readln" pero sin indicar ninguna variable en la que queremos almacenar lo que el usuario teclee.
Nota: nuevamente, hemos empleado la escritura indentada para intentar que el programa resulte más legible: los pasos que forman parte de cada "sentencia compuesta" están tabulados una posición más a la derecha, para que sea fácil ver dónde empieza y dónde termina el bloque.
Como se puede observar en la última orden "readln", no es necesario punto y coma antes de un "end", pero tampoco se considera un error si se conserva el punto y coma.

2.1.4. Sentencias "If" encadenadas

Si unas condiciones dependen de otras anteriores, podemos encadenar varias sentencias "if...then...else":
(* IF4.PAS, "if" encadenados             *)
(* Parte de CUPAS5, por Nacho Cabanes    *)
 
program if4;
 
var
    numero: integer;
 
begin
    writeLn('Escriba un numero');
    readLn(numero);
    if numero<0 span="" style="box-sizing: border-box; color: black; font-weight: bold;">then
writeLn('El numero es negativo.') else if numero>0 then writeLn('El numero es positivo.') else writeLn('El numero es cero.') end.   (* Ejemplo de ejecucion: Escriba un numero 0 El numero es cero. *)  

2.1.5. Varias condiciones simultáneas.

Si se deben cumplir varias condiciones a la vez, podemos enlazarlas con "and" (y). Si se pueden cumplir varias, usaremos "or" (o). Para detallar que una condición NO debe cumplirse, utilizaremos "not" (no):
if ( opcion = 1 ) and ( puntos > 500 ) then [...]
if ( opcion = 3 ) or ( enemigos = 0 ) then [...]
if not ( vidas > 0 ) then [...]
if ( opcion = 2 ) and not ( nivelDeAcceso < 40 ) then [...]

 (Puedes ver la versión más actualizada y con ejercicios propuestos aquí)

27 marzo 2014

Pascal - 1b: Tipos de datos básicos

1.6. Tipos de datos básicos

Como hemos visto, se puede emplear la palabra "integer" para indicar que una variable se usará para almacenar un número entero. Pero además podemos afinar si queremos que este número entero permita almacenar números muy grandes o podemos optimizar para que ocupe poco espacio. También podemos utilizar números "reales" (con cifras decimales). Vamos a verlo...

1.6.1. Números enteros

Ciertas versiones de Pascal, como Turbo Pascal y FreePascal, permiten escoger entre varios tipos de números enteros, de modo que podemos elegir el más adecuado en caso de que queramos no desperdiciar espacio o de que prefiramos almacenar datos de gran tamaño. Estos son los tipos más habituales:
TipoRango de valoresOcupa
Shortint-128..1278 bits (1 byte)
Integer-32768..3276716 bits (2 bytes)
Longint-2147483648..214748364732 bits (4 bytes)
Comp-9.2·1018..9.2·1864 bits (8 bytes)
Byte0..2558 bits (1 byte)
Word0..6553516 bits (2 bytes)
Así, un "byte" sería un tipo de datos adecuado para la edad de una persona (que no puede ser negativa, ni podemos esperar que necesite valores por encima de 255), y un "integer" no sería adecuado para la población de un país (porque no necesitaríamos datos negativos, y claramente puede ser un número por encima de 32.767). No todos los tipos de datos estarán disponibles en todos los sistemas. Por ejemplo el tipo "comp" puede no estar en sistemas antiguos, cuyo procesador no tenga un coprocesador matemático (Intel 386 y anteriores, por ejemplo).
Un ejemplo, que pidiese a una persona su edad en años (para lo que bastaría un "byte") y que calculase una aproximación de su edad en meses, multiplicando por 12 (para lo que parece adecuado un "word") podría ser:
(* Enteros sin signo: byte y word *)
 
program Enteros1;
 
var
  edad: byte;
  meses: word;
 
begin 
  write('Dime tu edad: ');
  readLn(edad);
  meses := edad * 12;
  writeLn('Aproximadamente tienes ', meses, ' meses');
end.
 
(* Ejemplo de ejecucion:
Dime tu edad: 20
Aproximadamente tienes 240 meses
*)
 
 

1.6.2. Números reales

En general, en la mayoría de cálculos necesitaremos tener en cuenta las cifras decimales, no siempre podremos trabajar con números enteros. Para esos casos, el lenguaje Pascal nos permite usar un tipo llamado "real".
Por ejemplo, si queremos convertir de centímetros a metros, tendremos que dividir entre 100, de modo que obtendremos cifras decimales (a no ser que deseemos perder precisión y usemos "div"), y necesitaremos emplear números reales:
(* Numeros reales, primer contacto *)
 
program Reales1;
 
var
  centimetros, metros: real;
 
begin 
  write('Dime la longitud, en centimetros: ');
  readLn(centimetros);
  metros := centimetros / 100;
  writeLn('Equivalen a ', metros, ' metros');
end.
 
(* Ejemplo de ejecucion:
Dime la longitud, en centimetros: 32.8
Equivalen a  3.28000000000000E-001 metros
*)
 
 
A la hora de introducir datos, debemos usar un punto para separar la parte entera de la parte fraccionaria, no una coma. De igual modo, los resultados se mostrarán también con punto. Pero además el resultado es poco legible, porque en Pascal, los números reales se muestran en notación científica a no ser que indiquemos lo contrario. Un número como 3e2 o 3E+2 equivaldría a 3·102, es decir, 3·100 = 300, mientras que 3e-2 o 3.0E-002 indicaría que el resultado es 3·10-2, es decir, 3x0.01 = 0.03
Podemos hacer que resulte más legible si indicamos cuantas cifras totales y cuantas cifras decimales queremos que se muestren, haciendo:
write(numero:cifrasTotales:cifrasDecimales);
De modo que el ejemplo anterior se podría reescribir de la siguiente forma, para que mostrase 4 cifras decimales y 5 (o más) cifras en total:
(* Numeros reales y formato en pantalla *)
 
program Reales2;
 
var
  centimetros, metros: real;
 
begin 
  write('Dime la longitud, en centimetros: ');
  readLn(centimetros);
  metros := centimetros / 100;
  writeLn('Equivalen a ', metros:5:4, ' metros');
end.
 
(* Ejemplo de ejecucion:
Dime la longitud, en centimetros: 32.8
Equivalen a 0.3280 metros
*)
 
 
Pero Turbo Pascal y FreePascal nos permiten emplear varios tipos de datos reales, con mayor o menor precisión y que ocupan más o menos espacio, para adecuarlos a nuestras necesidades:
TipoRango de valoresCifrasOcupa
real2.9e-39..1.7e3811-126 bytes
single1.5e-45..3.4e387-84 bytes
double5.0e-324..1.7e30815-168 bytes
extended3.4e-4932..1.1e493219-2010 bytes
En todos estos datos, la precisión se mide en "cifras significativas", que son las cifras distintas de cero que hay al principio o al final del número: un número como 0.0002 tiene una cifra significativa, 33 tiene 2 cifras significativas, al igual que 33000, mientras que 33001 tiene 6 cifras significativas.

1.6.3. Algunas funciones matemáticas

En el lenguaje Pascal podemos encontrar ciertas funciones matemáticas incorporadas. Por ejemplo, "sqrt" nos sirve para calcular una raíz cuadrada:
(* Raiz cuadrada *)
 
program Raiz;
 
var
  n: real;
 
begin 
  write('Dime un numero: ');
  readLn(n);
  writeLn('Su raiz es ', sqrt(n):2:1);
end.
 
(* Ejemplo de ejecucion:
Dime un numero: 4
Su raiz es 2.0
*)
 
 
De igual modo, existe otra función llamada "sqr", que calcula el cuadrado de un número.
También puedes obtener números al azar: random(x) nos da un número entre 0 (incluido) y x (no incluido). Antes deberás usar "randomize", para dar una valor a la secuencia de números al azar a partir del valor actual del reloj interno del ordenador::
(* Numero al azar entre 0 y 9 *)
 
program Azar;
 
begin 
  Randomize;
  writeLn('Numero al azar: ', random(10) );
end.
 
(* Ejemplo de ejecucion:
Numero al azar: 6
*)
 
 
Si debemos buscar un número entre dos valores cualesquiera, deberemos sumar una cantidad al valor de random. Por ejemplo, para obtener un número entre 10 y 30 usaríamos 10+random(21).
Si has estudiado trigonometría, quizá te alegre saber que puedes calcular senos (con la función "sin") y cosenos (con "cos"). Ambos datos esperan que entre paréntesis se les indique el ángulo, pero medido en radianes, no en grados (tampoco es un grave problema, la equivalencia es que 180 grados son PI radianes):
(* Coseno de 45 grados *)
 
program Coseno;
 
var
  pi, angulo, resultado: real;
 
begin 
  pi := 3.1416;
  angulo := pi / 4; (* 45 grados = PI / 4 radianes *)
  resultado := cos(angulo);
  writeLn('El coseno de 45 grados es ', resultado:4:3);
end.
 
(* Ejemplo de ejecucion:
El coseno de 45 grados es 0.707
*)
 
 
Y si has trabajado con logaritmos y exponenciales, tendrás a tu disposición ln(x) para calcular logaritmos neperianos y exp(x) para calcular el valor de "e elevado a x".
(* Potencia: un numero elevado a otro *)
 
program Potencia;
 
var
  base, exponente: real;
 
begin 
  write('Dime la base: ');
  readLn(base);
  write('Dime el exponente: ');
  readLn(exponente);
  writeLn('Su potencia es: ', 
     Exp(exponente * Ln(base)) );
end.
 
(* Ejemplo de ejecucion:
Dime la base: 2
Dime el exponente: 3
Su potencia es: 8.0
*)
 
 

1.6.4. Caracteres

Con la palabra "char" podemos indicar que una variable se usará para guardar un carácter (letra, número o símbolo). Cada carácter ocupa 1 byte:
(* Caracteres, toma de contacto *)
 
program Caracteres;
 
var
  letra1, letra2: char;
 
begin 
  write('Dime una letra: ');
  readLn(letra1);
  write('Dime otra letra: ');
  readLn(letra2);
  writeLn('Las letras eran ', letra1, ' y ', letra2);
end.
 
(* Ejemplo de ejecucion:
Dime una letra: a
Dime otra letra: s
Las letras eran a y s
*)
 
Si queremos dar un valor a una variable de tipo "char" (lo usaremos dentro de poco, cuando empecemos a comprobar condiciones), deberemos usar comillas simples:
(* Caracteres y valores prefijados *)
 
program Caracteres2;
 
var
  letra1, letra2: char;
 
begin 
  write('Dime una letra: ');
  readLn(letra1);
  letra2 := 'Z';
  writeLn('Tu letra era ', letra1, ' y la mia ', letra2);
end.
 
(* Ejemplo de ejecucion:
Dime una letra: a
Tu letra era a y la mia Z
*)
 

1.6.5. Pero hay más...

Esos no son los únicos tipos de datos. Dentro de poco veremos que también podemos almacenar valores verdadero/falso, cadenas de texto, conjuntos... Pero esos datos de mayor complejidad los iremos viendo poco a poco, a medida que los vayamos necesitando.