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

Tema 10

Ampliación de Estructuras de Control

Antes de pasar al estudio de las estructuras de datos complejas, se completa el repertorio de estructuras de control más frecuentes en los lenguajes imperativos, detallando las construcciones adoptados en Modula-2.

Se presentan algunas variantes para realizar la iteración y la selección, derivadas de las estructuras fundamentales de la Unidad Didáctica anterior, mostrando la posibilidad que existe de programarlas en función de ellas.

10.1 Estructuras complementarias de iteración

Como se explicó en el Tema 5, la programación estructurada propone la utilización del esquema WHILE como sentencia iterativa fundamental. Esta sentencia basta para poder realizar cualquier programa. Sin embargo, en el mismo Tema 5 también fue introducida la sentencia FOR cuya utilidad fundamental es programar un número de iteraciones que se conoce a priori y que no depende de la evolución de los cálculos realizados en cada iteración. La razón de que exista la sentencia FOR prácticamente en todos los lenguajes, es precisamente lo habitual de este tipo de iteraciones y la facilidad que proporciona el uso de la sentencia FOR en estos casos.

Las estructuras iterativas que se explican en este apartado están también disponibles habitualmente en todos los lenguajes y pretenden facilitar la programación de situaciones concretas pero muy frecuentes en cualquier programa. Utilizaremos para las explicaciones las sentencias disponibles en Modula-2: REPEAT y LOOP. Para posibilitar la terminación de la iteración utilizando una sentencia LOOP es necesario utilizar otra sentencia: EXIT. Esta última sentencia será explicada junto a la sentencia LOOP dado que está ligada por completo a ella y no se puede utilizar nunca de forma aislada.

10.1.1 Sentencia REPEAT
A veces resulta más adecuado comprobar la condición que controla las iteraciones al finalizar cada una de ellas, en lugar de hacerlo al comienzo de las mismas. En este caso, como se muestra en la Figura 10.1, siempre se ejecuta al menos una primera iteración.

Figura(10_1)
     Figura 10.1. Esquema de repetición

El formato de esta sentencia en Modula-2 es el siguiente:

REPEAT Secuencia_de_sentencias UNTIL Condición

la Condición que controla las repeticiones es una expresión cuyo resultado es un valor de tipo BOOLEAN. Si el resultado es FALSE se vuelve a ejecutar la Secuencia_de_sentencias y cuando el resultado es TRUE finaliza la ejecución de la sentencia.

Una situación típica en que resulta cómodo el empleo de esta sentencia es la que se produce cuando al finalizar cada iteración se pregunta al operador si desea continuar con una nueva. En todos estos casos, el programa siempre ejecuta la primera iteración y pregunta si se desea o no realizar otra más. Por ejemplo:

REPEAT
. . . .
… << operación >> …
. . . .
   WriteString( "¿Otra operación (S/N)?" );
   Read( tecla );
UNTIL tecla = "N"

En todos los programas realizados en los Temas anteriores se ha podido programar esta forma de operar utilizando la sentencia WHILE. Sin embargo, en los esquemas desarrollados ha sido necesario forzar la primera iteración inicializando la variable que controla la iteración a un valor distinto del necesario para la finalización. Para el mismo ejemplo anterior, esto se realizaría de la siguiente forma:

tecla := "S";
WHILE tecla <> "N" DO
   . . . .
   … << operación >> …
   . . . .
   WriteString( "¿Otra operación (S/N)?" );
   Read(tecla)
END;

Esta solución es menos elegante y además tiene como incoveniente la necesidad de inicializar la variable de control, con lo que en caso de olvido, la ejecución puede ser impredecible. Como se sabe, las variables pueden tomar aleatoriamente cualquier valor inicial y dependiendo del mismo, se ejecutará o no la primera iteración. Para estas situaciones es muy aconsejable la utilización de la sentencia REPEAT que evita todos estos problemas.

También resulta adecuado el empleo del esquema REPEAT cuando solamente son válidos unos valores concretos para una determinada respuesta. Si la respuesta no es correcta se solicitará de nuevo y no se continuará hasta obtener una respuesta dentro de los valores válidos. La filosofía en este caso es prácticamente la misma del caso anterior, por ejemplo:

REPEAT
   WriteString("¿Mes actual?");
   ReadInt(mes)
UNTIL (mes >0) AND (mes <= 12);

Evidentemente la utilidad de la sentencia REPEAT no está ligada exclusivamente a los casos indicados. En general, es aconsejable su uso cuando se sepa que al menos es necesaria una iteración y por tanto utilizando la sentencia WHILE es necesario forzar las condiciones para que dicha iteración se produzca.

10.1.2 Sentencias LOOP y EXIT
Todas las estructuras iterativas explicadas hasta ahora se construyen mediante una única sentencia del lenguaje. Esto contribuye a que los programas resultantes sean fáciles de entender. La estructura LOOP que se explica en este apartado, sin embargo, emplea dos sentencias independientes para programar un esquema iterativo, que puede ser más complejo y, si no se programa con cierta disciplina, también más confuso. Por tanto, convendrá reservar el empleo de esta construcción para los casos que resulte artificioso emplear alguna de las otras sentencias iterativas explicadas anteriormente.

Figura(10_2)
   Figura 10.2. Bucle indefinido con salida intermedia

Con las sentencias WHILE y REPEAT se puede evaluar la condición para acabar el bucle de iteraciones bien al comienzo o ben al final de la ejecución de la secuencia de sentencias. Con frecuencia los bucles se pueden expresar de una de estas dos formas de manera sencilla. Sin embargo, hay ocasiones en que la condición para terminar las iteraciones se ha de examinar inevitablemente en un punto intermedio distinto al comienzo o final de la iteración. En este caso se tiene la situación que se muestra en la Figura 10.2.

Este tipo de condiciones están ligadas a causas que impiden una continuación normal de la iteración en curso y aconsejan abandonar dicha iteración. Por ejemplo cuando se obtiene un resultado parcial sin sentido: peso, volumen, etc. de valor negativo, que invalida los posibles cálculos posteriores. Además, dentro de este tipo de bucle de iteración suelen existir varias condiciones distintas en distintos puntos por las que se debe abandonar la ejecución del bucle.

Para facilitar la programación de esta estructura se dispone de una sentencia para programar un bucle indefinido sin expresar la condición de terminación, y otra sentencia distinta para poder salir de dicho bucle en el momento deseado. La sentencia de Modula-2 para el bucle indefinido es la siguiente:

LOOP Secuencia_de_sentencias END

que indica que se ejecute siempre de forma repetitiva e incondicional las sentencias agrupadas entre las palabras clave LOOP y END. Para poder finalizar el bucle indefinido es necesario que dentro del mismo exista alguna sentencia de salida. Esta sentencia en Modula-2 contiene simplemente la palabra clave:

EXIT

y su ejecución provoca la salida inmediata desde el interior del bucle indefinido del que ella misma forma parte. La ejecución del programa continúa con la sentencia inmediatamente a continuación del END de dicho bucle.

La sentencia EXIT es incondicional, es decir, no contiene en sí misma el examen de ninguna condición. En un esquema de iteración normal esta sentencia deberá estar anidada dentro de un esquema condicional. En caso contrario, sólo se ejecutaría la parte de la primera iteración anterior a la sentencia EXIT, y al llegar a ella se terminaría inmediatamente el bucle. Un esquema aproximado de la combinación LOOP – EXIT se muestra en la Figura 10.3.

Figura(10_3)
     Figura 10.3. Sentencias LOOP y EXIT

Una sentencia EXIT sólo se puede usar dentro de otra tipo LOOP y se produce un error de compilación cuando se trata de usar fuera de un LOOP.

Un ejemplo típico de esta estructura de iteración es:

LOOP
   WriteString( "¿Mes Actual?");
   RedInt(mes);
   IF (mes > 0) AND (mes <= 12) THEN EXIT END;
   WriteString( "Dato fuera de rango; repita" );
END

En este ejemplo se ha reescrito el bucle para leer como dato el número de un mes, hasta que se introduzca un dato correcto, y añadiendo la escritura de un mensaje de advertencia en el caso de que el dato introducido no sea aceptable.

Dado que existe la posibilidad de anidar sentencias, la sentencia EXIT puede estar anidada dentro de otra u otras sentencias iterativas WHILE, REPEAT o FOR anidadas a su vez dentro del bucle indefinido. Por ejemplo:

LOOP
   ……
   FOR i := 1 TO 10 DO
      ……
      WHILE x > 23 DO
         ……
         IF z = 15 THEN EXIT END;
         ……
      END; (* Final del WHILE *)
      …..
   END; (* Final del FOR *)
   …..
END; (* Final del LOOP *)

Independientemente de la forma y el número de anidamientos, la ejecución de la sentencia EXIT provoca la salida y finalización inmediata del bucle indefinido que lo contenga, sin tener en cuenta para nada las otras sentencias. En el ejemplo anterior, cuando el valor de la variable z es igual a 15 al ejecutar la sentencia IF, se acaban todas las iteraciones incluidas dentro del bucle indefinido, aunque la variable x sea mayor de 23 y el índice de i no haya alcanzado todavía el valor 10.

Cuando se anidan dos o mas sentencias LOOP, cada sentencia EXIT se refiere siempre al bucle más interno de todos aquellos en los que pueda estar anidada. Por ejemplo:

LOOP                           (* Nivel 1º *)
   …..
   … EXIT …                   (* Salida del nivel 1º *)
   LOOP                        (* Nivel 2º *)
      …..
      LOOP                      (* Nivel 3º *)
         … EXIT …              (* Salida del nivel 3º *)
         ……
      END;                       (* Nivel 3º *)
      ……
   … EXIT …                    (* Salida del nivel 2º *)
   END                            (* Nivel 2º *)
   ……
END                               (* Nivel 1º *)

La posibilidad de emplear un número indeterminado de sentencias de salida y el efecto tan drástico que produce cada una de ellas, hace que resulte mucho más difícil de entender un programa en que se abuse de este tipo de estructura iterativa. En cualquier caso conviene evitar el uso de varios bucles indefinidos anidados.

Sólo es aconsejable utilizar esta estructura cuando resulte realmente apropiada (caso de manejo de errores o similar) o siempre que la utilización de las otras estructuras den lugar a un programa artificioso que pueda ser simplificado mediante el empleo de un bucle indefinido y sus correspondientes salidas.

En el apartado de programas completos se muestra un ejemplo para el manejo de errores sintácticos donde resulte más sencillo emplear un bucle indefinido que utilizar otro tipo de sentencia de iteración. El manejo de errores es un caso típico de tratamiento de excepciones según se vio en el apartado 8.3.2 del Tema 8. Las sentencias LOOP y EXIT constituyen un esquema muy adecuado para la programación del tratamiento de excepciones, cuando no interesa utilizar un procedimiento o función con múltiples sentencias de retorno.

10.2 Estructuras complementarias de selección

Para la selección entre varias alternativas es suficiente disponr de la sentencia IF, estudiada en el Tema 5. De hecho, existen lenguajes en los que la única sentencia disponible para la selección es la propuesta por la programación estructurada, que permite solamente la selección entre dos alternativas. La falta de claridad cuando se utilizan varias selecciones anidadas aconseja disponer de una sentencia de selección general como la estudiada en el Tema 5 para el lenguaje Modula-2.

Por las mismas razones de claridad y sencillez es habitual disponer de una sentencia que permite una selección por casos. Esta sentencia se conoce en la mayoría de los lenguajes con la palabra clave que la introduce: CASE. Este apartado está dedicado exclusivamente a dicha sentencia, estudiando la sintaxis y semántica que tiene en Modula-2.

10.2.1 Sentencia CASE
Cuando la selección entre varios casos alternativos depende del valor que toma una determinada variable o del resultado final de una expresión, es necesario realizar comparaciones de esa misma variable o expresión con todos los valores que pueda tomar, uno por uno, para decidir el camino a elegir. Así, en el programa para el cálculo del día de la semana del tema anterior con la variable M que guarda el mes teníamos:

IF         M = Enero        THEN       IncreDias := 0
ELSIF   M = Febrero      THEN      IncreDias := 3
ELSIF   M = Marzo        THEN      IncreDias := 3
ELSIF   M = Abril           THEN      IncreDias := 6
……
ELSIF   M = Octubre      THEN      IncreDias := 0
ELSIF   M = Noviembre   THEN     IncreDias := 3
ELSE    IncreDias := 5
END;

lo que supone una sentencia larga y reiterativa, si se tiene en cuenta que además para algunos casos, por ejemplo los meses de Febrero, Marzo y Noviembre, se tiene que ejecutar la misma acción.

Figura(10_4)
      Figura 10.4. Selección por Casos

Si lo que se necesita es comparar el resultado de una expresión, es la misma expresión la que se debe evaluar tantas veces como comparaciones se deben realizar. En este caso y por razones de simplicidad y eficiencia es aconsejable guardar el resultado de la expresión en una variable auxiliar y realizar las comparaciones con la variable.

Si el tipo de valor que determina la selección es un tipo ordinal: INTEGER, CARDINAL, CHAR, enumerao o subrango, se dispone en Modula-2 de la sentencia CASE cuya estructura se muestra en la Figura 10.4, y en la que se agrupan los casos que tienen el mismo tratamiento y se evalúa solamente una vez la expresión x.

Las distintas vías de ejecución están asociadas a grupos de valores que pueda tomar la expresión o variable x: A con el valor v1, B con los valores de v2 a v4, C con los valores v5, v6 y v7 y establecieno como vía alternativa la acción H para el resto de los valores distintos de los anteriores.

El esquema de esta sentencia es:

CASE valor OF
   valores : acción |
   valores : acción |
   . . . .
ELSE
   acción por defecto
END

La sentencia comienza con la palabra clave CASE y a continuación se indica la expresión o variable cuyo valor fija los casos que se quieren analizar, seguida de la palabra clave OF. Para cada vía de ejecución posible se detallan primeramente los valores que debe tomar la variable, separados por comas (,). Estos valores también se pueden expresar en forma de subrango separados por dos puntos seguidos (..). La secuencia de sentencias que se deben ejecutar se detallan a continuación de los valores y separadas de estos por dos puntos (:). Las distintas vías alternativas de ejecución y sus correspondientes valores se separan unos de otros por el símbolo de barra (|). La alternativa para el resto de los valores es opcional, y va precedida de la palabra clave ELSE. La sentencia finaliza con la palabra clave END.

Esta sentencia no se puede utilizar cuando la variable o el resultado de la expresión que controla la selección sea del tipo REAL u otro tipo no simple como los conjuntos estudiados en el tema anterior. En estos casos no queda más remedio que emplear la sentencia de selección general.

Como ejemplo se reescribe la selección anterior, según el mes:

CASE M OF
   Enero, Octubre :                      IncreDias := 0 |
   Mayo :                                    IncreDias := 1 |
   Agosto :                                  IncreDias := 2 |
   Febrero .. Marzo, Noviembre :   IncreDias := 3 |
   Junio :                                    IncreDias := 4 |
   Septiembre, Diciembre :           IncreDias := 5 |
ELSE
                                                 IncreDias := 6
END;

Evidentemente esta sentencia da lugar a un fragmento de programa más corto y fácil de entender.

En la sentencia CASE se deben incluir todos los posibles valores que pueda tomar la variable o expresión. Cuando se obtiene un valor que no está asociado a ninguna vía (y no hay alternativa ELSE), el programa finaliza por error. Si lo que sucede es que existen valores para los que no se debe realizar ninguna acción, entonces estos valores se deben declarar asociados a una secuencia de sentencias vacía. Por ejemplo:

CASE M OF
   Enero..Mayo:   |
   Julio..Noviembre:   |
   Junio, Diciembre: Sueldo := Sueldo + Extra
END;

otra forma de conseguir esto mismo es mediante una alternativa ELSE vacía. Por ejemplo:

CASE M OF
   Junio, Diciembre: Sueldo := Sueldo + Extra
ELSE
END;

La diferencia entre ambas es que en el primer caso se produciría un error que finalizaría la ejecución del programa si por cualquier causa la variable M toma un valor fuera del rango de mes declarado de Enero..Diciembre. En el segundo caso no se distingue la situación errónea de la que no lo es.

Si el programa está completamente probado, es muy probable que tal situación no se produzca nunca. Sin embargo, cuando un programa está todavía en fase de prueba es importante conocer todos los errores para analizar sus causas y corregirlos.

Por otro lado hay que tener en cuenta que un mismo valor nunca puede estar asociado a distintas alternativas dado que la ejecución resultaría ambigua. Este error puede producirse por una confusión, sobre todo cuando el mismo valor en un caso se indica dentro de un subrango y en otro de forma explícita. Por ejemplo:

CASE M OF
   Enero, Octubre:                               IncreDias := 0 |
   Mayo:                                             IncreDias := 1 |
   Agosto:                                           IncreDias := 2 |
   Febrero..Marzo, Noviembre               IncreDias := 3 |
   Junio:                                              IncreDias := 4 |
   Septiembre..Diciembre:                    IncreDias := 5 |
ELSE
                                                         IncreDias := 6
END;

Esta sentencia resulta ambigua para los meses de Octubre y Noviembre y es, por tanto, errónea.

Para acabar este apartado se detalla la sintasix formal de la sentencia CASE de Modula-2:

Sentencia_CASE ::=   CASE Expresión OF
                                  Caso { | Caso }
                                  [ ELSE Secuencia_de_sentencias ]
                                  END
Caso ::= Lista_de_valores : Secuencia_de_sentencias
Lista_de_valores ::= Valores { , Valores }
Valores ::= Expresión_constante [ .. Expresión_constante ]

El tipo del resultado de la Expresión y los Valores de cada caso deben ser compatibles.

Anuncio publicitario

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

9.6.3 Operaciones entre conjuntos
Las operaciones entre conjuntos que se pueden realizar en Modula-2 son: unión, intersección, diferencia y diferencia simétrica. Todas estas operaciones se tienen que realizar entre dos conjuntos del mismo tipo, siendo el resultado otro conjunto también del mismo tipo. Los símbolos de operación y sus significados respectivos son los siguientes:

       Operación                                                 Elementos del conjunto resultante
   X + Y Unión                                                    elementos de X, o de Y, o de ambos
   X * Y Intersección                                            elementos comunes a X e Y
   X – Y Diferencia                                               elementos de X que no están en Y
   X / Y Diferencia simétrica                                 elementos de X o de Y, pero no de ambos

Por ejemplo, dados los conjuntos:

X := Existencias{ Pera, Kiwi, Naranja };
Y := Existencias{ Limon, Pera };

