Saca partido a tu e-mail


Promotor:
Unilever España, S.A.
Av. Diagonal 569
08089 Barcelona
Nif: 2866x52x

Referencia a las bases legales de la web del experimento AXE que aparece en www.plisplasplus.net.

Obtén una cuenta de @plisplasplus.es gratuita

Pelota de fútbolPelota de fútbolPelota de fútbol::__//-\\__EstrellaEstrellaEstrellaEstrellaEstrellaEstrellaEstrella__//-\\__::Pelota de fútbolPelota de fútbolPelota de fútbol
Para dejar tus comentarios u opiniones puedes utilizar el foro de plisplasplus!.(aquí)
Pelota de fútbolPelota de fútbolPelota de fútbol::__//-\\__EstrellaEstrellaEstrellaEstrellaEstrellaEstrellaEstrella__//-\\__::Pelota de fútbolPelota de fútbolPelota de fútbol

                                                                                                         NotaNotaNotaNotaNotaNotaNota
                                                                                                             logoavatarlos40Rosa roja
                                                                                                         NotaNotaNotaNotaNotaNotaNota            

Anuncio publicitario

Fundamentos de programación con Módula-2 (Tema 14_3)

14.5 Desarrollo modular basado en abstracciones

La técnica de programación estructurada, basada en refinamientos sucesivos, puede ampliarse para contemplar la descomposición modular de un programa. La metodología de desarrollo será esencialmente la misms que se ha presentado en el Tema 8, referente al desarrollo usando abstracciones en forma de subprogramas.

14.5.1 Abstracciones para desarrollo modular
Cuando se ha introducido el concepto de abstracción en los Temas anteriores, sólo se había mencionado la posibilidad de aplicarlo a expresiones o acciones, definiéndolos como funciones o procedimientos.

Estas abstracciones se llaman, en general, abstracciones funcionales, con independencia de que sean una función propiamente dicha o bien un procedimiento que realiza una acción.

El mecanismo de módulos permite ampliar el repertorio de abstracciones que pueden reconocerse y desarrollarse por separado en el proceso de construcción de un programa. Estas nuevas abstracciones se denominan abstracciones de datos, y podemos distinguir en ellas las dos clases ya mencionadas, correspondientes a los tipos abstractos de datos, y a los datos abstractos, encapsulados.

La organización de un programa en módulos se representa gráficamente, como ya se ha indicado, mediante un diagrama de estructura. En este diagrama se pueden marcar de manera diferente cada una de las distintas clases de abstracciones que pueden desarrollar como módulos separados.

 

La Figura 14.3 representa la estructura de un programa que se desarrollará como ejemplo en el resto de este Tema. Se han identificado cuatro abstracciones como módulos separados. Las abstracciones denominadas Portada y Centrar son abstracciones funcionales, correspondientes, respectivamente, al programa principal y a un subprograma algo complejo que se ha desarrollado por separado. Estas abstracciones funcionales se representan con un rectángulo con el borde en trazo sencillo.

La abstracción Cajas es un tipo abstracto de datos. Para distinguirla se ha empleado trazo doble en el borde superior del rectángulo. Finalmente, la abstracción Pagina es un dato encapsulado, y se ha marcado con trazo doble todo el borde del rectángulo.

14.5.2 Desarrollo por refinamiento basado en abstracciones
Esta técnica ya se ha desarrollado para el caso de abstracciones funcionales, definidas dentro del propio programa que las usa. Las únicas novedades que se introducen ahora son las abstracciones de datos y la posibilidad de desarrollar también las abstracciones funcionales como módulos separados.

Aplicaremos la técnica de refinamiento a un programa para componer la página de texto correspondiente a la portada de un libro o documento, imprimiendo en dicha página bloques rectangulares de texto rellenos con un mismo carácter, o con un texto que se imprime con las líneas centradas dentro de la zona.

Con elementos de este tipo podremos componer, por ejemplo, la portada siguiente, en que escribe el título dentro de un rectángulo en medio de la portada, con cuatro rectángulos en trama uniforme situados en las esquinas. Estos elementos aparecen superpuestos unos sobre otros, en la forma:

 

El desarrollo se hace por refinamientos sucesivos. La acción principal se descompone inicialmente en una secuencia de acciones sencillas, tales como:

Fundamentos de programación con Módula-2 (Tema 14_2)

14.4 Realización de tipos abstractos en Modula-2

Tal como se ha indicado, el concepto de módulo está ligado a la idea de abstracción; en particular, los módulos de Modula-2 son un mecanismo muy apropiado para la definición de tipos abstractos de datos.

14.4.1 Definición de tipos abstractos como módulos
Los módulos de Modula-2 permiten definir bien tipos abstractos de datos, gracias a la posibilidad de agrupación de diferentes elementos relacionados entre sí, y a la ocultación de los detalles de realización interna.

Podríamos decir que la interfaz del módulo equivale a la especificación del tipo, abstracto, de datos, y que la realización del módulo establece su representación interna.

Continuando con el ejemplo de los meses del año, lo definiremos como tipo abstracto especificando la colección de valores posibles y la colección de operaciones de manipulación que necesitemos. Fijando de manera un tanto arbitraria estas operaciones de manipulación, podríamos escribir una primera versión de la especificación del tipo abstracto:

(*_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
_/-\_
_/-\_   Este módulo define el tipo abstracto MES
_/-\_
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
*)

DEFINITION MODULE Meses;

TYPE TipoMes =
   ( MesNulo, Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio,
     Agosto, Septiembre, Octubre, Noviembre, Diciembre );

PROCEDURE Siguiente( mes: TipoMes; n: INTEGER ): TipoMes;
(* Mes que será dentro de ‘n’ meses *)

PROCEDURE Leer( VAR mes: TipoMes );
(* Lee el mes, en letra, desde el fichero de entrada *)

PROCEDURE Escribir( mes: TipoMes; ancho: INTEGER );
(* Escribe el mes en letra, con el ancho indicado *)

END Meses.

Teniendo definida la interfaz podemos ya pasar a escribir algún programa que use ese tipo abstracto de datos. Por ejemplo, podemos programar simplemente el cálculo del mes final de un plazo, conociendo el mes inicial y la duración. El programa podría ser:

(*_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
_/-\_
_/-\_   Este programa determina el mes en que termina un plazo,
_/-\_   sabiendo el mes inicial y los mese que dura.
_/-\_
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
*)

MODULE MesFinal;
   FROM InOut IIMPORT ReadInt, WriteString, WriteLn;
   IMPORT Meses;
   VAR
      ahora, luego: Meses.TipoMes;
      plazo: INTEGER;

BEGIN
   (*– Leer los datos –*)
      LOOP
         WriteString( ‘¿En qué mes estamos? ‘ );
         Meses.Leer( ahora );
         Meses.Escribir( ahora, 10 ); WriteLn;
         IF ahora <> Meses.MesNulo THEN EXIT END;
         WriteString( ‘Mes no reconocido, repita’ ); WriteLn
      END;
      WriteString( ‘¿Cuántos meses faltan? ‘ );
      ReadInt( plazo ); WriteLn;
   (*– Calcular e imprimir el resultado –*)
      luego := Meses.Siguiente( ahora, plazo );
      WriteString( ‘El plazo acaba en ‘ );
      WriteLn
END MesFinal.

Las posibilidades de usar el tipo abstracto con sólo definir la interfaz incluyen la compilación real del programa que acabamos de escribir. No podremos, sin embargo, ejecutarlo hasta haber escrito la realización de tipo abstracto.

A continuación presentamos una posible realización del módulo Meses.

(*_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
_/-\_
_/-\_   Este módulo realiza el tipo abstracto MES
_/-\_
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
*)

IMPLEMENTATION MODULE Meses;

FROM InOut IMPORT Read, Write, EOL;
TYPE NombreMes = ARRAY [0..10] OF CHAR;
VAR nombres: ARRAY [MesNulo..Diciembre] OF NombreMes;

PROCEDURE Siguiente( mes: TipoMes; n: INTEGER ); TipoMes;
(* Mes que será dentro de ‘n’ meses *)
   VAR x, m: INTEGER;
BEGIN
   (*– ajusta el incremento a un año, como máximo –*)
      x := n MOD 12;
   (*– incrementa en forma circular –*)
      m := ORD(mes); INC( m, x);
      IF m > 12 THEN DEC( m, 12 ) ENDM
      RETURN VAL( TipoMes, m )
END Siguiente;

PROCEDURE Leer( VAR mes: TipoMes );
(* Lee el mes, en letra, desde el fichero de entrada *)
   VAR c: CHAR;

PROCEDURE Ensayo( c1, c2: CHAR; m1, m2: TipoMes): TipoMes;
(* Distingue entre dos meses por el siguiente carácter *)
BEGIN
   Read( c );
   IF CAP( c ) = c1 THEN RETURN m1
   ELSIF CAP( c ) = c2 THEN RETURN m2
   ELSIF RETURN MesNulo END
END Ensayo;

BEGIN (* Leer *)
   (*– Buscar el comienzo del nombre –*)
      REPEAT Read( c ) UNTIL c<>’ ‘;
   (*– Reconocer los primeros caracteres –*)
      CASE CAP( c ) OF
          ‘A’: mes := Ensayo( ‘B’, ‘G’, Abril, Agosto )
      |   ‘D’: mes := Diciembre
      |   ‘E’: mes := Enero
      |   ‘F’: mes := Febrero
      |   ‘J’: Read( c );
              IF CAP( c ) = ‘U’ THEN
                 mes := Ensayo( ‘L’, ‘N’, Julio, Junio )
              ELSE
                 mes := MesNulo
              END
      |   ‘M’: Read( c );
               IF CAP( c ) = ‘A’ THEN
                  mes := Ensayo( ‘R’, ‘Y’, Marzo, Mayo )
               ELSE
                  mes := MesNulo
               END
      |   ‘N’: mes := Noviembre
      |   ‘O’: mes := Octubre
      |   ‘S’: mes := Septiembre
      ELSE mes := MesNulo END;

   (*– Salar el resto del nombre –*)
      WHILE (CAP( c ) >= ‘A’) AND (CAP( c ) <= ‘Z’) DO
         Read( c )
      END
