Descargar

Lenguajes de programación (página 2)

Enviado por Robert Da Corte


Partes: 1, 2
Lenguajes de programación

lenguajes-pogramacion.zip

 Página anterior Volver al principio del trabajoPágina siguiente 

Los nombres de las variables siguen las reglas que ya habíamos mencionado para los identificadores en general, y no se indica ningún punto y coma entre la palabra "var" y el nombre de la variable (o variables que se declaran).

Con la palabra string decimos que la variable nombre va a contener una cadena de caracteres (letras o números). Un poco más adelante, en esta misma lección, comentamos los principales tipos de datos que vamos a manejar.  En concreto, string[20] indica que el nombre podrá estar formado hasta por 20 letras o números

Nota: en la variante del lenguaje Pascal conocida como "Extended Pascal", la longitud máxima de una cadena de texto se indica con paréntesis, de modo que si algún compilador protesta con "string[20]", habrá que probar con "string(20)".

Cuerpo del programa: En él comenzamos escribiendo un mensaje de aviso.  Esta vez se ha empleado writeln, que es exactamente igual que write con la única diferencia de que después de visualizar el mensaje, el cursor (la posición en la que se seguiría escribiendo, marcada normalmente por una rayita o un cuadrado que parpadea) pasa a la línea siguiente, en vez de quedarse justo después del mensaje escrito.

Después se espera a que el usuario introduzca su nombre, que le asignamos a la variable "nombre", es decir, lo guardamos en una posición de memoria cualquiera, que el compilador ha reservado para nosotros, y que nosotros no necesitamos conocer (no nos hace falta saber que está en la posición 7245 de la memoria, por ejemplo) porque siempre nos referiremos a ella llamándola "nombre".  De todo esto se encarga la orden readln.

Si queremos dar un valor a la variable nosotros mismos, desde el programa, usaremos la expresión := (un símbolos de "dos puntos" y otro de "igual", seguidos, sin espacios entre medias), así:

Edad := 17;

Finalmente, aparece en pantalla la palabra "Hola" seguida por el nombre que se ha introducido.  Como se ve en el ejemplo, "writeln" puede escribir varios datos, si los separamos entre comas.

En Pascal debemos declarar las variables que vamos a usar, avisar a nuestro compilador para que les reserve espacio.  Esto puede parecer incómodo para quien ya haya trabajado en lenguaje Basic, pero en la práctica ayuda a conseguir programas más legibles y más fáciles de corregir o ampliar.  Además, evita los errores que puedan surgir al emplear variables incorrectas: si queremos usar "nombre" pero escribimos "nombe", la mayoría de las versiones del lenguaje Basic no indicarían un error, sino que considerarían que se trata de una variable nueva, que no tendría ningún valor, y normalmente se le asignaría un valor de 0 o de un texto vacío.