los resultados de las distintas operaciones entre ellos son los siguientes:

       Operación                                                 Resultado en el conjunto Z
   Z := X + Y                                                       Existencias{ Pera, Kiwi, Naranja, Limon }
   Z := X * Y                                                        Existencias{ Pera }
   Z := X – Y                                                        Existencias{ Kiwi, Naranja }
   Z := X / Y                                                        Existencias{ Kiwi, Naranja, Limon }

Además de estos operadores, en Modula-2 existen dos procedimientos predefinidos para manejar conjuntos, que fueron ya mencionados en el Tema 7. Estos procedimientos son los siguientes:

EXCL( S, X )             Excluye el elemento X del conjunto S
INCL( S, X )              Incluye el elemento X en el conjunto S

El primer argumento S debe ser una variable de un tipo conjunto. El valor del elemento X puede darse, en general, en forma de expresión, que habrá de ser del tipo referencial. Por ejemplo:

INCL( marcadoHoy, Pera );
…..
EXCL( boleto, 4*5);
…..
INCL( listaLetras, Caracter );

El mismo resultado se puede conseguir con las operaciones de unión y diferencia, utilizando un conjunto formado por el único elemento a incluir o excluir. Esto es:

mercadoHoy := mercadoHoy + Existencias{ Pera };
…..
boleto := boleto – Tabla{ 4*5 };
…..
listaLetras := listaLetras + Letras{ Caracter };

Además de las anteriores operaciones entre conjuntos, que producen como resultado otro conjunto, también se pueden realizar entre conjuntos operaciones de realción que dan como resultado un valor BOOLEAN. Los operadores disponibles son los siguientes:

       Operación                                              Condición examinada
   X = Y   Equivalencia                                  X e Y tienen los mismo elementos
   X <> Y   Desigualdad                                X e Y tienen algún elemento distinto
   X <= Y   Inclusión                                     todos los elementos de X pertenecen a Y
   X >= Y   Inclusión                                     todos los elementos de Y pertenecen a X

Por ejemplo, a partir de los conjuntos que se indican a continuación:

X := Existencias{ Pera, Kiwi, Naranja };
Y := Existencias{ Limon, Pera };
U := Existencias{ Pera .. Limon };
V := Existencias{ Pera, Kiwi };
W := Existencias{ };

los resultados de distintas operaciones entre ellos son los siguientes:

       Operación                                   Resultado
   Y = U                                              FALSE
   V <= X                                             TRUE
   U <> V                                            TRUE
   X >= W                                            TRUE
   X = Y                                               FALSE
   U >= X                                             FALSE
   U <> Y                                            TRUE
   Y <= W                                           FALSE

Nótese que el conjunto vacío siempre está incluido en cualquier otro.

Otro operador que da lugar a un resultado de tipo BOOLEAN es el operador que permite comprobar si un elemento pertenece o no a un conjunto. Este operador necesita un operando de tipo conjunto y otro del tipo referencial de dicho conjunto, que será el elemento a comprobar. Este operador se indica con la palabra clave IN

Elemento IN Conjunto

El valor del elemento puede darse en forma de expresión. El resultado será TRUE cuando el elemento esté en el conjunto y FALSE en caso contrario. Por ejemplo, para los conjuntos anteriores se tienen los siguientes resultados:

       Operación                                   Resultado
   Pera IN X                                         TRUE
   Limon IN X                                        FALSE

9.7 Ejemplos de programas

Para finalizar este Tema se recogen en este apartado varios ejemplos que utilizan los nuevos tipos introducidos. El primer ejemplo hace especial hincapié en los tipos enumerados y los siguientes están dedicados fundamentalmente al manejo de conjuntos.

9.7.1 Ejemplo: Cálculo del día de la semana de una fecha
Con este programa se calcula qué día de la semana corresponde a una fecha cualquiera que se introduce como dato. Para el cálculo se supone conocido que el día 31 de Diciembre de 1988 fue Sábado. El programa sirve para fechas desde el año 1989 hasta el año 2088 (teniendo en cuenta el EFECTO 2000).

Las declaraciones de los días de la semana, los meses y los subrangos de días y años son las empleadas en los apartados anteriores. Asimismo, se utiliza la función SumarDias descrita en el apartado 9.2.2 para calcular el día de la semana que será dentro de N días conociendo el día de hoy.

La función DiadelaSemana realiza el cálculo del día de la semana teniendo en cuenta los siguientes aspectos:

  • El desfase en días de la semana que se introduce para cada mes, respecto al mismo día del mes de Enero. Por ejemplo, el mes de Febrero son 3, el mes de Marzo 3, .., el mes de Julio 6, etc.
  • Los años bisiestos son los múltiplos de 4.
  • Si el año es inferior a 89 se considera que es posterior al 2000
  • Cada año bisiesto pasado incrementa en 1 día el desfase.

El procedimiento EscribirDia escribe el tipo de día resultante. El listado del programa completo es el siguiente:

(****************************************************************************************
*
*   Programa: Calendario
*
*   Descripción: Programa para el cálculo del día de la semana que
*                      es cualquier fecha desde el 1/I/1989 hasta 31/XII/2088
*
****************************************************************************************)
MODULE Calendario;
   FROM InOut IMPORT
      WriteString, WriteLn, ReadInt, Read, Write;
   CONST
      DiasSemana = 7;
   TYPE
      TipoDia = (Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo);
      TipoMes = (Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre);
      RangoDias = [0..31];
      RangoAnnos = [0..99];
   VAR
      dia, mes, anno : INTEGER;
      tecla : CHAR;

PROCEDURE DiadelaSemana(D: RangoDias; M: TipoMes; A: RangoAnnos) : TipoDia;
(*==================================================================
Función para calcular el dia de la semana que corresponde a una fecha (D/M/A) cualquiera
===================================================================*)
   CONST
      OrigenA = 89;
      TreintaUnoDiciembre88 = Sabado;
   VAR
      bisiesto : BOOLEAN;
      sumaBisis, sumaAnnos, sumaDias : RangoDias;
   PROCEDURE SumarDias(Hoy : TipoDia; N: RangoDias) : TipoDia;
   (*================================================
   Función para sumar dias de la semana ciclicamente
   =================================================*)
      VAR
         aux : INTEGER;
   BEGIN
      aux := (ORD(Hoy) + N) MOD DiasSemana;
      RETURN VAL(TipoDia, aux);
   END SumarDias;

BEGIN
   IF M = Enero                 THEN sumaDias := 0
   ELSIF M = Febrero        THEN sumaDias := 3
   ELSIF M = Marzo          THEN sumaDias := 3
   ELSIF M = Abril             THEN sumaDias := 6
   ELSIF M = Mayo           THEN sumaDias := 1
   ELSIF M = Junio            THEN sumaDias := 4
   ELSIF M = Julio             THEN sumaDias := 6
   ELSIF M = Agosto         THEN sumaDias := 2
   ELSIF M = Septiembre   THEN sumaDias := 5
   ELSIF M = Octubre        THEN sumaDias := 0
   ELSIF M = Noviembre     THEN sumaDias := 3
   ELSE sumaDias := 5
   END;
   bisiesto := (A MOD 4) = 0;
   IF A < OrigenA THEN
      A := A + 100      (* Año posterior al 2000 *)
   END;
   sumaAnnos := A – OrigenA;  (* Años pasados desde el 89 *)
   sumaBisis := sumaAnnos DIV 4;  (* Bisiestos pasados *)
   sumaDias := sumaDias + D + sumaAnnos + sumaBisis;
   IF bisiesto AND (M > Febrero) THEN
      sumaDias := sumaDias + 1
   END;
   RETURN SumarDias(TreintaUnoDiciembre88, sumaDias)
END DiadelaSemana;

PROCEDURE EscribirDia(S : TipoDia);
(*==================================
Procedimiento para escribir el día de la semana
===================================*)
BEGIN
   IF S = Lunes                 THEN WriteString("Lunes")
   ELSIF S = Martes          THEN WriteString("Martes")
   ELSIF S = Miercoles      THEN WriteString("Miercoles")
   ELSIF S = Jueves           THEN WriteString("Jueves")
   ELSIF S = Viernes         THEN WriteString("Viernes")
   ELSIF S = Sabado          THEN WriteString("Sabado")
   ELSE WriteString("Domingo")
   END;
   WriteLn;
END EscribirDia;

BEGIN
   tecla := "S";
   WHILE tecla <> "N" DO
      WriteString("¿Dia Mes Año?");
      ReadInt(dia); Write(" ");
      ReadInt(mes); Write(" ");
      ReadInt(anno); WriteLn;
      EscribirDia(DiadelaSemana(dia, VAL(TipoMes,mes-1), anno));
      WriteString("¿Otra Fecha (S/N)?");
      Read(tecla); Write(tecla); WriteLn
   END
END Calendario.

Un resultado de la ejecución del programa es el siguiente:

¿Día Mes Año?1 4 93
Jueves
¿Otra Fecha (S/N)?S
¿Día Mes Año?1 10 94
Sabado
¿Otra Fecha (S/N)?N

9.7.2 Ejemplo: Criba de Eratóstenes
En el tema anterior se realizó un programa para imprimir de manera sucesiva los números primos según se calculaban dado que no se podian almacenar en ninguna variable estructurada. Ahora ya disponemos del tipo conjunto y en este ejemplo se trata de obtener la tabla completa de los números primos entre 1 y 500, mediante el método de criba de Eratóstenes, antes de imprimirlos por pantalla. Para ello se parte de un tipo de conjunto Lista definido sobre un referencial subrango de 1 a 500. El procedimiento Eratostenes inicializa el conjunto que se le pasa como argumento con todos los elementos. A continuación se realiza la criba excluyendo aquellos números que son múltiplos de un número primo. La elección del siguiente número primo se efectúa comprobando que no ha sido excluído todavía de la tabla.

El procedimiento EscribirLista escribe los elementos que pertenecen al conjunto de 10 en 10. El listado completo del programa es el siguiente:

(**************************************************************************************************
*
*   Programa: Criba
*
*   Descripción:
*                    Programa para obtener la criba de Eratostenes
*
**************************************************************************************************)
MODULE Criba;
   FROM InOut IMPORT
      WriteString, WriteLn, WriteInt;
   CONST
      Mayor = 500;        (* Valor mayor de la criba *)
   TYPE
      Margen = [1..Mayor];
      Lista = SET OF Margen;
   VAR
      numerosPrimos : Lista;

PROCEDURE Eratostenes(VAR Numeros : Lista);
(*=========================================
Procedimiento para realizar la criba
==========================================*)
   VAR primo, multiplo : CARDINAL;
BEGIN
   Numeros := Lista{1..Mayor};     (* Todos inicialmente *)
   FOR primo := 2 TO Mayor DIV 2 DO
      IF primo IN Numeros THEN
         multiplo := primo + primo;
         WHILE multiplo <= Mayor DO
            EXCL(Numeros,multiplo);
            multiplo := multiplo + primo
         END
      END
   END
END Eratostenes;

PROCEDURE EscribirLista(T : Lista);
(*==========================================
Procedimiento para escribir la lista de 10 en 10 elementos
===========================================*)
   VAR i, j: CARDINAL;
BEGIN
   WriteLn; WriteLn; j := 0;
   FOR i := 1 TO Mayor DO
      IF i IN T THEN
         WriteInt(i,4); j := j + 1;
         IF (j MOD 10) = 0 THEN
            WriteLn    (* Cambiar de línea con 10 elementos *)
         END
      END
   END; WriteLn
END EscribirLista;

BEGIN
   Eratostenes(numerosPrimos);
   WriteString("                 Tabla de Números Primos:");
   EscribirLista(numerosPrimos);
END Criba.

y el resultado de la ejecución es el siguiente:

                Tabla de N·meros Primos:

    1   2     3     5     7  11  13   17   19   23
  29  31   37   41   43  47  53   59   61   67
  71  73   79   83   89  97 101 103 107 109
113 127 131 137 139 149 151 157 163 167
173 179 181 191 193 197 199 211 223 227
229 233 239 241 251 257 263 269 271 277
281 283 293 307 311 313 317 331 337 347
349 353 359 367 373 379 383 389 397 401
409 419 421 431 433 439 443 449 457 461
463 467 479 487 491 499

9.7.3 Ejemplo: Contar letras y dígitos
En este ejemplo se trata de analizar un texto y contar el número de letras, dígitos y espacios en blanco de los que consta. Asimismo, se toma nota de cuáles son las letras y dígitos utilizados. Como resultado se escriben las letras no utilizadas y los dígitos utilizados.

Para almacenar las letras y dígitos utilizados se emplean conjuntos sobre los referenciales desde la A a la Z y desde el 0 al 9. Hay que tener en cuenta que la letra ñ no está incluida en dicho conjunto y por tanto se considera un signo de puntuación. El procedimiento Analizar va incluyendo en cada conjunto los elementos según se encuentran en el texto. El procedimiento DarResultados escribe los resultados obtenidos. El listado completo del programa es el siguiente:

(**************************************************************************************************
*
*   Programa: Contar
*
*   Descripción:
*      Programa para contar las letras y dígitos de un texto
*
**************************************************************************************************)
MODULE Contar;
   FROM InOut IMPORT
      WriteString, WriteLn, WriteInt, Write, Read;
   TYPE
      Caracteres = SET OF CHAR;
      Letras = SET OF ["A".."Z"];
      Digitos = SET OF ["0".."9"];
   VAR
      listaLetras : Letras;
      listaDigitos : Digitos;
      total, totalLetras, totalDigitos, totalBlancos : INTEGER;

PROCEDURE Analizar;
(*===============================
Procedimiento para analizar el texto y contar las letras, dígitos,
blancos y el total de caracteres de un texto
================================*)
   VAR c : CHAR;
BEGIN
   (*– Inicializar –*)
      total := 0; totalLetras := 0; c := " ";
      totalDigitos := 0; totalBlancos := 0;
      listaLetras := Letras{}; listaDigitos := Digitos{};
   (*– Bucle hasta localizar el punto final –*)
      WHILE c <> "." DO
         Read(c); Write(c); INC(total);
         IF c IN Caracteres{"a".."z", "A".."Z"} THEN
         (*– Contar y anotar la letra analizada –*)
            INC(totalLetras); c := CAP(c);
            INCL(listaLetras, c)
         ELSIF c IN Digitos{"0".."9"} THEN
         (*– Contar y anotar el dígito analizado –*)
            INC(totalDigitos); INCL(listaDigitos, c)
         ELSIF c = " " THEN
         (*– Contar el blanco analizado –*)
            INC(totalBlancos)
         ELSE
         END
      END
END Analizar;

PROCEDURE DarResultados;
(*=============================
Procedimiento para escribir los resultados
==============================*)
   VAR c : CHAR;
BEGIN
   WriteLn; WriteLn;
   WriteString("               RESULTADOS"); WriteLn;
   WriteString("               ==========="); WriteLn; WriteLn;
   WriteString("Caracteres Totales:"); WriteInt(total, 4); WriteLn;
   WriteString("Letras Totales:       "); WriteInt(totalLetras, 4); WriteLn;
   WriteString("Dígitos Totales:      "); WriteInt(totalDigitos, 4); WriteLn;
   WriteString("Blancos Totales:    "); WriteInt(totalBlancos, 4); WriteLn; WriteLn;
   WriteString("LISTA DE LETRAS NO UTILIZADAS"); WriteLn;
   FOR c := "A" TO "Z" DO
      IF NOT (c IN listaLetras) THEN Write(c); Write(" ") END
   END;
   WriteLn; WriteLn;
   WriteString("LISTA DE DIGITOS UTILIZADOS"); WriteLn;
   FOR c := "0" TO "9" DO
      IF c IN listaDigitos THEN Write(c); Write(" "); END
   END;
END DarResultados;

BEGIN
   Analizar;
   DarResultados
END Contar.

y el resultado de la ejecución es el siguiente:

En un lugar de la Mancha
a 123 kilometros de Madrid.

               RESULTADOS
               ===========

Caracteres Totales:  52
Letras Totales:         38
DÝgitos Totales:         3
Blancos Totales:       9

LISTA DE LETRAS NO UTILIZADAS
B F J P Q V W X Y Z

LISTA DE DIGITOS UTILIZADOS
1 2 3

9.7.4 Ejemplo: Juego de lotería primitiva
En este último ejemplo se simula el juego de la Lotería Primitiva. El boleto con los números elegidos y la tabla de los números premiados se guardan en un conjunto sobre el referencial de rango 1 a 49. El número complementario se guarda aparta utilizando un tipo subrango.

El procedimiento Sortear genera aleatoriamente los 6 números premiados y el complementario. En todos los lenguajes existe algún sistema para generar números aleatorios. Para generar dichos valores se utiliza el módulo Lib, que contiene un procedimiento RANDOMIZE encargado de iniciar el proceso de generación de números aleatorios y una función RANDOM que devuelve cada vez que se la invoca un nuevo número aleatorio comprendido entre 0 y el valor máximo que se le pasa como argumento (la mayoría de los compiladores de lenguajes de alto nivel incluyen funciones auxiliares o de utilidad, entre las que se encuentra la generación de valores seudoaleatorios). "aquí para descargar el módulo anterior"

En este caso el máximo valor aleatorio es 49. Se generan los 6 números premiados y el complementario por este procedimiento. Con cada nuevo número se comprueba que no figura ya en la Tabla de premiados y que es distinto de 0. En caso afirmativo se incluye en la Tabla.

El procedimiento LeerBoleto funciona de forma parecida al anterior. En este caso se comprueba que los números elegidos están dentro de los posibles (entre 1 y 49). Se permite realizar apuestas múltiples con hasta el doble de números, y se calcula el número de apuestas realizadas con los números elegidos.

El procedimiento EscribirTabla, escribe los números que pertenecen al conjunto que se le pasa como argumento.

Los números acertados se obtienen como el conjunto intersección entre los premiados y el boleto elegido. El complementario se comprueba aparte. El listado completo del programa es el siguiente:

(**************************************************************************************************
*
*   Programa: Primitiva
*
*   Descripción:
*      Programa para jugar a la lotería primitiva con el computador
*
**************************************************************************************************)
MODULE Primitiva;
   FROM InOut IMPORT
      WriteString, WriteLn, WriteInt, ReadInt, Read, Write;
   FROM Lib IMPORT
      RANDOMIZE, RANDOM;
   CONST
      Maximo = 49;        (* Número mayor que se puede elegir *)
      Numeros = 6;        (* Números que forman una apuesta *)
   TYPE
      Rango = [1..Maximo];
      Tabla = SET OF Rango;
   VAR
      boleto, premiados, acertados : Tabla;
      complementario : Rango;

