TAD de las pilas: Transformaciones entre pilas y listas

Utilizando el tipo abstracto de datos de las pilas, definir las funciones

tales que

  • listaApila xs es la pila formada por los elementos de xs. Por ejemplo,

  • pilaAlista p es la lista formada por los elementos de la pila p. Por ejemplo,

Comprobar con QuickCheck que ambas funciones son inversa; es decir,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

El tipo abstracto de datos de las pilas

1. El tipo abstracto de datos de las pilas

Una pila es una estructura de datos, caracterizada por ser una secuencia de elementos en la que las operaciones de inserción y extracción se realizan por el mismo extremo.

Las operaciones que definen a tipo abstracto de datos (TAD) de las pilas (cuyos elementos son del tipo a) son las siguientes:

tales que

  • vacia es la pila vacía.
  • (apila x p) es la pila obtenida añadiendo x al principio de p.
  • (cima p) es la cima de la pila p.
  • (desapila p) es la pila obtenida suprimiendo la cima de p.
  • (esVacia p) se verifica si p es la pila vacía.

Las operaciones tienen que verificar las siguientes propiedades:

  • cima(apila(x, p) == x
  • desapila(apila(x, p)) == p
  • esVacia(vacia)
  • not esVacia(apila(x, p))

2. Las pilas en Haskell

2.1. El tipo abstracto de datos de las pilas en Haskell

El TAD de las pilas se encuentra en el módulo Pila.hs cuyo contenido es el siguiente:

Para usar el TAD hay que usar una implementación concreta. En principio, consideraremos dos una usando listas y otra usando sucesiones. Hay que elegir la que se desee utilizar, descomentándola y comentando las otras.

2.2. Implementación de las pilas mediante listas

La implementación se encuentra en el módulo PilaConListas.hs cuyo contenido es el siguiente:

2.3. Implementación de las pilas mediante sucesiones

La implementación (que usa la librería Data.Sequence) se encuentra en el módulo PilaConSucesiones.hs cuyo contenido es el siguiente:

3. Las pilas en Python

3.1. El tipo abstracto de las pilas en Python

La implementación se encuentra en el módulo pila.py cuyo contenido es el siguiente:

Para usar el TAD hay que usar una implementación concreta. En principio, consideraremos dos una usando listas y otra usando sucesiones. Hay que elegir la que se desee utilizar, descomentándola y comentando las otras.

3.2. Implementación de las pilas mediante listas

La implementación se encuentra en el módulo pilaConListas.py en el que se define la clase Pila con los siguientes métodos:

  • apila(x) añade x al principio de la pila.
  • cima() devuelve la cima de la pila.
  • desapila() elimina la cima de la pila.
  • esVacia() se verifica si la pila es vacía.

Por ejemplo,

Además se definen las correspondientes funciones. Por ejemplo,

Finalmente, se define un generador aleatorio de pilas y se comprueba que las pilas cumplen las propiedades de su especificación.

3.3. Implementación de las pilas mediante deque

La implementación (que usa la librería deque) se encuentra en el módulo pilaConDeque.py y su contenido es el siguiente:

Valor de una expresión vectorial

Se consideran las expresiones vectoriales formadas por un vector, la suma de dos expresiones vectoriales o el producto de un entero por una expresión vectorial. El siguiente tipo de dato define las expresiones vectoriales

Definir la función

tal que valorEV e es el valorEV de la expresión vectorial e. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Valor de expresiones aritméticas generales

Las operaciones de suma, resta y multiplicación se pueden representar mediante el siguiente tipo de datos

La expresiones aritméticas con dichas operaciones se pueden representar mediante el siguiente tipo de dato algebraico

Por ejemplo, la expresión

se representa por

Definir la función

tal que valor e es el valor de la expresión e. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Máximos valores de una expresión aritmética

Las expresiones aritméticas generales se pueden definir usando el siguiente tipo de datos

Por ejemplo, la expresión

se puede definir por

Definir la función

tal que maximo e xs es el par formado por el máximo valor de la expresión e para los puntos de xs y en qué puntos alcanza el máximo. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Expresiones aritméticas reducibles

Las expresiones aritméticas con variables pueden representarse usando el siguiente tipo de datos

Por ejemplo, la expresión 2·(a+5) se representa por

Definir la función

tal que reducible a se verifica si a es una expresión reducible; es decir, contiene una operación en la que los dos operandos son números. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Sustitución en una expresión aritmética

Las expresiones aritméticas con variables pueden representarse usando el siguiente tipo de datos

Por ejemplo, la expresión 2·(a+5) se representa por

Definir la función

tal que sustitucion e s es la expresión obtenida sustituyendo las variables de la expresión e según se indica en la sustitución s. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Número de sumas en una expresión aritmética

Las expresiones aritméticas con variables pueden representarse usando el siguiente tipo de datos

Por ejemplo, la expresión 2·(a+5) se representa por

Definir la función

tal que sumas e es el número de sumas en la expresión e. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Valor de una expresión aritmética con variables

Las expresiones aritméticas con variables pueden representarse usando el siguiente tipo de datos

Por ejemplo, la expresión 2·(a+5) se representa por

Definir la función

tal que valor x e es el valor de la expresión x en el entorno e (es decir, el valor de la expresión donde las variables de x se sustituyen por los valores según se indican en el entorno e). Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

El tipo de las expresiones aritméticas con variables

1. El tipo de las expresiones aritméticas con variables en Haskell

La expresión 2*(a+5) puede representarse por

usando el tipo de las expresiones aritméticas con variables definido como se muestra a continuación.

2. El tipo de las expresiones aritméticas con variables en Python

La expresión 2*(a+5) puede representarse por

usando el tipo de las expresiones aritméticas con variables definido como se muestra a continuación.

Número de variables de una expresión aritmética

Las expresiones aritméticas construidas con una variable (denotada por X), los números enteros y las operaciones de sumar y multiplicar se pueden representar mediante el tipo de datos Expr definido por

Por ejemplo, la expresión X·(13+X) se representa por

Definir la función

tal que numVars e es el número de variables en la expresión e. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Valor de una expresión aritmética con una variable

Las expresiones aritméticas construidas con una variable (denotada por X), los números enteros y las operaciones de sumar y multiplicar se pueden representar mediante el tipo de datos Expr definido por

Por ejemplo, la expresión X·(13+X) se representa por

Definir la función

tal que valor e n es el valor de la expresión e cuando se sustituye su variable por n. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Tipo de expresiones aritméticas con una variable

1. El tipo de las expresiones aritméticas con una variable en Haskell

La expresión X·(13+X) se representa por

usando el tipo de las expresiones aritméticas con una variable (denotada por X) que se define como se muestra a continuación,

2. El tipo de las expresiones aritméticas con una variable en Python

La expresión X*(13+X) se representa por

usando el tipo de las expresiones aritméticas con una variable (denotada por X) que se define como se muestra a continuación,

Aplicación de una función a una expresión aritmética

Usando el tipo de las expresiones aritméticas básicas, definir la función

tal que aplica f e es la expresión obtenida aplicando la función f a cada uno de los números de la expresión e. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Valor de una expresión aritmética básica

Usando el tipo de las expresiones aritméticas básicas, definir la función

tal que valor e es el valor de la expresión aritmética e. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

El tipo de las expresiones aritméticas básicas

1. El tipo de las expresiones aritméticas básicas en Haskell

La expresión aritmética 2*(3+7) se representa por

usando el tipo de dato definido a continuación.

2. El tipo de las expresiones aritméticas básicas en Python

La expresión aritmética 2*(3+7) se representa por

usando el tipo de dato definido a continuación.

Valor de un árbol booleano

Se consideran los árboles con operaciones booleanas definidos por

Por ejemplo, los árboles

se definen por

Definir la función

tal que valor a) es el resultado de procesar el árbol a realizando las operaciones booleanas especificadas en los nodos. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Árbol de factorización

