5.2. Cómo  definir un TAD

            Para definir un TAD vamos a hacer uso del Lenguaje de las Especificaciones Algebraicas. Por medio de este lenguaje el usuario puede especificar TADes todo lo complejos como se desee.

 

5.2.1. Gramática del Lenguaje de las Especificaciones Algebraicas.

          En la definición de la gramática se va a hacer uso de una serie de operadores que el usuario de conocer para entenderlo correctamente:

            * implica que aquello que vaya dentro del paréntesis al que sucede se puede repetir 0 o más veces.

            + implica que aquello que vaya dentro del paréntesis al que sucede se puede repetir 1 o más veces.

            [] implica que aquello que sea encerrado en ellos es opcional, puede o no aparecer.

            | implica opcionalidad entre varias posibilidades.

      Se debe saber también que <ID> (identificador) es una cadena de caracteres que empiece por una letra, seguido de números, letras o “_” .

            Definición de la gramática:

 “TIPO” <ID>

[“DOMINIOS” <ID>(“,”<ID>)*]

[“GENERICOS

              TIPO

<ID>

[“OPERACIONES

    ( funcion() [“PRECONDICIONESprecondiciones()])+

]

            | “FUNCIONfuncion()

]

[“GENERADORES” (funcion())+]

[“CONSTRUCTORES” (funcion())+]

[“SELECTORES” (funcion())+]

[“AXILIARES” (funcion())+]

[“PRECONDICIONESprecondiciones()]

[“ECUACIONESecuaciones()]

funcion():            <ID> (“,”<ID>)*

:

[idToF()(“*idToF())*]

(“->” | ”-/>”)

idToF()

precondiciones():             (decl_var())+  (precondicion())+

ecuaciones():                (decl_var())+   (regla())+ 

idToF():                       <ID> [“(lista_idToF())”]

lista_idToF():             idToF() (“,IDToF())*

decl_var():                   <ID> (“,”<ID>)*  :idToF() 

precondicion():             termino():termino()

regla():                         termino()==partederecha()

termino():                     <ID> [“(termino() (“,termino())*”)”]

 partederecha():             termino()

                                    | “SEAlista_asignaciones()ENpartederecha()

                                    | “SItermino()ENTONCESpartederecha()