En Pascal disponemos de una serie de tipos predefinidos, y de otros que podemos crear nosotros para ampliar el lenguaje.  Los primeros tipos que veremos son los siguientes:

  • Integer.  Es un número entero (sin cifras decimales) con signo, que puede valer desde -32768 hasta 32767.  Ocupa 2 bytes de memoria.  (Nota: el espacio ocupado y los valores que puede almacenar son valores para Turbo Pascal, y pueden variar para otros compiladores).
  • Byte.  Es un número entero, que puede valer entre 0 y 255.  El espacio que ocupa en memoria es el de 1 byte, como su propio nombre indica. (Nota: es un tipo de datos definido por Turbo Pascal, y puede no estar disponible en otros compiladores, como es el caso de GNU Pascal).
  • Char.  Representa a un carácter (letra, número o símbolo). Ocupa 1 byte.
  • String.  Es una cadena de caracteres, empleado para almacenar y representar mensajes de más de una letra (hasta 255).  Ocupa 256 bytes. El formato en Pascal estándar (y en Turbo Pascal, hasta la versión 3.01) era string[n] (o string(n), según casos, como ya se han comentado), donde n es la anchura máxima que queremos almacenar en esa cadena de caracteres (de 0 a 255), y entonces ocupará n+1 bytes en memoria.  En las últimas versiones de Turbo Pascal (y otros) podemos usar el formato "string[n]" o simplemente "string", que equivale a "string[255]". En otros compiladores, como GNU Pascal, el tamaño permitido es mucho mayor (normalmente por encima de las 32.000 letras).
  • Real.  Es un numero real (con decimales) con signo. Puede almacenar números con valores entre 2.9e-39 y 1.7e38 (en notación científica, e5 equivale a multiplicar por 10 elevado a 5, es decir, podremos guardar números tan grandes como un 17 seguido de 37 ceros, o tan pequeños como 0,00…029 con 38 ceros detrás de la coma).  Tendremos 11 o 12 dígitos significativos y ocupan 6 bytes en memoria.
  • Boolean.  Es una variable lógica, que puede valer TRUE (verdadero) o FALSE (falso), y se usa para comprobar condiciones.
  • Array (nota: algunos autores traducen esta palabra como "arreglo").  Se utilizan para guardar una serie de elementos, todos los cuales son del mismo tipo.  Se deberá indicar el índice inferior y superior (desde dónde y hasta dónde queremos contar), separados por dos puntos (..), así como el tipo de datos de esos elementos individuales.  Por ejemplo, para guardar hasta 200 números enteros, usaríamos:

    lista: array[1..200] of integer

Se suele emplear para definir vectores o matrices.  Así, una matriz de dimensiones 3×2 que debiera contener números reales sería:

  matriz: array[1..3,1..2] of real

Para mostrar en pantalla el segundo elemento de la primera lista de números (o de un vector) se usaría

    write( lista[2] );

y para ver el elemento (3,1) de la matriz,

    writeln( matriz1[3,1] );

  • Record.  La principal limitación de un array es que todos los datos que contiene deben ser del mismo tipo.  Pero a veces nos interesa agrupar datos de distinta naturaleza, como pueden ser el nombre y la edad de una persona, que serían del tipo string y byte, respectivamente. Entonces empleamos los records o registros, que se definen indicando el nombre y el tipo de cada campo (cada dato que guardamos en el registro), y se accede a estos campos indicando el nombre de la variable y el del campo separados por un punto:

program Record1; var dato: record nombre: string[20]; edad: byte; end;

begin dato.nombre:='José Ignacio'; dato.edad:=23; write('El nombre es ', dato.nombre ); write(' y la edad ', dato.edad, ' años.'); end.

La única novedad en la definición de la variable es la aparición de una palabra end después de los nombres de los campos, lo que indica que hemos terminado de enumerar éstos.

Ya dentro del cuerpo del programa, vemos la forma de acceder a estos campos, tanto para darles un valor como para imprimirlo, indicando el nombre de la variable a la que pertenecen, seguido por un punto.  El conjunto :=  es, como ya hemos dicho, la sentencia de asignación en Pascal, y quiere decir que la variable que aparece a su izquierda va a tomar el valor que está escrito a la derecha (por ejemplo, x := 2 daría el valor 2 a la variable x).

Puede parecer engorroso el hecho de escribir "dato." antes de cada campo.  También hay una forma de solucionarlo: cuando vamos a realizar varias operaciones sobre los campos de un mismo registro (record), empleamos la orden with, con la que el programa anterior quedaría

program Record2;

var dato: record nombre: string[20]; edad: byte; end;

begin with dato do begin nombre:='José Ignacio'; edad:=23; write('El nombre es ', nombre ); write(' y la edad ', edad, ' años.'); end; end.

En este caso tenemos un nuevo bloque en el cuerpo del programa, delimitado por el "begin" y el "end" situados más a la derecha, y equivale a decir  "en toda esta parte del programa me estoy refiriendo a la variable dato".  Así, podemos nombrar los campos que queremos modificar o escribir, sin necesidad de repetir a qué variable pertenecen.

Nota: aquí vuelve a aparecer la escritura indentada: para conseguir una mayor legibilidad, escribimos un poco más a la derecha todo lo que depende de la orden "with". No es algo obligatorio, pero sí recomendable.