END Leer;

PROCEDURE Escribir( mes: TipoMes; ancho: INTEGER );
(* Escribe el mes en letra, con el ancho indicado *)
   VAR k: INTEGER;
BEGIN
   FOR k := 0 TO ancho-1 DO
      IF k < 10 THEN              (* escribir letras del nombre *)
         Write( nombre[ mes, k ] )
      ELSE                            (* rellenar con blancos *)
         Write( ‘ ‘ )
      END
   END
END Escribir;

BEGIN (* Meses *)
   nombres[ MesNulo    ] := ‘??????????’;
   nombres[ Enero      ] := ‘Enero     ‘;
   nombres[ Febrero    ] := ‘Febrero   ‘;
   nombres[ Marzo      ] := ‘Marzo     ‘;
   nombres[ Abril      ] := ‘Abril     ‘;
   nombres[ Mayo       ] := ‘Mayo      ‘;
   nombres[ Junio      ] := ‘Junio     ‘;
   nombres[ Julio      ] := ‘Julio     ‘;
   nombres[ Agosto     ] := ‘Agosto    ‘;
   nombres[ Septiembre     ] := ‘Septiembre’;
   nombres[ Octubre    ] := ‘Octubre   ‘;
   nombres[ Noviembre  ] := ‘Noviembre ‘;
   nombres[ Diciembre  ] := ‘Diciembre ‘;
END Meses.

Una vez compilados el programa principal y el módulo, podemos ya montar y ejecutar el programa completo, y obtener, por ejemplo:

 

14.4.2 Tipos opacos
El módulo Meses del ejemplo anterior define realmente un tipo abstracto de datos, ya que especifica la colección de valores posibles del TipoMes, y las operaciones que pueden realizarse con esos valores.

Sin embargo no se aplica de manera total la propiedad de ocultación, ya que es visible en la interfaz que el tipo se define como un tipo enumerado, y esto permite usar todas las operaciones posibles del lenguaje para estos tipos. Por ejemplo, nada impide emplear el procedimiento predefinido INC para intentar obtener el mes final del plazo, escribiendo:
                . . . .
                luego := ahora;
                INC( luego, plazo );
                . . . .
Esto sería un error, ya que estas setencias no tienen en cuenta que los meses van avanzando cíclicamente, y que después de Diciembre viene de nuevo Enero. El procedimiento INC no opera cíclicamente, sino que da error si se rebasa el final del año.

Para asegurarse de que a los datos de un tipo abstracto se les aplican únicamente las operaciones que se han definido expresamente para ellos, es posible usar un artificio que permite definir los llamados tipos opacos en Modula-2.

Un tipo opaco se define en un "módulo de definición" escribiendo sólo la parte inicial de una definición de tipo, en la forma:

               TYPE nombre_de_tipo;

Como puede observarse, sólo se especifica el nombre del tipo, pero no se da ninguna información acerca de los posibles valores de este tipo ni de su representación. De esta manera es imposible aplicar ninguna construcción del lenguaje a los valores de ese tipo, salvo las siguientes:

  • Un valor del tipo opaco puede asignarse a una variable del mismo tipo.
  • Los valores o variables de ese tipo pueden pasarse como argumentos.
  • Dos valores del mismo tipo pueden comparase para ver si son iguales.

La definición completa del tipo ha de hacerse en el correspondiente "módulo de implementación". Sin embargo esta definición está sometida a fuertes limitaciones, para permitir que el "módulo de definición" pueda compilarse sin usar ninguna información del "módulo de implementación".

Las versiones más estrictas de Modula-2 exigen que los tipos opacos se definan internamente como tipos puntero, exclusivamente. Otras versiones permiten definir estos tipos con más libertad, pero de modo que el espacio de memoria necesario para almacenar cada valor del tipo sea mayor que el espacio ocupado por un puntero. Esto permite, en general, definir también los tipos opacos como tipos ordinales de cualquier clase.

De todas formas la seguridad conseguida en el uso de la interfaz del módulo es a costa de complicar la invocación de ciertos elementos. Por ejemplo, no es posible definir funciones que devuelvan los valores constantes que se necesiten, o bien que determinen si el valor del tipo opaco es un valor en particular.

En el caso de tipos opacos que deban realizarse como estructuras de datos, será necesario, en general, realizarlos con variables dinámicas, y habrá que incluir sistemáticamente operaciones sobre el tipo abstracto para crear o destruir variables de ese tipo.

14.4.3 Datos encapsulados
Los esquemas anteriores equivalen a definir un tipo (abstracto), del que luego hay que declarar las variables necesarias. Cuando en un programa sólo hay que manejar una única variable de ese tipo, se puede conseguir fácilmente una ocultación total sin necesidad de recurrir a tipos opacos, si dicha variable única es interna al módulo en que se desarrolla el tipo abstracto.

Existe una analogía clara entre esta idea y las dos formas básicas de declaración de variables en Modula-2. En el primer caso escribiríamos, por ejemplo:

              TYPE Nombre = ARRAY [0..20] OF CHAR;
              . . . .
              VAR autor: Nombre;
              . . . .
              ReadString( autor );
              WriteString( autor )

En el segundo caso escribiríamos:

             VAR autor: ARRAY [0..20] OF CHAR;
             . . . .
             ReadString( autor );
             WriteString( autor )

En este segundo caso el tipo es anónimo, y no tiene nombre particular. Aplicando esta idea al manejo de datos abstractos en módulos, el primer caso, descrito en las acciones anteriores, equivaldría a escribir:

DEFINITION MODULE Nombres;
   TYPE Nombre = ARRAY [0..20] OF CHAR;
   PROCEDURE Leer( VAR n: Nombre );
   PROCEDURE Escribir( n: Nombre );
END Nombres.

MODULE Principal;
   IMPORT Nombres;
   VAR autor: Nombres.Nombre;
BEGIN
   Nombres.Leer( autor );
   Nombres.Escribir( autor );
END Principal.

El esquema correspondiente al segundo caso, sin tipo explícito, sería:

DEFINITION MODULE Autor;
   PROCEDURE Leer;
   PROCEDURE Escribir;
END Autor.

MODULE Principal;
BEGIN
   Autor.Leer;
   Autor.Escribir
END Principal.

En este segundo esquema el módulo no corresponde al tipo, sino a la variable, y de acuerdo con ello se ha elegido el nombre del módulo. La variable en sí estaría oculta en el "módulo de implementación", en la forma:

IMPLEMENTATION MODULE Autor;
   VAR autor: ARRAY [0..20] OF CHAR;
   . . . .
END Autor.

El módulo constituye una cápsula que protege al dato, de manera que sólo se puede acceder a él usando las operaciones de la interfaz, y ninguna otra en absoluto. Cuando se usa esta técnica no se pueden aplicar al dato desde el exterior del módulo ni siquiera las operaciones enunciadas para los tipos opacos, pues lo que está encapsulado no es ya el tipo, sino el dato mismo. Obsérvese que las operaciones que manejan el dato no lo mencionan como argumento.

Repetimos que esta técnica resulta fácil de aplicar sólo cuando existe únicamente una variable del tipo abstracto que se desea ocultar.

Fundamentos de programación con Módula-2 (Tema 14_1)

Tema 14
Módulos

 

En este Tema se da una introducción a la programación modular, en especial basada en el empleo de tipos abstractos de datos.

Se introduce el concepto de módulo, en general, y su realización en Modula-2, en particular. Se discuten brevemente las características necesarias para compilar módulos por separado, en forma segura, y la manera de conseguirlo en este lenguaje.

Se describe la noción de tipo abstracto de datos, y las técnicas para programarlos usando el mecanismo de módulos en Modula-2. Se menciona la posibilidad de realizar ocultación mediante tipos opacos, con las limitaciones impuestas por el lenguaje.

Finalmente se extiende la metodología de desarrollo de programas con la posibilidad de descomposición en módulos separados, estableciendo las recomendaciones del desarrollo modular basado en abstracciones.

14.1 Concepto de módulo

En programación, un módulo es, en general, un fragmento de programa utilizado en algún momento para la construcción del programa completo. Lo que distingue a un módulo propiamente dicho de un fragmento arbitrario del programa es el hecho de que en algún momento de la construcción haya sido reconocido como tal, y por tanto que se haya desarrollado o refinado en forma relativamente independiente del resto del programa. Podríamos definir:

MÓDULO: Fragmento de programa desarrollado en forma independiente.

El desarrollo independiente debe serlo en el máximo grado posible. Atendiendo a las técnicas de preparación de programas en lenguaje de programación simbólicos, diremos que un módulo debería ser compilado por separado, y no tratarse de un simple fragmento de texto dentro de un único programa fuente.

La razón de exigir compilación por separado para los distintos módulos de un programa obedece a la necesidad de limitar la complejidad de aquello que está siendo elaborado por una persona en un momento dado. Si el módulo se va a compilar por separado, la persona que lo desarrolle podrá concentrarse en él, prescindiendo en parte de cómo se utiliza ese módulo desde el resto del programa. De la misma forma, quien escriba el resto del programa no se preocupará de los detalles de cómo está codificado el módulo, sino sólo de cómo hay que usarlo.

