listas - prolog listing example
Solución agregada sobre múltiples hechos (2)
Intentando crear un predicado ( timePeriod/2
) que calcule el período de tiempo entre dos fechas para un hecho específico. He logrado hacer esto solo, pero tengo problemas cuando existen ''otras respuestas'' en la misma lista (es decir, más fácil de explicar con ejemplos).
Tengo los siguientes hechos basados en el conocimiento;
popStar(''Jackson'',1987,1991).
popStar(''Jackson'',1992,1996).
popStar(''Michaels'',1996,2000).
popStar(''Newcastle'',2000,2007).
popStar(''Bowie'',2008,2010).
Y la siguiente función, calcula el tiempo entre las fechas para un hecho específico (como se muestra a continuación).
Predicado (timePeriod / 2) -
timePeriod(PS,X) :-
bagof((Name,Start,End),popStar(Name,Start,End),PSs),X is End-Start+1)
Usando Bowie como ejemplo; devuelve X=3
(que es correcto).
Sin embargo, cuando hay repetición en la lista, con más de una respuesta disponible, el predicado simplemente dice ''falso''. Usando los hechos ''Jackson'' como ejemplo, quiero poder calcular ambos períodos de tiempo para ambos hechos; al mismo tiempo.
Entonces, si el predicado funcionaría para ambos hechos de Jackson, el predicado timePeriod
establecería X=10
.
Realmente apreciaría si alguien pudiera sugerir qué cambiar para que esto funcione correctamente.
Gracias.
Probablemente no entiendas muy bien qué hace foreach/3
. No creo que entiendo completamente foreach/3
tampoco. Sé con certeza que no es lo mismo que decir:
for each x in xs:
do foo(x)
Otra cosa: las "tuplas" en Prolog no son lo que cabría esperar, provenientes de un lenguaje como Python o Haskell. Esto: (a,b,c)
es en realidad esto: '',''(a,'',''(b,c))
. Mucho mejor es usar un término fijo, la forma genérica sería triple(a,b,c)
. Para un par, el modismo es First-Second
.
Entonces, puedes simplificar tu llamada a bagof/3
a esto:
?- bagof(From-To, pop_star(Name, Start, End), Ts).
Name = ''Bowie'',
Ts = [2008-2010] ;
Name = ''Jackson'',
Ts = [1987-1991, 1992-1996] ;
Name = ''Michaels'',
Ts = [1996-2000] ;
Name = ''Newcastle'',
Ts = [2000-2007].
Una vez que tenga una lista como la anterior, debe sumar las diferencias, que podrían ser algo así como:
periods_total(Ps, T) :-
maplist(period_length, Ps, Ls),
sum_list(Ls, T).
period_length(From-To, Length) :-
Length is To - From + 1.
Y luego puedes consultar de esta manera:
?- bagof(From-To, pop_star(''Jackson'', From, To), Ps), periods_total(Ps, T).
Ps = [1987-1991, 1992-1996],
T = 10.
?- bagof(From-To, pop_star(Name, From, To), Ps), periods_total(Ps, T).
Name = ''Bowie'',
Ps = [2008-2010],
T = 3 ;
Name = ''Jackson'',
Ps = [1987-1991, 1992-1996],
T = 10 ;
Name = ''Michaels'',
Ps = [1996-2000],
T = 5 ;
Name = ''Newcastle'',
Ps = [2000-2007],
T = 8.
SWI-Prolog tiene una buena biblioteca para manejar la agregación: se basa en los predicados estándar de ''todas las soluciones'' como findall / 3, setof / 3, bagof / 3, por lo que primero debe comprender los básicos de estos (como explicó Boris en su respuesta) . Con la biblioteca, una sola consulta resuelve su problema:
timePeriod(PS,X) :-
aggregate(sum(P), B^E^(popStar(PS,B,E),P is E-B+1), X).