Вы находитесь на странице: 1из 15

MEMORA PRCTICA

Asignatura: Teora de los Lenguajes de


Programacin

CURSO 2013-2014
GRADO INGENIERA INFORMTICA
Alumno: Vctor Colomo Gmez
DNI:
E-Mail: victor.colomo@gmail.com
Telfono:
Centro Asociado: Las Tablas (Madrid)

NDICE

1.- Funciones programas_______________________________2

2.- Cuestiones sobre la prctica_______________ __________8


Cuestin 1________________ ________________________8
Cuestin 2________ _______________________________10
Cuestin 3____ ___________________________________11
Cuestin 4________________________ _______________14
Cuestin 5_________________________ ______________15

FUNCIONES PROGRAMADAS
A continuacin se presentan las funciones empleadas en la prctica, se muestra el
cdigo de cada una de ellas y ejemplos de su correcto funcionamiento. En la carpeta
raz se ha incluido el archivo Sumas.hs.
Para poder utilizar varias funciones para el manejo de las lista se ha importado el
mdulo Data.List (import Data.List).
Se ha supesto que el nmero de sumandos puede ser mayor que el nmero de
resultados

FUNCIN dosQueSumen
A la hora de programar esta funcin me he encontrado con el siguiente problema
para aadir correctamente los sumandos: si un nmero es par y su mitad se
encuentra en la lista de sumandos. Inicialmente he desarrollado esta funcin:
dosQueSumen2 :: Int -> [Int] -> [Suma]
dosQueSumen2 n [] = []
dosQueSumen2 x xs = [(y,z) | y <-xs, z<-xs, x==y+z]

Como se muestra a continuacin su funcionamiento no es correcto

Para encontrar correctamente las parejas de sumandos se ha utilizado la


recursividad: inicialmente se compara el primer elemento de la lista (x) y
posteriormente con elem se busca si en el resto de la lista el elemento con valor
(n x), si est se aade el elemento y realiza la bsqueda en el resto de la lista, si
no est no se aade y se llama recursivamente a la funcin:
dosQueSumen :: Int -> [Int] -> [Suma]
dosQueSumen n [] = []
dosQueSumen n (x:xs)
| elem (n - x) xs = [(x, n - x)] ++ dosQueSumen n xs
| otherwise = dosQueSumen n xs

Con el siguiente ejemplo se muestra su correcto funcionamiento, adems se filtran


parte de las soluciones, pero no todas:

FUNCIN sinRepes
Se utiliza notElem para comprobar si el elemento (a,b) no se encuentra en la lista de
la forma (a,b) (b,a), si no est lo aadimos y vamos reduciendo el problema de
forma recursiva, si est no se aade (se aadir el ltimo que se encuentre).

sinRepes :: [Suma] -> [Suma]


sinRepes [] = []
sinRepes ((a,b):xs)
| (notElem (a,b) xs) && (notElem (b,a) xs) = [(a,b)] ++ (sinRepes xs)
| otherwise = sinRepes xs

FUNCIN eliminarSumandos
Funcin auxiliar utilizada en la funcin generarHijosNodo, se le pasan como
argumentos una pareja de sumandos y una lista. Devuelve la lista sin la pareja de
sumandos.

eliminarSumando :: [Suma] -> [Int] -> [Int]


eliminarSumando [] _ = []
eliminarSumando _ [] = []
eliminarSumando ((a,b):ds) c = delete a . delete b $ c

Su funcionamiento es el siguiente:

FUNCIN generarHijosNodo
Funcin auxiliar, a la que se le pasan como argumentos la lista de sumandos
disponibles y un nodo y devuelve todos los hijos posibles a partir de ese nodo.

generarHijosNodo :: [Suma] -> Nodo -> [Nodo]


generarHijosNodo [] (_, _ , _ ) = []
generarHijosNodo ((a,b):ds) ((x:xs),y,z) = [(xs, eliminarSumando [(a,b)] y, z ++ [(a,b)] )]
++ generarHijosNodo (ds) ((x:xs),y,z)

Su funcionamiento es el siguiente:

FUNCIN hijosNodo
La funcin devuelve una lista con los nodos resultantes de elegir una pareja de
sumandos de la lista de sumandos que sumen un elemento de la lista de resultados.
Si no hay sumandos disponibles la funcin devuelve la cadena vaca, en caso
contrario llama a la funcin generarHijosNodo.