El concepto de módulo, por tanto, está íntimamente ligado a la idea de abstracción. Un módulo debe definir un elemento abstracto (o varios relacionados entre sí) y debe ser usado desde fuera con sólo saber qué hace el módulo, pero sin necesidad de conocer cómo lo hace.

14.1.1 Especificación y realización
Igual que en cualquier otro elemento abstracto, en un módulo podemos distinguir dos puntos de vista, correspondientes a su especificación y a su realización. Tal como se ha indicado con anterioridad, la primera visión nos dice qué hace el módulo, y la segunda nos dice cómo lo hace.

La especificación de un módulo que contenga la definición de una serie de elementos abstractos consistirá, fundamentalmente, en el conjunto de las especificaciones de cada uno de ellos, por separado, más una indicación de los posibles efectos de unos sobre otros cuando se usan en forma combinada.

La realización del módulo consistirá en la realización de cada uno de los elementos abstractos contenidos en dicho módulo.

La especificación del módulo es todo lo necesario para poder usar los elementos definidos en él. Esta especificación constituye la interfaz (en inglés "interface") entre el módulo (incluida su realización) y el programa que lo usa.

La independencia entre la realización de un módulo y el programa que lo usa se incrementa si la realización de un elemento abstracto no esvisible desde donde se usa. Esta castacterística se denomina ocultación, y ha sido ya desarrollada en un Tema anterior para las funciones y procedimientos, explicando el mecanismo de visibilidad por bloques.

Referida a los módulos, la ocultación consiste en que el programa que usa un elemento de un módulo sólo tiene visible la información de la interfaz, pero no la de la realización.

14.1.2 Compilación separada
Los lenguajes de programación que permiten programar usando módulos pueden emplear diversas técnicas para definirlos e invocar los elementos definidos en ellos. Tal como se ha comentado antes, es importante que los módulos puedan compilarse por separado. Esto quiere decir que los lenguajes de programación deben permitir escribir un programa complicado como un conjunto de varios ficheros fuente distintos, cada uno de los cuales pueda compilarse de manera más o menos independiente de los demás.

Por otra parte, para que el uso de los elementos de un módulo sea correcto, habrá que hacerlo de acuerdo con la interfaz establecida. La interfaz debe ser tenida en cuenta al compilar un programa que use elementos de un módulo separado. Por el contrario, la realización del módulo debe permanecer invisible para el programa que lo usa con objeto de mantener la deseable ocultación de los detalles de los elementos abstractos contenidos en él.

En la Figura 14.1 se representa gráficamente la visibilidad deseable entre un módulo y el programa que lo usa.

Figura(14_1)
   Figura 14.1. Visibilidad de un módulo

Resumiendo:

COMPILACIÓN SEPARADA: El programa está formado por varios ficheros fuente, cada uno de los cuales se compila por separado.

COMPILACIÓN SEGURA: Al compilar un fichero fuente el compilador comprueba que el uso de elementos de otros módulos es consistente con la interfaz.

OCULTACIÓN: Al compilar un fichero fuente el compilador no usa información de los detalles de realización de elementos de otros módulos.

Entre las técnicas empleadas por los lenguajes de programación reales en lo que respecta a compilación separada, tenemos situaciones tales como las siguientes:

   a)  El fichero del programa y del módulo se tratan en forma totalmente separada, sin visibilidad de la interfaz (lenguaje FORTRAN y las primeras versiones del lenguaje C).

   b)  La parte necesaria de la interfaz se copia manualmente en el programa que la usa. La compilación de los ficheros del programa y del módulo se hace con total independencia (lenguaje C ANSI, con prototipos, y algunas versiones del lenguaje Pascal, con la directiva EXTERN).

   c)  La interfaz del módulo y su realización se escriben en ficheros separados. El mismo fichero de interfaz se usa tanto al compilar la realización del módulo como al compilar el programa que lo usa (lenguajes Modula-2 y Ada).

En los lenguajes que usan la técnica (a) no hay compilación segura. En los mencionados en (b) la seguridad es mayor, pero aún hay posibilidad de errores si no coincide la interfaz del módulo con la copia usada en el programa. Con los lenguajes que usan la técnica (c) la compilación es completamente segura.

14.1.3 Descomposición modular
La posibilidad de compilar módulos en forma separa permite repartir el trabajo de desarrollo de un programa, a base de realizar su descomposición modular. Los diferentes módulos pueden ser encargados a programadores diferentes, y gracias a ello todos pueden trabajar al mismo tiempo.

De esta forma se pueden desarrollar en un tiempo razonable los grandes programas correspondientes a las aplicaciones de hoy día, que totalizan cientos de miles de sentencias.

La descomposición modular de un programa puede reflejarse en un diagrama de estructura, tal como el de la Figura 14.2. En este diagrama se representa cada módulo en forma de un rectángulo, con el nombre del módulo en su interior, y se usan líneas para indicar las relaciones de uso entre ellos.

Figura(14_2) 
     Figura 14.2. Ejemplo de diagrama de estructura

En este ejemplo el módulo A usa elementos de los módulos B y C, y el módulo B usa elementos de C y D. Los módulos C y D no usan ningún otro módulo. Las líneas que indican relaciones de uso pueden llevar punto de flecha si es necesario indicar expresamente cuál es el sentido de la relación. Normalmente no es necesario, pues, como en este caso, un módulo que usa otro se dibuja encima de él, de manera que las líneas de uso se interpretan siempre de arriba a abajo, estableciendo al mismo tiempo una jerarquía entre módulos.

El objetivo de la ingenería de software es facilitar el desarrollo de la aplicación en forna organizada, y de manera que muchas personas puedan colaborar simultáneamente en un mismo proyecto. Para que la descomposición en módulos sea adecuada, desde ese punto de vista, conviene que los módulos resulten tan independientes unos de otros como sea posible. Esta independencia se analiza según dos criterios, denominados acoplamiento y cohesión.

El acoplamiento entre módulos indica cuántos elementos distintos o característicos de uno o varios módulos han de ser tenidos en cuenta a la vez al usar un módulo desde otro. Este acoplamiento debe reducirse a un mínimo.

La cohesión indica el grado de relación que existe entre los distintos elementos de un mismo módulo, y debe ser lo mayor posible. Esto quiere decir que dos elementos íntimamente relacionados deberían ser definidos en el mismo módulo.

14.2 Módulos en Modula-2

El nombre con que se ha bautizado al lenguaje Modula-2 nos indica ya que debe disponer de mecanismos adecuados para la definición de módulos que pueden ser desarrollados por separado.

Un programa descompuesto en módulos se escribe como un conjunto de ficheros fuente relacionados entre sí, y que pueden compilarse por separado. Cada fichero fuente constituye así una unidad de compilación. A continuación se describen las distintas unidades de compilación que pueden definirse en Modula-2.

14.2.1 Módulo principal
Un programa en Modula-2 se puede descomponer en varios módulos. Uno de estos módulos ha de ser el programa o módulo principal. La ejecución del programa completo equivale, en principio, a la ejecución del módulo principal. Por supuesto, el módulo principal puede invocar durante su ejecución operaciones definidas en otros módulos.

Todos los ejemplos de programas completos desarrollados hasta ahora se componían exclusivamente de un módulo principal. Para ser precisos habrá que decir que en estos ejemplos se usaban módulos "estándar", pero puede considerarse que estos módulos no son parte del programa o aplicación desarrollada.

El módulo principal debe importar explícitamente de los otros módulos todos los elementos que vaya a usar. La manera de escribir un módulo principal ya ha sido expuesta en los Temas anteriores, por lo que no merece la pena dar más explicaciones en este momento.

14.2.2 Módulos de definición
La terminología de Modula-2 es quizá un poco inadecuada en cuanto a los nombres dados a las unidades de compilación, ya que se llama módulo a cada una de ellas. Por una parte tenemos que el programa principal representa de alguna forma la aplicación completa, por lo que puede resultar confuso llamarle "módulo". Por otra parte, cada módulo que forme parte del desarrollo de un programa en Modula-2 ha de escribirse como dos ficheros o unidades de compilación separadas, cada una de las cuales se denomina "módulo" aunque en realidad sean partes de un mismo módulo.

Las dos unidades de compilación de un módulo corresponden, como se había apuntado, a la especificación (o interfaz) y la realización (o "implementación"). La especificación de un módulo se escribe como módulo de definición en Modula-2. La forma general de un módulo de definición es:

                DEFINITION MODULE  Nombre;
                   … listas de importaciones …
                   … definiciones …
                END Nombre.

Las listas de importaciones se escriben igual que para el módulo principal. Las definiciones pueden incluir definiciones de constantes, tipos y variables, y también pueden incluir especificaciones de subprogramas (procedimientos y funciones). Una especificación de subprograma consistirá en la cabecera de la función o procedimiento. Por supuesto, se pueden añadir cuantos comentarios se consideren apropiados para documentar la interfaz del módulo.

Como ejemplo consideramos un módulo que contenga los elementos necesarios para imprimir series de números formando varias columnas a lo ancho del listado. La impresión a varias columnas ya se había desarrollado anteriormente en dos ejemplos del Tema 8. Ahora reuniremos los elementos necesarios para formar con ellos un módulo separado. La interfaz de este módulo puede ser como la que aparece en el siguiente listado:

(*_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
_/-\_
_/-\_   Este módulo contiene los elementos para imprimir series
_/-\_   de números a varias columnas.
_/-\_
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_*)

DEFINITION MODULE Tabulación;
VAR
   numColumnas: INTEGER;               (* número de columnas *)
   ancho: INTEGER                        (* ancho de cada columna*)

