Lección 28: Sintaxis de las sentencias y declaraciones básicas
-
-
Senior Member - Moderador
11/07/2012#1 Lección 28: Sintaxis de las sentencias y declaraciones básicasSintaxis de las sentencias y declaraciones básicas:
Esta lección será extensa, pero simple.
Hemos visto entonces cómo crear un proyecto e incluir un archivo en él. Dicho archivo era único para nuestro proyecto y forma por lo tanto el módulo principal del mismo. Ahora continuaremos haciendo programas básicos con la única finalidad de aprender la sintaxis de Modula2. Esto no será muy difícil ya que, como dije en la Introducción a este curso, Pascal y Modula han sido lenguajes creados por una misma persona y por lo tanto no difieren demasiado, aunque obviamente hay cosas que cambian.
Como primera diferencia crucial es que Pascal no es Case Sensitive (sensible a mayúsculas y minúsculas) y Modula sí lo es. Esto ya lo expliqué. Ahora iremos viendo las sentencias y declaraciones básicas del lenguaje, tales como los IF, los FOR, WHILE, REPEAT y demás.
Tipos de datos primitivos:- INTEGER: Números enteros comprendidos entre -32768 y 32767. Estos valores pueden variar de un compilador a otro, pero por lo general son esos.
Tenemos definidas para estos tipos las operaciones de siempre: suma (+), resta (-), producto (*), división entera (DIV) y resto de la división entera (MOD), las cuales funcionan igual que en Pascal. - CARDINAL: Representa a los enteros NO negativos, o sea, a los números enteros que comienzan a partir del 0 inclusive. La razón de incluir este tipo es que ahora podemos representar números positivos comprendidos entre 0 y 65536 aproximadamente dependiendo del compilador.
Las operaciones para este tipo son las mismas que para los enteros, teniendo en cuenta que su resultado nunca puede salirse del rango de los CARDINAL, o sea, nunca puede ser menor que 0. Por ejemplo:
Tenemos una variable numero del tipo CARDINAL. Si hacemosCódigo:numero:= 7-6;
no pasa nada, numero valdrá 1 y ya, sin embargo, si hacemosCódigo:numero:= 6-7;
tendremos un error en tiempo de ejecución porque se querrá guardar el valor -1 en una variable CARDINAL y esto no es posible. - REAL: Exactamente igual que en Pascal. Presenta las mismas operaciones básicas: suma (+), resta (-), producto (*), división real (/).
- CHAR: Exactamente igual que en Pascal, incluso basado en la tabla Ascii. Ahora en Modula podemos designar un carácter tanto usando comillas simples como comillas dobles. Por ejemplo, si tenemos una variable car del tipo CHAR es lo mismo hacerCódigo:
car:= 'A';
que hacerCódigo:car:= A;
o usar indistintamente las distintas comillas para cerrar y abrir. - STRING (ARRAY OF CHAR): Los Strings son cadenas de caracteres. En Modula2 los Strings no funcionan igual que en pascal ya que son tratados como Arreglos de caracteres (ARRAY OF CHAR) y por tanto funcionan como arreglos comunes como los que ustedes ya conocen. Para declarar una variable del tipo STRING se declara en realidad del tipo ARRAY[largo_maximo] OF CHAR. Por ejemplo, si quiero declarar una variable llamada palabra del tipo STRING que pueda contener un máximo de 20 caracteres hago lo siguiente:Código:
VAR palabra: ARRAY[1..10] OF CHAR; Está mal escribir: VAR palabra: STRING;
La declaración de un ARRAY[rango] OF CHAR es exactamente igual que declarar un arreglo en pascal, por tanto, como rango podemos colocar Subrangos, valores de constantes, etc.
De este modo, los Strings funcionan de forma muy diferente que en Pascal, por lo tanto los veremos más adelante. Sin embargo, puedo decirles que asignaciones válidas de STRINGS pueden ser:Código:palabra:= Hola mundo; palabra:= H; palabra:= Y él dijo, este es mi mundo; palabra:= Y él dijo, 'este es mi mundo'; palabra:= 'Hola mundo'; palabra:= 'A';
Si tenemos dos variables del tipo STRING, palabra1 y palabra2, no es posible hacerCódigo:palabra1:= palabra2;
pero todo esto lo veremos más adelante. - BOOLEAN: Este tipo es exactamente igual que en Pascal. Los dos valores posibles para una constante, variable o expresión booleana son TRUE y FALSE. Claro está que estos valores van en mayúsculas.
El funcionamiento no cambia en absoluto, al igual que los operadores lógicos como NOT, AND u OR.
------------------------------------------------------------------------------------
Declaración de Constantes y Variables:
Al igual que en pascal, en Modula las constantes y las variables se declaran luego del encabezado del programa y de las importaciones de librerías. La palabra reservada para declarar constantes es CONST y para declarar variables es VAR.
Veamos un ejemplo de esto en un programa sencillo que lea datos de la entrada estándar y luego muestre mensajes apropiados. Para el uso de las constantes pondré dos ejemplos sencillos, uno que calculará el IVA de un producto (tal como lo hicimos en Pascal), y otro en el que usaremos una constante para declarar un STRING (tal como lo hacíamos con ARRAY en Pascal). En este ejemplo trabajaré con casi todos los tipos primitivos de Modula para que vean algunas cosas importantes que sí difieren con Pascal. Veremos primero el ejemplo en Pascal y luego en Modula para que puedan comparar:Código:PROGRAM EjemploDeVariablesYConstantes; CONST IVA= 23; VAR Nombre: STRING; Edad: INTEGER; Letra: CHAR; Precio: REAL; Entero: INTEGER; BEGIN (*Leemos todos los datos desde la entrada estándar.*) Write('Ingresa tu nombre: '); ReadLn(Nombre); Write('Ingresa tu edad: '); ReadLn(Edad); Write('Ingresa una letra: '); ReadLn(Letra); Write('Ingresa el precio de un producto: '); ReadLn(Precio); Write('Ingresa un entero: '); ReadLn(Entero); (*Mostramos todos los datos leídos.*) WriteLn; WriteLn('Tu nombre es: ',Nombre); WriteLn('Tu edad es: ',Edad); WriteLn('La letra es: ',Letra); WriteLn('El precio con IVA es: ',Precio+Precio*IVA/100); WriteLn('El entero es: ',Entero); END.
Como pueden observar, este es un programa muy sencillo y no requiere explicación, solo lean el código y ya. Ahora vamos el mismo programa pero en Modula:Código:MODULE EjemploDeVariablesYConstantes; FROM STextIO IMPORT ReadString,ReadChar, WriteString, WriteChar, WriteLn, SkipLine; FROM SWholeIO IMPORT ReadInt, WriteInt, ReadCard, WriteCard; FROM SRealIO IMPORT ReadReal, WriteReal; CONST IVA= 23.0; MAX_LARGO_NOMBRE= 20; VAR Nombre: ARRAY[0..MAX_LARGO_NOMBRE] OF CHAR; Edad: CARDINAL; Letra: CHAR; Precio: REAL; Entero: INTEGER; BEGIN (*Leemos todos los datos desde la entrada estándar.*) WriteString(Ingresa tu nombre: ); ReadString(Nombre); SkipLine; WriteString(Ingresa tu edad: ); ReadCard(Edad); SkipLine; WriteString(Ingresa una letra: ); ReadChar(Letra); SkipLine; WriteString(Ingresa el precio de un producto: ); ReadReal(Precio); SkipLine; WriteString(Ingresa un entero: ); ReadInt(Entero); SkipLine; (*Mostramos todos los datos leídos.*) WriteLn; WriteString(Tu nombre es: ); WriteString(Nombre); WriteLn; WriteString(Tu edad es: ); WriteCard(Edad,1); WriteLn; WriteString(La letra es: ); WriteChar(Letra); WriteLn; WriteString(El precio con IVA es: );WriteReal(Precio+Precio*IVA/100.0,1); WriteLn; WriteString(El entero es: ); WriteInt(Entero,1); END EjemploDeVariablesYConstantes.
Como podrán observar a primera vista, el código en Modula ha quedado bastante más cargado que el de Pascal a pesar de que la sintaxis es bastante parecida. Este es uno de los motivos por los cuales elegí Modula en lugar de otro lenguaje; como Pascal es parecido no será tan difícil migrar de uno a otro. Imaginen si en lugar de Modula hubiera elegido Java, habríamos pasado a una sintaxis TOTALMENTE distinta y a declaraciones nuevas para casi todo, entre muchas otras cosas.
Veamos paso a paso lo que hacemos:
Iniciamos nuestro programa con la palabra reservada MODULE y luego le damos un nombre que en este caso es EjemploDeVariablesYConstatnes. Luego vienen las importaciones. Como dije antes, en Pascal teníamos todo importado por defecto, pero ahora en Modula tendremos que traer explicitamente cada cosa que vayamos a utilizar; para esto debemos conocer la librería que contiene la rutina a utilizar, el nombre de la rutina, el tipo y la cantidad de parámetros que debemos pasarle para que funcione correctamente.
En este ejemplo concreto tenemos tres declaraciones de importaciones:Código:FROM STextIO IMPORT ReadString,ReadChar, WriteString, WriteChar, WriteLn, SkipLine;
Recuerden que luego de FROM debemos indicar el nombre de la librería de la que traeremos rutinas para luego indicar mediante IMPORT cada una de las rutinas. En el primer ejemplo de programa solo habíamos importado un procedimiento desde STextIO, sin embargo normalmente es necesario importar más de una rutina desde una misma librería, para esto declaramos cada rutina separada por comas y finalmente colocamos el punto y coma para cerrar la sentencia de importación. Es como si declaráramos varias variables de un mismo tipo.
Del mismo modo podríamos importar cada rutina en una línea por separado de la siguiente manera:Código:FROM STextIO IMPORT ReadString; FROM STextIO IMPORT ReadChar; FROM STextIO IMPORT WriteString;
Bien, la librería STextIO, como dije antes, contiene rutinas para leer y mostrar texto en la entrada y salida estándar respectivamente. De este modo, tenemos las rutinas:- ReadString: Lee una cadena de caracteres desde la entrada estándar, deja el cursor en la misma línea de lectura.
- ReadChar: Lee un carácter desde la entrada estándar, deja el cursor en la misma línea de lectura.
- WriteString: Imprime una cadena de caracteres en la salida estándar, deja el cursor en la misma línea de impresión.
- WriteChar: Imprime un carácter en la salida estándar, deja el cursor en la misma línea de impresión.
- WriteLn: Imprime una línea en blanco (no es el WriteLn de Pascal), o sea, baja un renglón.
- SkipLine: Saltea una línea (baja un renglón) y limpia la basura que puede quedar contenida en memoria. Esto lo hacíamos colocando ReadLn extra en los códigos de Pascal. Si no hacen esto luego de leer algún dato desde la entrada estándar, cuando vayan a leer otro de una nueva línea pueden tener problemas. Si van a seguir leyendo desde la misma línea pues no hay problema. Tanto SkipLine como WriteLn saltean una línea, pero SkipLine además limpia el buffer de entrada (no entraré en ese detalle ahora). Usen WriteLn para saltear renglones cuando quieran lograr espacios en blanco en su pantalla.
No se asusten, no tendrán que memorizar todos estos procedimientos, más adelante les diré donde tienen un listado completo de las librerías que trae el compilador para que busquen ahí cuando necesiten algo, pero aún no es el momento.
---------------------------------------------------------------------------Código:FROM SWholeIO IMPORT ReadInt, WriteInt, ReadCard, WriteCard;
Aquí tenemos más importaciones, solo que ahora son de una librería diferente, por eso debemos nuevamente colocar la palabra reservada FROM, el nombre de la librería, la palabra IMPORT y la lista de rutinas a importar separadas por coma. La línea termina en punto y coma.
SWholeIO viene de Standard Whole Input Output (Entra y Salida Estándar de Entero), donde aquí entero abarca tanto al tipo INTEGER como al tipo CARDINAL. De este modo la librería SWholeIO contiene las rutinas tanto para leer como para imprimir números del tipo INTEGER y CARDINAL.
Las rutinas que importamos en este ejemplo son:- ReadInt: Lee un entero desde la entrada estándar, deja el cursor en la misma línea de lectura.
- ReadCard: Lee un natural (CARDINAL) desde la entrada estándar, deja el cursor en la misma línea de lectura.
- WriteInt: Imprime un entero en la salida estándar, deja el cursor en la misma línea de impresión.
- WriteCard: Imprime un natural (CARDINAL) en la salida estándar, deja el cursor en la misma línea de impresión.
Estos dos últimos procedimientos llevan dos campos como parámetros. Primero número a mostrar sea INTEGER o CARDINAL, luego una coma y luego el campo que indica la cantidad mínima cifras a mostrar. Es conveniente que este campo sea siempre 1. Por ejemplo, para mostrar el CARDINAL 9 en pantalla escribimosCódigo:WriteCard(9,1);
Para mostrar el entero -14 escribimosCódigo:WriteInt(-14,1)
Veremos esto con más detalle cuando lleguemos a esas líneas de nuestro programa.
-----------------------------------------------------------------------------------
Finalmente en este ejemplo tenemos importaciones de SRealIO en la líneaCódigo:FROM SRealIO IMPORT ReadReal, WriteReal;
Esta librería funciona del mismo modo que SWholeIO. SRealIO viene de Standard Real Input Output (Entrada y Salida de Reales). De este modo tenemos varias rutinas para trabajar con números reales en nuestra entrada y salida estándar. Para este ejemplo hemos importado solo dos rutinas:- ReadReal: Lee un real desde la entrada estándar, deja el cursos en la misma línea de lecturar.
- WriteReal: Imprime un real en la entrada estándar, deja el cursor en la misma línea de impresión.
Este último procedimiento funciona del mismo modo que WriteInt .
Luego de estas importaciones tenemos la declaración de constantes en nuestro programa:Código:CONST IVA= 23.0; MAX_LARGO_NOMBRE= 20;
Esto no debe suponer una gran dificultad para ustedes porque es casi igual que en pascal, salvo un pequeño detalle: en pascal da lo mismo declarar la constante IVA= 23 que IVA= 23.0 porque podremos hacer nuestras cuentas tranquilamente mezclando los tipos INTEGER y REAL. Por ejemplo, podemos hacer
2*1,5 sin ningún problema, o 4.0+3. En Modula esto no funciona así. Los operandos en cualquier expresión matemática deben ser de un mismo tipo. Esto significa que Modula no nos permitirá realizar la operación 2*1.5 porque el 2 es INTEGER y el 1.5 es REAL. Para poder hacerlo deberíamos pasar el 2 a REAL simplemente escribiéndolo como 2.0, con eso basta.
Dado que utilizaré la constante IVA para realizar operaciones con los precios de los productos, los cuales están declarados como REAL, IVA debe ser también REAL.
Luego de la declaración de constantes tenemos la declaración de variables la cual es también como en pascal, o sea, escribimos la palabra reservada VAR para después declarar cada variable con su tipo. Recordar que el nombre de las variables así como de todo lo demás es sensible a mayúsculas y minúsculas, por lo tanto una variable llamada Abuela es distinta de otra llamada abuela.
El tipo de una variable se declara tal como en pascal, o sea, ponemos dos puntos y luego el tipo. Tengan en cuenta que es posible declarar varias variables de un mismo tipo en una misma línea, esto no ha cambiado.
Lo más destacable sea tal vez el tipo String, que como dije antes se declara como un arreglo de caracteres (ARRAY OF CHAR) indicando el largo máximo de caracteres a contener según el subrango que definamos. En este ejemplo:Código:Nombre: ARRAY[0..MAX_LARGO_NOMBRE] OF CHAR;
la variable Nombre puede contener un máximo de 21 caracteres, o sea, desde 0 hasta MAX_LARGO_NOMBRE, la cual es una constante declarada como un entero que vale 20.
NOTAS:
El procedimiento WriteString recibe un String (ARRAY OF CHAR) como argumento para mostrarlo en pantalla, de este modo podemos pasarle una Constante, una Variable, una Función que retorne como resultado un String, o un String anónimo, o sea, una palabra encerrada entre comillas, sean estas simples y/o dobles.
No podemos pasar más de un argumento a los procedimientos WriteString o WriteChar tal como lo hacíamos en Pascal. Por ejemplo, suponiendo que palabra es una variable String, en pascal podíamos hacerCódigo:WriteLn('La palabra ingresada es ',palabra);
pero en Modula no podemos hacerCódigo:WriteString(La palabra ingresada es ,palabra);
sino que debemos hacerCódigo:WriteString(La palabra ingresada es ); WriteString(palabra);
Lo mismo se cumple para los procedimientos WriteReal, WriteInt, WriteCard los cuales reciben dos argumentos tal como ya se explicó, y esto implica sí o sí dos argumentos, uno con lo que se mostrará en pantalla y otro con el valor modificador.
Los procedimientos de lectura de datos funcionan de la misma manera, por ejemplo, dadas dos variables enteras a y b, en Pascal era válido hacerCódigo:Read(a,b) o ReadLn(a,b)
pero en Modula debemos hacerCódigo:ReadInt(a); ReadInt(b);
--------------------------------------------------------------------------------
Sintaxis de las estructuras básicas: Sentencias
Veamos entonces como varía la sintaxis de algunas estructuras básicas respecto a como eran en Pascal. Con esto quiero decir que veremos como se escribía, por ejemplo, la sentencia IF en Pascal y como se escribe en Modula; haciendo esto con todas.
Sentencia IF..THEN:
En pascal teníamos dos posibles formas de esta sentencia, una para cuando teníamos que ejecutar un única instrucción luego del IF y otra para cuando teníamos que ejecutar más de una. La diferencia estaba en los bloques BEGIN/END:Código:IF condicion THEN instrucción;
Estamos de acuerdo en que esta forma hace exactamente lo mismo que la siguienteCódigo:IF condicion THEN Begin instrucción; End;
Sin embargo veamos estos dos casos
En el primer caso, solo ejecutaremos la instrucción1 si la condición es verdadera ya que es la única sentencia que está dentro del IF. La instrucción2 se ejecutará siempre ya que está fuera. Sin embargo en el segundo caso ambas instrucciones se ejecutarán solo si la condición es verdadera, y en caso contrario no se ejecutará nada. Esta sutil diferencia debería ya estar clara desde hace mucho.
De este modo podríamos asumir que la forma general del IF...THEN es la que tiene Begin y End ya que sirve tanto para cuando tenemos una instrucción o varias.
La forma en Modula no varía mucho excepto porque funcionan como si asumiéramos que siembre debe ir BEGIN y END solo que sin el BEGIN. Veamos estos IF en Modula:
Todos estos son IF válidos para Modula. Tenemos un caso con una única instrucción, otro con dos y otro con N instrucciones; todos ellos tienen la misma forma, o sea, siempre va un END al final del IF, seguido de un punto y coma (, pero nunca va un BEGIN ya que no es necesario; incluirlo es un error de sintaxis ya que Modula no lo aceptará.
----------------------------------------------------------------------
Sentencia IF...THEN...ELSE:
Creo que no hace falta dar más ejemplos que los que vimos, así que comparemos el IF...THEN...ELSE de Pascal con el de Modula:
Como podrán ver, el código Modula es mucho más claro que el de Pascal. En primer lugar se eliminan los constantes BEGIN y END ya que tanto IF como ELSE tienen sus propios bloques. Cuando tenemos un IF que no va seguido de ELSE entonces lo cerramos con un END tal como ya vimos, pero cuando hay un ELSE no debemos poner el END ya que ELSE cierra el bloque de IF y a su vez abre el suyo propio que luego sí se cerrará con END seguido de punto y coma. Siempre debe ir este END para cerrar un ELSE, exista una única instrucción o varias.
Anidación IF...THEN:
Dado que ahora IF tiene su propio bloque y no necesitamos marcarlo nosotros con BEGIN y END, es importante dejar claro como se anida, o sea, como usamos un IF seguido de otro. Simplemente hay que cerrar bien cada uno y listo:
Vemos allí dos ejemplos donde anidamos dos IF...THEN. La idea, como ya dije, es no olvidar colocar ningún END, sabiendo siempre cual de ellos cierra cual IF. Funcionan del mismo modo que los bloques BEGIN y END de pascal.
--------------------------------------------------------------------------------
Anidación IF...THEN con IF...THEN...ELSE:
Veamos solo los ejemplos genéricos, ya que creo que resulta bastante claro:
Podríamos ver aún más formas, pero ustedes mismos deberán probar, ya que esto en sí no es nada nuevo porque ustedes ya lo manejaban con los bloques BEGIN y END de Pascal.
Anidación IF...THEN...ELSE:
Veamos la última forma de anidación que tenemos con sentencias IF. Esta es importante ya que incluye una nueva palabra reservada: ELSIF.
En pascal, cuando teníamos un IF...THEN...ELSE que iba seguido otro IF, escribíamosCódigo:IF condicón THEN Begin {instrucciones} End Else IF condición2 THEN Begin {instrucciones} End;
En Modula podemos escribirlo de la misma manera respetando la sintaxis que ya hemos visto:Código:IF condición THEN {instrucciones} ELSE IF {instrucciones} END; END;
Sin embargo, previendo esto, el lenguaje nos provee de una sintaxis mucho más cómoda:Código:IF condición1 THEN {instrucciones} ELSIF condición2 THEN {instrucciones} ELSIF condición3 THEN {instrucciones} ELSIF . . . ELSIF condiciónN THEN {instrucciones} END;
Noten como podemos anidar N cantidad de IF...THEN...ELSE dejando un código bien legible.
Sentencia CASE...ELSE:
Esta sentencia tiene una diferencia respecto a la de Pascal. Su estructura es casi idéntica, sin embargo en pascal, cuando queríamos ejecutar más de una instrucción en una etiqueta del CASE, debíamos incluir un bloque BEGIN/END. En Modula eso no es así, la última instrucción de cada etiqueta finalizará con la barra vertical (|) en vez de terminar en punto y coma (.
Esto es siempre así, sea una sola instrucción o varias. Veamos ejemplos:
Veamos ahora un ejemplo con un código ya usado en Pascal cuando vimos CASE.
Código en PascalCódigo:Case opcion of '1': writeln('MENSAJE DESPLEGADO'); '2': begin write('Ingrese un número entero: '); readln(numero); writeln('Ha ingresado ',numero,'.'); end; '3': begin write('Ingrese un caracter: '); readln(opcion); writeln('Ha ingresado ',opcion); end; else writeln('ERROR. No existe la opción ',opcion); end; //De la instrucción CASE.
Código en ModulaCódigo:CASE opcion OF '1': WriteString('MENSAJE DESPLEGADO')| '2': WriteString('Ingrese un número entero: '); ReadInt(numero); WriteString('Ha ingresado '); WriteInt(numero,1)| '3': WriteString('Ingrese un caracter: '); ReadChar(opcion); WriteString('Ha ingresado '); WriteChar(opcion)| ELSE WriteString('ERROR. No existe la opción '); WriteChar(opcion); END; //De la instrucción CASE.
Espero que con estos ejemplos quede claro. Si ya sabían manejar CASE en pascal, esto no será para nada distinto.
Secuencia de repetición FOR:
El FOR normal de Modula es casi idéntico al de Pascal salvo que al igual que IF, FOR tiene su propio bloque y por lo tanto debe terminar siempre con un END, tenga una única instrucción dentro o varias. En Pascal teníamos que crear un bloque BEGIN/END para encerrar varias instrucciones dentro de un FOR, pudiendo o no usar esto para cuando teníamos una sola.
De esta manera, teniendo una única instrucción a realizar, daba lo mismo cualquiera de estas dos formas de escribir el FOR:
Sin embargo, si teníamos más de una no había más remedio que utilizar el bloque BEGIN/END para que todas estuvieran dentro de la iteración.
En Modula, el FOR tiene una única forma:Código:FOR i:= 0 TO N DO {instrucciones} END;
O sea que siempre debe terminar con un END y luego un punto y coma.
Los tipos posibles para la variable de control, en estos ejemplos llamada i, son los mismos que en Pascal, agregando ahora el tipo CARDINAL.
Todos estos son FOR incrementales, o sea que la variable de control va aumentando su valor a medida que la iteración se repite. Sin embargo pascal nos proveía de una forma para el FOR decremental, o sea, aquel en que la variable de control disminuía su valor en cada repetición:Código:FOR i:= 10 DOWNTO N DO Begin {instrucciones} End;
Para este ejemplo asumimos que N es menor que 10 ya que i irá reduciendo su valor. Como deberían recordar, lo único que hacemos para esto es cambiar la palabra reservada TO por DOWNTO. Sin embargo en Modula esto no sucede igual, de modo que un FOR decremental quedaría así:Código:FOR i:= 10 TO N BY -1 DO {Instrucciones} END;
En esta forma del FOR asumimos que N es menor que i, por tanto debemos agregar el modificador BY y el valor de decremento por iteración. Es recomendable que siempre que usen esto decrementen con -1 y no con otros valores ya que eso traerá problemas.
Sentencia de repetición WHILE...DO:
WHILE también mantiene casi la misma forma que en Pascal, solo que todo hasta ahora, no requiere un bloque BEGIN/END sino que él forma su propio bloque y por esto debe siempre terminar con un END y un punto y coma, sea que tiene una única instrucción o varias. De este modo su forma es:
Sentencia de repetición REPEAT...UNTIL:
Esta sentencia en Pascal ya formaba su propio bloque, o sea, no necesitábamos un BEGIN/END para encerrar sus instrucciones, sino que todo quedaba delimitado por sí mismo. Esto en Modula no fue modificado porque no tendría sentido hacerlo, por lo tanto esta estructura permanece igual:
------------------------------------------------------------------------------------
Sintaxis de las estructuras básicas: Tipos
Hemos conocido las diferencias entre Pascal y Modula de las estructuras simples que componen a un lenguaje. Veamos ahora las diferencias en las declaraciones de tipos definidos por nosotros mismos.
Al igual que en Pascal, los tipos se definen luego de las constantes y bajo la palabra reservada TYPE.
Subrangos:
Dado el mismo ejemplo que les había dado en pascal, comparen la diferencia entre un lenguaje y otro, no es nada del otro mundo:
Arreglos:
La definición de arreglos es igual en Modula que en Pascal, con la salvedad de que Modula es CaseSensitive. La declaración es entonces así:Código:Identificador= ARRAY[subrango] OF TIPO;
donde TIPO es un tipo ya definido previamente por nosotros o uno predefinido del lenguaje. El subrango entre corchetes puede ser un tipo subrango ya declarado anteriormente o un nuevo subrango que declaremos allí mismo. Todo esto es igual que en Pascal.
El subrango también indica cómo se numeran las celdas del arreglo.
Los arreglos se utilizan del mismo modo que en Pascal, o sea, declaramos una variable del tipo arreglo y la utilizamos como en este ejemplo, donde usaré un FOR para dar valores a las celdas:Código:MODULE EjemploArreglo; TIPE MiArreglo= ARRAY[1..10] OF INTEGER; VAR varArray: MiArreglo; BEGIN FOR i:=1 TO 10 DO varArray[i]:= i; END; END EjemploArreglo.
Los arreglos bidimensionales también son idénticos a Pascal.
Enumerados:
Estos también son idénticos a Pascal, tanto en su declaración como en su uso.Código:TYPE diasSemana=(domingo, lunes, martes, miercoles, jueves, viernes, sabado); Mes= (enero, febrero, marzo, abril, mayo, junio, julio, agosto, setiembre, octubre, noviembre, diciembre); puntoCardinal= (norte, sur, oeste, este);
Estas declaraciones funcionan tanto en Modula como en Pascal. No hace falta decir más.
Recuerden que los ordinales de un enumerado comienzan en la posición 0.
Registros:
Este tipo también es igual en Pascal y en Modula:Código:TYPE NombreDelRegistro= RECORD campo1: tipo; campo2: tipo; . . . campoN: tipo; END;
Registro con variante:
Funciona casi igual que en Pascal, con la salvedad de que en Modula no podemos definir nuevos tipos dentro del CASE en el registro. Por ejemplo, cuando hablé de Registro con Variante en Pascal, les di este ejemplo:Código:TYPE Categorias= (encargado, peon); Funcionario= Record Nombre: String; Documento: Integer; Case categoria: Categorias of encargado: (grado: (Bajo, Medio, Alto)); peon: (rango:(Novato, Practico); cargaHoraria: 40..48); End;
En este ejemplo teníamos un registro que representaba a un funcionario de una empresa el cual tenía como datos su Nombre, Documento y Categoría. Dependiendo del valor de esta última podíamos tener el Grado o el Rango del funcionario, pudiendo guardar la Carga Horaria si el funcionario era un Peon de la empresa..
No explicaré el funcionamiento de esto, asumo que ya lo saben. Noten que los tipos Grado y Rango son enumerados definidos directamente dentro del CASE, o sea, no los definimos antes. Lo mismo pasa con el subrango cargaHoraria. En Modula esto no funciona así, debemos definir los tipos que necesitamos antes del registro con variante. Este mismo ejemplo pasado a Modula quedaría así:Código:TYPE Categorias= (encargado, peon); Grado= (Bajo, Medio, Alto); Rango= (Novato,Practico); CargaHoraria= [40..48]; Funcionario= RECORD Nombre: ARRAY[0..20] OF CHAR; Documento: INTEGER; CASE categoria: Categorias OF encargado: Grado: grados| peon: rango:Rango; cargaHoraria: CargaHoraria| END; END;
Noten que además de definir los tipos previamente debemos respetar la estructura del CASE el cual lleva el símbolo | en la sentencia final de cada etiqueta y además termina con un END. También debemos colocar el END que corresponde al RECORD.
Punteros:
Aprovecho ahora para decirles que si no tienen claro el tema de los punteros regresen al curso de Pascal ya que en Modula serán nuestro tipo base para trabajar, o sea, todo lo haremos con punteros. De verdad, si no entendieron este tema no sigan ya que yo no me detendré aquí a explicar cómo funcionan, qué es un alias, etc.
En pascal definíamos un tipo puntero así:Código:TYPE MiTipoPuntero= ^Tipo;
Donde Tipo correspondía a algún tipo definido previamente, o uno primitivo de Pascal.
Luego definíamos una o más variables de este tipo, por ejemploCódigo:VAR miVariable: MiTipoPuntero;
Para acceder al lugar de memoria al que apuntaba nuestra variable del tipo puntero usábamos el operador circunflejo (^):Código:miVariable^:= algun_valor_apropiado; WriteLn(miVariable^):
etc.
Recordar que debíamos usar los procedimientos NEW y DISPOSE para obtener y liberar memoria respectivamente. Estos procedimientos eran primitivos de pascal.
En Modula lo único que cambia respecto a los punteros es su declaración. Siguiendo el mismo ejemplo, definimos un puntero a algún tipo de la siguiente manera:
TYPE
MiTipoPuntero= POINTER TO Tipo;
Donde Tipo es algún tipo definido previamente, importado de alguna librería o primitivo del lenguaje. Lo demás sigue funcionando exactamente igual que en Pascal, excepto por un pequeño detalle que daré a continuación:Código:MODULE TipoPuntero; TYPE Puntero= POINTER TO INTEGER; VAR p1, p2: Puntero; entero: INTEGER; BEGIN NEW(p1); NEW(p2); p1^:=10; p2^:=20; entero:= p1^ + p2^; DISPOSE(p1); DISPOSE(p2); END TipoPuntero.
Si ustedes intentan correr este código en el XDS verán que no compila. Tendrán exactamente un error NEW: ALLOCATE not known in this scope por cada vez que hayan invocado a NEW en su código; y un error DISPOSE= DEALLOCATE not known in this scope por cada invocación al procedmiento DISPOSE.
Esto es simplemente porque NEW y DISPOSE tampoco están cargados por defecto en Modula, sino que debemos importarlos desde una librería, la cual se llama Storage. Sin embargo no importaremos directamente a NEW y a DISPOSE, sino que importaremos a ALLOCATE y DEALLOCATE, con lo cual ya no tendremos problemas. La línea de importación sería así:Código:FROM Storage IMPORT ALLOCATE, DEALLOCATE;
Teniendo todo esto en cuenta ya podemos trabajar con punteros.
-------------------------------------------------------------------------------------
Sintaxis de las estructuras básicas: Procedimientos y Funciones
Básicamente estas estructuras funcionan igual que en pascal, salvo pequeños detalles que no implican complicación alguna:
Procedimientos:
En Pascal declarábamos un procedimiento de la siguiente manera:Código:PROCEDURE NombreDelProcedmiento(parametros en caso de ser necesarios); CONST (*Declaración de constantes locales en caso de que estas fueran necesarias*) TYPE (*Declaración de tipos locales en caso de que estos fueran necesarios*) VAR (*Declaración de variables locales en caso de que estas fueran necesarias*) (*Declaración de procedimientos y funciones locales en caso de que estos fueran necesarios*) BEGIN (*Bloque principal del procedimiento*) END;
En Modula esto es casi idéntico, salvo el END del final, ya que este debe ir seguido del nombre del procedimiento seguido de un punto y coma. La forma genérica sería así:Código:PROCEDURE NombreDelProcedmiento(parametros en caso de ser necesarios); CONST (*Declaración de constantes locales en caso de que estas fueran necesarias*) TYPE (*Declaración de tipos locales en caso de que estos fueran necesarios*) VAR (*Declaración de variables locales en caso de que estas fueran necesarias*) (*Declaración de procedimientos y funciones locales en caso de que estos fueran necesarios*) BEGIN (*Bloque principal del procedimiento*) END NombreDelProcedimiento;
Su invocación es igual que en pascal.
Funciones:
Las funciones sí cambian bastante en Modula respecto a las de Pascal. Recordemos cómo declarábamos de forma genérica a una función:Código:FUNCTION NombreDeLaFuncion(parametros en caso de ser necesarios):Tipo_Retorno; CONST (*Declaración de constantes locales en caso de que estas fueran necesarias*) TYPE (*Declaración de tipos locales en caso de que estos fueran necesarios*) VAR (*Declaración de variables locales en caso de que estas fueran necesarias*) (*Declaración de procedimientos y funciones locales en caso de que estos fueran necesarios*) BEGIN (*Bloque principal de la función*) NombreDeLaFunciom:= //Algún valor del tipo Tipo_Retono. (*Con esta asignación le dábamos a la función el valor que debía retornar una vez finalizara su ejecución. END;
Ahora en Modula esto ha cambiado bastante. Como primera cosa, ya no tenemos la palabra reservada FUNCTION, sino que usamos PROCEDURE también para definir funciones. ¿Cómo sabe Modula cuando estamos definiendo una función o un procedimiento? Simplemente verifica si definimos o no un tipo para retornar.
De este modo el encabezado de un PROCEDIMIENTO en Modula esCódigo:PROCEDURE NombreDelProcedmiento(parametros en caso de ser necesarios);
y el de una función esCódigo:PROCEDURE NombreDeLaFuncion(parametros en caso de ser necesarios):Tipo_Retono;
Ahora bien, esta no es la única diferencia respecto a Pascal. Las funciones también terminan con un END y su nombre seguido de un punto y coma, eso supongo que resulta obvio. Pero en realidad el último cambio importante es la sentencia de retorno de la función. Mientras que en Pascal usábamos el identificador de la misma y le asignábamos un valor adecuado a modo de que al terminar la ejecución nuestra función nos retornara algo, en Modula ahora tenemos la sentencia RETURN.
Veamos un ejemplo de una función simple en Pascal, la cual realiza la división real entre dos números. Si el denominador vale 0, entonces la función retorna 0:Código:FUNCTION Division(nominador, denominador: REAL): REAL; BEGIN IF denominador=0 THEN Division:= 0 ELSE Division:= nominador/denominador; END;
En Modula esta misma función sería así:Código:PROCEDURE Division(nominador, denominador: REAL): REAL; BEGIN IF denominador=0.0 THEN RETURN 0.0; ELSE RETURN nominador/denominador; END; END Division;
En este ejemplo tenemos dos sentencias RETURN. Siempre luego de ejecutar esta sentencia, la función termina, por lo tanto, si ustedes colocan instrucciones debajo de RETURN con la esperanza de que sean ejecutadas, olvídenlo, porque Modula interpretará que ya no hay nada por hacer y por eso estamos retornando un valor.
En este ejemplo o usamos un RETURN o el otro, no hay modo de no llegar a uno. Sin embargo si ustedes no cuidan esto y llega una instancia en la que su función no encuentre algo que retornar, tendrán un error en tiempo de ejecución. Para darles un ejemplo tonto:Código:PROCEDURE Division(nominador, denominador: REAL): REAL; BEGIN IF denominador<>0.0 THEN RETURN nominador/denominador; END; END Division;
Si el usuario ingresa un denominador distinto de 0, no pasa nada, sin embargo si este justo vale 0 su programa caerá dando el error #RTS unhandled exception: function without RETURN statement, lo cual implica que la función no encontró nada que retornar. Cuidado con este tipo de errores.
Pasaje de parámetros:
El pasaje de parámetros en Modula sigue siendo exactamente igual a Pascal, de modo que, si queremos que sea por Referencia anteponemos la palabra VAR antes del parámetro que será pasado mediante ese método.
Más adelante pondremos énfasis en este tema al tratar las listas encadenadas, ya que como dije antes, los punteros serán el centro de nuestro trabajo en Modula2. -
-
-
Senior Member - Moderador
25/07/2012#3 Re: Lección 28: Sintaxis de las sentencias y declaraciones básicasPor ahora estoy solo con este curso de programación que pasa por Pascal, Modula y Java con lo cual veremos MySQL y alguna otra cosa.
De momento no estoy escribiendo otros cursos, pero se escuchan sugerencias. -
-
Bueno una sugerencia factible, sería obviamente que postearas algún curso de algo que sepas y manejes, claramente orientado a la programación. Se nota que eres inteligente, tenemos la misma edad , me estas enseñando a programar y razonar jeje, valla que si. por X circunstancias no he podido ingresar de lleno a una uni. Pero pronto lo haré. En fin ya me salí del tema. Puedes postear algun otro lenguaje que conozcas, o por ejemplo como hacer algoritmos, cosas así, supongo que ya sería tu criterio.
Cuidate y Saludos. -
-
Senior Member - Moderador
26/07/2012#5 Re: Lección 28: Sintaxis de las sentencias y declaraciones básicasPues amigo, en este curso recorreremos tres lenguajes de programación (Pascal, Modula y Java) en un recorrido progresivo de aprendizaje hasta manejar bien Java y la programación orientada a objetos. Además veremos MySQL para bases de datos.
La mejor forma de aprender a hacer algoritmos es haciéndolos, y justamente en el recorrido de este curso aprenderás mucho acerca de ello. También veremos aspectos de diseño y arquitectura de sistemas en tres capas y algunas cosas más.
Te aseguro que sabiendo eso, viendo un manual de cualquier otro lenguaje lo aprendes enseguida. La idea es con este curso romper ese muro que te impide poder seguir un manual de programación justamente porque uno no sabe programar y el manual no es muy amigable que digamos.
Si has llegado hasta aquí, con un poco de práctica dirás que ya conoces dos lenguajes de programación. Cuando lleguemos a Java conocerás tres, pero hay que ver bastantes cosas antes de eso.
Saludos y gracias por tus palabras. -