Los divisores medios de un número son los que ocupan la media entre los divisores de n, ordenados de menor a mayor. Por ejemplo, los divisores de 60 son [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60] y sus divisores medios son 6 y 10. Para los números que son cuadrados perfectos, sus divisores medios de son sus raíces cuadradas; por ejemplos, los divisores medios de 9 son 3 y 3.

El árbol de factorización de un número compuesto n se construye de la siguiente manera:

  • la raíz es el número n,
  • la rama izquierda es el árbol de factorización de su divisor medio menor y
  • la rama derecha es el árbol de factorización de su divisor medio mayor

Si el número es primo, su árbol de factorización sólo tiene una hoja con dicho número. Por ejemplo, el árbol de factorización de 60 es

Definir la función

tal que arbolFactorizacion n es el árbol de factorización de n. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Elementos del nivel k de un árbol

Los árboles binarios con valores en las hojas y en los nodos se definen por

Por ejemplo, el árbol

se representa por

Un elemento de un árbol se dirá de nivel k si aparece en el árbol a distancia k de la raíz.

Definir la función

tal que nivel k a es la lista de los elementos de nivel k del árbol a. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Existencia de elementos del árbol que verifican una propiedad

Los árboles binarios con valores en las hojas y en los nodos se definen por

Por ejemplo, el árbol