PROCEDURE Imprimir( numero: INTEGER );
(* Imprime el número, tabulando *)

PROCEDURE Terminar;
(* Completa la impresión de la última línea *)

END Tabulacion.

Los parámetros de impresión pueden fijarse modificando directamente las variables en la interfaz del módulo. Si no se modifica el valor de estas variables está previsto usar unos parámetros por defecto.

La forma exacta de escribir un módulo de definición se describe mediante la siguientes reglas BNF:

Módulo_definición ::=
          Cabecera_definición
          { Definición_de_elementos }
          END Identificador .

Cabecera_definición ::=
          DEFINITION MODULE Identificador ;
          { Lista_importados ; }
          [ Lista_exportados ; ]

Definición_de_elementos ::=

          Declaración_de_constantes |
          TYPE { Identificador [ = Esquema_de_tipo ] ; } |
          Declaración_de_variables |
          Cabecera_subprograma ;

La lista de exportación sólo se emplea en versiones antiguas de Modula-2, y en ella se enumeran explícitamente los elementos que pueden ser usador por otros módulos, en la forma

                                     EXPORT QUALIFIED   … lista de identificadores …;

En las versiones actuales todos los elementos del "módulo definición" se exportan implícitamente.

14.2.3 Módulos de implementación
Los módulos de implementación de Modula-2 contienen la realización de un módulo. Esta realización tiene exactamente la misma forma que un módulo principal, precedido de la palabra clave IMPLEMENTATION:

                                   IMPLEMENTATION MODULE Nombre;
                                      … lista de importaciones …
                                      … definiciones …

                                   BEGIN
                                      … sentencias de inicialización …
                                   END Nombre.

Las sentencias ejecutables que figuran al final del módulo son opcionales (si no hay sentencias se suprime también la palabra BEGIN). Estas sentencias se ejecutan automáticamente al comienzo de la aplicación, antes de que se ejecute el programa principal,  y antes de que se ejecute la inicialización de cualquier otro módulo que utilice este módulo.

Si examinamos la jerarquía de módulos reflejada en el diagrama de estructura de una aplicación, podemos darnos cuenta de que las partes ejecutables de los módulos (incluyendo el programa principal) se ejecutan estrictamente de abajo a arriba, empezando por inicializar los módulos que no usan a ningún otro, luego los que usan éstos, y terminando por ejecutar el módulo principal.

En el siguiente ejemplo tenemos la realización del módulo para tabular series de números:

(*_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
_/-\_
_/-\_   Este módulo contiene los elementos para imprimir series
_/-\_   de números a varias columnas.
_/-\_
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_*)

IMPLEMENTATION MODULE Tabulacion;
   FROM InOut IMPORT WriteLn, WriteInt;

VAR   columna: INTEGER;              (* columna a imprimir *)

PROCEDURE Iniciar;
(* Prepara la impresión desde la 1ª columna *)
BEGIN
   columna := 1
END Iniciar;

PROCEDURE Imprimir( numero: INTEGER );
(* Imprime el número, tabulando *)
BEGIN
   IF columna > numColumnas THEN
      WriteLn;
       columna := 1
   END;
   WriteInt( numero, ancho );
   INC( columna )
END Imprimir;

PROCEDURE Terminar;
(* Completa la impresión de la última línea *)
BEGIN
   IF columna > 1 THEN WriteLn END;
   Iniciar
END Terminar;

BEGIN
   (*– fijar parámetros por defecto e iniciar impresión –*)
      numColumnas := 4;
      ancho := 10;
      Iniciar

END Tabulacion.

Las sentencias de inicialización asignan valores por defecto a los parámetros y preparan el módulo para imprimir números desde la primera columna.

14.2.4 Uso de módulos
Los elementos definidos en un módulo se pueden usar desde otro, en la forma que ya se ha venido realizando en los módulos estándar. Ahora daremos una visión más completa de los mecanismos de Modula-2 para usar elementos de otros módulos.

Para usar elementos de otros módulos hay que importarlos. La importación se realiza al comienzo del programa (o módulo) que desea usarlos. La forma de importación usada hasta el momento ha sido:

                                FROM nombre_de_módulo IMPORT lista_de_nombre;

Con esta forma de importación los elementos importados se usan directamente por su nombre, tal como si hubieran sido definidos directamente en el programa que los usa.

Existe otra forma de importación, que se escribe simplemente:

                                IMPORT  nombre_de_módulo;

Con esta segunda forma de importación se puede usar cualquier elemento definido en ese módulo, pero no directamente por su nombre, sino designándolo mediante la combinación:

                                nombre_de_módulo . nombre_de_elemento

Esta notación se llama nombre cualificado, y tiene una apariencia similar a la referencia a campos de un registro, ya que también se usa el punto (.) para ligar los dos identificadores usados para la designación de un elemento particular.

A continuación se muestra un sencillo programa para tabular la serie de números del 1 al 20, usando el módulo de tabulación anterior, y empleando los parámetros de tabulación por defecto:

(*_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
_/-\_
_/-\_   Este programa imprime la serie de números del 1 al 29,
_/-\_   a varias columnas.
_/-\_
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_*)

MODULE Serie;

   FROM Tabulacion IMPORT Imprimir, Terminar;

   VAR k: INTEGER;

BEGIN
   FOR k := 1 TO 20 DO
      Imprimir( k )
   END;
   Terminar
END Serie.

El resultado es:

-/@\

En este ejemplo se emplea la misma forma de importación usada habitualmente en los ejemplos anteriores.

Este ejemplo funciona correctamente gracias al orden apropiado en que se ejecutan automáticamente las acciones descritas en la parte ejecutable de cada módulo. La inicialización de módulo de tabulación se ejecuta antes que el módulo principal que lo utiliza. De esta manera al invocar por primera vez la operación de Imprimir las variables de la interfaz del módulo de tabulación contienen los parámetros por defecto y el contador de columnas oculto en su realización está inicializado a 1.

A continuación se muestra un programa similar al anterior, ampliado para mostrar cómo se pueden ajustar los parámetros de tabulación a los valores deseados en cada caso:

(*_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_
_/-\_
_/-\_   Este programa imprime la serie de números del 1 al 29,
_/-\_   dos veces, formando diferentes números de columas.
_/-\_
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\_*)

MODULE Serie2;

   IMPORT Tabulacion;

   VAR k: INTEGER;

BEGIN
   (*– imprimir a 3 columnas de 13 caracteres –*)
      Tabulacion.numColumnas := 3;
      Tabulacion.ancho := 13;
      FOR k := 1 TO 29  DO
         Tabulacion.Imprimir( k )
      END;
      Tabulacion.Terminar;
   (*– imprimir a 6 columnas de 5 caracteres –*)
      Tabulacion.numColumnas := 6;
      Tabulacion.ancho := 5;
      FOR k := 1 TO 29 DO
         Tabulacion.Imprimir( k )
      END;
      Tabulacion.Terminar;
END Serie2.

Ahora los resultados son:

-/@\

En este ejemplo se ha usado la forma de importación simple, que exige usar nombres cualificados.

Quizá convenga hacer algunas recomendaciones de estilo con respecto al empleo de nombres cualificados. La referencia simple a elementos importados usando directamente su nombre resulta, en principio, más sencilla. Sin embargo sólo puede considerarse recomendable en aquellos casos en que los elementos nombrados son bien conocidos por todos los que han de consultar en algún momento el texto del programa. Esto ocurre, por ejemplo, con los elementos definidos en los módulos "estándar" asociados al lenguaje.

Por el contrario, cuando una aplicación tiene un número relativamente importante de módulos particulares, el uso de los nombres simples puede acabar siendo confuso, ya que es difícil recordar de memoria en qué módulo está definido cada uno. Incluso se pueden presentar ambigüedades, ya que el nombre de un elemento de un módulo puede coincidir con el otro elemento distinto definido en otro módulo.

En estos últimos casos es preferible usar nombres cualificados, aunque la redacción del programa sea algo más prolija, ya que se gana en precisión, y en cada punto en que se usa un elemento importado queda explícitamente indicado en qué módulo se encuentra definido.

14.3 Tipos abstractos de datos

En programación tradicional, se ha identificado el concepto de tipo de dato con el de la clase de valores que pueden tomar los datos de ese tipo. De hecho esta idea se ha aplicado más a la forma de representación de los valores que a los valores en sí. Por ejemplo, si tenemos que operar con valores correspondientes a los meses del año, lo podremos hacer representándolos mediante números (1 al 12) o bien mediante ristras de caracteres (‘ENE’, ‘FEB’, etc.), entre otras posibilidades. Estas representaciones se asocian con diferentes tipos de datos. En Modula-2 escribiríamos:

                 TYPE TipoMes = INTEGER;

o bien:

                TYPE TipoMes = ARRAY [0..3] OF CHAR;

Un enfoque más moderno de la programación trata de asociar la idea de tipo de datos con la clase de valores, abstractos, que pueden tomar los datos. Esto quiere decir que la representación o codificación particular de los valores no cambia, de hecho, el tipo del dato considerado. Un paso adelante en este sentido ha sido la introducción (en lenguajes como Pascal, Modula-2 o Ada) de los tipos enumerados, en que se definen colecciones de valores, abstractos, asociados a identificadores utilizables dentro del programa.

Los valores de los tipos enumerados no son valores numéricos, ni ristras de caracteres, aunque pueden transformarse en esas otras formas de representación usando apropiadamente los mecanismos del lenguaje. Por ejemplo:

               TYPE TipoMes = (Enero, Febrero, … Diciembre);
               VAR mes: TipoMes;
               . . . .
               WriteInt( ORD(mes)+1, 2 )