Para mostrar datos, tanto en pantalla como en impresora, se emplean write y writeln.  La diferencia entre ambos es que "write" deja el cursor en la misma línea, a continuación del texto escrito, mientras que "writeln" baja a la línea inferior.  Ambas órdenes pueden escribir tipos casi de cualquier clase: cadenas de texto, números enteros o reales, etc.  No podremos escribir directamente arrays, records, ni muchos de los datos definidos por el usuario.

Cuando se desee escribir varias cosas en la misma línea, todas ellas se indican entre un mismo paréntesis, y separadas por comas.

Un comentario para quien ya haya programado en Basic: en la mayoría de las versiones de este lenguaje si separamos varios datos mediante comas en una sentencia PRINT, se verán separados en pantalla por un cierto número de espacios.  En ese aspecto, la "," de Pascal recuerda más al ";" de Basic, ya que escribe los datos uno a continuación del otro.  De hecho, si fueran números, ni siquiera aparecerían espacios entre ellos (también al contrario que en la mayoría de versiones de Basic):

WRITELN (1,10,345);   daría como resultado     110345.  

Se puede especificar la anchura de lo escrito, mediante el símbolo de dos puntos. La cifra que indique la anchura,  si se trata de un número real y queremos indicar también el número de decimales, esto se hace también después de los dos puntos, con el formato ":anchura_total:decimales".  Como ejemplos:

program Write1;

var nombre: string[40]; edad: byte; resultado: real; begin nombre := 'Pepe'; edad := 18; resultado := 13.12; write ('Hola, ',nombre,' qué tal estás? '); writeln (resultado:5:2); writeln('Hola,',nombre:10,'. Tu edad es: ',edad:2); end.

En el caso de una cadena de texto, la anchura que se indica es la que se tomará como mínima: si el texto es mayor no se "parte", pero si es menor, se rellena con espacios por la izquierda hasta completar la anchura deseada.

Igual ocurre con los números: si es más grande que la anchura indicada, no se "parte", sino que se escribe completo.  Si es menor, se rellena con espacios por la izquierda.  Los decimales sí que se redondean al número de posiciones indicado:

program Write2;

var num: real; begin num := 1234567.89; writeln(num); (* La lnea anterior lo escribe con el formato por defecto: exponencial *) writeln(num:20:3); (* Con tres decimales *) writeln(num:7:2); (* Con dos decimales *) writeln(num:4:1); (* Con un decimal *) writeln(num:3:0); (* Sin decimales *) writeln(num:5); (* Qu hara ahora? *) end.

La salida por pantalla de este programa sería:

 1.2345678900E+06          1234567.890 1234567.89 1234567.9 1234568  1.2E+06

Aquí se puede observar lo que ocurre en los distintos casos:

  • Si no indicamos formato, se usa notación científica (exponencial).
  • Si la anchura es mayor, añade espacios por la izquierda.
  • Si es menor, no se trunca el número.
  • Si el número de decimales es mayor, se añaden ceros.
  • Si éste es menor, se redondea.
  • Si indicamos formato pero no decimales, sigue usando notación exponencial, pero lo más compacta que pueda, tratando de llegar al tamaño que le indicamos.

Para tomar datos del usuario, la forma más directa es empleando readln, que toma un texto o un número y asigna este valor a una variable.  No avisa de lo que está haciendo, así que normalmente convendrá escribir antes en pantalla un mensaje que indique al usuario qué esperamos que teclee:

writeln('Por favor, introduzca su nombre'); readln(nombre);

"Readln" tiene algunos inconvenientes:

  • No termina hasta que pulsemos RETURN.
  • La edición es incómoda: para corregir un error sólo podemos borrar todo lo que habíamos escrito desde entonces, no podemos usar las flechas o INICIO/FIN para desplazarnos por el texto.
  • Si queremos dar un valor a una variable numérica y pulsamos " 23" (un espacio delante del número) le dará un valor 0.

En Pascal contamos con una serie de operadores para realizar sumas, restas, multiplicaciones y otras operaciones no tan habituales:

 Operador 

 Operación

 Operandos 

 Resultado 

+

Suma

enteros reales

entero real

Resta

enteros reales