hijosNodo :: Nodo -> [Nodo]


hijosNodo ((x:xs),y,z)
| length ((dosQueSumen x y)) == 0 = []
| otherwise = generarHijosNodo (sinRepes(dosQueSumen x y)) ((x:xs),y,z)

Como se muestra a continuacin la funcin funciona correctamente:

FUNCIN esSol
Devuelve true si un nodo es solucin (si la lista de resultados est vaca).

esSol :: Nodo -> Bool


esSol ((as),(ys),(zs))
| (as) == [] = True
| otherwise = False

FUNCIN imprimeLista
Funcin auxiliar que transforma todos los nodos que son solucin en una sola lista
de soluciones

imprimeLista :: [Nodo] -> [Solucion]


imprimeLista [] = []
imprimeLista [( [] , _ ,(zs) )] = [(zs)]
imprimeLista ((_,_,(zs)):y) = [(zs)] ++ imprimeLista (y)

FUNCIN resuelveSuma
Funcin principal encargada de resolver el problema

resuelveSumas :: Problema -> String


resuelveSumas (a ,b) = imprimeSoluciones ( imprimeLista (bt esSol hijosNodo (a,
b,[])))

CUESTIONES SOBRE LA PRCTICA


CUESTIN 1: crear una nueva funcin resuelveSumas2, que filtre de la lista de
soluciones aquellas soluciones que sean iguales. Recurdese que tanto las sumas
como los sumandos de una de ellas pueden aparecer en cualquier orden.
Para resolver esta funcin se ha seguido el siguiente planteamiento:
1) Ordenar los elementos de las tuplas de menor a mayor -> (10,8)
ello se ha modificado la funcin dosQueSumen3
dosQueSumen3 :: Int -> [Int] -> [Suma]
dosQueSumen3 n [] = []
dosQueSumen3 n (x:xs)
| elem (n - x) xs = [ ((min (x) ( n-x)), ( max (x) (n-x) )
| otherwise = dosQueSumen3 n xs

(8,10). Para

)] ++ dosQueSumen3 n xs

2) En hijosNodo2 modificamos la llamada a la funcin dosQueSumen3:

hijosNodo2 :: Nodo -> [Nodo]


hijosNodo2 ((x:xs),y,z)
| length ((dosQueSumen3 x y)) == 0 = []
| otherwise = generarHijosNodo (sinRepes (dosQueSumen3 x y)) ((x:xs),y,z)

3) Ordenamos las tuplas de menos a mayor segn su primer elemento (mediante


sort) -> (5,7)(9,3)
(3,9) (5,7). Para ello se ha modificado la funcin imprimeLista2
imprimeLista2 :: [Nodo] -> [Solucion]
imprimeLista2 [] = []
imprimeLista2 [( [] , _ ,(zs) )] = [sort(zs)]
imprimeLista2 ((_,_,(zs)):y) = [sort(zs)] ++ imprimeLista2 (y)

4) Modificamos resuelveSumas2, para llamar a impreLista2 y se aade la funcin


nub para que se eliminen las soluciones que son idnticas:
resuelveSumas2 :: Problema -> String
resuelveSumas2 (a ,b) = imprimeSoluciones (nub ( imprimeLista2 ( bt esSol hijosNodo2
(a, b,[]) ) ))

CUESTIN 2: En un lenguaje Orientado a Objetos, cul sera la estructura


adecuada para representar un nodo? Qu diferencias presentara el manejo de
dicha estructura con respecto al tipo de datos Nodo que se usa en la versin
funcional?
Para representar un nodo en un lenguaje Orientado a Objeto como Java,
deberamos utilizar una clase, con sus atributos y los mtodos necesarios para
poder operar con ella.

public class TipoNodo


