Los predicados que pide el ejercicio son para pasar entre las diferentes representaciones expuestas, y se resumen a continuación: A <---> B <---> C A <---> C Puede verse que para pasar de A <---> C podremos usar el pasaje intermedio de A <---> B <---> C, con lo cual nos concentraremos en los subproblemas a) y c) pues el b) se resolverá de manera trivial. Los predicados se llamarán: a_b(+grafo_a, -grafo_b) a_b(-grafo_a, +grafo_b) a_c(+grafo_a, -grafo_c) a_c(-grafo_a, +grafo_c) b_c(+grafo_b, -grafo_c) b_c(-grafo_b, +grafo_c) Ya podemos escribir la solución al ítem b): a_c(G1, G3) :- a_b(G1, G2), b_c(G2, G3). Si nos aseguramos que a_b/2 y b_c/2 puedan usarse para hacer conversiones en cualquiera de los sentidos, entonces a_c/2 también gozará de dicha propiedad. Analicemos primero el caso más fácil, que es pasar de la notación (B) a la (C), pues son muy similares. En la lista de salida habrá tantos elementos como en la lista de entrada, y cada elemento de la lista de salida se obtiene de procesar uno y sólo uno de la lista de entrada. La solución entonces pasa por crear un predicado auxiliar para traducir un elemento aislado, y llamar dicho predicado para cada elemento de la lista de origen: b_c([], []) :- !. b_c([X|Xs], [Y|Ys]) :- b_c_uno(X,Y), b_c(Xs,Ys). b_c_uno([Nodo|Conectados], Resultado) :- append([Nodo], Conectados, Resultado). Como no hemos hecho ninguna suposición acerca de cuál argumento está instanciado y cuál está libre, estos predicados funcionan en cualquier sentido: tanto para pasar de B->C como de C->B. Veamos ahora el caso de pasar de (A) a (B). La lista resultado tendrá tantos elementos como cantidad de elementos tiene la primer sublista. El contenido de cada elemento se obtiene de buscar en la segunda sublista los arcos. Podemos tratar este problema de manera análoga al caso anterior, escribiendo un predicado que se ocupe de generar un elemento de la lista de salida, y llamarlo para cada elemento de la primer sublista. a_b([Nodos,Arcos], Resultado) :- a_b_aux(Nodos, Arcos, Resultado). Lo primero que hago es hacerme fácil el caso base, separando la lista dato en las partes que me convienen. De esta maner creo un prodicado auxiliar a_b_aux/3 que será trivial de implantar: a_b_aux([], _, []) :- !. a_b_aux([N|Ns], Arcos, [R|Rs]) :- a_b_uno(N, Arcos, R), a_b_aux(Ns, Arcos, Rs). Por el mismo motivo, escribo un predicado a_b_uno/3 que lo único que hace es construir la lista elemento en el formato requerido por (B), pero que no se preocupa en encontrar lo que va dentro: a_b_uno(N, Arcos, [N|[Conectados]]) :- a_b_uno_aux(N, Arcos, Conectados). Ahora me toca buscar aquellos que son los nodos conectados a N. Si se acaban los arcos, tengo el caso base: a_b_uno_aux(_, [], []) :- !. Me quedan dos casos para cubrir: cuando el primer arco de la lista de arcos comienza con el nodo que busco, y cuando no. a_b_uno_aux(N1, [[N1,N2]|As], [N2|Ns] ) :- a_b_uno_aux(N1, As, Ns). a_b_uno_aux(N1, [[N2,_]|As], Ns ) :- N1 \= N2, a_b_uno_aux(N1, As, Ns). Si en la cláusula a_b_uno_aux/3 que trata el caso con N1 como primer nodo de un arco en el segundo argumento ponemos un cut, lo que sucede es que Prolog no tiene que explorar caminos alternativos que no le darán ningún nuevo éxito. Si el cut no está, cuando se encuentra el éxito Prolog pregunta si deseamos buscar nuevas soluciones, y si le decimos que lo haga, no encuentra otras (porque no las hay). Este problema de traducción genera una solución canónica si por ejemplo ordenamos ascendentemente los nombres de los nodos en las listas que deban aparecer. Si el cut se pone, como a continuación, entonces el programa queda determinístico. a_b_uno_aux(N1, [[N1,N2]|As], [N2|Ns] ) :- !, a_b_uno_aux(N1, As, Ns). Al encarar a_b/2 lo hemos construido de forma que solamente haga la conversión A-->B. Si eliminamos de a_b/2 y sus predicados subordinados los cut, el problema que enfrentamos es que aparece una recursión infinita al tratar de correr prueba_c_a/0. Se deja como ejercicio a la lectora para que vea esto. El código sin los cut queda como a continuación: (ej-05a.pl) a_b([Nodos,Arcos], Resultado) :- a_b_aux(Nodos, Arcos, Resultado). a_b_aux([], _, []). a_b_aux([N|Ns], Arcos, [R|Rs]) :- a_b_uno(N, Arcos, R), a_b_aux(Ns, Arcos, Rs). a_b_uno(N, Arcos, [N|[Conectados]]) :- a_b_uno_aux(N, Arcos, Conectados). a_b_uno_aux(_, [], []). a_b_uno_aux(N1, [[N1,N2]|As], [N2|Ns] ) :- a_b_uno_aux(N1, As, Ns). a_b_uno_aux(N1, [[N2,_]|As], Ns ) :- N1 \= N2, a_b_uno_aux(N1, As, Ns). Ahora lo que falta es pasar de B ---> A. Claramente hay tres tareas qe realizar: construir la lista de nodos, construir la lista de arcos, y lluego armar la lista resultados. Escribiremos un predicado para cada una de estas cosas. b_a(B, [Nodos,Arcos]) :- b_a_nodos(B, Nodos), b_a_arcos(B, Arcos). La lista en formato (B) está formada por listas cuyo primer elemento es un nodo del grafo sin repetir. De allí sacaremos nuestra lista de nodos: b_a_nodos( [], [] ) :- !. b_a_nodos( [[N,_]|Xs], [N|Ns] ) :- b_a_nodos(Xs, Ns). La lista de arcos que necesito en el formato (A) se puede construir tratando cada elemento del formato (B) por turno y concatenando los resultados parciales; como se ha hecho antes, se definirá un predicado auxiliar que resuelva el caso de un nodo por vez: b_a_arcos( [], [] ) :- !. b_a_arcos( [X|Xs], Arcos) :- b_a_arcos_uno(X, Y), b_a_arcos(Xs, Z), append(Y, Z, Arcos). b_a_arcos_uno([Nodo,Conectados], Arcos) :- b_a_arcos_uno_aux(Nodo, Conectados, Arcos). b_a_arcos_uno_aux(_, [], []) :- !. b_a_arcos_uno_aux(N1, [N2|Ns], [[N1,N2]|As]) :- b_a_arcos_uno_aux(N1, Ns, As). Enonces el a_c/2 sólo pasa correctameente de A --> C. Para pasar de C --> A habrá que usar otra alternativa, por ejemplo: c_a(C,A) :- b_c(B,C), b_a(B,A). En resumen, todas las conversiones solicitadas se realizan con: A B C A - a_b/2 a_c/2 B b_a/2 - b_c/2 C c_a/2 b_c/2 - La tabla se lee por ejemplo: para convertir de formato (B) [renglón B] hacia formato (A) [columna A], se debe emplear b_a/2. Si se deseara escribir predicados que sirvan tanto de ida como de vuelta para las conversiones, se deberá comprobar con var/1 o nonvar/1 para saber si un argumento está asociado a una variable sin instanciar o instanciado, respectivamente. Por ejemplo, para pasar de A <--> B: a_b_ida_y_vuelta(A, B) :- var(A), nonvar(B), b_a(A,B), !. a_b_ida_y_vuelta(A, B) :- nonvar(A), /* no importa que es B */ a_b(A,B). Los predicados var/1 y nonvar/1 forman parte del estándar ISO Prolog. EOF ej-05.txt