entero real

*

Multiplicación

enteros reales

entero real

/

División

enteros reales

real

div

División entera

enteros

entero

mod

Resto

enteros

entero

En operaciones como +, – y * supongo que no habrá ninguna duda: si sumo dos números enteros obtengo un número entero, si resto dos reales obtengo un número real, y lo mismo pasa con la multiplicación. Los problemas pueden venir con casos como el de 10/3.  Si 10 y 3 son números enteros, qué ocurre con su división?  En otros lenguajes como C, el resultado sería 3, la parte entera de la división.  En Pascal no es así: el resultado sería 3.333333, un número real.  Si queremos la parte entera de la división, deberemos utilizar div.  Finalmente, mod nos indica cual es el resto de la división.  El signo – se puede usar también para indicar negación

Operadores lógicos

Podremos encadenar proposiciones de ese tipo (si A y B entonces C) con: and (y), or (ó), not (no) y los operadores relacionales, que se usan para comparar y son los siguientes:

 Operador 

 Operación

=

Igual a

<>

 No igual a (distinto de) 

<

Menor que

>

Mayor que

<=

 Menor o igual que

>=

Mayor o igual que

Igual que antes, algunos de ellos (>=, <=, in) los utilizaremos también en los conjuntos.

Operaciones entre bits

Los operadores "and", "or" y "not", junto con otros, se pueden utilizar también para operaciones entre bits de números enteros:

 Operador 

 Operación

not

Negación

and

 Producto lógico

or

Suma lógica

xor

Suma exclusiva

shl

 Desplazamiento hacia la izquierda 

shr

Desplazamiento a la derecha

Explicar para qué sirven estos operadores implica conocer qué es eso de los bits, cómo se pasa un número decimal a binario.

Precedencia de los operadores

Para terminar este tema, debemos conocer la precedencia (o prioridad) de los operadores:

 Operadores 

 Precedencia

 Categoría

@ not

Mayor (1)

Operadores unarios

* / div mod and shl shr

2

 Operadores de multiplicación 

+ – or xor

3

Operadores de suma

= <> < > <= >= in

Menor (4)

Operadores relacionales

Esto quiere decir que si escribimos algo como 2+3*4, el ordenador primero multiplicará 3*4 (la multiplicación tiene mayor prioridad que la suma) y luego sumaría 2 a ese resultado, de modo que obtendríamos 14. Si queremos que se realice antes la suma, que tiene menor nivel de precedencia, deberíamos emplear paréntesis, así: (2+3)*4.

Condiciones

Vamos a ver cómo podemos evaluar condiciones desde Pascal.  La primera construcción que trataremos es if … then.  En español sería "si … entonces", que expresa bastante bien lo que podemos hacer con ella.  El formato es "if condicion then sentencia".

 program if;

 var numero: integer;

 begin    writeln('Escriba un número');    readln(numero);    if numero>0 then writeln('El número es positivo');  end.

La "condición" debe ser una expresión que devuelva un valor del tipo "boolean" (verdadero/falso).  La "sentencia" se ejecutará si ese valor es "cierto" (TRUE).  Este valor puede ser tanto el resultado de una comparación.

Condiciones y variables boolean

Así, una forma más "rebuscada" (pero que a veces resultará más cómoda y más legible) de hacer lo anterior sería, usando una variable "boolean":

 program if;

 var    numero: integer;    esPositivo: boolean;

 begin    writeln('Escriba un número');    readln(numero);    esPositivo := (numero>0);    if esPositivo then writeln('El número es positivo');  end.

Condiciones y sentencias compuestas.

La "sentencia" que sigue a "if .. then" puede ser una sentencia simple o compuesta. Las sentencias compuestas se forman agrupando varias simples entre un "begin" y un "end":

program if;

 var    numero: integer;

 begin    writeln('Escriba un número');    readln(numero);    if numero<0 then      begin      writeln('El número es negativo.  Pulse INTRO para seguir.');      readln      end;  end.

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.

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":

 program if;

 var    numero: integer;

 begin    writeln('Escriba un número');    readln(numero);    if numero<0 then      writeln('El número es negativo.')    else      writeln('El número es positivo o cero.')  end.

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.