[“SI NOpartederecha() 

lista_asignaciones(): (<ID> “=termino())+

 

 5.2.2. Cómo definir una función

Como se puede apreciar en la gramática del lenguaje, a la hora de definir una función se pueden hacer para varias a la vez, siempre y cuando todas vayan a tener exactamente los mismo tipos, tanto en sus parámetros como en su tipo de retorno, y sean además condicionales o no.

            Para de definir una función se escribirá primero su nombre. Éste debe empezar por letra y puede ir seguido de tantas letras, números o “_” como se desee. Si queremos definir más de una función de golpe bastará con poner una coma “,” y seguidamente escribir otro nombre de función, así tantas veces como queramos.

            Una vez definidos todos los nombre de funciones  se procederá a escribir “:” lo que implica que comenzamos a definir sus tipos.

            Una función puede tener o no parámetros de entrada, es decir, argumentos. Pero siempre ha de tener un tipo de retorno.

            Si la función que estamos definiendo tiene algún argumento, entonces escribiremos a continuación el nombre del tipo del primer argumento. Éste tipo debe ser uno de los siguientes:

            -El mismo tipo que estamos especificando.

            -Uno de los tipos genéricos definidos en la zona de GENÉRICOS.

            -Alguno de los tipos declarados en la zona de dominios.

  Si el tipo es alguno de los definidos en los dominios, y este a su vez posee genéricos que esperan ser instanciados, es aquí donde debemos instanciarlo.

  Si el tipo es el propio que estamos definiendo, éste nunca debe aparecer instanciado ya tenga genéricos o no.

 Si queremos definir mas argumentos de entrada para la función que estamos definiendo, bastará con escribir una coma “,” y volver a repetir la operación.

  Una vez terminada la zona de definición de parámetros de entrada escribiremos “->” o “-/>” dependiendo de cómo queramos que sea la función. Si escribimos “-/>” estaremos declarando al función como condicional y por lo tanto deberemos escribirle al menos una precondición en la zona de precondiciones. Si por el contrario escribimos    ->” significará que la función que estamos definiendo no está condicionada. 

  Seguidamente escribiremos un único tipo de retorno para función que estamos definiendo de igual modo en que se especificaron los tipos de los parámetros de entrada. 

  Y con esto queda definida una nueva función dentro del tipo que estemos especificando, o varias en su caso. Veamos algunos ejemplos:

            Suma,producto:Natural*Natural->Natural

            SumaLista:Lista-/>Natural

            OrLista:Lista(Lista(Natural))->Booleano

            Crear:->Pila

 5.2.3. Cómo instanciar un tipo.

            El proceso de instanciación de un tipo es bien sencillo. Cuando vayamos a hacer uso de un tipo, ya sea en la definición de funciones o de variables, éste puede ser instanciado siempre y cuando posea genéricos.

            En ese caso bastará con escribir el nombre del tipo seguido de un paréntesis “(“ y una lista con los tipos y funciones con los que vamos a instanciar sus genéricos separados por comas “,”, y acabaremos cerrando el paréntesis “)”.

            Los elementos con los que vamos a instanciar deben cumplir una serie de condiciones que se van a exponer a continuación.

            -Si lo que vamos a instanciar es un Tipo Genérico, solo se podrá hacer con tipos declarados en la zona de dominios, que a su vez pueden ser tipos instanciables, o por tipos genéricos propios del TAD que se está definiendo actualmente.

            -Si lo que vamos a instanciar es una Función Genérica, sólo se podrá hacer con Funciones propias del tipo que estemos definiendo siempre y cuando estas hayan sido declaradas previamente. O bien con funciones Genéricas propias del tipo que se está definiendo actualmente.

             Un ejemplo de instanciación es el siguiente.

                        Pila(Lista(Natural),TMax)

             Como se ha dicho, tanto los tipos Pila, Lista y Natural deben de haber sido declarados en la zona de dominios. Y Tmax debe ser una función propia del tad que se está definiendo, ya sea genérica o no.

  5.2.4. Cómo definir un Genérico.

            En nuestro lenguaje existen dos clases de Genéricos: los Tipos Genéricos y las Funciones Genéricas. Ambos van a ser entidades “huecas” que van a hacer referencia a un Tad o a una Función respectivamente.  Su uso es muy útil, pues se nos permite gracias a ellos definir un conjunto de TADes de una sola vez. Así pues definiendo el Tad Lista que contenga un Tipo Genérico Elemento estaremos definiendo cualquier clase de Lista posible, pudiendo ser ésta empleada para contener cualquier otro Tipo. Es decir, podremos hacer uso de Lista(Natural), Lista(Booleano) e incluso estructuras más complejas como Lista(Lista(Lista(Natural)))).

             La definición de genéricos se realiza en su correspondiente zona, la cual empieza por la palabra reservada “GENERICOS”. En ella se pueden definir tantos como queramos. Veamos cada una de las dos posibilidades.

 

·        Para definir un Tipo Genérico se escribe la palabra reservada “TIPO” y a continuación se escribe un nombre. Éste, al igual que los nombres de las funciones, debe comenzar por letra y puede ir seguido de tantos números, letras o “_” como se desee.

Seguidamente se le pueden exigir que tenga una serie de operaciones. Para ello se escribe la palabra reservada “OPERACIONES”. Tras esto se pueden escribir tantas funciones como se desee. En ellas se puede hacer uso de los tipos definidos en los dominios y de los tipos genéricos definidos previamente en este mismo TAD, incluyendo este mismo.

A cada una de estas operaciones se le puede exigir además que cumplan unas precondiciones. Para ello se emplea la palabra reservada “PRECONDICIONES” después de la función. Es importante destacar que estas precondiciones solo pueden hacer uso de funciones declaradas anteriormente para este mismo genérico. Veamos un ejemplo:

GENERICOS

TIPO

Elemento

OPERACIONES

MenorIgual:Elemento*Elemento->Booleano

Resta:Elemento*Elemento-/>Elemento

PRECONDICIONES a,b:Elemento

Resta(a,b):MenorIgual(b,a)

 

·        Para definir una Función Genérica haremos uso de la palabra reservada “FUNCIÓN”. Tras ella se hará la definición de una función, pero en este caso sólo está permitido definir una por vez. Para definir más de una se debe volver a usar la palabra reservada “FUNCIÓN”. A las funciones genéricas no se les permite definir precondiciones. Veamos un ejemplo:

GENERICOS

FUNCION

Cota:->Natural

                                    FUNCION

                                                Máximo:Pila-/>Natural

 

 5.2.5. Cómo definir una precondición.

             Las precondiciones establecen reglas que se deben cumplir  entre los argumentos de una función. Con ello conseguimos evitar entrar a usar algunas funciones bajo circunstancias para las cuales no funcionarían correctamente o no tendrían sentido.

             La definición de precondiciones se realiza en la zona dedicada a tal efecto que comienza por la palabra reservada “PRECONDICIONES”.  En ella tenemos la posibilidad de hacer una declaración de variables, las cuales pueden ser usadas para establecer las precondiciones.  A continuación se procede a definir precondiciones:

            Se escribe el nombre de una función seguido de “(“ y a continuación una lista con tantos argumentos como esa función espere, éstos pueden ser variables o funciones a su vez. Se escribe “)”. Luego “:” y seguidamente otra función, “(“, su lista de argumentos, y por ultimo “)”.

             La función que se emplea en la parte izquierda debe haber sido declarada como condicional, es decir, se debe haber empleado “-/>” en su definición, mientras que la función que se use en la parte derecha debe devolver un tipo Booleano con el propósito de poder comprobar si se cumple o no la relación entre los argumentos de la primera.

             Los argumentos de las funciones de ambas partes deben coincidir en número y tipo con los esperados por ellas. Éstos han sido declarados en la definición de la función.

            Además se debe declarar al menos una precondición para cada una de las funciones declaradas como condicionales, pudiendo tener una misma función más de una precondición.

     Veamos un ejemplo:

PRECONDICIONES

            n: Elemento

            p: Pila

            apilar(n,p):not(Esllena(p))

            cima(p):not(Esvacia(p))

5.2.6. Cómo declarar variables.

            Una declaración de variables se realiza de forma sencilla. Bastará con escribir un nombre, que debe empezar por letra y puede ir seguido por tantos números, letras o “_” como se desee. A continuación se escribe “:” y seguidamente el nombre de un tipo.

            Esta operación se puede repetir tantas veces como se desee.

Para declarar mas de una variable del mismo tipo se puede hacer de una vez escribiendo tras el nombre de la primera variable una coma “,” y volviendo a escribir otro nombre. De este modo podemos definir tantas como queramos, pero todas van a ser del mismo tipo.

            Cuando se escribe el nombre de una variable, éste tiene que ser nuevo, es decir, no se va a permitir dos variables con el mismo nombre dentro de un mismo Tad.

            Además debemos tener en cuenta que los tipos que empleemos para las variables han de ser tipos definidos previamente ya sean simples o instanciados. Éstos pueden ser cualesquiera de la lista de genéricos, el propio tipo que se está definiendo o alguno de los usados por alguna de las funciones. No se podrá hacer uso de ningún otro tipo.

            Veamos un ejemplo:

            a,b,c: Natural

            e: Elemento

            d: Lista