PROCEDURE Sortear(VAR SeisN : Tabla; VAR C : Rango);
(*=============================================
Procedimiento que realiza el sorteo aleatoriamente. Lo devuelve
en una tabla y el número complementario
==============================================*)
   VAR
      i, n : CARDINAL;
BEGIN
   RANDOMIZE;
   SeisN := Tabla{};
   i := 0;
   WHILE i <= Numeros DO
      n := RANDOM(Maximo);     (* Número aleatorio *)
      IF NOT (n IN SeisN) AND (n <> 0) THEN
         IF i < Numeros THEN
            INCL(SeisN,n)
         ELSE
            C := n
         END;
         INC(i)
      END
   END
END Sortear;

PROCEDURE LeerBoleto(VAR B : Tabla);
(*====================================
Procedimiento para leer el boleto en una tabla.
El máximo de números que se pueden seleccionar son 6*2=12
=====================================*)
   VAR
      apuestas, i, aux : INTEGER;
      n : Rango;
      tecla : CHAR;
BEGIN
   B := Tabla{};
   WriteString("Introduzca los números del boleto");
   WriteLn; i := 0; tecla := "S";
   WHILE (i < 2*Numeros) AND (tecla <> "N") DO
      WriteInt(i+1,3); WriteString("º número ?");
      ReadInt(aux); WriteLn; n := VAL(Rango, aux);
      IF NOT (n IN B) AND (n > 0) AND (n <= Maximo) THEN
         B := B + Tabla{n};
         i := i + 1
      END;
      IF ( i >= Numeros) AND (i < 2*Numeros) THEN
         WriteString("¿Más números (S/N)?");
         Read(tecla); Write(tecla); WriteLn
      END
   END;
   IF i = 12 THEN apuestas := 924
   ELSIF i = 11 THEN apuestas := 462
   ELSIF i = 10 THEN apuestas := 210
   ELSIF i = 9 THEN apuestas := 84
   ELSIF i = 8 THEN apuestas := 28
   ELSIF i = 7 THEN apuestas := 7
   ELSE apuestas := 1
   END;
   WriteLn; WriteString("Total apuestas =");
   WriteInt(apuestas, 6); WriteLn; WriteLn
END LeerBoleto;

PROCEDURE EscribirTabla(T : Tabla);
(*=====================================
Procedimiento para escribir los números de una tabla
======================================*)
   VAR
      i : CARDINAL;
BEGIN
   FOR i := 1 TO Maximo DO
      IF i IN T THEN
         WriteInt(i,3)
      END;
   END;
   WriteLn
END EscribirTabla;

BEGIN
   (*– Sortear –*)
      Sortear(premiados, complementario);
   (*– Leer numeros elegidos –*)
      LeerBoleto(boleto);
   (*– Escribir números premiados –*)
      WriteString("Números premiados:");
      EscribirTabla(premiados);
      WriteString("Complementario =");
      WriteInt(complementario, 3); WriteLn; WriteLn;
   (*– Acertados = Coinciden entre boleto y premiados –*)
      acertados := premiados * boleto;
   (*– Escribir aciertos –*)
      WriteString("Números acertados:");
      EscribirTabla(acertados);
      IF complementario IN boleto THEN
         WriteString("y el complementario");
      END
END Primitiva.

Un ejemplo del resultado de la ejecución es el siguiente:

Introduzca los números del boleto
  1º número ?23
  2º número ?12
  3º número ?5
  4º número ?8
  5º número ?37
  6º número ?21
¿Más números (S/N)?S
  7º número ?44
¿Más números (S/N)?N

Total apuestas =     7

Números premiados: 11 21 27 35 37 39
Complementario =  3

Números acertados: 21 37

Curso de Filosofía elemental (31)

SELECCIÓN
DE
TEXTOS FILOSÓFICOS

 

HERACLITO (s. V a. J. C.)

DEL DEVENIR COMO ANTERIOR AL SER Y AL CONCEPTO

   … Pues mientras que todas las cosas se corrompen (cambian o de vienen) de acuerdo con mi palabra, los hombres se comportan como si no tuviesen experiencia alguna de ello, empleando palabras y acciones propias para explicar las cosas, cada una por su propia naturaleza, y señalar el verdadero estado de la cuestión. Pero los hombres son así tan inconscientes de las cosas que obran cuando están despiertos como cuando duermen (…).
   Aquellos que hablan con la mente no pueden más que hacerse fuertes con lo común a las cosas, al modo como una ciudad se hace fuerte con su ley, y muchos lo hacen aún más fuertemente que ella. Pero todas las leyes humanas se nutren con la divina y una (eterna y móvil), y ésta impera donde quiere y prevalece siempre y en todo momento.
                                                                                                                                                                          (Fragmentos. Col. Diels, 1, 114.)

PARMENIDES (s. V a. J. C.)

DEL SER INMUTABLE Y UNO

Ven, pues; voy a hablarte (y te ruego te penetrers bien de mis palabras).
Cuáles son las únicas vías lícitas de indagación. La primera Sostiene lo que es y no puede ser, y éste Es el sendero de la convicción que sigue la verdad. Pero el otro Afirma: no es y este no-ser tiene que ser.
Este último camino, he de decírtelo, no puede seguirse.
Pues lo que no es no puedes conocerlo (ya que esto Se encuentra más allá de nuestro alcance), ni puedes expresarlo con palabras.
Pues pensar y ser son una y la misma cosa.
                                                                                                                                                (Poema de Parménides. Fragmentos. Col. Diels, 2 y 3.)

PLATON (s. IV a. J. C.)

EL MITO DE LA CAVERNA (SOBRE LA CONDICION HUMANA)

–Ahora represéntate el estado de la naturaleza humana, con relación a la ciencia y a la ignorancia, según el cuadro que te voy a trazar. Imagina un antro subterráneo, que tenga en toda su longitud una abertura que dé libre paso a la luz, y en esta caverna a hombres encadenados desde la infancia, de suerte que no puedan mudar de lugar ni volver la cabeza a causa de las cadenas que les sujetan las piernas y el cuello, pudiendo solamente ver los objetos que tienen enfrente. Detrás de ellos, a cierta distancia y a conveniente altura, supón un fuego cuyo resplandor les alumbra y un camino escarpado entre este fuego y los cautivos. Imagina a lo largo de este camino un muro semejante a los tabiques que los charlatanes ponen entre ellos y los espectadores para ocultarles la combinación y los resortes secretos de las maravillas que les muestran.
–Ya me represento todo eso.
–Figúrate personas que pasan a lo largo del muro llevando objetos de toda clase, figuras de hombres, de animales, de madera o de piedra, de suerte que todo esto aparezca sobre el muro. Entre los portadores de todas estas cosas, unos se detienen a conversar y otros pasan sin decir nada.
–!Extraños prisioneros y cuadro singular!
–Se aparecen, sin embargo, a nosotros punto por punto. Por lo pronto, ¿crees que pueden ver otra cosa de sí mismos y de los que están a su lado que las sombras que van a producirse enfrente de ellos en el fondo de la caverna?
–¿Cómo habían de poder ver más, si desde su nacimiento están precisados a tener la cabeza inmóvil?
–Y respecto de los objetos que pasan detrás de ellos, ¿podrán ver otra cosa que las sombras de los mismos?
–Nada más podrán ver.
–Si pudieran conversar unos con otros, ¿no convendrían en dar a las sombras que ven los nombres de las cosas mismas?
–Sin duda.
–Y si en el fondo de su prisión hubiera un eco que repitiese las palabras de los transeuntes, ¿no se imaginaría oir hablar a las sombras mismas que pasan delante de sus ojos?
–Así sería.
–En fin, ¿no creerían que pudiera existir otra realidad que estas mismas sombras?
–Sin duda.
–Mira ahora lo que naturalmente debe suceder a estos hombres, si se les libera de las cadenas y se les cura de su error. Que se desligue a uno de estos cautivos, que se le fuerce de repente a levantarse, a volver la cabeza, a marchar y mirar del lado de la luz; hará todas estas cosas con un trabajo increible; la luz le ofenderá a los ojos, y el alucinamiento que habrá de causarle le impedirá distinguir los objetos cuyas sombras veía antes. ¿Qué crees que respondería si se le dijese que hasta entonces sólo había visto fantasmas y que ahora tenía delante de su vista objetos más reales o más aproximados a la verdad? Si en seguida se le muestran las cosas a medida que se vayan presentando y a fuerza de preguntas se le obliga a decir lo que son, ¿no se le pondrá en el mayor conflicto y no estará él mismo persuadido de que lo que veía antes era más real que lo que ahora se le muestra?
–Sin duda.
–Y si se le obligase a mirar al fuego, ¿nosentiría dolor en los ojos? ¿No volvería la vista para mirar a las sombras, en las que se fija sin esfuerzo? ¿No creería hallar en éstas más distinción y claridad que en todo lo que ahora se le muestra?
–Seguramente.
–Si después se le saca de la caverna y se le lleva por el sendero áspero y escarpado hasta encontrar la claridad del sol, ¡qué suplicio seria para él verse arrastrado de esa manera! ¡Cómo se enfurecería! Y cuando llegara a la luz del sol, deslumbrados sus ojos de tanta claridad, ¿podría ver ninguno de estos numerosos objetos que llamamos seres reales?
–Al pronto no podría.
–Necesitaría indudablemente algún tiempo para acostumbrarse a ello. Lo que distinguiría más fácilmente sería, primero, las sombras; después, las imágenes de los hombres y demás objetos mismos. Luego dirigiría sus miradas al cielo, al cual podría mirar más fácilmente durante la noche a la luz de la luna y de las estrellas que en pleno día a la luz del sol.
–Sin duda.
–Y al fin podría no sólo ver la imagen del Sol en las aguas y dondequiera que se refleja, sino fijarse en él y contemplarlo allí donde verdaderamente se encuentre.
–Así sería.
–Después de esto, comenzando a razonar, llegaría a concluir que el Sol es el que crea las estaciones y los años, el que gobierna todo el mundo visible y el que es en cierta manera la causa de todo lo que se veía en la caverna.
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

–Y bien, mi querido Glaucón, ésta es precisamente la imagen de la condición humana. El antro subterráneo es este mundo visible; el fuego que le ilumina es la luz del Sol; este cautivo que sube a la región superior y que la contempla es el alma que se eleva hasta la esfera inteligible. He aquí, por lo menos, lo que yo pienso, ya que quieres saberlo. Sabe Dios si es conforme con la verdad. En cuanto a mí, lo que pienso es lo siguiente: En los últimos límites del mundo inteligible está la Idea de lBien, que se percibe con dificultad; pero una vez percibida no se puede menos de sacar la consecuencia de que ella es la causa primera de todo lo que hay de bello y de bueno en el universo; que, en este mundo visible, ella es la que produce la luz y el astro de que ésta procede directamente; que en el mundo invisible engendra la verdad y la inteligencia, y, en fin, que ha de tener fijos los ojos en esta idea el que quiera conducirse sabiamente en la vida pública y en la privada.
–Soy de tu dictamen, en cuanto puedo comprender tu pensamiento.
–Admite, por lo tanto, y no te sorprenda, que los que han llegado a esta sublime contemplación desdeñen tomar parte en los negocios humanos y sus almas aspiren sin cesar a fijarse en este lugar elevado. Así debe suceder si es que ha de ser conforme con la pintura alegórica que yo te he trazado.
                                                                                                                                                                                                     (República, L. VII.)

ARISTOTELES (s. IV a. J. C.)

ORIGEN DEL ESTADO Y DE LA SOCIEDAD

   Todo Estado es, evidentemente, una asociación, y toda asociación no se forma sino en vista de algún bien, puesto que los hombres, cualesquiera que ellos sean, nunca hacen nada sino en vista de lo que les parece ser bueno. Es claro, por lo tanto, que todas las asociaciones tienden a un bien de cierta especie, y que el más importante de todos los bienes debe ser el objeto de la más importante de las asociaciones, de aquella que encierra todas las demás y a la cual se llama precisamente Estado o asociación política.
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   La asociación de muchos pueblos forman un Estado completo, que llega, si puede decirse así, a bastarse absolutamente a sí mismo, teniendo por origen las necesidades de la vida y debiendo su subsistencia al hecho de ser éstas satisfechas.
   Así, el Estado procede siempre de la naturaleza, lo mismo que las primeras asociaciones, cuyo fin último es aquél; porque la naturaleza de una cosa es precisamente su fin, y lo que es cada uno de los seres cuando ha alcanzado su completo desenvolvimiento se dice que es su naturaleza propia, ya se trate de un hombre, de un caballo o de una familia. Puede añadirse que este destino y este fin de los seres es para los mismos el primero de los bienes, y bastarse a sí mismo es, a la vez, un fin y una felicidad. De donde se concluye evidentemente que el Estado es un hecho natural, que el hombre es un ser naturalmente sociable y que el que vive fuera de la sociedad por propia voluntad y no por efecto del azar es, ciertamente, o un ser degradado o un ser superior a la especie humana; y a él puden aplicarse aquellas palabras de Homero:
<<Sin familia, sin leyes, sin hogar …>>
   El hombre que fuese por naturaleza tal como lo pinta el poeta, sólo respiraría guerra, porque sería incapaz de unirse con nadie, como sucede a las aves de rapiña.
   Si el hombre es infinitamente más sociable que las abejas y que todos los demás animales que viven en grey, es evidentemente, como he dicho muchas veces, porque la naturaleza no hace nada en vano. Pues bien: ella concede la palabra al hombre exclusivamente. Es verdad que la voz puede realmente expresar la alegría y el dolor, y así no les falta a los demás animales, porque su organización les permite sentir estas dos afecciones y comunicárselas entre sí; pero la palabra ha sido concedida para expresar el bien y el mal y, por consiguiente, lo justo y lo injusto, y el hombre tiene esto de especial entre todos los animales: que sólo él percibe el bien y el mal, lo justo y lo injusto, y todos los sentimientos del mismo orden cuya asociación constituye precisamente la familia y el Estado.
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   Lo que prueba claramente la necesidad natural del Estado y su superioridad sobre el individuo es que, si no se admitiera, resultaría que puede el individuo entonces bastarse a sí mismo aislado así de todo como del resto de las partes; pero aquel que no puede vivir en sociedad y que en medio de su independencia no tiene necesidades, no puede ser nunca miembro del Estado; es un bruto o un Dios.
   La naturaleza arrastra, pues, instintivamente a todos los hombres a la asociación política. El primero que la instituyó hizo un inmenso servicio, porque el hombre, que cuando ha alcanzado toda la perfección posible es el primero de los animales, es el último cuando vive sin leyes y sin justicia. En efecto, nada hay más monstruoso que la injusticia armada. El hombre ha recibido de la naturaleza las armas de la sabiduría y de la virtud, que debe emplear sobre todo para combatir las malas pasiones. Sin la virtud es el ser más perverso y más feroz, porque sólo siente los arrebatos brutales del amor y del hambre. La justicia es una necesidad social, porque el derecho es la regla de la vida para la asociación política y la decisión de lo justo es lo que constituye el derecho.
                                                                                                                                                                       (La Política, I. Trad. P. Azcárate.)

SENECA (s.I)

DE LA PROVIDENCIA DIVINA

   Entre Dios y los varones justos hay una cierta amistad muda, mediante la virtud; y cuando dije amistad, debiera decir una estrecha familiaridad, y aún una cierta semejanza (…). Porque Dios, como el buen padre, cría con aspereza a veces y severidad a los hijos que más ama. Por lo cual, cuando vieres que los varones justos y amados de Dios padecen trabajos y fatigas, y que caminan cuesta arriba, y que al contrario los malvados están felices y abundantes en placeres, persuádete de que, así como nos agrada la modestia de los hijos y los enfrenamos con melancólico recogimiento, así hace Dios no teniendo en deleites al varón bueno, a quien somete a ejercicio para que se haga duro, porque lo prepara para sí.
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   Marchítase la virtud si no encuentra adversario, y conócese cuán grande es cuando el sufrimiento muestra su valor. Sábete, pues, que los hombres buenos han de obrar en consecuencia, sin temer lo áspero y difícil ni dar quejas a la adversa fortuna. Atribuyan a bien todo cuanto les sucediere, conviértanlo así en bien, pues no está la monta en lo que sufre, sino en el denuedo con que se sufre. ¿No consideras cuán diferentemente perdonan los padres y las madres? Ellos quieren que sus hijos se ejerciten en los estudios sin consentirles ociosidad, secándolos tal vez el sudor y tal vez las lágrimas; pero las madres procuran retenerlos en su seno y a su sombra sin que jamás lloren, sin que se entristezcan ni trabajen.
   Dios tiene para con los buenos ánimo paternal, y cuanto más apretadamente los ama, los fatiga ya con obras, ya con dolores  o con pérdidas, para que con ello cobren verdadero esfuerzo. Los que están cebados en la pereza desmayan no sólo en el trabajo, sino también con el peso, desfalleciendo con su misma carga. La felicidad que nunca fue ofendida no sabe sufrir golpe alguno; pero donde se ha tenido continua pelea con las incomodidades, críanse callos con las injurias sin rendirse a los infortunios, pues aunque el fuerte caiga, pelea de rodillas.
                                                                                                                                   (A Lucilio, I, II. Trad. Fernández Navarrete.)

SAN AGUSTIN (s. IV)

DE LA LEY ETERNA

   AGUSTÍN. –Quien vivir honestamente, pregunto yo, ¿ama la ley tan sólo porque le parece recta, o la encuentra llevadera y hermosa porque, mediante ella, viene a los hombres de buena voluntad la felicidad y a los malos la miseria?
   EVODIO. –La ama con toda su alma y, precisamente, vive bien porque la observa.
   AGUSTÍN. –Pues bien: al amarla, ¿ama algo mutable y temporal, o algo estable y sempiterno?
   EVODIO. –Es evidente que en ella ama una cosa eterna e inmutable.
   AGUSTÍN. –Y los que viven mal, ¿juzgas que, deseando ser felices, aman la ley eterna?
   EVODIO. –Pienso que de ninguna manera la aman.
   AGUSTÍN. –¿Y aman alguna otra cosa?
   EVODIO. –Aman muchas cosas: desde luego aman aquello que les causa una vida poco recomendable.
   AGUSTÍN. –¿Te refieres a las riquezas, honores, placeres, vanidad del cuerpo?
   EVODIO. –Y no a otras.
   AGUSTÍN. –Y esas cosas perniciosas para una vida breve, ¿son eternas?
   EVODIO. –Decir eso sería un absurdo.
   AGUSTÍN. –Hay, pues, dos clases de hombres: unos que aman lo transitorio y otros que aman lo eterno. Conviene también que haya dos leyes: una eterna y otra temporal. Pues bien: ¿qué clase de súbditos pondrías en cada una de esas leyes?
   EVODIO. –Es muy fácil responder a tu pregunta: los que viven espiritualmente felices están bajo la ley eterna; los miserables, bajo la ley temporal.
   AGUSTÍN. –Eso está bien, pero siempre que añadas que los que están bajo la ley temporal no se librarán de la ley eterna. De ahí todo lo que es justo, y lo que justamente va variando puede ser ordenado. Por lo demás, los que de buena voluntad viven según la ley eterna no necesitan una ley temporal.
                                                                                                                                                                     (Del libre albedrío, I, XV. Trad. Agustín Martínez.)