En este enfoque más moderno de la programación se identifican los tipos de datos en forma completamente abstracta, llegando a la idea de tipos abstractos de datos. Esto quiere decir que un programa que use ese tipo de datos no debería necesitar ningún cambio por el hecho de modificar la representación o codificación de los valores de ese tipo. Si analizamos con cuidado qué necesita un programa para poder usar datos de un tipo, encontraremos que hace falta:

  • Hacer referencia al tipo en sí, mediante un nombre, para poder definir variables, subprogramas, etc.
  • Hacer referencia a algunos valores particulares, generalmente como constantes con nombre.
  • Invocar operaciones de manipulación de los valores de ese tipo, bien usando operadores en expresiones aritméticas o bien mediante subprogramas.

El conjunto de todos estos elementos constituye el tipo abstracto de datos:

TIPO ABSTRACTO DE DATOS: Agrupación de una colección de valores y una colección de operaciones de manipulación.

Es importante comprender que estas colecciones son cerradas, es decir sólo se deben poder usar los valores abstractos y las operaciones declaradas para ese tipo. Esto no ocurría cuando se asociaba el tipo de valor con su forma de representación. Por ejemplo, si representamos los meses del año mediante números, podremos usar el número 33, aunque no sea ningún mes válido, al igual que podremos multiplicar los números dos meses aunque esto no tenga ningún sentido.

14.4

::= DEFINICION

| alternativa

{} REPETICION

[]OPCION

()AGRUPAR

Estudiar Informática en España (artículo de revista)

Convierte tu pasión en profesión

Si estás leyendo esto es porque la informática forma parte de tus aficiones, e incluso de tus pasiones. ¿Has pensado alguna vez en convertirla en un trabajo gratificante? Para ello, una buena forma de empezar es enterarte de qué es lo que tienes que estudiar y dónde puedes hacerlo en España. Aquí tienes algunas pistas.

Basta echar un vistazo a las páginas web de ofertas de empleo para comprobar que convertirse en profesional de la informática es una buena elección. Los tiempos de la caída de las ".com" terminaron y actualmente sólo los comerciales lo tienen más fácil que los informáticos para encontrar trabajo. Ya sea en un negocio familiar o en una importante sociedad cotizada, antes de incorporarte a este mercado laboral has de prepararte.
   Ahora es un momento ideal para lanzarte a estudiar y, además, las posibilidades para hacerlo en España son muchas. Lejos queda ya ese 1976 cuando la Politécnica de Madrid, la de Cataluña y la Universidad del País Vasco sorprendieron anunciando que en sus aulas se podía estudiar una titulación nueva: Informática. Durante estos 31 años, la carrera ha ido evolucionando y el sistema educativo del siglo XXI ofrece múltiples opciones.

Las empresas se rifan a los informáticos

  El escaparate educativo comienza en la universidad tradicional, que ofrece tres carreras dentro del campo de la informática: Ingeniero en Informática, Ingeniero Técnico en Informática de Gestión, e Ingeniero Técnico en Informática de Sistemas. Pero eso no es todo, ya que además de las licenciaturas y diplomaturas existen otras posibilidades de estudios, como los títulos propios de algunas universidades privadas, carreras extranjeras y Formación Profesional. Elijas lo que elijas, recuerda que cualquier empresa tiene ordenadores y requiere de la labor de los informáticos. Entre las actividades que podrás realizar una vez que hayas finalizado los estudios está la de integrar los sistemas de la empresa, instalando y ajustando los sistemas operativos y las redes informáticas, y también trabajar como arquitecto de soluciones, técnico en desarrollo de programación y mantenimiento de aplicaciones.
   Las Comunidades Autónomas que generan mayor número de empleos son, por este orden, Madrid, Cataluña, Andalucía, Valencia y País Vasco, que suman un 73% del total de las ofertas, según el informe anual de
www.infoempleo.com. Además, las empresas que trabajan en los sectores de informática, consultoría, telecomunicaciones, enseñanza e industrial acumulan el 58% de las ofertas de informática, y son los departamentos de informática, comercial, producción y servicios generales los más necesitados, concentrando el 80% del total del trabajo de los informáticos.
   Hasta aquí las buenas noticias ya que, como suele ser norma en muchas profesiones, en el 80% de las ofertas de empleo se requiere experiencia, y en el 50% se precisa conocimiento de otros idiomas, especialmente inglés. Estos datos indican que lo complicado es encontrar tu primer empleo, pero recuerda que la mayoría de las universidades ofrecen prácticas en empresas que te ayudarán a "meter la cabeza". Aunque de encontrar trabajo ya te preocuparás cuando acabes los estudios. Ahora es el momento de elegir.
   Para empezar, la carrera de Ingeniero en Informática -estipulada en cinco años- presenta la dificultad de que sus asignaturas tienen un marcado carácter técnico, poco apta para estudiantes que tengan problemas con las Matemáticas. Además, la constante evolución del sector provoca que, según los expertos de la editorial Círculo del Progreso, el alumno tenga algunas carencias en la formación recibida cuando se enfrenta al mercado laboral.
   A pesar de este "handicap", el objetivo de esta titulación es, según el Ministerio de Educación y Ciencia, formar a profesionales capacitados para crear todo tipo de programas y aplicaciones informáticas que se ajusten a las necesidades de la empresa que lo haya contratado. El contenido básico de la hermana mayor de las ingenierías está compuesto por conocimientos técnicos clásicos, tales como Matemáticas, Lógica, Ingeniería, Física, Electrónica y Estadística, más lenguajes informáticos de programación.

Dificultad alta, acceso sencillo

   Una vez finalizado el primer ciclo, tres años, se puede acceder -por si te lo has pensado mejor- a otras titulaciones ajenas a la informática, como Ciencias y Técnicas Estadísticas, Ingeniero en Electrónica, Investigación y Técnicas de Mercado, etc. Esta característica es compartida por las titulaciones menores de informática: Ingeniero Técnico en Informática de Gestión e Ingeniero Técnico en Informática de Sistemas. La primera de ellas tiene unos objetivos finales similares a la ingeniería de cinco años, pero con menos materias al durar sólo tres años.
   De todas formas, no te confíes, ya que acabar estas titulaciones en 3 o 5 años está al alcance de unos pocos, según el Consejo de Universidades. De hecho, para la ingeniería de cinco años, la duración suele ser de 7,3 años, mientras que para la técnica roza casi los cinco años. A pesar de estos números, la informática no es ni de lejos la carrera más difícil. Semejante galardón se lo lleva la Ingeniería de Arquitectura, con una media de 8,4 años, seguida de Aeronáutica, con 7,8 años.
   La última titulación, la Técnica en Informática de Sistemas (también en tres años), tiene el objetivo de formar estudiantes capaces de manejar múltiples aplicaciones informáticas, sistemas operativos y sistemas de transmisión de datos. Es decir, ideal para los que estén interesados en integrarse en el departamento de sistemas de una compañía, aunque también cuenta con cursos especializados para acceder a puestos de programador.
   Si llegados a este punto ya has decidido qué quieres hacer y qué carrera se adapta mejor a tus objetivos, ahora falta otro paso: comprobar que la nota obtenida en selectividad te permite acceder a ella. A la gran mayoría de las universidades públicas y privadas les basta con un 5, aunque existen contadas excepciones como la Politécnica de Valencia que pide un 6,32 para cursar la ingeniería en Informática; o la Universidad del País Vasco, que sólo acepta notas superiores a 6,12 para la técnica de Gestión.
   Y si eres de aquellos a los que no le asustan los retos, hay universidades en España (Internacional de Cataluña, Rey Juan Carlos, Politécnica de Cataluña, Jaén, Vic y Málaga) que ofrecen la posibilidad de estudiar dobles licenciaturas, como Informática de Gestión más Administración y Dirección de Empresas, o Empresariales, Estadística, Matemáticas… Eso sí, habrás de dedicarle más tiempo y esfuerzo.
   Si, por el contrario, no andas sobrado de tiempo o acudir a clase cada día es más una carga que una satisfacción, existen otras dos opciones. En la Universidad a Distancia (
UNED) también se cursan las tres titulaciones y con tutorías específicas. Por ejemplo, en el caso de la Técnica de Informática de Gestión se puede acudir tres días a la semana. Al no tener que acudir a clase, la lógica indica que obtener el título por la UNED requiere de mayor fuerza de voluntad, ya que no hay una obligación diaria de acudir a ningún centro.
   En línea similar está la Universitat Oberta de Catalunya, la universidad virtual. En este centro se emplean de manera intensiva las tecnologías de la información y la comunicación, para así tratar de superar las barreras de tiempo y espacio y ofrecer un modelo educativo más personalizado. En la UOC, estudiantes, profesores y gestores interactúan y cooperan en el "Campus Virtual".

La opción privada

   Otra posibilidad más que existe para convertirte en un experto informático son las carreras privadas y las que imparten universidades extranjeras con presencia en España. Estas titulaciones han adquirido prestigio en los últimos años, aunque para acceder a ellas es necesario disponer de una holgada solvencia económica ya que su coste es de una media de 3.500 € por curso. Existen muchas opciones, aunque algunas de las más interesantes son las titulaciones que ofrece la Universidad de Mississippi, la Saint Louis University, el Instituto Cibernos y el Centro de Estudios Superiores Franceses.