5.2.7. Cómo definir ecuaciones.

            La definición de ecuaciones es una parte básica de la especificación de TADes pues son las encargadas de dar sentido a las funciones. Por ello se recomienda que se preste especial atención a su elaboración.

            La definición de una ecuación se realiza en la zona de dedicada a tal efecto que comienza por la palabra reservada “ECUACIONES”. En ella tenemos la posibilidad de hacer una declaración de variables, las cuales pueden ser usadas para establecer las ecuaciones. A continuación se procede a definir tantas ecuaciones como se desee.

            En una ecuación vamos a distinguir dos partes: una parte Izquierda y una parte Derecha. Para definir una ecuación bastará con escribir la parte izquierda, luego “==” y por último la parte Derecha. Veamos por separado ambas partes:

 

·        Parte Izquierda: Se escribe el nombre de una función seguido de “(“ y a continuación una lista con tantos argumentos como esa función espere, éstos pueden ser variables o funciones a su vez. Se escribe “)”. La función debe ser una de las funciones declaradas a lo largo de la especificación del TAD. Las funciones genéricas pueden ser usadas como argumentos de la función principal de la parte Izquierda.

Los  argumentos que se usan en una función deben coincidir tanto en número como en tipo con los esperados por los parámetros de la función.

 

·        Parte Derecha: Existen tres tipos de Partes Derechas posibles en una función: Un Término, una Parte Derecha que emplee el conjunto de operadores “SEA” ..... “EN” o una Parte Derecha en donde se emplee conjunto de operadores “SI” ..... “ENTONCES”........ “SI NO”.

 

-Para el primero de los casos se escribirá o bien el nombre de una de las variables declaradas hasta el momento en el TAD (cuyo tipo coincida con el tipo de retorno de la función de la parte izquierda de la ecuación), o bien cualquiera de las funciones de que se dispone, tanto las propias del TAD como todas las pertenecientes a los tipos de los dominios. Incluso se pueden usar las funciones genéricas del TAD. (el tipo de retorno de la función usada debe coincidir con el tipo de retorno de la función con la que se ha construido la parte izquierda de la ecuación).

Si se emplea una función entonces se debe escribir su nombre seguido de “(“ y de una lista de argumentos que coincidan en número y tipo con los declarados para la función que se está usando para construir la parte derecha. Estos argumentos pueden ser a su vez variables o funciones. Tras la lista de argumentos se debe cerrar el paréntesis “)”.

 

-En el segundo caso se debe escribir la palabra reservada “SEA” y a continuación una lista de asignaciones. Seguidamente debe escribirse la palabra reservada “EN” y de nuevo una parte derecha cuyo tipo de retorno coincida con el tipo de retorno de la función de la parte izquierda.

 

La lista de asignaciones tiene la siguiente estructura: Se escribe una de las variables presentes en el TAD seguido de “=” y de una función con su respectiva lista de argumentos (de igual manera que lo explicado anteriormente). Con esto estamos diciendo que cada vez que escribamos la variable en realidad nos estamos refiriendo a aquello que le ha sido asignado. Si se quiere hacer una nueva asignación bastará con repetir este proceso. Se pueden declarar tantas asignaciones como se desee.

 

-En el tercer caso se escribirá la palabra reservada “SI” seguida de una función, que actúa como condición, cuyo tipo de retorno sea Booleano o similar, con su respectiva lista de argumentos (de igual manera que lo explicado anteriormente). A continuación “ENTONCES” y una parte derecha cuyo tipo coincida con el del tipo de retorno de la función que se ha usado para construir la parte izquierda de la ecuación.

Seguidamente se escribe “SI NO” y de nuevo otra parte derecha del mismo tipo.

El comportamiento es el siguiente: se reducirá la condición y en caso de que sea verdadera se tomará la parte derecha que sucede al “ENTONCES”, en otro caso se optará por la parte derecha que sucede al “SI NO”.

 Veamos un ejemplo completo:

     n, m, pi : Natural

    suma(cero(),m)==m

    suma(suc(n),m)==suc(suma(n,m))

    producto(cero(),m)==cero()

    producto(suc(n),m)==suma(m,producto(n,m))

    resta(n,cero())== n

    resta(suc(n),suc(m))==resta(n,m)

     miOperacion(n,m) == si menor_igual(n,m) entonces

                                                            si igual(n,m) entonces

                                                                        sea pi=suma(n,m) en producto(pi,m)

                                                            si no suma(n,m)

                                                si no resta(n,m)