SOBRE EL TIEMPO

   ¿Qué es, pues, el tiempo? Si nadie me lo pregunta, lo sé; si quiero explicarlo a quien me lo pide, no lo sé. No obstante, con seguridad digo que si nada pasara no habría tiempo pasado, y si nada acaeciera no habría tiempo futuro, y si nada hubiese no habría tiempo presente.
   Estos dos tiempos, pues, el pasado y el futuro, ¿cómo son, puesto que el pretérito ya no es y el futuro no es todavía? Mas el presente, si siempre fuese presente y no pasara a pretérito, ya no fuera tiempo, sino eternidad. Si el presente, pues, para ser tiempo tiene que pasar a pretérito, ¿cómo podemos afirmar que es, si su causa de ser es que será pasado, de tal manera que no decimos con verdad que el tiempo es sino porque camina al no ser?
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   Si el futuro y el pasado existen, quiero saber en dónde están (…). Dondequiera que estén y comoquiera que sean, allí no están sino como presentes. En la narración verídica de las cosas pasadas lo que se extrae de la memoria no son las cosas mismas que pasaron, sino las palabras que sus imágenes hicieron concebir, las cuales, pasando a través de nuestros sentidos, quedaron en nuestro espíritu marcadas como huellas. Así, mi infancia, que ya no es, reside en un pasado que tampoco es; mas cuando la evoco y la refiero, veo su imagen en el presente, porque esta imagen está todavía en mi memoria.
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   Lo que ahora se me aparece claro y evidente es que ni el futuro ni el pasado son. Impropiamente, pues, decimos: los tiempos son tres: pretérito, presente y futuro. Con mayor propiedad se diría acaso: los tiempos son tres: presente del pasado, presente del presente, presente del futuro.
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   Oí decir a un hombre docto que el tiempo era el movimiento del Sol, de la Luna y de las estrellas, y no asentí. ¿Por qué no es el tiempo también el movimiento de todos los cuerpos? Acaso, si las lumbres del cielo se detuviesen y continuara moviéndo la rueda del alfarero, ¿no habría tiempo con qué medir sus vueltas ni nada que nos permitiera decir que ellas se dan a intervalos iguales o que unas se hacen más tardíamente y otras más velozmente; que los unos son más largos y los otros son más corotos? ¿O quizá cuando dijéramos esto no hablaríamos nosotros en el tiempo o no habría en nuestras palabras unas sílabas largas o otras breves, cabalmente porque las unas resuenan un tiempo más largo y otras más corto?
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   En ti, espíritu mío, mido el tiempo. No me contradigas: ello es; no me contradigas con el estruendo y el tropel de tus impresiones. En ti repito, mido el tiempo. La impresión que dejan en ti las cosas transitorias, aun cuando han pasado ya, permanecen; esta impresión es lo que yo mido cuando está presente, no las realidades que pasaron y la produjeron; esta impresión mido cuando mido el tiempo. Pues o ella es el tiempo o no mido el tiempo.
   ¿Y qué decir cuando medimos el silencio y decimos que aquel silencio tuvo tanta duración como aquella voz?
… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   Mientras tanto, mis años discurren entre gemidos. Vos, Señor, sois mi solaz y mi Padre eterno. Mas yo me dispersé en el tiempo, cuyo orden desconozco, y en tumultuosas vicisitudes son destrozados mis pensamientos, las íntimas entrañas de mi alma, hasta que fluya a Vos, purificado y fundido, en la cendra de vuestro amor.
                                                                                                                                                                          (Confesiones. L. 11. Trad. L. Riber.)

SANTO TOMAS DE AQUINO (S. XIII)

DE DIOS COMO FIN ULTIMO DE CUANTO ES

   Que existe un Ser primero de todos, que posee la plena perfección de todo el ser, al que llamamos Dios, es cosa demostrada, y también que de la abundancia de su perfección dispensa el ser a todo lo que existe, de tal suerte que sea preciso reconocerlo no sólo como el primero, sino como el primer principio de todos los seres. Ahora bien: este ser no lo confiere a los otros por necesidad de su naturaleza, sino por una  decisión de su voluntad. En consecuencia, Dios es el dueño de sus obras, pues cada uno domina lo que está sometido a su voluntad. Pero esta dominación de Dios sobre las cosas que ha producido es absoluta, dado que El no tiene necesidad de socorro de un agente exterior para producirlas, ni de un fundamento material, puesto que El es el creador universal de cuanto es. Por otra parte, cuando se producen las cosas por la voluntad de un agente, cada una de ellas es ordenada por este agente en vista de un cierto fin. En cuanto a su fin último, cada cosa lo alcanza por su acción; pero es preciso que esta acción sea dirigia por Aquel que ha conferido a las cosas los principios por los cuales obran. Es, pues, necesario que Dios, que es en Si naturalmente perfecto, y cuya potencia dispensa al ser a todo lo que existe, rija todos los seres y no sea dirigido por ninguno, y no hay nada que esté sustraído a su gobierno, como no hay nada que no haya recibido de El su existencia. Los mismo, pues, que El es perfecto como ser y como causa, lo mismo es perfecto en su gobierno.
   Si consideramos ahora el resultado de la dirección que Dios imprime a las cosas, se nos aparecerá diferente en los diversos seres, según las diferencia de su naturaleza.
   Ciertos seres, en efecto, han sido creados por Dios de tal manera que, poseyendo un intelecto, llevan su semejanza y representan su imagen. Por esto, estos seres no son solamente dirigidos, sino capaces de dirigirse ellos mismos, por medio de sus acciones, hacia el fin que les conviene. De estos seres, los que se someten en su propia conducta al gobierno divino son admitidos por ese gobierno mismo a alcanzar su fin último, y son, por el contrario, excluidos si se han conducido de otra manera.
   Pero existen otros seres desprovistos de intelecto, y que no se dirigen por sí mismo hacia su fin y son dirigidos por otro…
                                                                                                                                                                               (Summa contra gentes. I. III.)

ACTOS HUMANOS Y ACTOS DEL HOMBRE

   El hombre se diferencia de las criaturas sin razón en que es dueño de sus actos, y por eso las únicas acciones que se llaman humanas en su sentido propio son aquellas de las que el hombre es dueño. Pero el hombre es dueño de sus actos gracias a la razón y a la voluntad, y por eso el libre albedrío es llamado <<facultad de la voluntad y de la razón>>. Se llaman, pues, humanas, en sentido propio, las acciones que proceden de una voluntad deliberada; que si, por otra parte, ciertas acciones distintas de éstas convienen al hombre, pueden llamarse <<acciones del hombre>>, pero no <<humanas>> en sentido propio, pues no son acciones del hombre en tanto que hombre. Pero es evidente que todas las acciones que proceden de una cierta facultad son producidas por ella según lo que requiere la naturaleza de su objeto; el objeto de la voluntad es el fin y el bien; es necesario, en consecuencia, que todos los actos humanos sean en vista de un fin.
                                                                                                                                                                                          (Summa Theologica, I. II.)

DESCARTES (s. XVII)

DEMOSTRACIÓN <<A PRIORI>> DE LA EXISTENCIA DE DIOS

   Quise indagar luego otras verdades; y habiéndome propuesto el objeto de los geómetras (…), repasé algunas de sus más simples demostraciones, y habiendo advertido que esa gran certeza que todo el mundo les atribuye se funda tan sólo en que se conciben con evidencia, según la regla antes dicha, advertí también que no había nada en ellas que me asegurase de la existencia de su objeto; pues, por ejemplo, yo veía bien que, si suponemos un triángulo, es necesario que los tres ángulos sean iguales a dos rectos; pero nada veía que me asegurase que en el mundo haya triángulo alguno; en cambio, si volvía a examinar la idea que yo tenía de un ser perfecto, encontraba que la existencia está comprendida en ella del mismo modo que en la idea de un triángulo está comprendido el qe sus tres ángulos sean iguales a dos rectos, o, en la de una esfera, el que todas sus partes sean igualmente distantes del centro, y hasta con más evidencia aún; y que, por consiguiente, tan cierto es por lo menos que Dios, que es ese ser perfecto, es o existe, como lo pueda ser una demostración de geometría.
   Pero si hay algunos que están persuadidos de que es difícil conocer lo que sea Dioa, y aun lo que sea el alma, es porque no levantan nunca su espíritu por encima de las cosas sensibles y están tan acostumbrados a considerarlo todo con la imaginación –que es un modo de pensar particular para las cosas materiales– que es lo que no es imaginable les parece no ser inteligible. Lo cual está bastante manifiesto en la máxima que los mismos filósofos admiten como verdadera en las escuelas, y que dice <<que nada hay en el entendimiento que no haya estado antes en el sentido>>; en donde, sin embargo, es cierto que nunca han estado las ideas de Dios y del alma; y me parece que los que quieren hacer uso de su imaginación para comprender esas ideas son como los que para oir los sonidos u oler los olores quisieran emplear los ojos; y aun hay esta diferencia entre aquéllos y éstos; que el sentido de la vista no nos asegura menos de la verdad de sus objetivos que el olfato y el oído de los suyos, mientras que ni la imaginación ni los sentidos pueden asegurarnos nunca cosa alguna, como no intervenga el entendimiento.
                                                                                                                                                                 (Discurso del Método. IV. Trad. García Morente.)

LEIBNIZ (s. XVII)

DE LA ARMONIA PREESTABLECIDA

   Obligado, pues, a aceptar que no es posible que el alma ni ninguna otra sustancia verdadera pueda recibir algo de fuera, a no ser mediante la divina omniotencia, fui poco a poco inclinándome hacia una opinión que me sorprendió, pero que parece invitable y que en realidad tiene muchas ventajas y muy vonsiderables bellezas. Y es que deberá decirque que Dios ha creado originariamente el alma o cualquiera otra unidad real de tal suerte que todo nazca en ella de su propio fondo, por perfecta espontaneidad y, sin embargo, con perfecta conformidad a las cosas de fuera (…). Y por eso sucede que, representando cada una de esas sustancias exactamente el universo entero a su manera y según cierto punto de vista, y llegando al alma las percepciones de las cosas exteriores, en el momento preciso, por virtud de las propias leyes del alma, como aparte del mundo y como si nada existiera sino Dios y ella –para servirme del modo de expresarse que usa cierta persona de grande elevación de espíritu y de muy celebrada santidad– habrá un acuerdo perfecto entre todas esas sustancias que produce los mismos efectos que se advertirían si comunicasen unas con otras por transmisión de las especies o cualidades que el mundo de los filósofos imagina (…).
   Y puesto que esa naturaleza del alma es representativa del universo de un modo muy exacto, aunque más o menos distinto, resulta que la serie de las representaciones que produce el alma para sí misma responderá naturalemente a la serie de los cambios del universo; como asimismo, por otra parte, el cuerpo ha sido también acomodado al alma para las coyunturas en que ésta se concibe activa hacia afuera, lo cual es tanto más razonable cuanto que los cuerpo están hechos para los solos espíritus capaces de entrar en sociedad con Dios y celebrar su gloria. Así, pues, cuando se ve la posibilidad de esta hipótesis de las concordancias vease también que es la más razonable y que da una idea maravillosa de la armonía del universo y de la perfección de las obras de Dios (…). Hállase en mi hipótesis también una prueba nueva de la existencia de Dios, de sorprendente claridad. Pues esa perfecta concordancia de tantas sustancias que no tienen comunicación unas con otras no puede proceder sino de una causa común.
                                                                                                 (Nuevo sistema de la Naturaleza, 14. Trad. García Morente.)

KANT (s. XVIII)

LA LEY MORAL

   Pero ¿cuál puede ser esa ley cuya representación, aun sin referirnos al efecto que se espera de ella, tiene que determinar la voluntad, para que ésta pueda llamarse buena en absoluto y sin restricción alguna? Como he sustraído la voluntad a todos los afanes que pudieran apartarla del cumplimiento de una ley, no queda nada más que la universal legalidad de las acciones en general –que debe ser el único principio de la voluntad–; es decir, yo no debo obrar nunca más que de modo que pueda querer que mi máxima deba convertirse en ley universal. Aquí es la mera legalidad en general –sin poner por fundamento ninguna ley determinada a ciertas acciones– la que sirve de principio a la voluntad, y tiene que servirle de principio si el deber no ha de ser por doquiera una vana ilusión y un concepto quimérico; y con todo esto concuerda perfectamente la razón vulgar de los hombres en sus juicios prácticos, y el principio citado no se aparta nunca de sus ojos.
   Sea, por ejemplo, la pregunta siguiente: ¿Me es lícito, cuando me halle apurado, hacer una promesa con el propósito de no cumplirla? (…). ¿Podría yo decirme a mí mismo: cada cual puede hacer una promesa falsa cuando se halla en un apuro del que no puede salir de otro modo? Y bien pronto me convenzo de que, si bien puedo querer la mentira, no puedo querer, empero, una ley universal de mentir; pues, según esta ley, no habría propiamente ninguna promesa, porque sería vano fingir a otros mi voluntad respecto de mis futuras acciones, pues no creerían ese fingimiento, o si por precipitación lo hicieren pagaríanme con la misma moneda; por tanto, mi máxima, tan pronto como se tornase ley universal, destruiríase a sí misma (…).
                                                     (Fundamentación de la metafísica de las costumbres. I. Trad. García Morente.)

BALMES (s. XIX)

TEMPORALIDAD Y CONTINGENCIA

   …La percepión del tiempo en nosotros viene a parar en la percepción de la no necesidad de las cosas; desde el momento en que percibimos un ser no necesario, percibimos un ser que puede dejar de ser, en cuyo caso tenemos ya idea de la sucesión o del tiempo real o posible. Aquí asalta una reflexión grave: la idea del tiempo es la idea de la contingencia; la conciencia del tiempo es la conciencia de nuestra debilidad.
   La idea del tiempo es tan íntima en nuestro espíritu que sin ella no nos formaríamos idea del yo. La conciencia de la identidad del yo supone un vínculo que es imposible encontrar sin la memoria. Esta incluye por necesidad la relación del pasado y, por consiguiente, la idea del tiempo.
                                                                                                                                             (Filosofía Fundamental, L. V, c. XVII.)

VAZQUEZ MELLA (s. XIX-XX)

SOBERANIA SOCIAL Y SOBERANIA POLITICA

   El concepto de individuo, que tanto se repite y que sirve de centro a todo un sistema, no es otra cosa que un concepto puramente abstracto (…). Nacemos en el seno de una familia, de una clase, de una sociedad, y ni la misma forma de educación, ni la parte que llegan a constituir las costumbres en nuestro carácter, ni la lengua que hablamos, ni la enseñanza con que se cultiva nuestra inteligencia, son obra nuestra; existían antes de que nosotros viniéramos al mundo y han ido formando nuestro carácter y desarrollando nuestras facultades. Y si despojáramos al hombre concreto de esa atmósfera social en que vive y se desarrolla, y en la que después llega a actuar libremente, ¿qué es lo que queda, fuera de su naturaleza y de sus facultades no actuadas y como latentes?
… … … … … …

… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   La teoría que sustento se funda en dos leyes sociales que la sociología positivista ha olvidado: una es la ley de cooperación universal, que se funda en la limitación del ser finito. Sólo el Ser Infinito se basta a sí mismo; el ser finito necesita, por su limitación, del concurso de los demás. Por eso tiene derecho a unir con ellos sus fuerzas para conservarse y perfeccionarse, y este es un derecho innato del ser humano… Por eso yo defiendo la existencia de la persona colectiva a pesar y por encima de la voluntad del Estado.
   La otra ley sociológica que llamo ley de las necesidades, puede formularse así: toda institución permanente se funda en una necesidad de la naturaleza humana: la satisfacción es el fin inmediato de esa institución… Y esta ley indica que hay en  la naturaleza humana necesidades que no pueden satisfacerse sin medios colectivos y que tienen un fin que no depende del Estado.

… … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … … …

   Frente a la soberanía política señalamos la verdadera autonomía social que la limita, erizada, por decirlo así, de libertades y de derechos que empiezan en los personales, que se afirman en la familia y siguen por sus prolongaciones en la universidad, el gremio, el municipio, la región, formando una jerarquía de personas colectivas que amuralla la soberanía del Estado, contenida por esa serie escalonada de baluartes que marca alrededor de ella un círculo sagrado que no puede traspasar el poder sobernao sin convertirse en tiránico (…). La sobernaía social es, así, la jerarquía de personas colectivas, de poderes organizados, de clases, que ascienden desde la familia hasta la soberanía que se llama política concretada en el Estado, a la que deben auxiliar, pero también contener.
                                                                                                                  (Obras completas, T. VIII, pág. 150; T.X, pág. 167; T. XV, págs. 170 y siguientes.)

BERGSON (s. XX)