Formación profesional

   Vilipendiada hace años, la Formación Profesional es una opción más que válida para aprender una profesión. Los titulados en informática por FP se encargan de arreglar los ordenadores, y se preocupan para que la gestión y la administración de la empresa pueda realizarse sin problemas. Hay tres posibilidades para obtener el grado de Técnico Superior, y las tres de dos años de duración y 2.000 horas lectivas. Se accede a ellas desde el Bachillerato.
   El objetivo del título de Administración de Sistemas Informáticos es mantener los ordenadores de una empresa totalmente operativos, Desarrollo de Aplicaciones Informáticas tiene por objeto desarrollar herramientas informáticas para los departamentos de una compañía, y Explotación de Sistemas Informáticos hace hincapié en el mantenimiento de redes e Internet. De todas formas, y al igual que ocurre con las ingenierías, lo que aquí se aprende puede quedar obsoleto en pocos meses por el rápido desarrollo del sector. Por esto motivo, es imprescindible completar la FP con cursos especializados.
   Pero si quieres meterte de lleno en el "corazón" de la informática, has de saber que expertos "cirujanos" como IBM, Oracle, o Microsoft imparten cursos propios. La ventaja en estos casos es que recibirás formación adaptada a las necesidades de la empresa y es más fácil acceder a la plantilla.
   Ahora sólo queda que tú decidas. Y recuerda que, por mucho que aprendas, la pasión por la informática no se enseña.

Pistas en Internet

Federación Nacional de Asociaciones de Ingenieros Informáticos: www.ai2.as

Ingenieros en Informática: www.ingenieroseninformatica.org

Algoritmo. Revista digital para programadores: www.algoritmodigital.com

Estudiantes de informática de España: www.ritsi.org

Internet Society: www.isoc.org

Consejo Superior de Informática: www.csi.map.es

Asociación de Técnicos en Informática: www.ati.es

Recursos para webmaster: http://directorio.adfound.com

_____________________________________________________________________

Revista -Personal Computer & Internet- Septiembre – 2007 Nº 56 – Año IV

Fundamentos de programación con Módula-2 (28)

Tema 12
Registros

 

En este Tema, tal como indica su título, se introduce la estructura de tupla y su realización mediante la construcción RECORD de Modula-2. Se ilustra su uso con ejemplos, mostrando la posibilidad de combinar estructuras tupla y formación.

Se introduce también, de forma breve, la estructura "unión" y su realización mediante registros con variantes en Modula-2.

Finalmente se analiza la correspondencia entre los esquemas abstractos de las estructuras de datos y los de las estructuras de control.

12.1 El esquema tupla

Ya se ha explicado anteriormente que los datos pueden ser simples o estructurados, y que un dato estructurado contiene varios elementos de información, que son sus componentes. Hasta el momento se han visto dos esquemas de datos estructurados: los conjuntos y las formaciones.

Otra forma de construir un dato estructurado a base de agrupar elementos de información es usando el esquema de tupla o agregado. En este esquema el dato estructurado está formado por una colección de componentes, cada una de las cuales puede ser de un tipo diferente.

Por ejemplo, una fecha se describe habitualmente como un dato compuesto de los elementos día, mes y año. Un punto en el plano cartesiano se describe mediante dos números, que son sus coordenadas. El nombre completo de una persona es la colección formada por el nombre de pila y sus dos apellidos. Como ejemplos concretos podemos poner:

       Dato                              Valor
       fecha                       < 12, Octubre, 1992 >
       punto                       < 4, -2 >
       nombre_completo     < Fernando, Jiménez, Rodríguez >

En estos ejemplos se ha supuesto un orden implícito entre las componentes. En la fecha se ha escrito primero el día, luego el mes, y luego el año. Es importante identificar claramente a qué componentes corresponde cada elemento de información. El punto <4, -2> es distinto del punto <-2, 4>.

En realidad el orden es hasta cierto punto arbitrario. Normalmente cada componente se identifica mediante un nombre simbólico. En los ejemplos anteriores podríamos nombrar las componentes en la forma:

       Dato                              Valor
       fecha                       < día: 12, mes: Octubre, año: 1992 >
       punto                       < x: 4, y: -2 >
       nombre_completo     < nombre: Fernando, apellido1: Jiménez, apellido2: Rodríguez >

Identificando cada componente por su nombre, se pueden escribir en el orden que convenga:

       Dato                              Valor
       fecha                       < mes: Octubre, día: 12, año: 1992 >
       punto                       < x: 4, y: -2 >
       nombre_completo     < apellido1: Jiménez, apellido2: Rodríguez, nombre: Fernando >

Tras las consideraciones anteriores, podríamos dar una definición de este esquema de datos:

TUPLA:   Colección de elementos componentes, de diferentes tipos, cada uno de los cuales se identifica por un nombre.

Un aspecto importante del empleo de datos estructurados corresponde al punto de vista de abstracción. Una tupla, como cualquier otro dato compuesto, puede verse en forma abstracta como un todo, prescindiendo del detalle de sus componentes. La posibilidad de hacer referencia a toda la colección de elementos mediante un nombre único correspondiente al dato compuesto, simplifica en muchos casos la escritura del programa que lo maneja.

12.2 Los tipos registros

Los esquemas de tupla pueden usarse en programas en Modula-2 definiéndolos como estructuras del tipo registro. Un registro (en inglés "record") es una estructura de datos formada por una colección de elementos de información llamados campos (en inglés "fields").

12.2.1 Definición de registros
La descripción de un tipo registro en Modula-2 se hace en la forma:

       RECORD
          nombre: tipo;
          nombre: tipo;
          . . . .
       END;

donde cada una de las parejas nombre: tipo, separadas por punto y coma (;), define un campo o elemento componente. Esta descripción puede usarse para definir el tipo registro como un tipo con nombre, o bien directamente como descripción del tipo de una variable. Como ejemplos de definiciones tenemos:

       TYPE TipoMes = (Enero, Febrero, … Diciembre);
       TYPE TipoFecha = RECORD
                                      dia: [1..31];
                                      mes: TipoMes;
                                      anno: INTEGER
                                   END;

       VAR   punto1, punto2: RECORD
                                           x: REAL;
                                           y: REAL
                                        END;

Cuando varios campos seguidos son del mismo tipo, la descripción puede abreviarse escribiendo seguidos los nombres de los campos, separados por comas (,) y poniendo el tipo común a todos ellos sólo una vez al final, detrás del carácter de dos puntos (:). Por ejemplo:

       VAR   punto1, punto2: RECORD
                                           x, y: REAL;
                                        END;

12.2.2 Uso de registros
Al manejar datos estructurados de tipo registro se dispone de dos posibilidades: operar con el dato completo, o bien operar con cada campo por separado.

Las posibilidades de operar con el dato completo son bastante limitadas. La única operación admisible en Modula-2 es la de asignación. El valor de un dato de tipo registro puede asignarse directamente a una variable de su mismo tipo. Por ejemplo, con las definiciones anteriores es posible escribir:

       punto2 := punto1

En estas asignaciones debe cumplirse la compatibilidad de tipos. Dos estructuras son compatibles en asignación si son del mismo tipo, o de tipos sinónimos, o son variables declaradas conjuntamente, como en este ejemplo. No es suficiente la llamada compatibilidad estructural; es decir, dos estructuras con los mismos campos no son compatibles si sus definiciones se hacen por separado.

También es posible pasar como argumento un dato de tipo registro a una función o procedimiento. En este caso es obligatorio que la estructura registro esté definida como un tipo con nombre, ya que en la definición de los argumentos de un subprograma sus tipos han de designarse mediante identificadores. Por ejemplo, podemos especificar los subprogramas siguientes:

       PROCEDURE LeerFecha( VAR fecha: TipoFecha );
       (* Leer el día, mes y año *)

       PROCEDURE Distancia( p1, p2: TipoPunto ): REAL;
       (* Distancia entre p1 y p2 *)

Para poder escribir la segunda especificación, habrá sido necesario definir previamente:

       TYPE TipoPunto = RECORD
                                      x, y: REAL;
                                   END;

No sería válido poner:

       PROCEDURE Distancia( p1, p2: RECORD … END ): REAL;

ya que no es admisible según las reglas de Modula-2. Además, no tendría sentido hacerlo, pues no se podría invocar el subprograma, ya que al no existir nombre para el tipo de los puntos, no podría definirse y pasarse como argumento ningún dato estructurado que fuera compatible con el tipo argumento.

Las operaciones de tratamiento de estructuras registro consisten normalmente en operar con sus campos por separado. La forma de hacer referencia a un campo es mediante la notación:

       registro.campo

Cada campo se puede usar como cualquier otro dato del correspondiente tipo; es decir, se pueden usar los valores de los campos en expresiones aritméticas, se puede asignar valor a cada uno de los campos de una variable de tipo registro, y se pueden pasar los campos como argumentos en llamadas a subprogramas. Como ejemplo daremos una posible definición de los subprogramas anteriores:

PROCEDURE LeerFecha( VAR fecha: TipoFecha );
(* Leer el día, mes y año *)
   VAR d: INTEGER;
BEGIN
   ReadInt( d );
   IF (d>0) AND (d<=31) THEN
      fecha.dia := d
   ELSE
      fecha.dia := 1
   END;
   LeerMes( fecha.mes );
   ReadInt( fecha.anno )
END LeerFecha;

PROCEDURE Distancia( p1, p2: TipoPunto ); REAL;
(* Distancia entre p1 y p2 *)
   VAR   dx, dy: REAL;
BEGIN
   dx := p2.x – p1.x;
   dy := p2.y – p1.y;
   RETURN sqrt( dx*dx + dy*dy )
END Distancia;

Las versiones modernas de Modula-2 permiten que una función devuelva como resultado un valor estructurado. Esto representa una gran ventaja a la hora de escribir programas claros, bien organizados, pero la posibilidad de hacerlo depende del compilador particular que se esté utilizando. Por ejemplo, es muy elegante poder escribir:

PROCEDURE PuntoMedio( p1, p2: TipoPunto ): TipoPunto;
   VAR   m: TipoPunto;
BEGIN
   m.x := (p1.x + p2.x) / 2.0;
   m.y := (p1.y + p2.y) / 2.0;
   RETURN m
END PuntoMedio;
. . . .
VAR   a, b, centro: TipoPunto;
. . . .
centro := PuntoMedio( a, b );

Si el compilador no admite esta posibilidad, siempre es posible transformar el planteamiento anterior en otro equivalente, en que la función se transforma en un procedimiento, y el resultado se devuelve como un argumento adicional, pasado por referencia. El ejemplo siguiente, aunque menos elegante, es equivalente al anterior y será admitido por cualquier compilador.

PROCEDURE PuntoMedio( p1, p2: TipoPunto; VAR m: TipoPunto );
BEGIN
   m.x := (p1.x + p2.x) / 2.0;
   m.y := (p1.y + p2.y) / 2.0
END PuntoMedio;
. . . .
VAR a, b, centro: TipoPunto;
. . . .
PuntoMedio( a, b, centro );

12.2.3 La sentencia WITH
Para simplificar la redacción de los programas que usan registros campo a campo, existe una sentencia que permite fijar de antemano la estructura registro con la que se quiere operar, de manera que la referencia a cada campo pueda hacerse simplemente con el nombre; es decir, en lugar de escribir

       registro.campo

bastará escribir:

       campo

ya que el registro concreto con que se ha de operar ha sido establecido de antemano. El formato de esta sentencia es:

       WITH registro DO
          Secuencia_de_sentencias
       END

En la secuencia de sentencias se puede hacer referencia a los campos del registro escribiendo sólo su nombre. Usando esta sentencia podemos reescribir, en forma más sencilla, alguno de los ejemplos anteriores:

PROCEDURE LeerFecha( VAR fecha: TipoFecha );
(* Leer el día, mes y año *)
   VAR   d: INTEGER;
BEGIN
   WITH fecha DO
      ReadInt( d );
      IF (d>0) AND (d<=31) THEN
         dia := d
      ELSE
         dia := 1
      END;
      LeerMes( mes );
      ReadInt( anno )
   END
END LeerFecha;

Las sentencias WITH pueden anidarse. Al hacerlo hay que tener en cuenta que si los registros tienen campos con los mismo nombres, se presenta una situación de ambigüedad en la parte más interna. En este caso se entiende que el nombre de un campo hace referencia al campo del registro de la sentencia WITH más interna que lo incluya y que tenga un campo con dicho nombre. Así se indica en el siguiente esquema:

VAR
   a: RECORD
          uno, dos: …
       END;
   b: RECORD
          dos, tres:
       END;

. . . .
WITH a DO
   … uno …                 = a.uno
   … dos …                 = a.dos

   WITH b DO
      … uno …              = a.uno
      … dos …              = b.dos
      … tres …              = b.tres
   END
END

De acuerdo con lo anterior, en caso de manejarse a la vez varios registros con los mismos campos sólo se podrá usar la sentencia WITH para simplificar las referencias a los campos de uno de ellos, lo cual podrá resultar quizá más confuso que si no se simplificase, por la asimetría que introduce. Por ejemplo:

PROCEDURE Distancia( p1, p2: TipoPunto ): REAL;
(* Distancia entre p1 y p2 *)
   VAR   dx, dy: REAL;
BEGIN
   WITH p1 DO
      dx := p2.x – x;
      dy := p2.y – y
   END;
   RETURN sqrt( dx*dx + dy*dy )
END Distancia;

Como puede observarse, se hace referencia de manera diferente a los campos p1 y p2, lo cual introduce cierta confusión.

12.2.4 Ejemplo: Cálculos con fracciones
Como ejemplo de empleo de registros, se reescriben aquí algunos de los subprogramas desarrollados en el Tema 8 para realizar cálculos con fracciones. La nueva versión aparece listada a continuación:

FROM InOut IMPORT
   WriteString, Write, WriteInt, WriteLn, Read, ReadInt;

TYPE TipoFraccion =
   RECORD
      numerador, denominador: INTEGER
   END;
. . . . . . . .
PROCEDURE ReducirFraccion( VAR fracion: TipoFraccion );
(* Simplificar la fracción *)
   VAR   divisor: INTEGER;
BEGIN
   WITH fracion DO
      divisor := 2;
      WHILE (divisor <= numerador) AND (divisor <= denominador) DO
         WHILE (numerador MOD divisor = 0) AND (denominador MOD divisor = 0) DO
            numerador := numerador DIV divisor;
            denominador := denominador DIV divisor
         END;
         INC( divisor )
      END
   END
END ReducirFraccion;

PROCEDURE SumaFracciones( f1, f2: TipoFraccion ): TipoFraccion;
(* SumaFracciones = f1 + f2 *)
   VAR   suma: TipoFraccion;
BEGIN
   suma.numerador := f1.numerador*f2.denominador + f2.numerador*f1.denominador;
   suma.denominador := f1.denominador*f2denominador;
   ReducirFraccion( suma );
   RETURN suma
END SumaFracciones;

PROCEDURE RestaFracciones( f1, f2: TipoFraccion ): TipoFraccion;
(* RestaFracciones = f1 – f2 *)
   VAR   menosf2: TipoFraccion;
BEGIN
   menosf2.numerador := -f2.numerador;
   menosf2.denominador := -f2.denominador;
   RETURN SumaFracciones( f1, menosf2 )
END RestaFracciones;

PROCEDURE LeerFraccion( VAR fraccion: TipoFraccion );
(* Lee la fraccion y la simplifica *)
BEGIN
   WITH fraccion DO
      ReadInt( numerador ); Write( ‘/’ );
      ReadInt( denominador ); Write( ‘ ‘ );
      ReducirFraccion( fraccion )
   END
END LeerFraccion;

PROCEDURE EscribirFraccion( fraccion: TipoFraccion );
(* Imprime la fracción como ‘numerador/denominador’ *)
BEGIN
   WITH fraccion DO
      WriteInt( numerador, 1 );
      Write( ‘/’ );
      WriteInt( denominador, 1 );
   END
END EscribirFraccion;

En este ejemplo cada fracción se almacena como un registro con dos campos, correspondientes al numerador y al denominador, respectivamente. Comparando esta versión con la del Tema 8 se aprecia que las cabeceras de los subprogramas, y por tanto las llamadas, son ahora más sencillas. El precio a pagar es una ligera complicación al tener que hacer referencia a los valores de numerador y denominador como campos de registros, y no directamente como argumentos separados.

12.3 Estructuras combinadas

Una característica de los lenguajes de programación modernos es que se pueden combinar con bastante libertad elementos de la misma naturaleza. Esto ocurre en Modula-2 con las sentencias estructuradas, que permiten anidar esquemas de tipo secuencia, selección e iteración unos dentro de otros.

Con las estructuras de datos ocurre algo similar. Se pueden definir estructuras cuyas componentes son a su vez estructuras, sin límite de complejidad de los esquemas de datos resultantes.

12.3.1 Formas de combinación
Las estructuras ARRAY y RECORD se pueden combinar entre sí de la forma que se desee. Los campos de un registro pueden ser formaciones, y las componentes de una formación pueden ser registros. Por supuesto, también se pueden definir registros cuyos campos sean a su vez registros o cualquier otro tipo de datos. Podemos analizar algunos ejemplos:

       TYPE TipoPunto = RECORD
                                      x, y: REAL;
                                   END;

       TYPE Triangulo = ARRAY [1 .. 3] OF TipoPunto;

       CONST   maxPuntos = 100;

       TYPE   ListaDePuntos =
          RECORD
             numeroPuntos: INTEGER;
             puntos: ARRAY [1 .. maxPuntos] OF TipoPunto;
          END;

       TYPE   Poligonal = ListaDePuntos;

       TYPE   Poligono = ListaDePuntos;

En estos ejemplos tenemos la representación de un triángulo como una formación de tres puntos, que son registros. La lista de puntos se estructura como un registro, uno de cuyos campos es una formación de puntos, que son a su vez registros. Las últimas declaraciones definen tipos sinónimos de la lista de puntos, en general, con nombres particulares para ser usados en casos particulares.

Los siguientes ejemplos definen registros que contienen campos que son ristras de caracteres. El último ejemplo es un registro que contiene a su vez otro registro anidado.

       TYPE   NombreCompleto =
          RECORD
             nombre: ARRAY [0 .. 20] OF CHAR;
             apellido1, apellido2: ARRAY [0 .. 30] OF CHAR
          END;

       TYPE   LineaDeTexto = ARRAY [0 .. 40] OF CHAR;

       TYPE   TipoProvincia =
          ( SinProvincia, Alava, Albacete, … Zaragoza );

       TYPE   DatosPersonales =
          RECORD
             nombreyApellidos: NombreCompleto;
             domicilio: RECORD
                               calle: LineaDeTexto;
                               numero: INTEGER;
                               zona, poblacion: LineaDeTexto;
                               codigoPostal: INTEGER;
                               provincia: TipoProvincia;
                            END;
          END;

Cuando se utilizan estructuras combinadas, para hacer referencia a una componente en particular hay que usar los selectores apropiados, combinados uno tras otro si es necesario. Como ejemplos de uso de las estructuras definidas en este apartado, podemos poner:

VAR
   pieza: Triangulo;
   camino: ListaDePuntos;
   empleado: DatosPersonales;