Sentencias "If" encadenadas

Las sentencias "if…then…else" se pueden encadenar;

 program if;

 var    numero: integer;

 begin    writeln('Escriba un número');    readln(numero);    if numero<0 then      writeln('El número es negativo.')    else if numero>0 then      writeln('El número es positivo.')    else      writeln('El número es cero.')  end.

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 negar, "not" (no):

if ( opcion = 1 ) and ( terminado = true ) then […] if ( opcion = 3 ) or ( teclaPulsada = true ) then […] if not ( preparado ) then […] if ( opcion = 2 ) and not ( nivelDeAcceso < 40 ) then […]

Pero cuando queremos comprobar entre varios posibles valores, sería muy pesado tener que hacerlo con muchos "if" seguidos o encadenar muchos con "and" u "or"..  Hay una alternativa que resulta mucho más cómoda: la orden case.

Su sintaxis es:

case expresión of   caso1: sentencia1;   caso2: sentencia2;   …   casoN: sentenciaN; end;

O bien, si queremos indicar lo que se debe hacer si no coincide con ninguno de los valores que hemos enumerado, usamos else:

case expresión of   caso1: sentencia1;   caso2: sentencia2;   …   casoN: sentenciaN; else   otraSentencia; end;

En Pascal estándar, esta construcción se empleaba con otherwise en lugar de "else" para significar "en caso contrario", así que si alguien de los que me lee no usa TP/BP, sino un compilador que protesta con el "else"  program case;

 var    letra: char;

 begin    WriteLn('Escriba un símbolo');    ReadLn(letra);    case letra of      ' ':                 WriteLn('Un espacio');      'A'..'Z', 'a'..'z':  WriteLn('Una letra');      '0'..'9':            WriteLn('Un dígito');      '+', '-', '*', '/':  WriteLn('Un operador');    else  { otherwise en SURPAS }      WriteLn('No es espacio, ni letra, ni dígito, ni operador');    end;  end.

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, al igual que en los "arrays").,

Constantes y tipos

Definición de constantes

Cuando desarrollamos un programa, nos podemos encontrar con que hay variables que realmente "no varían" a lo largo de la ejecución de un programa, sino que su valor es constante.

Hay una manera especial de definirlas, que es con el especificador "const", que tiene el formato

const Nombre = Valor;

Veamos un par de ejemplos antes de seguir

const MiNombre = 'Nacho Cabanes'; const PI = 3.1415926535; const LongitudMaxima = 128;  

Estas constantes se manejan igual que variables como las que habíamos visto hasta hora, sólo que no se puede cambiar su valor. Así, es valido hacer

Writeln(MiNombre); if Longitud > LongitudMaxima then … OtraVariable := MiNombre; LongCircunf := 2 * PI * r;

pero no podríamos hacer

PI := 3.14; MiNombre := 'Nacho'; LongitudMaxima := LongitudMaxima + 10;

Constantes "con tipo"

El identificador "const" tiene también en Turbo Pascal otro uso menos habitual: definir lo que se suele llamar constantes con tipo, que son variables normales y corrientes, pero a las que damos un valor inicial antes de que comience a ejecutarse el programa. Se usa

    const variable: tipo = valor;

 Así, volviendo al ejemplo de la clave de acceso, podíamos tener una variables "intentos" que dijese el número de intentos.  Hasta ahora habríamos hecho

var   intentos: integer;

begin   intentos := 3; …

Ahora ya sabemos que sería mejor hacer, si sabemos que el valor no va a cambiar:

const   intentos = 3;

begin …

Pero si se nos da el caso de que vemos por el nombre que es alguien de confianza, que puede haber olvidado su clave de acceso, quizá nos interese permitirle 5 o más intentos.  Qué hacemos?  Ya no podemos usar "const" porque el valor puede variar, pero por otra parte, siempre comenzamos concediendo 3 intentos, hasta comprobar si es alguien de fiar.  Conclusión: podemos hacer

const intentos: integer = 3;

begin …