DURACION REAL Y TIEMPO EXTERIOR

   La pura duración interior no es sino una sucesión de cambios cuantitativos que se funden, que se penetran entre sí, sin poderse señalar entre ellos límites imprecisos, sin ninguna tendencia a exteriorizarse los unos con relación a los otros, sin ningún parentesco con el número: tal es una heterogeneidad pura (…).
   Cuando miro sobre el cuadrante de un reloj el movimiento de la aguja que corresponde a las oscilaciones del péndulo, no mido la duración como se cree habitualmente: me limito a contar simultaneidades, que es algo muy distinto. Fuera de mí, en el espacio, no hay nunca más que una posición determinada de la aguja y del péndulo, porque de las posiciones pasadas nada queda. Dentro de mí, en cambio, se prosigue un proceso de organización y de penetración mutua de los hechos de conciencia, que constituye la duración verdadera. Precisamente porque mi duración es tal es por lo que me represento lo que yo llamo las oscilaciones del péndulo al mismo tiempo que percibo la oscilación actual, única realidad fuera de mí. Ahora, suprimamos por un instante el yo que piensa estas oscilaciones llamadas sucesivas y nada habrá más que una sola oscilación del péndulo, una posición del mismo más exactamente: nada de duración, por consiguiente.
   Suprimamos, a su vez, el péndulo y sus oscilaciones: no habrá entonces más que la duración heterogénea del yo, sin momentos exteriores unos a otros, sin relación alguna con el número (no hay unidad posible para la duración interior, que es pura heterogeneidad cualitativa, siempre distinta). Así, en nuestro yo hay una sucesión sin exterioridad recíproca, puesto que la oscilación presente es radicalmente distinta de la oscilación anterior, que no existe ya; pero ausencia de sucesión, puesto que ésta existe solamente para un espectador consciente que recuerde el pasado y yuxtaponga las dos oscilaciones o sus símbolos en un espacio auxiliar.
   Ahora bien, entre esta duración sin exterioridad y esta exterioridad sin sucesión se produce una relación comparable a lo que llaman los físicos un fenómeno de endósmosis. Dado que las fases sucesivas de nuestra vida consciente, en su interpretación íntima, se corresopnden, sin embargo, con las oscilaciones del péndulo que le son  simultáneas; dado, por otra parte, que estas oscilaciones son netamente distintas porque la una no existe ya cuando la otra se produce, contraemos el hábito, al relacionarlas, de establecer la misma distinción entre los momentos sucesivos de nuestra vida consciente: las oscilaciones del péndulo la descomponen, por decirlo así, en partes exteriores unas a otras: de aquí la idea errónea de una duración interna espacializada, sobre un fondo homogéneo, análoga a la sucesión exterior cuyos momentos idénticos se siguen sin penetrarse.
                                                          (Ensayo sobre los datos inmediatos de la conciencia, II.)

Registro en el foro de PLISPLASPLUS

En este breve FAQ o ejemplo, vamos a ver cuáles son los pasos necesarios para realizar con éxito el alta de un nuevo usuario en; http://plisplasplus.creatuforo.com. En primer lugar, podríamos adjuntar la definición de foro que se encuentra en Wikipedia: "Los foros en Internet son también conocidos como foros de mensajes, de opinión o foros de discusión y son una aplicación web que le da soporte a discusiones u opiniones en línea." En general, además de la simplicidad de uso para publicar, leer o postear en un foro, nos encontramos con multitud de herramientas o "gadtches" que hacen muy atractiva la navegación y de posibilidades que aumentan la comunicación día a día con el avance tecnológico correspondiente.

/:Pasemos por tanto a ver cómo realizar un registro de usuario:\

1.- Entrar en el foro (aquí). En la nueva ventana abierta del navegador observamos la página inicial que muestra las categorías en la que se estructura el foro, sólo las disponibles para usuarios no registrados o invitados y en la que podemos leer lo que lo usuarios escriben o postean. Es importante resaltar que un usuario registrado o miembro es el que crea temas, responde mensajes, añade post, etc. Así que para formalizar un nueva alta, haremos click en el enlace correspondiente (Registrarse), como muestra la siguiente imagen.

Dibujo3 Dibujo4 -(click en Registrarse)

Lo que se nos muestra a continuación es la normativa vigente en relación a las condiciones de uso y es necesario aceptarlas para continuar el registro. Al aceptar las normas nos encontramos con un formulario, en el que rellenaremos los distintos campos que se encuentra divididos en 3 grupos; Información de Registración, Información de Perfil y Preferencias. Como muestra la siguiente captura de pantalla.

Dibujo5

2.- Breve descripión del formulario de nuevo usuario:

Información de Registración, en esta sección nos encontramos con 11 campos obligatorios y todos tienen que ser rellenados o disponer de datos válidos.

  • Nombre de Usuario: Aquí ponemos el nick que deseamos usar en el foro
  • Email: Debemos escribir una dirección de correo electrónico válida y propia, pues necesitaremos verificar el alta en ella
  • Contraseña: Escribimos el password para acceder al foro como usuario registrado
  • Confirmar contraseña: Debemos escribir de nuevo el password elegido
  • Ocupación: Seleccionamos una opción que se ajuste en lo posible a nuestra actividad diaria
  • País: Seleccionamos la opción correspondiente a nuestro pais de residencia
  • Código Postal: Escribimos los dígitos que determinan nuestra dirección de residencia
  • Edad: Seleccionamos nuestra edad
  • Sexo: Seleccionamos la opción adecuada
  • Intereses: Picamos en las opciones que se ajusten a nuestros gustos
  • Código de información: En este campo o casilla debemos escribir los caracteres que coincidan con la imagen, como en el siguiente ejemplo:

Dibujo6 En este ejemplo, el código a incluir en el formulario es; 7RXVÑ

Información de Perfil, en esta sección nos encontramos con 9 campos en los que podemos especificar datos asociados al nuevo usuario.

  • Número ICQ: Nuestro identificador de esta aplicación de mensajería instantanea
  • Dirección AIM: Nuestro identificador personal
  • MSN Messenger: Nuestra id en este programa de conversación en linea
  • Yahoo Messenger: Nuestro identificador en este programa de mensajería
  • Sitio Web: Escribimos una dirección de Internet propia
  • Ubicación: Escribimos información complementaria a nuestro país de residencia
  • Ocupación: Escribimos datos que definan nuestra actividad profesional mas concretamente
  • Intereses: Escribimos una pequeña relación de hobies
  • Firma: En este campo podemos incluir un texto o similar que se adjunta en los mensajes que se publican en el foro

Preferencias, en esta sección nos encontramos con 13 campos en los que diseñamos las características inherentes al foro.

  • Mostrar siempre mi Email: Habilita la visibilidad para los demás usuarios de nuestra dirección de correo electrónico, es decir, los usuarios conocen nuestro email.
  • Ocultar su status online: Mediante esta opción permanecemos como usuario invisible, es decir, si elegimos "Si", los usuarios del foro no saben si estamos conectados
  • Siempre avisarme cuando hay respuestas: Con esta opción recibimos un correo electrónico en nuestra cuenta
  • Notificarme por nuevos Mensajes Privados: Por defecto recibimos un correo electrónico cuando nos escriban un MP(mensaje privado) de otro usuario del foro
  • Desplegar nueva ventana cuando hay Mensajes Privados: Habilitamos la opción de leer los MP de manera independiente
  • Siempre adjuntar mi Firma: Habilitamos la insercción del texto del campo Firma
  • Siempre permitir BBCode: Habilitamos la posibilidad de lectura de texto enriquecido
  • Siempre permitir HTML: Habilitamos la posibilidad de adjuntar texto en este lenguaje en el foro
  • Siempre activar Smilies: Se permite que se usen códigos de imágenes, por ejemplo en lugar de :-), aparece Sonrisa , etc
  • Idioma del Foro: Elegimos nuestro idioma
  • Estilo del Foro: Picamos en una de las opciones, por defecto no se puede cambiar
  • Zona horaria: Elegimos una opción
  • Formato de Fecha: Elegimos la visualización

Para un registro rápido, podemos rellenar los campos correspondientes a la sección: "Información de Registración" y más tarde configurar tanto nuestro Perfil como editar las Preferencias descritas anteriormente.

Una vez que el formulario se complete, haremos click en el botón enviar y si todos los datos son correctos veremos una ventana en nuestro navegador web como la siguiente;

Dibujo1

Ya por último, sólo bastaría con revisar la bandeja de entrada de nuestro correo electrónico con la información básica para acceder al foro.

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

Tema 9
Tipos Definidos:
Enumeración y Conjuntos

 

Después de haber sido introducidas todas las estructuras fundamentales para la construcción de programas, ahora se pasa a estudiar las estructuras de datos. En este Tema se indican las primeras formas en que el programador puede definir sus propios tipos de datos.

En primer lugar se estudian los tipos escalares simples definidos por enumeración y cómo se utilizan. Como caso especial de tipo enumerado ya predefinido se hace especial hincapié en el tipo BOOLEAN, precisando su importancia dentro de la programación. A continuación se estudia la estructura conjunto y se muestra la diferencia que existe entre los conjuntos en Modula-2 y el concepto matemático de conjunto.

Para finalizar, se presentan varios ejemplos que emplean los tipos introducidos y muestran las posibilidades que ofrecen.

9.1 Definición de tipos

Una de las ventajas fundamentales de los lenguajes de alto nivel es la posibilidad que ofrecen al programador de definir sus propios tipos de datos. Los tipos predefinidos: INTEGER, CARDINAL, CHAR y REAL, ya presentados en el Tema 2, nos han permitido la elaboración de programas para la realización de cálculos o el manejo de caracteres. Sin embargo, si se trata de realizar un programa para jugar al ajedrez resulta mucho más adecuado utilizar datos que representen a los peones, caballos, torres, alfiles, reyes y reinas del tablero. Razonamientos similares se pueden hacer si se quieren realizar programas que manejen días de la semana, deportes, colores, alimentos, etc.

Mediante la definición de tipos de datos por el programador se consigue que cada información que maneja el computador tenga su sentido específico. El tipo establece los posibles valores que puede tomar ese dato. Además, al igual que sucedía con los tipos predefinidos, a cada nuevo tipo que se define se asocian un conjunto de operaciones que se pueden realizar con él. Por tanto, la definición de tipos supone crear un nuevo nivel de abstracción dentro del programa.

En Modula-2 la declaración de los tipos se realiza, junto a la declaración de las constantes y variables, dentro de la Parte_declarativa de cualquier Bloque, en el programa principal o en cualquiera de sus procedimientos o funciones. Esta declaración se inicia con la palabra clave TYPE. Por ejemplo:

TYPE
   TipoEdad = INTEGER;
   TipoSexo = CHAR;

Cada tipo se define mediante un nombre o identificador seguido del símbolo igual (=), y a continuación la descripción concreta del tipo que se quiere definir. Esta última parte de la declaración es el objeto de los apartados siguientes de este Tema. Las diferentes definiciones de tipos se separan mediante punto y coma (;).

En las declaraciones anteriores se definen nuevos tipos, haciéndolos equivalentes o sinónimos de otros tipos ya definidos (en este caso, predefinidos). Quizá en estos ejemplos la declaración de tipo no cubre todos los objetivos señalados anteriormente, pues no establece ninguna especificidad. Esto es, convendría establecer que la edad no puede ser negativa ni superior a un valor determinado o que el sexo sólo puede tomar determinados valores. En todo caso es importante señalar que la definición de un nuevo tipo puede utilizar (y normalmente utiliza) otros tipos ya definidos.

La definición de tipos es solamente una declaración de los esquemas de datos que se necesitan para organizar la información de un programa. Para almacenar información es necesario declarar y utilizar variables de los correspondientes tipos, de la misma forma que se hace con los tipos predefinidos.

Por ejemplo, se podrían usar los tipos sinónimos anteriores en la forma:

VAR edad1, edad2: TipoEdad;
        sexo: TipoSexo;
. . . .
edad2 := edad1 + 1;
sexo := "V";
. . . .

De manera formal, la sintaxis y ubicación de la declaración de tipos dentro de la Parte_declarativa del Bloque es la siguiente:

Bloque ::= Parte_declarativa Parte_ejecutiva END
Parte_declarativa ::= { Declaración }
Declaración ::= Declaración_de_constantes |
                       Declaración_de_tipos |
                       Declaración_de_variables |
                       Declaración_subprograma
Declaración_de_tipos ::= TYPE { Definición_de_tipo ; }
Definición_de_tipo ::= Identificador = Esquema_de_tipo
Esquema_de_tipo ::= Identificador_de_tipo |
                                Tipo_enumerado |
                                Tipo_subrango |
                                Tipo_conjunto              (etc…)

En los apartados siguientes se indica la forma en que se pueden definir los esquemas de los tipos enumerados, subrango o conjuntos.

9.2 Tipos enumerados

Aparte de los valores predefinidos básicos (números, caracteres, etc.) en Modula-2 se pueden definir y utilizar nuevos valores simbólicos de la manera que se indica a continuación.

9.2.1 Definición de tipos enumerados
Una manera sencilla de definir un nuevo tipo de dato es enumerar todos los posibles valores que puede tomar. En Modula-2 esta enumeración se realiza mediante una lista con los valores separados por comas (,) y encerrados entre paréntesis. Cada posible valor describe mediante un identificador. Estos indentificadores al mismo tiempo quedan declarados como valores constantes. Por ejemplo:

TYPE
   TipoDia = (Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo);
   TipoMes = (Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre);
   EstadoCivil = (Casado, Soltero, Viudo, Divorciado);
   Color = (Rojo, Amarillo, Azul);
   Frutas = (Pera, Manzana, Limon, Naranja, Kiwi);
   Cardinal = (Norte, Sur, Este, Oeste);
   Pieza = (Rey, Dama, Alfil, Caballo, Torre, Peon);

La enumeración implica un orden que se establece entre los valores enumerados. Este orden se define de forma implícita e impone que el primer elemento de la lista ocupa la posición 0, el siguiente la 1, y así sucesivamente hasta el último, que ocupa la posición N-1, siendo N el número de elementos enumerados. Los tipos de datos enumerados forman parte de una clase de tipos de Modula-2 denominados tipos ordinales, a la cual pertenecen también los tipos INTEGER, CARDINAL y CHAR, pero no el tipo REAL.

La sintaxis exacta de la declaración de los tipos enumerados es la siguiente:

Tipo_enumerado ::= (Lista_de_identificadores)
Lista_de_identificadores ::= Identificador { , Identificador }

9.2.2 Uso de tipos enumerados
Los tipos enumerados se emplean de manera similar a los tipos predefinidos. El identificador de tipo se puede emplear para definir variables de ese tipo, y los identificadores de los valores enumerados se emplean como las constantes con nombre. Usando las definiciones anteriores podremos escribir:

VAR diaSemana : TipoDia;
        colorCoche : Color;
        mes : TipoMes;
. . . .
diaSemana := Lunes;
colorCoche := Azul;
FOR mes := Junio TO Diciembre DO . . . .
. . . .

Puesto que entre los valores enumerados existe un orden definido, podremos emplear con ellos los operadores de comparación, y escribir:

IF mes >= Julio THEN . . . .
. . . .
WHILE diaSemana < Sabado DO . . . .
. . . .
IF coloCoche = Rojo THEN . . . .

Al igual que para el resto de los tipos ordinales, con los tipos enumerados se pueden utilizar la función predefinida ORD para obtener la posición de un valor en la lista de valores del tipo. Por ejemplo:

ORD( Casado )            = 0
ORD( Kiwi )                 = 4
ORD( Diciembre )         = 11

La operación inversa, que permita conocer qué valor enumerado ocupa una determinada posición, se consigue mediante la función predefinida VAL, que se invoca en la forma:

VAL( T, N )

y devuelve el valor que ocupa la posición N en la colección de valores del tipo T. Con los ejemplos anteriores se cumple que:

VAL( EstadoCivil, 0 )        = Casado
VAL( Frutas, 4 )               = Kiwi
VAL( TipoMes, 11 )          = Diciembre

Otras operaciones aplicables a los tipos ordinales, y por tanto a los enumerados, corresponden a los procedimientos predefinidos INC y DEC, que reemplazan un valor por el siguiente o anterior, respectivamente (o por el N-esimo siguiente o anterior, si se invoca con dos argumentos). Por ejemplo:

colorCoche := Rojo;
mes := Diciembre;
INC( colorCoche );
DEC( mes, 3 )

da como resultado

colorCoche = Amarillo     y      mes = Septiembre

Sin embargo

mes := Diciembre;
INC( mes )

provocará un error, ya que no existe el mes siguiente a Diciembre.

Un dato de tipo enumerado se puede pasar como argumento de procedimientos o funciones y puede ser el resultado de una función. Por ejemplo, si conocemos el día de la semana de hoy y queremos calcular qué día de la semana será dentro de N días, podemos emplear la siguiente función:

PROCEDURE SumarDias( Hoy: TipoDia; N: INTEGER): TipoDia;
   CONST DiasSemana = 7;
   VAR aux: INTEGER;
BEGIN
   aux := ( ORD(Hoy) + N) MOD DiasSemana;
   RETURN VAL( TipoDia, aux );
END SumarDias;

Como se puede observar, primeramente se calcula el ordinal del nuevo dia entre 0 y 6, según el orden establecido en la definición de TipoDia y finalmente se devuelve este ordinal convertido al tipo correspondiente mediante la función predefinida VAL.

9.3 El tipo predefinido BOOLEAN

Al introducir las estructuras de selección o iteración se han descrito sentencias de Modula-2 que utilizan expresiones lógicas o de condición. En ese momento se dijo, de manera informal, que el valor de una condición podía ser cierto o falso. De manera más precisa podemos indicar ahora que en Modula-2 existe el tipo predefinido BOOLEAN que responde a la siguiente definición, análoga a la de un tipo enumerado:

TYPE BOOLEAN = (FALSE, TRUE)

Esta definición no es necesario escribirla ya que está implícita en el lenguaje. El nombre BOOLEAN es el identificador del tipo, y las constantes simbólicas TRUE y FALSE corresponden a los valores de verdad cierto y falso, respectivamente. Como tipo ordinal se cumple:

ORD( FALSE )    = 0
ORD( TRUE )      = 1

A partir del tipo predefinido BOOLEAN, ahora es posible declarar variables de este tipo y utilizarlas, de forma similar al resto de variables, para guardar resultados de expresiones condiconales. Por ejemplo

VAR bisiesto : BOOLEAN;
. . . .
bisiesto := (anno MOD 4) = 0;

Asimismo, es posiblre realizar operaciones entre ellas. En concreto, entre operandos booleanos (variables o no) es posible realizar las operaciones lógicas ya indicadas en el Tema 5 para formar expresiones lógicas y cuyos operandos son los siguientes:

Operador                               Operación lógica
AND o el símbolo &                 Conjunción
OR                                         Disyunción
NOT o el símbolo ~                  Negación

Esto permite formar expresiones y sentencias tales como la siguiente:

IF bisiesto AND (mes > Febrero) THEN
   totalDias := totalDias + 1
END;

Los resultados de las expresiones lógicas para los distintos operandos y operadores son los siguientes:

   a             b           a AND b           a OR b           NOT a
TRUE      TRUE        TRUE              TRUE             TRUE
TRUE      FALSE      FALSE            TRUE             FALSE
FALSE    TRUE

        FALSE            TRUE             TRUE
FALSE    FALSE      FALSE            FALSE           TRUE

El tipo booleano, como cualquier otro tipo enumerado, se puede pasar como argumento de un procedimiento o función y puede ser devuelto como resultado de una función. De hecho es bastante habitual definir funciones cuyo resultado es un valor booleano, cuando se quiere realizar un test sobre los argumentos de la función. Este tipo de funciones se denominan predicados. Un ejemplo de este tipo de funciones es la función predefinida ODD. Esta función determina si el valor del argumento es impar. Por ejemplo:

ODD( 7 )      = TRUE
ODD( 2 )      = FALSE

9.4 Tipos subrango

Otra forma de especificar nuevos tipos de datos es estableciendo rangos parciales de valores de otro tipo ya existente. Con esto no se definen nuevos valores, pero sí un nuevo tipo con una colección limitada de valores válidos.

9.4.1 Definición de tipos subrango
Un tipo subrango se define a partir de otro tipo ordinal ya definido, que se toma como tipo base. La forma de realizar esto es declarar un identificador diferente para el nuevo tipo y establecer los límites mínimo (primero) y máximo (último) del subrango de variación. Estos límites, en Modula-2, se escriben separados por dos puntos seguidos (..) y se encierran entre corchetes:

[primero .. último]

Los valores primero y último serán valores constantes del tipo base que deben cumplir la relación:

primero < último

Por ejemplo:

TYPE Laborable = [ Lunes .. Viernes];
          PrimerSemestre = [ Enero .. Junio];
          Citricos = [ Limon .. Naranja ];
          Porcentaje = [ -100 .. 100 ];
          RangoLetras = [ "A" .. "Z" ];
          RangoDigitos = [ "0" .. "1" ];

Como se puede observar, el tipo base puede ser cualquier tipo ordinal, bien sea predefinido o definido por enumeración. Normalmente no es necesario indicar explícitamente cuál es ese tipo base, ya que se deduce fácilmente de los valores dados como primero y último. Sin embargo hay situaciones ambiguas, y en particular cuando se hacen definiciones como la siguiente:

RandoDias = [ 1 .. 31 ];

La ambigüedad se produce porque este rango de valores enteros positivos es común a los tipos INTEGER y CARDINAL. Por defecto, el compilador consideraría que el tipo base de este ejemplo es CARDINAL. Si queremos que sea INTEGER, podemos indicarlo expresamente en la forma:

RandoDias = INTEGER[ 1 .. 31 ];

Aunque resulte superfluo, la indicación explícita del tipo base se puede hacer siempre que se desee. Las declaraciones anteriores pueden reeescribirse como:

TYPE Laborable = TipoDia[ Lunes .. Viernes];
          PrimerSemestre = TipoMes[ Enero .. Junio];
          Citricos = Frutas[ Limon .. Naranja ];
          Porcentaje = INTEGER[ -100 .. 100 ];
          RangoLetras = CHAR[ "A" .. "Z" ];
          RangoDigitos = CHAR[ "0" .. "1" ];

Sobre el tipo REAL no es posible definir ningún subrango, debido a que este tipo no es tipo ordinal.

Las reglas BNF para la definición de tipos subrango son las siguientes:

Tipo_subrango ::= [ Identificador_de_tipo ]
                           [ Límite_inferior .. Límite_superior ]
Límite_inferior ::= Expresión_constante
Límite_superior ::= Expresión_constante

En esta reglas se indica, además, que para expresar los límites se pueden emplear expresiones cuyo resultado sea un valor constante del tipo adecuado.

9.4.2 Uso de tipos subrango
Las variables de un tipo subrango tienen la misma consideración que las variables de su tipo base. Por tanto, se pueden utilizar en expresiones o sentencias de asignación como si fueran variables del tipo base. Por ejemplo, una variable de tipo RangoLetras se puede utilizar como si fuera de tipo CHAR. La ventaja principal que ofrecen el tipo subrango es que, previamente a cualquier asignación a una variable, se puede comprobar automáticamente que el valor a asignar está dentro de los límites declarados. Si dicho valor está fuera del rango, el programa finaliza inmediatamente por error.

9.5 Tipos estructurados

Todos los tipos de datos presentados hasta este momento se denominan escalares, y son datos simples, en el sentido de que no se pueden descomponer. En general, no tiene sentido tratar de reconocer fragmentos de información independientes dentro de un valor entero, o un carácter, o el valor simbólico de un día de la semana o el número de un día del mes.

En muchas aplicaciones resulta conveniente, o incluso necesario, manejar globalmente elementos de información que agrupan colecciones de datos. Por ejemplo, puede ser apropiado manejar como un dato único el valor de una fecha que incluye la información del día, el mes y el año como elementos componentes separados. Con este objetivo, los lenguajes de programación dan la posibilidad de definir tipos de datos estructurados.

Un tipo estructurados de datos, o estructura de datos, es un tipo cuyos valores se construyen agrupando datos de otros tipos más sencillos. Los elementos de información que integran un valor estructurado se denominan componentes. Todos los tipos estructurados se definen, en último término, a partir de tipos simples combinados.

Los próximos apartados están dedicados al tipo estructurado conjunto, y en temas sucesivos se estudiarán las formaciones y los registros.

9.6 Conjuntos

Con el tipo enumerado Frutas definido en el apartado 9.2.1 sólo es posible tener variables que guarden en cada momento un valor único de fruta. Por ejemplo, si declaramos la variable:

VAR miFruta : Frutas;

con ella sólo es posible guardar un determinado valor de fruta en cada momento:

miFruta := Pera;     (* miFruta ahora es Pera *)
INC(miFruta);          (* miFruta pasaría a ser Manzana *)

Supongamos una frutería que se dedica al comercio de todas las frutas definidas anteriormente por enumeración como el tipo Frutas. Dependiendo de la temporada o del momento del día es posible que alguna de las frutas esté agotada, pero habitualmente la frutería dispondrá de existencias de hasta las cinco clases de frutas diferentes: Pera, Manzana, Limon, Naranja, Kiwi. Si se quiere conocer en cada momento de que fruta hay o no hay existencias sería necesario disponer de tantas variables del tipo Frutas como frutas diferentes hayan sido declaradas. Por ejemplo:

VAR
   fruta1, fruta2, fruta3, fruta4, fruta5 : Frutas;

Los incovenientes de esta solución son los siguientes:

  • Cualquiera de estas variables sería un "contenedor" capaz de guardar cualquier clase de fruta y varias o todas ellas podrían guardar la misma clase de fruta. Hay que recordar que nuestro objetivo sólo es saber de que clase de fruta hay o no hay existencias.
  • Sería necesario poder indicar cuando una variable "contenedor" está vacía y esto no es posible dado que las variables de tipo Frutas sólo pueden contener valores de ese tipo y no existe el valor NoFruta dentro del tipo enumerado Frutas.

Una posible solución alernativa sería declarar tantas variables de tipo BOOLEAN como clases de frutas tenemos. Por ejemplo:

VAR
   hayPera, hayManzana, hayLimon, hayNaranja, hayKiwi: BOOLEAN;

Los incovenientes de esta solución son los siguientes:

  • Estas variables no tienen ninguna relación con el tipo enumerado Frutas y cualquier modificación por separado con el tipo enumerado o en las variables booleanas puede ser causa de un error difícil de detectar y corregir.
  • Para ambas soluciones, con Frutas o BOOLEAN, se necesitan declarar tantas variables como clases de frutas existan y en el caso habitual de disponer de varias decenas de frutas diferentes  está no parecer ser una solución razonable.

Como conclusión se puede decir que necesitamos un nuevo tipo de dato estructurado, capaz de agrupar en una misma variable todos los indicadores de si hay o no hay elementos de un tipo referencial previamente enumerado. Este nuevo tipo de dato estructurado se denomina conjunto y está asociado al concepto matemático de CONJUNTO para el que los matemáticos han establecido un álgebra específica que determina sus propiedades y operaciones.

Como ejemplo, a partir del tipo referencial Frutas podemos definir una variable estructurada de tipo conjunto cuyos posibles valores serían los siguientes:

1 valor con NINGUNA fruta:                           1 valor con TODAS las frutas:

     CONJUNTO VACÍO                                        COJUNTO REFERENCIAL

5 valores con una sola fruta:                          5 valores con cuatro frutas:

     Pera                                                              Pera – Manzana – Limon – Naranja
     Manzana                                                       Pera – Manzana – Limon – Kiwi
     Limon                                                            Pera – Manzana – Naranja – Kiwi
     Naranja                                                          Pera – Limon – Naranja – Kiwi
     Kiwi                                                              Manzana – Limon – Naranja – Kiwi

10 valores con dos frutas:                              10 valores con tres frutas:

     Pera – Manzana                                             Pera – Manzana – Limon
     Pera – Limon                                                 Pera – Manzana – Naranja
     Pera – Naranja                                               Pera – Manzana – Kiwi
     Pera – Kiwi                                                    Pera – Limon – Naranja
     Manzana – Limon                                           Pera – Limon – Kiwi
     Manzana – Naranja                                         Pera – Naranja – Kiwi
     Manzana – Kiwi                                              Manzana – Limon – Naranja
     Limon – Naranja                                              Manzana – Limon – Kiwi
     Limon – Kiwi                                                  Manzana – Naranja – Kiwi
     Naranja – Kiwi                                                Limon – Naranja – Kiwi

Como se puede observar, el orden de las frutas no es relevante ya que el valor del conjunto: (Pera – Manzana) es el mismo que el del conjunto: (Manzana – Pera), esto es: hay Manzana y hay Pera.

También se puede observar que tenemos 32 valores diferentes, que son todos los subconjuntos posibles que se pueden obtener a partir del CONJUNTO REFERENCIAL que contiene a todas las frutas. En general, a partir de un tipo referencial de N valores posibles se forman conjuntos con 2^N valores. Por tanto, en nuestro caso para los 5 valors posibles del tipo enumerado Frutas tenemos 32 subconjuntos posibles.

La utilización de los conjuntos tiene como ventajas adicionales que todos los lenguajes disponen de las operaciones básicas entre conjuntos: Unión, Intersección, etc. Así, para actualizar las existencias de frutas de nuestra frutería cuando llega un camión con nuevas frutas, sólo se necesita realizar la operación UNIÓN entre los conjuntos de frutas de la frutería y frutas del camión.

Una aplicación típica de los conjuntos aparece en el tratamiento del estado y las alarmas de los equipos conectados a un computador (modem, impresora, ratón, teclado, etc.). En general, lo que más nos interesa saber es si hay o no hay conexión, sobretemperatura, error de paridad, atasco, etc. A lo largo de este capitulo se detallan más ejemplos de la utilidad de los conjuntos.

9.6.1 Definición de tipos conjunto
Para manejar este tipo de datos en Modula-2 se pueden definir tipos conjunto. La definición de un tipo conjunto se realiza tomando como base o conjunto referencial el conjunto de todos los valores posibles de un tipo ordinal definido previamente. Por analogía con el vocabulario de conjuntos, llamaremos tipo referencial a este tipo base. En el ejemplo anterior el tipo referencial es el tipo Frutas.

La definición de un tipo conjunto en Modula-2 utiliza las palabras clave SET OF, seguidas de la indicación del tipo referencial en el que está basado, de la siguiente forma:

TipoConjunto = SET OF TipoReferencial

Por ejemplo:

TYPE Existencias = SET OF Frutas;
         Caracteres = SET OF CHAR;
          Letras = SET OF [ "A" .. "Z" ];
          Digitos = SET OF RangoDigitos;
          Errores = SET OF (Exceso, Defecto);
          Mezcla = SET OF Color;
          Tabla = SET OF [ 1 .. 49 ];

Como se puede observar, en la definición se pueden utilizar tipos ordinales definidos anteriormente en el programa o tipos predefinidos del lenguaje, o enumeraciones o subrangos de cualquiera de ellos introducidos en la propia definición. No se pueden declarar conjuntos sobre valores reales, ya que no es un tipo ordinal y no se pueden declarar subrangos sobre ellos.

La definición del lenguaje Modula-2 establece que el compilador determine limitaciones para el tipo referencial. La limitación es que los ordinales del rango de valores del referencial deben estar comprendidos entre cero y un límite positivo particular para cada compilador. Este límite suele permitir definir conjuntos sobre el tipo CHAR, pero no es posible, en general, definir conjuntos directamente sobre los tipos CARDINAL o INTEGER.

Formalmente, la declaración de un tipo conjunto tiene la siguiente sintasix:

Tipo_conjunto ::= SET OF Tipo_simple

Tipo_simple ::=
         Identificador_de_tipo |
         Tipo_enumerado |
         Tipo_subrango

9.6.2 Construcción de conjuntos
A partir de la definición de los tipos conjuntos se pueden hacer declaraciones y posteriormente trabajar con ellas. Por ejemplo:

VAR mercadoHoy: Existencias;
        boleto: Tabla;
        listaLetras: Letras;
        paleta: Mezcla;

Para asignar valores a las variables es necesario, obviamente, indicar el valor de tipo conjunto que se desa asignar. Una forma de expresar un valor de tipo conjunto es indicar expresamente cuáles son sus elementos. Esto se hace mediante una expresión de construcción de conjunto, en la que se enumeran, entre llaves ({}), los elements del conjunto a incluir, separados por comas (,) y precedidos por el identificador del tipo del conjunto al que pertenecen los elementos. Por ejemplo:

mercadoHoy := Existencias{ Kiwi, Limon, Pera };
…..
listaLetras := Letras{ };
…..
paleta := Mezcla{ Rojo };

Si no se indica ningún elemento dentro de los corchetes, el conjunto asignado es el conjunto vacío. La lista de elementos puede ser tan larga como sea necesario y el orden en que se relacionan es irrelevante. Para abreviar, si hay varios elementos consecutivos basta con indicar el primero y el último separados por dos puntos seguidos (..). Por ejemplo:

listaLetras := Letras{ "A", "D", "H" .. "M" };
…..
boleto := Tabla{ 1 .. 5, 10 .. 15, 20 .. 25 };

En el caso de que el ordinal del primer elemento sea superior al del último, el rango que se está indicando es un rango vacío. Por ejemplo:

paleta := Mezcla{ Azul .. Rojo };

asigna el conjunto vacío a la variable paleta. Como se puede obsevar existen distintas formas de indicar el mismo conjunto. Por ejemplo:

mercadoHoy := Existencias{ Limon, Naranja .. Kiwi, Pera };
mercadoHoy := Existencias{ Pera, Limon, Naranja, Kiwi };
mercadoHoy := Existencias{ Pera, Limon .. Kiwi };
mercadoHoy := Existencias{ Pera, Limon .. kiwi, Naranja };

Todas estas expresiones dan como resultado el mismo conjunto. En el último caso, aunque se repite la inclusión del elemento Naranja, este sólo aparece una vez en el conjunto asignado a la variable mercadoHoy.

Las versiones modernas de Modula-2 permiten que los valores de los elementos del conjunto que se construye puedan ser dados en forma de expresión. Por ejmplo:

boleto := Tabla{ x .. (x+y) };

Esta misma manera de formar conjuntos también se puede utilizar para declarar constantes de tipo conjunto. Por ejemplo:

CONST
           Verde = Mezcla{ Azul, Amarillo };
           todasLasFrutas = Existencias{ Pera .. Kiwi };
           boletoInicial = Tabla{ };

En este caso, si se usarán expresiones para los elementos del conjunto, deberán ser expresiones constanes.

Las reglas BNF para las expresiones de construcción de conjuntos son las siguientes:

Construcción_de_conjunto ::=
      Identificador_de_tipo { Lista_de_elementos }
Lista_de_elementos ::= [ Elementos {, Elementos }]
Elementos ::= Expresión_constante [.. Expresión_constante]

Curso de Filosofía elemental (30)

XXXVIII

Nacionalidad y federación

 

184. LA FEDERACIÓN HISTÓRICA EN LA GÉNESIS DE LAS NACIONALIDADES

   Las nacionalidades que hoy conocemos no son formaciones espontáneas por el mero crecimiento de la familia. Es cierto que si observamos aisladamente las naciones tal como ahora existen tenemos la impresión de que cada una de ellas –dentro de unas fronteras ya antiguas, con una lengua común, con costumbres peculiares– es como una unidad natural, casi de la misma índole que la unidad geográfica de una isla. Pero si ahondamos en la historia descubrimos que este conjunto, que hoy nos parece relativamente homogéneo, es el resultado de la fusión de elementos muy diversos, fusión lograda muchas veces por la guerra; otras, por pactos que han hecho posible una convivencia unificadora.
   Sobre una unidad, al principio débil, la convivencia ha ido lentamente, al correr de los siglos, limando diferencias, estableciendo nuevos lazos, dando ocasión a la realización de ideales comunes. Con frecuencia esta unidad espiritual y de relación (comercial, económica, cultural) que se va produciendo por la convivencia tiene por único origen una común fe religiosa, y a menudo también cristaliza en una unidad política cuando sobre los pueblos que conviven aparece un peligro común. Así, la conciencia nacional de Alemania surgió con ocasión de la invasión napoleónica.
   En general, cuando varios pueblos políticamente independientes conviven en empresas comunes durante varios siglos, alcanzan un punto de evolución en el cual la unión política se hace posible; de esta unión resultará una más intensa convivencia y, con ella, el origen de una nación más amplia, en la que las diferencias de los componente no quedan borradas, pero sí articuladas en un sistema superior. Se entiende por federación, en un sentido amplio, la forma de unión política que no borra las diferencias y derechos de cada uno de los componentes. La federación es, sin duda, uno de los métodos más deseados y eficaces en la configuración de las naciones, mucho más deseable que la conquista, por ejemplo. La importancia del factor religioso en la formación de federaciones y de naciones queda patentizado en el carácter originario de las grandes civilizaciones históricas (la Cristiandad, el Islam, etc.).

   Como ejemplos de proceso federativo en la génesis de pueblos y nacionalidades se pueden citar los siguientes:

a) En el mundo antiguo, las anfictionías de las ciudades griegas, que fueron un medio decisivo en la configuración del espíritu nacional helénico. Eran asociaciones de ciudades fundadas en vínculos originariamente religiosas (anfictiones se llamaba a aquellos que viven alrededor de un santuario), que adquirían en seguida un carácter político.

b) En la Edad Media, la incorporación monárquica –federación en el fondo– fue el origen de muchas naciones europeas: León y Castilla, Aragón y Cataluña; luego Castilla, Aragón y Navarra hicieron, por ejemplo, la unidad nacional española.