. . . .
pieza[1].x := 2.33;
pieza[1].y := -3.45;
pieza[2].x := pieza[1].y;
pieza[2].y := 88.3;
. . . .
camino.numeroPuntos := 3;
camino.puntos[1].x := 5.67;
camino.puntos[1].y := 7.21;
camino.puntos[2] := pieza[3];
camino.puntos[3] := camino.puntos[2];
. . . .
empleado.nombreyApellidos.nombre := ‘Alberto          ‘;
WriteString( empleado.domicilio.calle );
IF empleado.domicilio.poblacion[0] = ‘ ‘ THEN
   WriteString( ‘Sin domicilio’ )
END;
. . . .

En estos ejemplos aparecen combinados los selectores de campo de registro y de componentes de formación. La combinación es admisible cuando una componente de una estructura es a su vez una estructura de datos. A cada estructura se debe aplicar el selector que le corresponda.

12.3.2 Tablas
Aunque el estudio de las estructuras de datos excede del ámbito de este libro, resulta interesante mencionar algunos esquemas típicos que se obtienen combinando estructuras básicas. Este es el caso del esquema de tabla, que puede plantearse como una formación simple de registros. Por ejemplo, podemos definir una tabla destinada a contener la identificación de las provincias:

VAR   provincias: ARRAY TipoProvincia OF
                             RECORD
                                siglas: ARRAY [1 .. 2] OF CHAR;
                                nombre: ARRAY [0 .. 30] OF CHAR;
                                codigo, prefijo: INTEGER;
                             END;

En esta tabla se podrán almacenar los valores apropiados, asignándolos a los correspondientes campos, por ejemplo:

WITH   provincias[ Madrid ] DO
   siglas[1] := ‘M’;
   siglas[2] := ‘ ‘;
   nombre := ‘Madrid’;
   codigo := 28;
   prefijo := 91
END

Estos datos podrán ser usados luego en programas que manejen direcciones postales, matrículas de coches, números de teléfono, etc.

Pueden construirse estructuras de datos bastante complejas combinándolas de manera que en algunas de ellas hagamos referencia a datos almacenados en otras. Una forma de hacer referencia a los datos de una tabla es usando el índice correspondiente a la posición de cada registro. A continuación se muestra un ejemplo para manejo de figuras geométricas a partir de puntos:

TYPE   TipoPunto = RECORD
                                 x, y: REAL;
                              END;

CONST   maxPuntos = 1000;

TYPE IndicePunto = [1 .. maxPuntos];

VAR   puntos: ARRAY IndicePunto OF TipoPunto;

TYPE   TipoTriangulo = ARRAY [1 .. 3] OF IndicePunto;

TYPE TipoCirculo = RECORD
                                centro: IndicePunto;
                                radio: REAL
                             END;

En este ejemplo, la tabla puntos almacena las coordenadas de todos los puntos utilizados para definir cualquiera de las figuras geométricas que se manejan. Las figuras se definen almacenando referencias a los puntos de la tabla, en lugar de almacenar directamente las coordenadas de los puntos. Un triángulo T definido por tres puntos A, B y C se podría registrar, por ejemplo, almacenando los puntos, arbitrariamente, en las posiciones 10, 11 y 12 de la tabla de puntos:

VAR   trianguloT: TipoTriangulo;
. . . .
LeerPunto( puntos[10] );
LeerPunto( puntos[11] );
LeerPunto( puntos[12] );
trianguloT[1] := 10;
trianguloT[2] := 11;
trianguloT[3] := 12

Cuando se hacen recorridos o búsquedas en tablas y se quiere usar la sentencia WITH para simplificar el manejo de los registros, es importante tener en cuenta que el registro al que hace referencia la sentencia WITH se determina sólo una vez, al comienzo, y no cada vez que se hace referencia a un campo.

Por ejemplo, si queremos convertir todas las figuras geométricas en su imagen especular respecto al eje vertical, tendremos que invertir el signo de la abscisa de cada punto, es decir, tendremos que sustituir el valor de la coordenada x por -x. Para ello será válido escribir:

VAR k: INTEGER;
. . . .
k := 1;
WHILE k <= maxPuntos DO
   WITH puntos[k] DO
      x := -x
   END;
   INC( k )
END

En cambio, sería incorrecto escribir:

VAR k: INTEGER;
. . . .
k := 1;
WITH puntos[k] DO
   WHILE k <= maxPuntos DO
      x := -x;
      INC( k )
   END
END

Esta segunda versión lo que haría es cambiar de signo la coordenada x del primer punto repetidamente, ya que el bucle WHILE se ejecuta íntegramente dentro del ámbito de la sentencia WITH referida al registro puntos[k] evaluado con k=1.

12.4 El esquema unión

Hay aplicaciones en las que resultaría deseable que el tipo de un dato variase según las circunstancias. Si las posibilidades de variación son un conjunto finito de tipos, entonces se puede decir que el tipo del dato corresponde a un esquema que es la unión de los tipos particulares posibles. Cada uno de los tipos particulares constituye una variante del tipo unión. Representamos simbólicamente este esquema en la forma:

       tipo_unión = variante  |  variante  …

Como situaciones típicas en las que pueden aplicarse los esquemas unión tenemos, entre otras, las siguientes:

  • Datos que pueden representarse de diferentes maneras.
  • Programas que operan indistintamente con varias clases de datos.
  • Datos estructurados con elementos opcionales.

Algunos ejemplos concretos serían:

número_general = entero  |  fracción  |  real  |  complejo
coordenadas = coordenadas_cartesianas  |  coordenadas_polares
figura = punto  |  círculo  |  cuadrado  |  rectángulo  |  rombo  |  triángulo  |  elipse
datos_persona = datos_soltero  |  datos_menor  |  datos_casado

El primer caso correspondería a un programa que opere indistintamente con números de diferentes clases. Los números enteros son datos simples, al igual que los reales, aunque las colecciones de valores son diferentes. Los números fraccionarios se representan como dos números enteros (numerador y denominador), y los complejos como dos números reales (partes real e imaginaria).

En el segundo caso tenemos dos sistemas de coordenadas diferentes para representar los puntos del plano. Las coordenadas cartesianas son dos longitudes, mientras que las polares son una longitud y un ángulo.

En el tercer caso tenemos un programa que maneja un repertorio limitado de elementos gráficos. Para cada figura se necesitará conocer una colección de parámetros diferentes.

En el último caso, los datos de un soltero mayor de edad constituirían la información básica de una persona. Los menores deberían tener además un tutor o persona responsable de ellos, y los casados deberían tener una fecha de matrimonio y un cónyuge.

«Abriendo» un reproductor de DVD’s

Pues un amigo (Pedro-el Moro-) me trajo al kiosco un reproductor para que lo mirara e intentara averiguar lo que le sucedía, ya que la insertar un disco DVD el reproductor no funcionaba correctamente. En primer lugar, realice una búsqueda rápida y breve que me permitiera saber a priori si el aparato podía ser "abierto" y realizarle tras ésta tarea una limpieza de la lente. Veamos entonces algunas imágenes del "chisme" en cuestión…

10122007(001)Se trata concretamente de un aparato marca; TECNIMAGEN y con la posibilidad de leer discos CD o DVD, indistintamente, así que lo que hice es buscar todas los tornillos o similares para conseguir "ver" el interior del reproductor. En principio en el panel central se observan los "botones" típicos de apagado/encendido, de apertura del cargador de disco y los de reproducción del disco que se encuentra en el interior.

blog1

A continuación se muestras 2 imágenes en las que se observa la carcasa inferior y el panel trasero del TECNIMAGEN donde se encuentran los conectores para reproducir el contenido en un televisor, monitor, etc.

                              10122007(002) blog2 

Pasemos a continuación a visualizar ya el interior del reproductor TECNIMAGEN;

blog3 blog4 blog5 
blog6

En las imágenes anteriores se observan los distintos componentes internos.


Al terminar de limpiar la lente "láser", que permite leer el contenido del disco CD o DVD, puse todos los tornillos en su sito y el aparato volvió a su estado normal. Acto seguido, conecté la fuente de alimentación del aparato y le inserté un disco CD y el reproductor funcionaba correctamente. Al insertar un DVD, el reproductor mostraba en el LED una información de error. Por lo que la limpieza de la lente fue insatisfactoria y el problema no se solucionó. En mi opinión se debe, quizás, a un error de software, y la tarjeta que se encarga de decodificar la información de los discos insertados, DVD’s en este caso, tendría que ser debidamente revisada. En próximas entregas mas aparatos y "chismes" para analizarlos e intentar encontrar curiosidades.

Inaguración del consultorio local de BADOLATOSA

El miércoles 5 de diciembre del 2007 se ha inagurado el nuevo centro de salud en Badolatosa, situado en la zona del "arroyo de la
                                                     05122007(074)
Fontana" aporta a los vecinos y vecinas del pueblo unas instalaciones magníficas para los quehaceres diarios en relación a las visitas al médico de cabecera, análisis, recetas, etc.  El edificio cuenta con una sola planta, en donde se encuentran las distintas habitaciones para las visitas, así como un mostrador de información y unos servicios para uso público.

 

Algunas imágenes del acto se muestran a continuación, la afluencia de vecinos y vecinas que se acercó fue numerosa.

 

También se observan algunas de las distintas habitaciones o consultas con las que cuenta este nuevo centro de salud.

05122007(016) 05122007(006) 05122007(002) 05122007(001) 05122007(020) 05122007 05122007(019) 05122007(022) 05122007(078) 05122007(076) 05122007(030) 05122007(014) 05122007(062) 05122007(061) 05122007(050)

Ahora sólo falta que se haga un uso responsable de las nuevas instalaciones y que nos duren a todos los vecinos y vecinas del pueblo mucho tiempo y que sean útiles.