se representa por

Definir la función

tal que algunoArbol a p se verifica si algún elemento del árbol a cumple la propiedad p. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Árboles con igual estructura

Los árboles binarios con valores en las hojas y en los nodos se definen por

Por ejemplo, los árboles

se pueden representar por

Definir la función

tal que igualEstructura a1 a2 se verifica si los árboles a1 y a2 tienen la misma estructura. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Árboles con bordes iguales

Los árboles binarios con valores en las hojas se pueden definir por

Por ejemplo, los árboles

se representan por

Definir la función

tal que igualBorde t1 t2 se verifica si los bordes de los árboles t1 y t2 son iguales. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Árboles balanceados

Los árboles binarios con valores en los nodos se pueden definir por

Por ejemplo, el árbol

se puede representar por

Diremos que un árbol está balanceado si para cada nodo la diferencia entre el número de nodos de sus subárboles izquierdo y derecho es menor o igual que uno.

Definir la función

tal que (balanceado a) se verifica si el árbol a está balanceado. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Rama izquierda de un árbol binario

Los árboles binarios con valores en los nodos se pueden definir por

Por ejemplo, el árbol

se puede representar por

Definir la función

tal que ramaIzquierda a es la lista de los valores de los nodos de la rama izquierda del árbol a. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Suma de un árbol

Los árboles binarios con valores en los nodos se pueden definir por

Por ejemplo, el árbol

se puede representar por

Definir por recursión la función

tal sumaArbol x es la suma de los valores que hay en el árbol x. Por ejemplo,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

El tipo de los árboles binarios con valores en los nodos

1. El tipo de los árboles binarios con valores en los nodos en Haskell

El árbol, con valores en los nodos,

se puede representar por

usando el tipo de los árboles con valores en los nodos definido como se muestra a continuación.

2. El tipo de los árboles binarios con valores en los nodos en Python

El árbol binario, con valores en los nodos,

se puede representar por

usando el tipo de los árboles binarios con valores en los nodos definido como se muestra a continuación.

Árbol de profundidad n con nodos iguales

El árbol binario

se puede representar por

El tipo de los árboles binarios se puede definir por

Definir las funciones

tales que

  • repeatArbol x es es árbol con infinitos nodos x. Por ejemplo,

  • replicate n x es el árbol de profundidad n cuyos nodos son x. Por ejemplo,

Comprobar con QuickCheck que el número de hojas de replicateArbol n x es 2^n, para todo número natural n.
Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Subárbol de profundidad dada

El árbol binario

se puede representar por

El tipo de los árboles binarios se puede definir por

La función take está definida por

Definir la función

tal que takeArbol n t es el subárbol de t de profundidad n. Por ejemplo,

Comprobar con QuickCheck que la profundidad de takeArbol n x es menor o igual que n, para todo número natural n y todo árbol x.

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Imagen especular de un árbol binario

El árbol binario

se puede representar por

El tipo de los árboles binarios se puede definir por

Definir la función

tal que espejo x es la imagen especular del árbol x. Por ejemplo,

Comprobar con QuickCheck las siguientes propiedades, para todo árbol x,

Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python

Recorrido de árboles binarios

El árbol binario

se puede representar por

El tipo de los árboles binarios se puede definir por

Definir las funciones

tales que

  • preorden es la lista correspondiente al recorrido preorden del árbol x; es decir, primero visita la raíz del árbol, a continuación recorre el subárbol izquierdo y, finalmente, recorre el subárbol derecho. Por ejemplo,

  • postorden x es la lista correspondiente al recorrido postorden del árbol x; es decir, primero recorre el subárbol izquierdo, a continuación el subárbol derecho y, finalmente, la raíz del árbol. Por ejemplo,

Comprobar con QuickCheck que la longitud de la lista obtenida recorriendo un árbol en cualquiera de los sentidos es igual al número de nodos del árbol más el número de hojas.
Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.


Soluciones en Haskell


Soluciones en Python