5.2.8. Cómo especificar un TAD.

            La definición de un TAD comienza por la palabra reservada “TIPO” seguida del nombre del TAD. Este nombre debe comenzar por una letra y puede ir seguido de tantos números, letras o “_” como se desee. El TAD se finaliza con la palabra reservada “FIN” seguida de nuevo del nombre del TAD.

            Entre ambas partes podemos hacer uso de varias zonas:

-Dominios: esta zona comienza con la palabra reservada “DOMINIOS” y en ella podemos declarar los nombres, separados por comas “,”,  de los TADes que se van a emplear a lo largo de toda la definición.

-Genéricos: esta zona comienza con la palabra reservada “GENERICOS” y en ella podemos declarar tantos genéricos como se desee. (consultar cómo declarar genéricos).

-Generadores: esta zona comienza con la palabra reservada “GENERADORES” y  en ella podemos declarar tantas funciones como queramos, las cuales van a ser marcadas como generadoras. Las funciones generadoras son aquellas que sirven para construir los elementos pertenecientes al TAD de forma unívoca. (consultar cómo declarar funciones).

-Constructores: esta zona comienza con la palabra reservada “CONSTRUCTORES” y en ella podemos declarar tantas funciones como queramos. Las funciones constructoras son aquellas que, sin ser generadoras, producen términos pertenecientes al TAD. (consultar cómo declarar funciones).

-Selectores: esta zona comienza con la palabra reservada “SELECTORES” y en ella podemos declarar tantas funciones como queramos. Las funciones selectoras son aquellas que dan como resultado términos no pertenecientes al TAD. (consultar cómo declarar funciones).

-Auxiliares: esta zona comienza con la palabra reservada “AUXILIARES” y en ella podemos declarar tantas funciones como queramos. Las funciones auxiliares son aquellas que usa el diseñador como apoyo a la especificación y no estarán presentes en el interfaz del Tipo Abstracto de Datos. (consultar cómo declarar funciones).

-Precondiciones: esta zona comienza con la palabra reservada “PRECONDICIONES” y en ella vamos a definir al menos una precondición para cada una de las funciones declaradas como condicionales. Su objetivo es permitir su uso sólo bajo ciertas circunstancias entre los argumentos. (consultar cómo declarar precondiciones).

-Ecuaciones: esta zona comienza con la palabra reservada “ECUACIONES” y en ella vamos a definir al menos una ecuación para cada una de las funciones definidas como no generadoras, pudiendo dar también ecuaciones para estas últimas.

5.3. Entorno de trabajo.

El entorno de trabajo consta de una serie de zonas que ofrecen al usuario distintas funcionalidades.

1.      Barra de Menú. La barra de menú contiene todos las posibles acciones que el usuario va a poder usar.

2.      Barra de Herramientas. En ella se encuentran botones para el accedo inmediato a las operaciones mas usuales, tales como compilación , reducción, apertura de un TAD, creación de un nuevo TAD, etc.

3.      Entorno de Edición. Será el área de trabajo del diseñador. En ella se escriben las especificaciones de los TADes.

4.      Árboles de información. Se distinguen dos. Árbol de Tades Cargados en donde aparecen todos los TADes cargados en memoria tras una compilación. Y Árbol de TAD actual, en donde aparece toda la información referente al TAD compilado.

5.      Área de resultados de compilación. En ella el compilador comunica al usuario todo lo acontecido durante el proceso de compilación: corrección o no de compilación, mensajes de error y advertencias.

6.      Barra de archivos. En ella aparecerán tanto el número de línea en el cual se encuentra el cursor en este momento, como todos los Ficheros abiertos, pudiendo el usuario pasar de una a otro de forma cómoda.

7.      Área de Reducciones. Es el interfaz que usará el usuario para reducir términos tras haber realizado una compilación.

5.3.1. Configuración del entorno de trabajo.