{
// Atributos
private int [] resultados;
private int [] sumandos;
private suma [] solucion; // Antes tenemos que definir el tipo suma
// Constructor
public TipoNodo(int [] resultados, int [] sumandos, suma [] solucion)
{
this.resultados = resultados;
this.sumandos = sumandos;
this.solucion = solucin;
}
// Mtodos de necesarios
public int devuelveSum ()
{
return resultados.lenght; // Devuelve el nmero de resultados
}
public suma devuelveSolucion ()
{
return solucion;
// Devuelve la solucin
}
// Resto de mtodos necesario

En la versin funcional (Haskell) la estructura de datos utilizada son las listas. Las
listas son muy potentes y hay muchas funciones disponibles para poder operar con
ellas. Los lenguajes funcionales tiene la propiedad de la transparencia referencial,
con lo que se evitan los efectos colaterales (su valor slo depende de sus
argumentos). Tambin nos aprovechamos de la evaluacin perezosa que retrasa el
clculo de una expresin hasta que sea necesario, con lo que tambin se puede
mejorar la velocidad de computacin. Adems se pueden crear listas infinitas, cosa
que no podemos realizar en Java.
En Java utilizamos una clase para representar un nodo. En una clase se incluyen
los atributos y las funciones. Podramos utilizar un Array o ArrayList para representar
una lista de nodos. Como ocurre con Haskell, para el manejo de Arrays Java
dispone de multitud de funciones.

10

CUESTIN 3: Escriba en pseudocdigo Java (no es necesario que compile


correctamente) una funcin de recorrido en profundidad para este problema.
Compare la eficiencia, referida a la programacin, de dicha funcin en Haskell,
Prolog y Java.
Para un recorrido en profundidad en Java se ha utilizado el esquema de Vuelta
Atrs o retroceso (estudiado en la asignatura de 2 Programacin y estructura de
datos avanzados), ya que es necesario realizar una bsqueda exhaustiva
recorriendo todas las sumas parciales posibles hasta que se encuentre una solucin.
Existen dos tipos, el recursivo y el iterativo, para este problema se ha elegido el
recursivo. El iterativo sera as:

fun vuelta-atrs(ensayo)
si vlido(ensayo)
entonces dev ensayo
si no
lista-ensayo compleciones(ensayo)
mientras no vaca(lista-ensayo) ^ no resultado
hijo primero(lista-ensayo)
lista-ensayo resto(lista-ensayo)
si condiciones-de-poda(hijo)
entonces resultado vuelta-atrs(hijo)
fsi
fmientras
fsi
ffun

Como no se puede optimizar con ningn parmetro, no se puede realizar


ramificacin y poda. El esquema general recursiva para que nos muestre todas las
soluciones posibles es:

fun vuelta_atrs (ensayo)


si vlido (ensayo)
entonces dev ensayo
sino para hijo compleciones (ensayo)
hacer si condiciones de poda (hijo)
entonces vuelta_atrs (hijo)
fsi
ffun

Para nuestro problema:


Ensayo: nodo del rbol. Un ensayo constar de tres elementos:
- Array con la lista de resultados disponibles.
- Array con los sumandos disponibles.
- Array de tipo suma, que contendr la solucin parcial.
Vlido: un ensayo ser vlido si el nodo es solucin (si la lista de resultados est
vaca).
Compleciones: se generan tantos ensayos (nodos hijos) como sumas parciales
posibles haya desde el ltimo resultado
11

Condiciones de poda: no hay condiciones de poda para este problema.


La clase encargada ser Algoritmo. Que debera incluir dos mtodos:
- VueltaAtras: recorre la lista de compleciones.
- Compleciones: genera todos los movimientos posibles dado un tablero. Como este
mtodo no se pide en el enunciado omitimos su cdigo. Tendra que realizar las
mismas operaciones que la funcin hijosNodo utilizada en Haskell
Para ir almacenando los nodos hijos se utiliza la estructura de datos stack (pila). El
cdigo resultante podra ser como el siguiente:

import java.util.*;
public class Algoritmo
{
// MTODO Principal que recorre las lista de compleciones
public static void VueltaAtras(TipoNodo candidato) {
int sum=candidato.devuelveSum(); // N de sumandos
boolean exito=false;
// Variable para marcas si hemos encontrado una solucin
// Pila para almacenar los hijos
Sack <TipoNodo> listaCompleciones=new Stack <TipoNodo> ();
TipoNodo hijo=new TipoNodo(n); // Objeto TipoNodo, temporal para los clculos
if(sum==0){
// Si la lista de sumas es cero --> Solucin encontrada
exito=true;
numero_soluciones++;
} else { // Si no hemos encontrado una solucin, seguimos explorando el candidato
listaCompleciones=compleciones(candidato); // Rellenamos la pila de candidatos en
// compleciones
// Mientras la pila no este vaca todava no se han explorado todas las posibilidades
while ( !(listaCompleciones.isEmpty) {
hijo=listaCompleciones.pop();
// Extraemos el ltimo elemento de la pila
VueltaAtras(hijo);
// Lo reenviamos para completarlo
}
}
}

Respecto a Haskell no existe el concepto de variable, por lo que no existe el


concepto de asignacin, en consecuencia desaparece el concepto de bucle (ya que
es necesaria una variable de control que modifica cada vez que se ejecuta el cuerpo
de bucle). Los bucles deben ser resueltos mediante la recursin.
Como se ha indica anteriormente, Haskell tiene la propiedad de transparencia
refencial (ausencia de efectos colaterales), con lo que se facilita la depuracin (ya
que el problema siempre se repetir), esto no ocurre con un programa imperativo.
Tampoco es obligatorio definir el tipo de las funciones (es fuertemente tipado), los
tipos son inferidos automticamente, con lo que no puede haber uso dudoso de
tipos. La memoria es administrada: no hay que preocuparse por los punteros, el
Garbage Collector se ocupa de ellos. El programador puede preocuparse de la
implementacin del algoritmo, no de administracin de memoria.
12

Prolog es un buen lenguaje con una sintaxis concisa, ausencia de declaracin de


variables e independencia con el mecanismo de ejecucin, aunque compromete
otros principios como son la legibilidad, la eficiencia en la ejecucin y la fiabilidad.
Permite al programador orientar su trabajo hacia cual es el problema en vez de
como resolverlo, su semntica procedimental permite utilizar caractersticas de
control de la ejecucin para conseguir mayor eficiencia. En Prolog tampoco existen
los efectos colaterales
Algunas veces es incapaz de reconocer que un problema es (para su propio
conocimiento) inaplicable o insuficiente. Si el programa no contiene suficiente
informacin para contestar una consulta, es incapaz de reconocerlo y responde
negativamente.

13

CUESTIN 4: Indique, con sus palabras, qu permite gestionar el predicado


predefinido no lgico, corte (!), en Prolog. Cmo se realiza este efecto en Java?
El operado no lgico de corte (!), no tiene argumentos y es un predicado que
siempre es cierto. Permite controlar la ejecucin del algoritmo de Backtracking,
realizando una poda (elimina) de las opciones de rama en que se encuentra a partir
del nodo anterior al de su aparicin. Optimiza el tiempo de ejecucin, ya que no se
malgasta tiempo explorado nodos que sabemos que no contribuirn a una solucin.
En Java, inicialmente se puede pensar en la instruccin break (provoca la salida
forzada de las estructuras de control continuando la ejecucin en la siguiente
instruccin), pero no tiene los mismos efectos que el predicado de corte. Lo ms
parecido es realiza una poda (si es posible) en el algoritmo de Backtracking:

fun vuelta_atrs (ensayo)


si vlido (ensayo)
entonces dev ensayo
sino para hijo compleciones (ensayo)
hacer si condiciones de poda (hijo)
entonces vuelta_atrs (hijo)
fsi
ffun

14

CUESTIN 5: Compare la estructura de control que permite recorrer los hijos de


un determinado nodo en la funcin bt, en Haskell, Prolog y Java. Establezca las
similitudes y diferencias entre dichas estructuras de control.
En Java se ha utilizado una pila (stack) para el manejo de los nodos. Se analizan los
hijos de un nodo mediante una estructura de control while y se van extrayendo
elementos de la pila hasta que esta est vaca. Java permite estructuras de control
recursivas e iterativas
En Haskell no existe el concepto de bucle, se utiliza la recursin (de forma explcita)
para recorrer los nodos hijos. La estructura es:
bt :: (a -> Bool) -> (a -> [a]) -> a -> [a]
bt esSol compleciones nodo
| esSol nodo = [nodo]
| otherwise = concat (map (bt esSol compleciones) (compleciones nodo))

Prolog tampoco tiene estructuras de control, el procedimiento de resolucin es


recursiva y en profundidad.
Prolog es un lenguaje lgico que utiliza mecanismos de unificacin (comparacin de
partes de las frmulas basadas en la sustitucin lgica de variables) y de resolucin
con una estrategia predefinida y retroceso automtico (backtracking), es decir, la
recursin de realiza de forma implcita.

15

Вам также может понравиться