I am having trouble with recursive searching of list and creation of list of lists from the result..
The knowledge base contains team name, number of wins and zone they are in, all associated withe their team number. I am passing list of team numbers in Teams and I am searching for a matching pair with findMinMax/3. The result I need is...
List of lists of paired teams (ex. X = [[gonzaga, washington], [iowa, oklahoma], …])
And 1 unmatched team (resulted from odd number of teams) or 0 (in case of even)
I figured out everything else and can get up to the part [gonzaga, washington], but failing at recursive portion...
findPair(Teams,[HL|TL],Rest) :-
findMinMax(Teams,Min,Max),
delete(Teams,Min,TeamsNoMin),
delete(TeamsNoMin,Max,Rest),
createPair(Min,Max,Pair), %Pair = "["Min_team","Max_team"]"
append(HL,[Pair],TL),
findPair(Rest,TL,[]).
A general recursive scheme
Here I'll try to show you how we usually perform recursion in Prolog. It's not simple to get for beginners because the lists are built "backwards": nothing gets really built until we hit the end of the list.
The reason for this "build backwards" principle is that once a variable is set, you can't set it to another value, so for example it'd be hard to say that the result is [1] at the first step of the recursion and then becomes [1, 2]. Instead, what we say in Prolog is that the result head is 1 and that the result tail is the result of the recursive call (yeah read it twice if it got messy : d). So as long as we do not hit a base case (a case where no recursion is performed), we don't bind variables definitely (ie we always let a part of the term unbound).
For a predicate rec/2: rec(Input, Result) producing a result list from an input list by linking their elements with somepredicate/2, we'd write:
rec([InputHead|InputTail], [ResultHead|ResultTail]) :-
somepredicate(InputHead, ResultHead),
rec(InputTail, ResultTail).
to represent that.
Here you can see that we stated that the head of the result is ResultHead and that its tail is calculated thanks to the call rec(InputTail, ResultTail).
Now that's fine but we need to stop at some point, when the list is empty, for example. We'd write that as follows:
rec([], []).
which means: when the input list is empty, so is the result list.
An application to your predicate
Now, to fix your problem, you first have to fix the recursive clause:
findPair(Teams,[HL|TL],Rest) :-
findMinMax(Teams,Min,Max),
delete(Teams,Min,TeamsNoMin),
delete(TeamsNoMin,Max,Rest),
createPair(Min,Max,Pair), %Pair = "["Min_team","Max_team"]"
append(HL,[Pair],TL),
findPair(Rest,TL,[]).
would become
findPair(Teams, [Pair|Tail], LeftOver) :-
findMinMax(Teams, Min, Max),
delete(Teams, Min, TeamsNoMin),
delete(TeamsNoMin, Max, Rest),
createPair(Min, Max, Pair), %Pair = "["Min_team","Max_team"]"
findPair(Rest, Tail, LeftOver).
Important to note: now Rest has become two separate variables. The last argument of findPair/3 doesn't get changed anymore, since in the recursive call we do not know anything about it yet, so we can't bind it, and the in-predicate Rest is therefore now independant and just represents the teams that have not been handled yet and are therefore of interest for the tail of our result list (and for LeftOver).
Now we have to handle the base cases:
when there are no teams left
findPair([], [], []).
Here we say that when Teams is empty, so are the Result and LeftOver.
when there is one team left
findPair([Last], [], [Last]).
Here we say that when Teams has only one element, LeftOver is equal to Teams and Result is empty.
Resulting code is:
findPair([], [], []).
findPair([Last], [], [Last]).
findPair(Teams, [Pair|Tail], LeftOver) :-
findMinMax(Teams, Min, Max),
delete(Teams, Min, TeamsNoMin),
delete(TeamsNoMin, Max, Rest),
createPair(Min, Max, Pair), %Pair = "["Min_team","Max_team"]"
findPair(Rest, Tail, LeftOver).
To make your clauses exclusive you could replace Teams with [Not, Empty|AtAll] to ensure the last clause is used only with lists of length 2 or more, or just add a guard such as Teams = [_, _|_], at the start of the clause.
Hope it helped and do not hesitate to ask for clarifications in comments :)
Related
So in the tag prolog someone wanted to solve the "the giant cat army riddle" by Dan Finkel (see video / Link for description of the puzzle).
Since I want to improve in answer set programming I hereby challenge you to solve the puzzle more efficient than me. You will find my solution as answer. I'll accept the fastest running answer (except if it's using dirty hacks).
Rules:
hardcoding the length of the list (or something similar) counts as dirty hack.
The output has to be in the predicate r/2, where it's first argument is the index of the list and the second its entry.
Time measured is for the first valid answer.
num(0..59).
%valid operation pairs
op(N*N,N):- N=2..7.
% no need to add operations that start with 14
op(Ori,New):- num(Ori), New = Ori+7, num(New), Ori!=14.
op(Ori,New):- num(Ori), New = Ori+5, num(New), Ori!=14.
%iteratively create new numbers from old numbers
l(0,0).
{l(T+1,New) : op(Old,New)} = 1 :- l(T,Old), num(T+1), op(Old,_).
%no number twice
:- 2 #sum {1,T : l(T,Value)}, num(Value).
%2 before 10 before 14
%linear encoding
reached(T,10) :- l(T,10).
reached(T+1,10) :- reached(T,10), num(T+1).
:- reached(T,10), l(T,2).
:- l(T,14), l(T+1,_).
%looks nicer, but quadratic
%:- l(T2,2), l(T10,10), T10<T2.
%:- l(T14,14), l(T10,10), T14<T10.
%we must have these three numbers in the list somewhere
:- not l(_,2).
:- not l(_,10).
:- not l(_,14).
#show r(T,V) : l(T,V).
#show.
Having a slightly more ugly encoding improves grounding a lot (which was your main problem).
I restricted op/2 to not start with 14, as this should be the last element in the list
I do create the list iteratively, this may not be as nice, but at least for the start of the list it already removed impossible to reach values via grounding. So you will never have l(1,33) or l(2,45) etc...
Also list generation stops when reaching the value 14, as no more operation is possible/needed.
I also added a linear scaling version of the "before" section, although it is not really necessary for this short list (but a cool trick in general if you have long lists!) This is called "chaining".
Also note that your show statement is non-trivial and does create some constraints/variables.
I hope this helps, otherwise feel free to ask such questions also on our potassco mailing list ;)
My first attempt is to generate a permutation of numbers and force successor elements to be connected by one of the 3 operations (+5, +7 or sqrt). I predefine the operations to avoid choosing/counting problems. Testing for <60 is not necessary since the output of an operation has to be a number between 0 and 59. The generated List l/2 is forwarded to the output r/2 until the number 14 appears. I guess there is plenty of room to outrun my solution.
num(0..59).
%valid operation pairs
op(N*N,N):- N=2..7.
op(Ori,New):- num(Ori), New = Ori+7, num(New).
op(Ori,New):- num(Ori), New = Ori+5, num(New).
%for each position one number
l(0,0).
{l(T,N):num(N)}==1:-num(T).
{l(T,N):num(T)}==1:-num(N).
% following numbers are connected with an operation until 14
:- l(T,Ori), not op(Ori,New), l(T+1,New), l(End,14), T+1<=End.
% 2 before 10 before 14
:- l(T2,2), l(T10,10), T10<T2.
:- l(T14,14), l(T10,10), T14<T10.
% output
r(T,E):- l(T,E), l(End,14), T<=End.
#show r/2.
first Answer:
r(0,0) r(1,5) r(2,12) r(3,19) r(4,26) r(5,31) r(6,36) r(7,6)
r(8,11) r(9,16) r(10,4) r(11,2) r(12,9) r(13,3) r(14,10) r(15,15)
r(16,20) r(17,25) r(18,30) r(19,37) r(20,42) r(21,49) r(22,7) r(23,14)
There are multiple possible lists with different length.
I have an array L of some type, I'm trying to extract the data to an array, for example:
L=[day(sunday),day(monday)]
to
Target=[sunday,monday]
Tried using forall and searched for related questions on Prolog lists.
extract_data_to_list(L,Target) :-
member(day(Day),L),
length(L, L1),
length(Target, L1),
member(Day,Target).
Current output:
?- extract_data_to_list([day(sunday),day(monday)],Target).
Target = [sunday, _5448] ;
Target = [_5442, sunday] ;
Target = [monday, _5448] ;
Target = [_5442, monday].
Desired output:
?- extract_data_to_list([day(sunday),day(monday)],Target).
Target=[sunday,monday]
This is an ideal problem for maplist:
day_name(day(DayName), DayName).
dates_daylist(Dates, DayList) :-
maplist(day_name, Dates, DayList).
Maplist applies day_name to each corresponding pair of elements in Dates and DayList.
This is an ideal problem for library(lambda) for SICStus|SWI:
maplist(\day(N)^N^true, Dates, Daylist).
I have a couple other ways you can do this, just in case you're wondering.
?- findall(D, member(day(D), [day(monday), day(tuesday)]), Days).
Days = [monday, tuesday].
The trick here is that you can use findall/3 to drive a simple loop, if the Goal (argument 2) uses member/2. In this case, we're unifying day(D) with each item in the list; no further work really needs to happen besides the unification, so we're able to "tear off the wrapping" just with member/2 but you could drive a more complex loop here by parenthesizing the arguments. Suppose you wanted to change day to day-of-week, for instance:
?- findall(DoW, (member(day(D),
[day(monday), day(tuesday)]), DoW=day_of_week(D)),
Days).
Days = [day_of_week(monday), day_of_week(tuesday)].
Making the goal more complex works, in other words, as long as you parenthesize it.
The second trick is specific to SWI-Prolog (or Logtalk, if you can use that), which is the new library(yall):
?- maplist([Wrapped,Day]>>(Wrapped=day(Day)),
[day(monday),day(tuesday)], X).
X = [monday, tuesday].
library(yall) enables you to write anonymous predicates. [Wrapped,Day]>>(Wrapped=day(Day)) is sort of like an inline predicate, doing here exactly what #lurker's day_name/2 predicate is doing, except right inside the maplist/3 call itself without needing to be a separate predicate. The general syntax looks something like [Variables...]>>Goal. This sort of thing was previously available as library(lambda) and has been a feature of Logtalk for many years.
I am working on a constraints programming problem in Prolog and I am having problems trying to define a domain for lists of list. The initial challenge of the problem is as follows:
trains([[1,2,0,1], %from station, to station, departs at, arrives at
[2,3,4,5],
[2,3,0,1],
[3,4,5,6],
[3,4,2,3],
[3,4,8,9]]).
threepath(A,D,Ps):-
Ps = [[A,B, _T0, T1], [B,C, T2, T3], [C,D, T4, _T5]],
T2 #> T1,
T4 #> T3,
trains(Ts),
tuples_in(Ps, Ts).
After that, I am expected to expand on this to accommodate for any number of trains instead of only 3. Here is my attempt at doing that:
anypath(A,D,Ps,N):-
length(Ps,N),
Ps ins Xs,
Xs = [A,B,C,D],
Xs ins 1..9. %How to define the domain for a list of length 4 inside a list of variable length.
However, I am not very sure how to define a domain for lists of list. So far, I have defined length(Ps, N) so that Ps can have any length. Then, I tried to define the variables inside of Ps so that they will be list of length 4 but failed horribly.
Additionally, I am also not sure how to define the constraints for a variable length of Ps like the 3 scenario case where T2 #> T1 and T3 #> T4. The pattern that I am seeing is the last element of the next list should be greater than the third element of the list before it but I am stuck on the syntax to represent this constraint as well.
Right now, I am trying to use recursion to somehow set the Head of Ps to a list of length 4 and to recurse through to do the same with the tails since I won't be able to know how long the list Ps will be.
I would be grateful if someone can shed some light on this.
Update on Progress 25/3/2015
I read on an example of another problem that a maplist was used to produce inner lists. An excerpt of the code is:
length_(Length, List) :- length(List, Length).
child_row(X) :- X ins 1..16 .
ww(X) :-
write(X),
write('/').
print_row(Row) :-
maplist(ww, Row),
nl.
children(Class) :-
length(Class, 4),
maplist(length_(4), Class),
maplist(child_row , Class),
From what I understand, maplist(length_(4), Class) applies the length_(4) to every element inside Class and creates inner lists of length 4 as a result. So, I tried to apply this to my problem and here is my attempt:
length_(Length, List) :- length(List, Length).
anypath(A,D,Ps,N):-
length(Ps,N),
maplist(length_(4), Ps),
%constraint(Ps),
trains(Ts),
tuples_in(Ps, Ts).
However, I get an error message saying "length/2: Type error: list' expected, found4'" regardless of whether N is set to 3 or 4 and I don't quite understand this as well since it should work the same way as the example above and gtrace is a bit messy to detect what's wrong for me.
I am currently stuck at the moment and I will update if I figure anything out.
So, I have another question that I hope can be answered is "What is the normal practice of creating inner lists and how do you normally do it yourself?".
Thanks!
You can flatten the list with
append(Trains, FlatTrains)
and then constrain the domain of FlatTrains
FlatTrains ins 1..9
I am new to prolog and I am trying to create a predicate and am having some trouble.
I have a list of cities that are connected via train. They are connected via my links/2 clause.
links(toronto, ajax).
links(toronto, markham).
links(toronto, brampton).
links(brampton, markham).
links(markham, ajax).
links(brampton, mississauga).
links(mississauga, toronto).
links(mississuaga, oakville).
links(oakville, st.catharines).
links(oakville, hamilton).
links(hamilton, st.catharines).
I am writing a predicate called addnewcities which will take a list of cities and then return a new list containing the original list, plus all the cities that are directly connected to each of the cities in the original list.
Here is a (rough looking) visual representation of the links.
If my input list was [toronto] I want my output to be (order doesnt matter) [ajax,markham,brampton,mississauga,toronto].
If input was [oakville,hamilton] I want the output to be [mississauga,st.catharines,oakville,hamilton].
Here is my predicate so far.
addnewcities([],_).
addnewcities([CitiesH|Tail],Ans):- directer(CitiesH,Ans2), merger(Ans2,[CitiesH],Ans), addnewcities(Tail,Ans).
directer/2 takes a city and saves a list containing all the directly connected cities in the second arg.
merger/3 just merges two lists making sure there are no duplicates in the final list.
When my input is a list with one element ie [toronto] it works!
But when I have a list with multiple elements [toronto,ajax] it says "false" every time.
I'm pretty sure my issue is that when it recurses for the second time, merge is what says its false. I just don't know how to get around this so that my list can keep being updated instead of being checked if true or false.
Any help is appreciated!
this query uses library support to solve the problem:
addcities(Cs, L) :-
setof(D, C^(member(C,Cs), (C=D;link(C,D);link(D,C))), L).
This should work for what you want:
addcities(A,B):-
addcitiesaux(A,[],B).
addcitiesaux([],X,X).
addcitiesaux([X|Xs],L,R):-
link(X,A),
\+ member(A,L),
!,
addcitiesaux([X|Xs],[A|L],R).
addcitiesaux([X|Xs],L,R):-
link(A,X),
\+ member(A,L),
!,
addcitiesaux([X|Xs],[A|L],R).
addcitiesaux([X|Xs],L,R):-
addcitiesaux(Xs,[X|L],R).
I have a list in prolog that contains several items. I need to 'normalized' the content of this list and write the result to a new list. But I still have problem in doing it.
The following code shows how I did it:
normalizeLists(SourceList, DestList) :-
% get all the member of the source list, one by one
member(Item, SourceList),
% normalize the item
normalizeItem(Item, NormItem),
% add the normalize Item to the Destination List (it was set [] at beginning)
append(NormItem, DestList, DestList).
The problem is in the append predicate. I guess it is because in prolog, I cannot do something like in imperative programming, such as:
DestList = DestList + NormItem,
But how can I do something like that in Prolog? Or if my approach is incorrect, how can I write prolog code to solve this kind of problem.
Any help is really appreciated.
Cheers
Variables in Prolog cannot be modified, once bound by unification. That is a variable is either free or has a definite value (a term, could be another variable). Then append(NormItem, DestList, DestList) will fail for any NormItem that it's not an empty list.
Another problem it's that NormItem it's not a list at all. You can try
normalizeLists([], []).
normalizeLists([Item|Rest], [NormItem|NormRest]) :-
% normalize the item
normalizeItem(Item, NormItem),
normalizeLists(Rest, NormRest).
or (if your Prolog support it) skip altogether such definition, and use an higher order predicate, like maplist
...
maplist(normalizeItem, Items, Normalized),
...