Es muy importante tener en cuenta los siguientes puntos:

 

·        Los TADes creados por el programa son guardados haciendo uso de un direccionamiento relativo al directorio /tades.  Es decir, todos los ficheros se van a buscar y a guardar en el directorio /tades que debe haber dentro del directorio donde se haya instalado el programa. Es por esto que cuando se va a abrir un archivo ya creado directamente se nos muestra el contenido de este directorio.

No se recomienda que se cambie de directorio de trabajo pues el programa podría no funcionar de forma incorrecta.

 

·        Las comprobaciones tanto de las precondiciones como de las condiciones de las sentencias SI ve van a hacer reduciendo el término y mirando si el resultado coincide con el término V() o bien con el término VERDAD(). Es por esto que para poder hacer uso de precondiciones o sentencias SI debe disponerse en el directorio de trabajo de un TAD que tenga una de estas funciones como generadora. Además este tipo debe ser declarado en la zona de dominios. De no ser así, las reducciones no funcionarían de forma correcta pues al no llegar a uno de estos términos se entendería que la precondición o la condición no se cumple respectivamente.

Se recomienda que se mantenga el tipo Booleano en el directorio de trabajo.

5.4. Cómo compilar un TAD.

            Bastará con haberlo especificado y pulsar el botón de compilación. El proceso de compilación comenzará y mostrará en la zona de resultados de compilación todo lo acontecido durante el proceso, de este modo el usuario será informado sobre la corrección o no de la especificación realizada, conociendo, en su caso los errores cometidos y advertencias que debe tener presente.

5.5. Cómo reducir un Término.

            La herramienta estará disponible tras la compilación de un Tad que no presente genéricos sin instanciar.  Para acceder a ella sólo debemos pulsar su correspondiente botón de la barra de herramientas y ésta aparecerá.

            Para reducir un termino debemos escribirlo en la zona destinada a tal propósito y pulsar el botón de PLAY.

            Las reducciones aparecen en el área de reducciones. Se hace uso de diferentes colores para ilustrar el proceso de reducción.       

·        En rojo: aparece escrito el primer término que unifica con alguna de la partes izquierdas de las ecuaciones empleadas en la especificación del TAD.

·        En azul: aparece escrito la reducción realizada sobre el término que se ha reducido. De esta forma al usuario le será bastante fácil hacer un seguimiento sobre el comportamiento del TAD.

·        En negro: aparece aquello que no sea ninguno de los dos casos anteriores. Es por esto que cuando la reducción termina y se haya llegado a la forma canónica, aparecerá toda la línea en negro. 

El usuario dispone además de distintos grupos de  botones para controlar el proceso de reducción:

-Por un lado aparecen las opciones Paso a Paso y Completa. si seleccionamos Paso a Paso se realizará una única reducción por cada pulsación del botón PLAY. Si por el contrario seleccionamos Completa, el proceso no se interrumpirá hasta llegar al final o ser detenido por el usuario. 

-Por otro lado tenemos los botones PLAY, PAUSA y STOP. Inicialmente sólo aparecerá habilitado el botón de PLAY, de modo que el usuario, tras escribir un término puede pulsarlo y comenzará el proceso de reducción. Una vez iniciado, éste se deshabilitará y aparecerán habilitados los botones de PAUSA y STOP. Si se presiona el botón de PAUSA el proceso queda detenido a la espera de que se vuelva a pulsar el de STOP o el de PLAY. En cambio, si el pulsado es el botón de STOP, la reducción se detendrá por completo. 

-Y por último tenemos los botones:

·        Limpiar término. La pulsación de este botón produce que todo lo que haya escrito en el área de término a reducir sea borrado.

·        Limpiar reducciones. La pulsación de este botón produce que todo lo que haya escrito en el área de reducciones sea borrado.

·        Cerrar reducciones. La pulsación de este botón produce que se cierre la ventana de reducciones. Para volver a mostrarla bastará con volver a pulsar el botón que la muestra de la barra de herramientas.