Insisto: una "constante con tipo" es exactamente igual que una variable, con las ventajas de que está más fácil de localizar si queremos cambiar su valor inicial y de que el compilador optimiza un poco el código, haciendo el programa unos bytes más pequeño.

Definición de tipos

El "tipo" de una variable es lo que determina qué clase de valores podremos guardar en ella. Para nosotros, es lo que indicamos junto a su nombre cuando la declaramos. Por ejemplo,

    var PrimerNumero: integer;

Indica que vamos a usar una variable que se va a llamar PrimerNumero y que almacenará valores de tipo entero.  Si queremos definir una de las fichas de lo que será nuestra agenda, también haríamos:

    var ficha: record       nombre: string;       direccion: string;       edad: integer;       observaciones: string       end;

Procedimientos y funciones

La programación estructurada trata de dividir el programa el bloques más pequeños, buscando una mayor legibilidad, y más comodidad a la hora de corregir o ampliar.

Por ejemplo, en el caso de nuestra maravillosa agenda, podemos empezar a teclear directamente y crear un programa de 2000 líneas que quizás incluso funcione, o dividirlo en partes, de modo que el cuerpo del programa sea  

begin   InicializaVariables;   PantallaPresentacion;   Repeat   PideOpcion;   case Opcion of       '1': MasDatos;       '2': CorregirActual;       '3': Imprimir;     …     end;   Until Opcion = OpcionDeSalida;   GuardaCambios;   LiberaMemoria end.

En nuestro caso (en el lenguaje Pascal), estos bloques serán de dos tipos:

procedimientos (procedure) y funciones (function). La diferencia entre ellos es que un procedimiento ejecuta una serie de acciones que están relacionadas entre sí, y no devuelve ningún valor, mientras que la función sí que va a devolver valores.  Veamoslo con un par de ejemplos:

procedure Acceso; var   clave: string;                         (* Esta variable es local *) begin   writeln(' Bienvenido a SuperAgenda ');   writeln('=========================='); (* Para subrayar *)   writeln; writeln;                      (* Dos líneas en blanco *)   writeln('Introduzca su clave de acceso');   readln( clave );                      (* Lee un valor *)   if clave <> ClaveCorrecta then        (* Compara con el correcto *)     begin                               (* Si no lo es *)

    writeln('La clave no es correcta!'); (* avisa y *)     exit                                 (* abandona el programa *)     end end;

El cuerpo de un procedimiento se encierra entre "begin" y "end", igual que las sentencias compuestas y que el propio cuerpo del programa.

Un procedimiento puede tener sus propias variables, que llamaremos variables locales, frente a las del resto del programa, que son globales.  Desde dentro de un procedimiento podemos acceder a las variables globales (como ClaveCorrecta del ejemplo anterior), pero desde fuera de un procedimiento no podemos acceder a las variables locales que hemos definido dentro de él. La orden exit, que no habíamos visto aún, permite interrumpir la ejecución del programa (o de un procedimiento) en un determinado momento.

Veamos el segundo ejemplo: una función que eleve un número a otro (esa posibilidad no existe "de forma nativa" en Pascal), se podría hacer así, si ambos son enteros:

function potencia(a,b: integer): integer;   (* a elevado a b *) var   i: integer;                      (* para bucles *)   temporal: integer;               (* para el valor temporal *) begin   temporal := 1;                   (* incialización *)   for i := 1 to b do     temporal := temporal * a;      (* hacemos "b" veces "a*a" *)

  potencia := temporal;            (* y finalmente damos el valor *) end;

Esta función se llama "potencia".

Tiene dos parámetros llamados "a" y "b" que son números enteros (valores que "se le pasan" a la función para que trabaje con ellos).

El resultado va a ser también un número entero.

"i" y "temporal" son variables locales: una para el bucle "for" y la otra almacena el valor temporal del producto.

Antes de salir es cuando asignamos a la función el que será su valor definitivo.

 

Robert Da Corte

x-zibit4[arroba]hotmail.com

 Página anterior Volver al principio del trabajoPágina siguiente 
Partes: 1, 2
 Página anterior Volver al principio del trabajoPágina siguiente