185. ESTRUCTURA FEDERATIVA INTERNA DE LAS ANTIGUAS NACIONALIDADES. SU CARÁCTER ABIERTO A MÁS AMPLIAS FEDERACIONES

   El proceso que a lo largo de la Edad Media creó las antiguas nacionalidades europeas fue un proceso esencialmente federativo. Pero puede decirse también que la vida y constitución interna de los pueblos fue durante aquellos siglos y los principios de la Edad Moderna una coexistencia federal. Cada pueblo de España, por ejemplo, se concebía como una comunidad de familias y vecinos, y contaba con una propiedad comunal, que se consideraba como patrimonio de todas esas familias, inalienable porque no pertenecía sólo a la generación presente, sino también a las venideras. Cada municipio poseía además su organización jurídica y sus ordenanzas propias, adaptadas a sus costumbres y modos de vida. A lo largo de las luchas de la Reconquista, todos los pueblos se consideraban, como por un derecho natural, independientes en lo que convenía al gobierno interior o municipal, pues los reyes y señores feudales se limitaban a exigir las pechas o tributos y la aportación personal para la guerra. El Estado, en el concepto moderno de una estructura nacional uniforme de la que todo organismo inferior recibe vida delegada, no existió en la Antigüedad ni en la Edad Media.
   Federal fue asimismo la génesis o formación de lo que hoy llamamos España –la unión histórica de los pueblos peninsulares–, como federal es su escudo, constituido por la agrupación de cuatro diferentes bajo una misma corona. Esta federación se realizaba a veces a favor de la política matrimonial de las casas reinantes; otras, a causa del proceso de homogeneización y convivencia que entre los pueblos se operaba y de sus consiguientes convivencias históricas. La no realización de alguno de estos dos factores dificultaba a veces la federación; pero ésta, por uno u otro camino, se verificaba al cabo o encontraba, al menos, un cauce para ello, abierto siempre a una progresiva federación.

186. EL NACIONALISMO MODERNO, INMOVILIZADOR EN ESTADOS UNITARIOS Y CERRADOS

   La pérdida de la unidad religiosa, primero, como consecuencia de la escisión protestante, y la Revolución francesa, más tarde, truncaron el proceso de federación en los pueblos cristianos, destruyendo la interna estructura federativa de la sociedad y estableciendo las nacionalidades como unidades absolutas, es decir, indivisibles e infederables.
   En la Revolución francesa se pretendió constituir la Nación en torno a la llamada Voluntad General (Asamblea Constituyente de los ciudadanos) y dar una Constitución unitaria y centralista al país, que acabase con las variedades políticas internas de origen histórico. Francia sería concebida desde entonces como un Estado unitario, una unidad cerrada y absoluta, sin variedades ni diferencias interiores ni apertura hacia una unión exterior. Revoluciones semejantes se operaron en los demás países europeos a raíz de las guerras napoleónicas (en España, las Cortes de Cádiz), y las naciones americanas, que por entonces se separaban de la Corona española, adoptaron también el régimen constitucional de la Revolución francesa. La razón de Estado y el interés nacional fueron desde ese momento principios inapelables en la política de cada nación y crearon abismos entre las distintas naciones europeas, partes antaño de una misma cristiandad, desencadenando al mismo tiempo las terribles guerras nacionalistas de nuestro siglo.

187. TENDENCIAS FEDERALISTAS DE LA ACTUALIDAD

   Aunque un verdadero y eficaz federalismo resulte imposible a partir de la existencia de Estados nacionales unitarios, es decir, carentes de una previa estructura federal en el seno de los mismos, las desastrosas guerras a que condujeron las pasiones nacionalistas han despertado en la Europa de hoy un renacer del federalismo en el anhelo general de una superación orgánica de las actuales nacionalidades. Incluso han surgido, a partir de 1948, instituciones de carácter europeísta, fruto de esa necesidad federativa que tan fuertemente se ha sentido.
   Cabe señalar, ante todo, la O.E.C.E. (Organización Europea de Cooperación Económica), que ha desempeñado una gran función de aproximación entre las naciones europeas, aun cuando su constitución obedeciera a una finalidad circunstancial: la administración de la ayuda norteamericana del Plan Marshall. Pero la primera realidad de carácter verdaderamente europeísta fue el Consejo de Europa, de función más bien consultiva. Muy pronto se constituyeron otras organizaciones más especializadas, de carácter económico e industrial; organizaciones que, sin duda, están tejiendo lazos cada vez más estrechos entre muchos países europeos. Son éstas: la Comunidad del Carbón y del Acero (C. E. C. A.) (1951), el Euratom (Comunidad Europea de Energía Nuclear) (1958) y el Mercado Común (1959).
   El Mercado Común comenzó a discutirse en 1955, pero el tratado fundacional se firmó en Roma el 26 de marzo de 1957, para ser puesto en aplicación el 1 de enero de 1959. El Mercado Común proclama cinco libertades: 1ª) Libertad de circulación de mercancías (abolición de aduanas, supresión de contingentes de exportaciones e importación). 2ª) Libertad de circulación de capitales. 3ª) Libertad de circulación de personas (es decir, de la mano de obra). 4ª) Libertad de prestación de servicios. 5ª) Libertad de establecimiento. Estas libertades se establecen a través de los seis países de la llamada Pequeña Europa.

 

XXXIX

Sociedad internacional

 

188. RELACIONES ENTRE LOS ESTADOS

   La Europa medieval, y aun la moderna hasta las guerras de religión, formaba, al menos teóricamente, una unidad espiritual, y aun política, que era la cristiandad. Aunque la desgarraran mil rivalidades interiores, y aunque los príncipes llegaran a ser, de hecho, soberanos independientes, se mantuvo siempre la idea del primado espiritual del Papa y del primado temporal del Emperador (Sacro Imperio Romano Germánico). La cristiandad oraba así <<por la paz y concordia entre los príncipes cristianos>>, y éstos se unían en empresas comunes a la cristiandad (como las Cruzadas, la Reconquista, los mismos Concilios), manteniéndose un recurso de conciliación en las luchas y un cauce posible de futura integración.
   A partir de la paz de Westfalia (1645), que señala, con el agotamiento de las armas españolas, la pérdida de la unidad religiosa, los Estados se constituyen en soberanos, y la cristiandad, como unidad de fe y (hasta cierto punto) de estructura, se sustituye por una coexistencia de Estados absolutamente independientes. La paz se sostiene sólo por el llamado equilibrio europeo de fuerzas. La Revolución, más tarde, consumó este principio de nacionalidades cerradas al negar todo carácter religioso al poder y hacerlo depender de la <<Voluntad Nacional>>; al hacer también de cada nación una estructura centralizada, cuya única ley y principio de organización corresponden al Estado.
   Los Estados nacionales no pueden, sin embargo, vivir en aislamiento, sino que han de convivir y relacionarse entre sí. El nacionalismo extremo de los sistemas totalitarios (1930 a 45) propugnó el ideal de la autarquía, según el cual la economía y la política interior y exterior de cada nación debe tender a bastarse a sí misma sin ninguna forma de dependencia ni aun de intercambio respecto del exterior. Tal ideal es contrario a la comunidad natural de los humanos, a la comunidad en una fe y a la variedad complementaria de tierras, productos y caracteres en los diversos países. Las relaciones entre los Estados, como personas jurídicas dentro de un Derecho Internacional Público, es algo necesario e ineludible que deriva del orden natural.

189. EL DERECHO DE GENTES

   Al igual que las relaciones entre personas individuales, las relaciones entre Estados deben someterse a la moral, ya que los hombres todos participan de una naturaleza común y están sometidos a una misma ley natural. Fue Nicolás de MAQUIAVELO, en la época del Renacimiento, quien primeramente trató de sustraer el orden político del dominio de la moral natural, suponiendo que rige en él una mera técnica de gobierno autónoma, y que nada debe prevalecer a la <<razón de Estado>> y a la voluntad del Príncipe.
   Ese derecho común a todos los hombres fue llamado por los antiguos juristas romanos ius gentium o derecho de gentes. <<Se llama derecho de gentes a lo que la razón natural estableción entre todas las gentes>>. (VITORIA, De Iuris, I, 18). Es el derecho positivo en lo que tiene de más elemental por dimanar de la naturaleza humana de un modo inmediato, y resulta, por lo mismo, común a las legislaciones de los diversos pueblos y civilizaciones. Es también el derecho mínimo y elemental que supervive entre los pueblos en caso de guerra. Por ejemplo, atravesar un territorio y aun establecerse en él siempre que no entrañe daño para sus moradores, cuidar y mantener a los heridos y prisioneros de guerra, etc., son exigencias del derecho de gentes.
   Ahora bien, dado el principio moderno de la soberanía de los Estados, ¿a quién recurrir en caso de conflicto de intereses? ¿Qué normas de común aceptación podrán resolverlo? Según algunos autores sólo cabe la guerra, cuya declaración es un hecho anexo a la soberanía estatal.
   Corresponde a los españoles la gloria de haber desarrollado la doctrina del Derecho de Gentes aplicándola al Derecho Internacional como la base de su obligatoriedad universal. El Siglo de Oro español (XVI-XVII) prolonga, dentro de la Edad Moderna, el pensamiento cristiano y mantiene, dentro de la gran pérdida de la unidad religiosa con que se inicia esa Edad, la idea de un orden natural engendrado por la ley divina. A partir de los pensadores españoles de esta época (Vitoria, Vives, Suárez) la noción de Derecho de Gentes pierde su acepción de derecho positivo emanado directamente del derecho natural (lo común a todas las legislaciones positivas) para hacerse equivalente –como lo es hoy– a Derecho Internacional Público.
   El dominico Francisco de VITORIA (1480-1546) establece que el Derecho de Gentes es un cuerpo de leyes universales aplicable a las relaciones entre los pueblos, y que su obligatoriedad no surge de la convención humana, sino de la ley divina (De potestate civili). El P. SUÁREZ (1548-1617), jesuita, el más ilustre filósofo clásico español, autor de las Disputaciones metafísicas, en su Tratado de las leyes, es quien, antes de Grocio, sienta las bases del Derecho Internacional Público moderno. Aunque cada Estado sea soberano y libre, y constituya en sí una comunidad perfecta, es al propio tiempo una parte de la Humanidad, y no puede prescindir de las demás.
   El derecho internacional positivo, y no meramente abstracto y teórico, es hoy ya una realidad efectiva.

190. EL DERECHO A LA GUERRA

   Desde el punto de vista del Derecho Internacional, la guerra es una situación de vilencia entre dos o más Estados, acompañada de ruptura de relaciones pacíficas.
   Las guerras se clasifican en ofensivas, defensivas, preventivas y de intervención (que tienen lugar cuando un Estado se decide por un beligerante ya en guerra).
   El derecho a la guerra es el tema de la célebre Relección de Vitoria De iure belli. En ella se reconoce, efectivamente, el derecho a la guerra, pero sólo en un caso a saber: cuando se trata de restaurar un derecho injustamente violado –una iniura–, siempre que la restauración no pueda conseguirse por medios pacíficos.
   El fin de la guerra es la paz, que sólo es tal cuando reina la justicia.
   No es precisamente la guerra defensiva (cuando se es atacado) el único título del derecho a la guerra. Incluso la guerra defensiva puede ser injusta si el ataque puede ser detenido por negociaciones pacíficas. En cambio, una guerra ofensiva puede ser justa cuando por medio de ella se pretende restaurar el derecho violado. Así, si los españoles tienen derecho a hacer la guerra en América –dice el P. Vitoria–, no es por la negativa de los indios a admitir la ley evangélica, puesto que la fe no puede imponerse por la fuerza. Se funda en el hecho de que los indios habían negado a los españoles los derechos que les correspondían, como miembros de la comunidad universal, a residir en América, comerciar con los indígenas, propagar el Evangelio (sin imponerlo) y naturalizarse en dichas tierras.
   La distinción entre guerras justas e injustas del P. Vitoria es negada por otro clásico español, Luis Vives, para quien la guerra nunca es justa, aunque pueda ser necesaria.

191. CONDICONES DE LA GUERRA JUSTA

   Se admitia el derecho a la guerra o se considere a ésta como una necesidad, los autores clásicos enumeran cinco condiciones para que su declaración sea justa o, al menos, un hecho admisible.
   1ª Que sea declarada por autoridad legítima. Según el P. Vitoria, la declaración de guerra es uno de los asuntos que no debe resolver el rey sin anuencia de la representación de los súbditos más sabios y virtuosos, por ser cuestión que afecta a todo el cuerpo de la sociedad civil o república.
   2ª Que se haga por una causa justa (restauración de un derecho violado injustamente).
   3ª Que tenga esperanza de éxito.
   4ª Que pueda ordenarse al bien común internacional.
   5ª Que se hayan agotado todos los medios pacíficos.

192. SOCIEDADES DE NACIONES Y TRIBUNALES ARBITRALES

   Después de desaparecer históricamente tras las guerras de religión el ideal de una unidad estructural de la Cristiandad (concepción medieval del Pontificado y el Imperio), se intentó todavía establecer un orden supranacional europeo sobre bases religiosas en la Santa Alianza, que puso término a las guerras de Napoleón. Posteriormente a ese ensayo se han iniciado otros, pero ya sobre bases religiosamente neutras.
   La extensión y gravedad de la Guerra Europea 1914 inspiró a los vencedores el establecimiento de una Sociedad de las Naciones, con garantías mutuas para la independencia e integridad de todos los Estados, grandes o pequeños. Su vida fue efímera, puesto que veinte años más tarde estallaba otra conflagración de proporciones y ámbito superiores (Guerra Mundial de 1939-45).
   Las potencias aliadas, vencedoras también en esta segunda gran guerra, fundaron en la Conferencia de San Francisco (1945) la Carta y Organización de las Naciones Unidas (O. N. U.), que ha llegado a agrupar a todos los países en un sistema de relación permanente. Son órganos de la O. N. U.:
   1º. La Asamblea General.
   2º. El Consejo de Seguridad.
   3º. El Consejo Económico y Social.
   4º. El Consejo de Administración Fiduciaria.
   5º. El Tribunal Internacional de Justicia.
   6º. La Secretaria Permanente.
   7º. Organismos especializados (como la U. N. E. S. C. O. para asuntos culturales y de enseñanza).

   Posteriormente la O. N. U. ha llegado a poseer un pequeño ejército o policía internacional que ha intervenido con eficacia en conflictos locales diversos (Corea, Chipre, etc.). Sin embargo, la situación mundial que viene presidiendo la O. N. U. en sus más de veinte años de existencia no ha dejado nunca de ser statu quo armado entre las grandes potencias occidentales y las del grupo soviético.
   Con independencia de esta Organización existen antiguos y acreditados Tribunales de Arbitraje a los que se someten las potencias por propia voluntad para dirimir sus litigios. Así el Tribunal de La Haya (Cour Permanet d’Arbitrage), fundado en 1899 y subsistente todavía.

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

8.2.5 Ejemplo: Tabular la serie de Fibonacci
El procedimiento de imprimir tabulando desarrollado en el ejemplo de imprimir tabla de números primos, puede aprovecharse para imprimir en forma de tabla otras series de valores. Por ejemplo, podemos tabular la serie de Fibonacci, que ya se describió en el Tema 6. Lo que necesitamos ahora es sustituir las sentencias de escritura usadas en aquel ejemplo

                   WriteInt( termino, 10 );
                   WriteLn

por una llamada al procedimiento

                   ImprimirTabulando( termino )

y, por supuesto, copiar en la parte de declaraciones la definición del procedimiento de tabular, y añadir al comienzo del programa la inicialización del contador columnas. El programa completo aparece listado a continuación:

(*****************************************************************************************
*
*    Programa: Fibonaci
*
*    Descripción:
*      Este programa imprime todos los términos de la serie
*      de Fibonaci, dentro del rango de valores positivos del tipo INTEGER:
*        1 .. MAX(INTEGER)
*      Se imprime tabulando a cuatro columnas
*
*****************************************************************************************)
MODULE Fibonaci;

(*========================================================================================
    IMPORTACIÓN Y DECLARACIONES DEL PROGRAMA
========================================================================================*)
  FROM InOut IMPORT WriteInt, WriteLn;

  VAR
    columna: INTEGER;            (* columna a imprimir *)
    termino: INTEGER;            (* término de la serie *)
    anterior: INTEGER;            (* término anterior *)
    aux: INTEGER;

  PROCEDURE ImprimirTabulando( k: INTEGER );
  (* Imprimir ‘k’ tabulando a 4 columnas de 10 caracteres *)
  BEGIN
    IF columna > 4 THEN
      columna := 1;
      WriteLn
    END;
    WriteInt( k, 10);
    INC( columna )
  END ImprimirTabulando;

BEGIN

(*=======================================================================================
    PARTE EJECUTABLE DEL PROGRAMA
=======================================================================================*)
  (*– iniciar la tabulación –*)
    columna := 1;
  (*– generar el comienzo de la serie –*)
    anterior := 0;
    termino := 1;
    ImprimirTabulando( anterior );
    ImprimirTabulando( termino );
  (*– generar el resto de la serie –*)
    WHILE MAX(INTEGER)-termino >= anterior DO
      aux := termino + anterior;
      anterior := termino;
      termino := aux;
      ImprimirTabulando( termino )
    END;
  WriteLn
END Fibonaci.

El resultado de la ejecución en una máquina con números enteros de 16 bits ( MAX(INTEGER) = 32767 ) es el siguiente:

   0         1         1         2
   3         5         8        13
  21        34        55        89
 144       233       377       610
 987      1597      2584      4181
6765     10946     17711     28657


8.2.6 Desarrollo para reutilización
Para aplicar de manera eficaz las técnicas de reutilización de software es preciso pensar en las posibles aplicaciones de un cierto subprograma en el momendo de especificarlo, con independencia de las necesidades particulares del programa que se está desarrollando en ese momento.

Esta estrategia de desarrollo tiene ventajas e incovenientes. La principal ventaja es que se amplía el conjunto de aplicaciones en que se podrá reutilizar más adelante el subprograma que se está desarrollando ahora. Su principal incoveniente es que será más costoso hacer el desarrollo del subprograma planteado como operación de uso general, que planteado como operación particular, hecha a medida del programa que lo utiliza en este momento.

En el ejemplo del árbol de Navidad, nos encontramos con que al buscar analogías entre distintas operaciones para resolverlas con un subprograma común, estábamos generalizando al mismo tiempo dichas operaciones, estableciendo parámetros que permitían particularizarla para cada caso.

En el caso de subprogramas planteados simplemente con el fin de limitar el nivel de detalle en una sección determinada de un programa, no se siente esta necesidad de generalizar,  es más fácil de plantear la operación particularizada para las necesidades de ese momento.

En el ejemplo de tabular las series de valores, se ha planteado de entrada la operación de tabulación de manera que impone tanto el número de columnas como el ancho de cada una. Si queremos escribir un subprograma de tabulación de resultados que sea realmente de uso general, convendría dejar libertad para fijar las características del listado como parámetros modificables, que se puedan particularizar para cada caso.

De esta manera se podría haber ampliado el campo de aplicación del subprograma de tabular si el número de columnas y el ancho de cada una fuesen parámetros variables. Además, para simplificar el uso del procedimiento de tabulación se podrían agrupar todas las acciones de inicializacióon en una sola acción abstracta, invocada como subprograma, en que se fijen los parámetros particulares del listado. La especificación de esta acción inicial podría ser:

         PROCEDURE IniciarTabulacion( columnas, ancho: INTEGER );
         (* Iniciar la tabulación on los parámetros indicados *)

Para ilustrar esta técnica, modificaremos el programa de tabular la serie de Fibonacci de acuerdo con lo expuesto, decidiendo el formato del listado (7 columnas de 6 caracteres) desde el programa principal. El programa modificado es el siguiente:

(******************************************************************************************************************************************
*
*              Programa: Fibonac2
*
*              Descripción:
*
*                Este programa imprime todos los términos de la serie de Fibonaci, dentro del rango de valores positivos
*                del tipo INTEGER: 1 ..MAX(INTEGER). Se imprime tabulando a siete columnas
*
*
*
******************************************************************************************************************************************)

MODULE Fibonac2;
   FROM InOut IMPORT WriteInt, WriteLn;

   VAR
      TABcolumna: INTEGER;                                 (* columna a imprimir *)
      TABultima: INTEGER;                                  (* última columna *)
      TABancho: INTEGER;                                   (* ancho de cada columna *)

   VAR
      termino: INTEGER;                                  (* término de la serie *)
      anterior: INTEGER;                                       (* término anterior *)
      aux: INTEGER;              

PROCEDURE IniciarTabulacion( columnas, ancho: INTEGER );
   (* Iniciar la tabulación con los parámetros indicados *)
BEGIN
   TABultima := columnas;
   TABancho := ancho;
   TABcolumna := 1;
END IniciarTabulacion;

PROCEDURE ImprimirTabulando( k: INTEGER );
  (* Imprimir ‘k’ tabulando a TABultima columnas de TABancho caracteres *)
BEGIN
   IF TABcolumna > TABultima THEN
      TABcolumna := 1;
      WriteLn
   END;
   WriteInt( k, TABancho );
   INC( TABcolumna );
END ImprimirTabulando;

BEGIN
  (*– inicializar la tabulación –*)
      IniciarTabulacion( 7, 6 );
  (*– generar el comienzo de la serie –*)
     anterior := 0;
     termino := 1;
     ImprimirTabulando( anterior );
     ImprimirTabulando( termino );
  (*– generar el resto de la serie –*)
     WHILE MAX(INTEGER)-termino >= anterior DO
        aux := termino + anterior;
        anterior := termino;
        termino := aux;
        ImprimirTabulando( termino )
     END;
   WriteLn
END Fibonac2.

y el resultado de la ejecución es el siguiente:

    0     1     1     2     3     5     8
   13    21    34    55    89   144   233
  377   610   987  1597  2584  4181  6765
10946 17711 28657

Conviene comentar algunos aspectos de estilo utilizados en este ejemplo. Como se emplean variables globales para la tabulación, se ha procurado separar la declaración de estas variables de la declaración de las variables propias del programa principal. Además, se han nombrado todas las variables usadas por los procedimientos de tabulación, empezando sus nombres con el prefijo TAB, para evitar confusiones.

En realidad esto es un recurso artificioso para separar las distintas partes del programa. Este recurso se ha utilizado de momento porque todavía no se ha presentado el mecanismo de definición de módulos en Modula-2. Usando el mecanismo de módulos se pueden desarrollar subprogramas reutilizables, escritos en forma realmente independiente, de una manera mucho más adecuada.

8.2.7 Desarrollo ascendente
La metodología de desarrollo ascendente (en inglés "Bottom-Up") consiste en ir creando subprogramas que realicen operaciones significativas de utilidad para el programa que se intenta construir, hasta que finalmente sea posible escribir el programa principal, de manera relativamente sencilla, apoyándose en los subprogramas desarrollados hasta ese momento.

La técnica tiene una cierta analogía con el desarrollo de subprogramas pensando en su reutilización posterior. Al hablar de desarrollo para reutilización se ha dicho que los subprogramas podían surgir en el proceso de refinamiento de un programa concreto, al identificar ciertas operaciones, pero debían definirse pensando en futuras aplicaciones. En este caso se trata de que la identificación de las operaciones no surga de un proceso de descomposición o refinamiento de alguna acción particular, sino simplemente pensando en el programa que se desarrolla, casi como una más de las posibles aplicaciones futuras.

Como ejemplo desarrollaremos un programa que opere como una calculadora, pero con fracciones. Una fracción se compondrá de un numerador y un denominador enteros. La calculadora podrá sumar, restar, multiplicar o dividir fracciones, y los resultados los presentará con la fracción en forma simplificada, dividiendo por los factores comunes al numerador y al denominador.

Con independencia de los detalles de operación de la calculadora, pueden desarrollarse incialmente procedimientos útiles para esta aplicación; en particular procedimientos para realizar cálculos con fracciones, así como leerlas o imprimirlas. En el siguiente listado se presenta una colección apropiada de procedimientos, sobre los cuales se podrá desarrollar luego el programa principal de la calculadora

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

   . . . . . . . .

   PROCEDURE ReducirFraccion( VAR n, d: INTEGER );
   (* Simplificar la fracción n/d *)
      VAR divisor: INTEGER;
   BEGIN
      divisor := 2;
      WHILE (divisor <= n) AND (divisor <=d) DO
         WHILE (n MOD divisor = 0) AND (d MOD divisor = 0) DO
            n := n DIV divisor;
            d := d DIV divisor
         END;
         INC( divisor )
      END
   END ReducirFraccion;

   PROCEDURE SumarFracciones( n1, d1, n2, d2: INTEGER; VAR n3, d3: INTEGER );
   (* n3’/d3′ = n1/d1 + n2/d2 *)
   BEGIN
      n3 := n1*d2 + n2*d1;
      d3 := d1*d2;
      ReducirFraccion( n3, d3 )
   END SumarFracciones;

   PROCEDURE RestarFracciones( n1, d1, n2, d2: INTEGER; VAR n3, d3: INTEGER );
   (* n3’/d3′ = n1/d1 – n2/d2 *)
   BEGIN
      SumarFracciones( n1, d1, -n2, d2, n3, d3 )
   END RestarFracciones;

   PROCEDURE MultiplicarFracciones( n1, d1, n2, d2: INTEGER; VAR n3, d3: INTEGER );
   (* n3’/d3′ = n1/d1 * n2/d2 *)
   BEGIN
      n3 := n1*n2;
      d3 := d1*d2;
      ReducirFraccion( n3, d3 )
   END MultiplicarFracciones;

   PROCEDURE DividirFracciones( n1, d1, n2, d2: INTEGER; VAR n3, d3: INTEGER );
   (* n3’/d3′ = n1/d1 / n2/d2 *)
   BEGIN
      n3 := n1*d2;
      d3 := d1*n2;
      ReducirFraccion( n3, d3 )
   END DividirFracciones;

   PROCEDURE LeerFraccion( VAR n, d: INTEGER );
   (* Lee la fracción y la simplifica *)
   BEGIN
      ReadInt( n ); Write( ‘/’ );
      ReadInt( d ); Write( ‘ ‘ );
      ReducirFraccion( n, d )
   END LeerFraccion;

   PROCEDURE  EscribirFraccion( n, d: INTEGER );
   (* Imprime la fracción como n’/d’ *)
   BEGIN
   WriteInt( n, 1 );
   Write( ‘/’ );
   WriteInt( d, 1 )
   END EscribirFraccion;

Contando con esos procedimientos se puede ahora desarrollar el programa principal de la calculadora, que se presenta en el programa Fraccion. En este ejemplo se supone que cada operación se realiza entre un valor acumulado y un nuevo operando. La operación se inicia con una tecla de operación, y a continuación se introduce el valor del operando. Las operaciones previstas son +, -, *, /. Además habrá teclas de operación para imprimir el resultado acumulado (=) y para iniciar una nueva serie de operaciones (espacio en blanco). La tecla F marcará el fin del funcionamiento del programa.

(********************************************************************************
*
*   Programa: Fraccion
*
*   Descripción:
*      Este programa es una calculadora que suma, resta, multiplica o
*      divide fracciones
*
*********************************************************************************)
MODULE Fraccion;

. . . . <<definiciones de los procedimientos>> . . . . 

   VAR
      num, den: INTEGER;                    (* acumulador *)
      nn, dd: INTEGER;                         (* operando *)
      operacion: CHAR;                         (* tecla de operación *)

PROCEDURE LeerOperacion( VAR op: CHAR );
(* Lee la tecla de operación *)
BEGIN
   Write( ‘?’ ); Read( op );
   op := CAP( op );
   Write( op ); Write( ‘ ‘ );
END LeerOperacion;

BEGIN
   num := 0;
   den := 0;
   LeerOperacion( operacion );
   WHILE operacion <> ‘F’ DO
      IF operacion = ‘+’ THEN
         LeerFraccion( nn, dd );
         SumarFracciones( num, den, nn, dd, num, den )
      ELSIF operacion = ‘-‘ THEN
         LeerFraccion( nn, dd );
         RestarFracciones( num, den, nn, dd, num, den )
      ELSIF operacion = ‘*’ THEN
         LeerFraccion( nn, dd );
         MultiplicarFracciones( num, den, nn, dd, num, den )
      ELSIF operacion = ‘/’ THEN
         LeerFraccion( nn, dd );
         DividirFracciones( num, den, nn, dd, num, den )
      ELSIF operacion = ‘ ‘ THEN         (* Iniciar nuevos cálculos *)
         LeerFraccion( num, den );          (* con num y den *)
      ELSIF operacion = ‘=’ THEN
         WriteString( ‘           ‘);
         EscribirFraccion( num, den );
      ELSIF operacion = ‘F’ THEN
         (* fin de operacion *)
      ELSE
         WriteString( ‘Pulse +, -, *, /, ESPACIO, =, o F’ )
      END;
      WriteLn;
      IF operacion <> ‘F’ THEN
         LeerOperacion( operacion )
      END
   END
END Fraccion.

Un posible ejemplo de la ejecución del programa es el siguiente: 

?  5/20
?=            1/4
?+ 3/5
?- 2/4
?=            7/20
?* 5/6
?=            7/24
?F

En esta aplicación de la técnica de desarrollo ascendente se puede apreciar que el desarrollo inicial de procedimientos para realizar cálculos con fracciones nos ha permitido disponer de una extensión del lenguaje Modula-2, equivalente a definir el tipo FRACCION. Podríamos decir que los procedimientos de cálculo constituyen en conjunto una máquina virtual de operar con fracciones, sobre la cual se ha desarrollado el programa de la calculadora. El desarrollo es ascendente porque primero se han construído los subprogramas, de nivel inferior, y luego el programa que los usa, de nivel superior.

8.3 Programas robustos.

La corrección de un programa exige que los resultados sean los esperados, siempre que el programa se ejecute con unos datos de entrada aceptables. La cuestión que nos ocupa en este momento es: ¿Cuál debe ser el comportamiento del programa si los datos son incorrectos?

Un programa se dice que es robusto si su operación se mantiene en condiciones controladas aunque se le suministren datos erróneos.

8.3.1 Programación a la defensiva
La postura más cómoda desde el punto de vista del programador es declinar toda responsabilidad en el caso de que los datos no sean válidos. Si los datos de entrada no cumplen con los requisitos previstos, el programa puede entonces hacer cualquier cosa. Es frecuente que un programa se escriba sin tener en cuenta la posibilidad de que los datos no sean esperados, pues con ello se simplifica su desarrollo.

Sin embargo esta postura no es admisible en la práctica. Como cualquier otra actividad humana, la escritura y uso de programas está sujeta a errores, y es importante conseguir que las consecuencias de esos errores no sean demasiado graves. Por ejemplo, un programa de gestión de un almacén deberá prever que se notifique la retirada de más cantidad de un producto que la anotada como existencias. En este caso el programa deberá hacer algo "razonable", tal como emitir un mensaje de aviso y obligar a repetir la operación, o simplemente asumir que el valor de las existencias estaba equivocado, y preguntar por el valor real de las existencias, o alguna otra cosa similiar. Lo que no parece "razonable" es anotar un valor negativo para las existencias sin dar ningún aviso, o, en general, seguir operando con valores manifiestamente erróneos que podrían dar lugar más adelante a una parada indeseada del programa ("aborto") al intentar ejecutar alguna instrucción de máquina inadmisible con esos valores.

Otro ejemplo ilustrativo puede ser el de un programa para calcular el valor medio de una serie de datos, dividiendo la suma de todos por el número de datos introducidos. Cabe la posibilidad de que no se introduzca ningún dato, lo cual dará lugar a un intento de realizar una división por cero, que en muchos casos produce el "aborto" del programa. Si el cálculo de la media es lo único que hace el programa, el efecto no parece muy grave, pero si este cálculo es parte de las operaciones que realiza, por ejemplo, el programa de control de una central nuclear, los resultados pueden conducir a una catástrofe mundial. Lo realmente importante es detectar los errores en cuanto se produzcan, y poder así programar operaciones de corrección o tratamiento apropiadas para estas situaciones excepcionales.

La llamada programación a la defensiva (en inglés, "defensive programming") consiste en que cada programa o subprograma esté escrito de manera que desconfíe sistemáticamente de los datos o argumentos con que se le invoca, y devuelva siempre como resultado:

a) el resultado correcto, si los datos son admisibles, o bien
b) una indicación precisa de error, si los datos no son admisibles.

Lo que no debe hacer nunca el programa es devolver un resultado como si fuese normal, cuando en realidad es erróneo, ni "abortar". Esto da lugar a una propagación de errores, que puede aumentar la gravedad de las consecuencias, y hacer que la identificación del fallo del programa resulte mucho más difícil, ya que el efecto se puede manifestar sólo más adelante, en otra parte del programa sin relación aparente con la que falló.

La mejora de la robustez del programa tiene como contrapartida una cierta pérdida de eficiencia, al tener que hacer comprobaciones adicionales. Si la eficiencia es un factor decisivo, algunas de estas comprobaciones pueden eliminarse en la versión final del programa, cuando se determine con seguiridad que el programa no contiene errores.

Consideremos el caso de una función para calcular el factorial de un número:

        n! = 1 x 2 x 3 x … x n

El código de la función podría ser:

PROCEDURE Factorial( n: INTEGER ): INTEGER;
   VAR k, f: INTEGER;
BEGIN
   f := 1;
   FOR k := 2 TO n DO
      f := f * k
   END;
   RETURN f
END Factorial;

Esta función no es robusta. El factorial sólo está definido para valores de n positivos, incluido el cero, cuyo factorial, por convenio vale 0! = 1. Para valores negativos el factorial no está definido, y sin embargo la función codificada en la forma anterior devuelve resultado 1, indistinguible del resultado correcto correspondiente a 0! o 1!.

Lo que hace falta es devolver una indicación clara de error para argumentos negativos. Una forma de hacerlo podría ser devolver un resultado cero o negativo en estos casos, ya que ese resultado no puede coincidir con el factorial de ningún número. La función se recodificaría en la forma:

PROCEDURE Factorial( n: INTEGER ): INTEGER;
   VAR k, f: INTEGER;
BEGIN
   IF n < 0 THEN
      f := 0
   ELSE

      f := 1;
      FOR k := 2 TO n DO
         f := f * k
      END;
   END;
   RETURN f
END Factorial;

En realidad la función sigue sin ser del todo robusta, ya que no se ha previsto la posibilidad de que el factorial que se intenta calcular exceda del rango admisible de valores de tipo INTEGER. Esto ocurre fácilmente incluso para valores relativamente pequeños del argumento (p.ej., 20! = 2432902008176640000). En la sección siguiente se presenta una versión más robusta de esta función.

8.3.2 Tratamiento de excepciones
Ante la posibilidad de errores en los datos con que se opera, hay que considerar dos actividades diferentes:

1) Detección de la situación de error.
2) Corrección de la situación de error.

Si una operación se ha escrito como subprograma, la programación a la defensiva recomienda que la primera actividad (detección del posible error) se haga dentro del subprograma, sin confiar en que quienes usen el subprograma lo invoquen siempre con datos correctos.

La segunda actividad, sin embargo, no puede realizarse, en general, dentro del subprograma, ya que el tratamiento adecuado de la situación excepcional podrá ser diferente en cada invocación. Lo que ha de hacer el subprograma es devolver una indicación precisa del error, y dejar que sean los programas que lo invocan quienes decidan cómo actuar frente al error en cada caso.

El esquema típico de un fragmento de programa con tratamiento de excepciones será:

. . . .
Operación( argumentos );
IF hubo error THEN
   tratamiento del error
END;
. . . .

Existen varios esquemas de programa posibles para tratamientos de errores. Un modelo recomendado es el de terminación. En este modelo, si se detecta error en una sección o bloque del programa, la acción de tratamiento del error reemplaza al resto de las acciones pendientes de dicha sección, con lo cual tras la acción correctora se da por terminado el bloque. En algunos lenguajes de programación modernos, tales como el lenguaje Ada, existen construcciones o sentencias adecuadas para programar este esquema. En Modula-2 no existen sentencias especiales de manejo de excepciones, y hay que programarlas con los medios disponibles. Un subprograma desarrollado en Modula-2 siguiendo el modelo de terminación podría programarse según el siguiente esquema:

PROCEDURE Operación( argumentos );
BEGIN
   … acción 1
   IF error THEN
      … tratamiento 1
      RETURN
   END;
   … acción 2
   IF error THEN
      … tratamiento 2
      RETURN
   END;
   . . . .
END Operación;

Aplicaremos este esquema a una variante mejoradade la función para calcular el factorial de un número, detectando la situación de exceso de capacidad ("overflow"), y devolviendo un valor negativo en ese caso.

PROCEDURE Factorial( n: INTEGER ): INTEGER;
   VAR k, f: INTEGER;
BEGIN
   IF n < 0 THEN
      RETURN 0
   END;
   f := 1;
   FOR k := 2 TO n DO
      IF f > MAX(INTEGER) DIV k THEN
         RETURN -1
      END;
      f := f*k
   END;
   RETURN f
END Factorial;

Esta función opera de manera robusta sea cual sea el rango de enteros de la máquina. Si se sabe de antemano cuál es dicho rango, se podría aumentar algo la eficiencia determinando por anticipado cuál es el mayor valor para el que se puede calcular el factorial, y detectando directamente si el valor del argumento excede de dicho límite, definido como parámetro constante.