Block world problem search runs out of stack space - search
I have the following code:
move(state(on(X, NewX), OldY, Z), state(NewX, on(X, OldY), Z)).
move(state(on(X, NewX), Y, OldZ), state(NewX, Y, on(X, OldZ))).
move(state(OldX, on(Y, NewY), Z), state(on(Y, OldX), NewY, Z)).
move(state(X, on(Y, NewY), OldZ), state(X, NewY, on(Y, OldZ))).
move(state(OldX, Y, on(Z, NewZ)), state(on(Z, OldX), Y, NewZ)).
move(state(X, OldY, on(Z, NewZ)), state(X, on(Z, OldY), NewZ)).
path(X,X,[]).
path(X,Y,[Z|ZS]) :-
move(X,Z),
path(Z,Y,ZS).
Where move give us the possible movements that you can use and path should give us the path that you have to take from X to Y.
The problem is that the predicate path doesn't work as I want, i.e., if I type path(state(on(c,on(b,on(a,void))), void, void), state(void, void, on(c,on(a,on(b,void)))), X). I got ERROR: Out of local stack, but I want that that X would be
X=[state(void, void, on(c,on(a,on(b,void)))),
state(void, on(c,void), on(void(a,on(b,void))),
state(on(a,void), on(c,void), on(b,void)),
state(on(b,on(a,void)), on(c,void), void),
state(on(c,on(b,on(a,void))), void, void)].
So what am I doing wrong?
For a first test, there is no need to rewrite your code. Not since summer 19721. Instead, you can reformulate your queries sparingly.
Instead of asking for a concrete answer which demands from your Prolog system quite a bit of ingenuity, let's formulate your answer as a query! I tried it and realized that you have some nasty syntax errors in them, and after that, the query failed..
But there is a cheaper way! Let's just limit the length of the list and let Prolog fill out the rest. How long shall that list be? We know not (that is, I don't). OK, so let's try out any length! Also this is something Prolog loves. It is as easy as:
?- length(X,N), % new
path( state(on(c,on(b,on(a,void))), void, void),
state(void, void, on(c,on(a,on(b,void)))),
X).
X = [ state(on(b,on(a,void)),on(c,void),void),
state(on(a,void),on(c,void),on(b,void)),
state(void,on(c,void),on(a,on(b,void))),
state(void,void,on(c,on(a,on(b,void)))) ],
N = 4
; ... .
See what I did? I only added length(X, N) in front. And out of a sudden Prolog answered with a shorter answer than you expected!
Now, is this really the best way to ask? After all, many of the answers might be simple cycles, putting a block into one place and back again... Are there really any cycles? Let's ask that first:
... --> [] | [_], ... .
?- length(X,N),
path( state(on(c,on(b,on(a,void))), void, void),
state(void, void, on(c,on(a,on(b,void)))),
X),
phrase((...,[E],...,[E],...), X).
X = ...
N = 6,
E = state(void,on(c,void),on(a,on(b,void)))
; ... .
Oh yes, there are! Now it does make sense to rule out such paths. Here is a clean way:
alldifferent([]).
alldifferent([X|Xs]) :-
maplist(dif(X), Xs),
alldifferent(Xs).
?- alldifferent(X),
length(X,N),
path( state(on(c,on(b,on(a,void))), void, void),
state(void, void, on(c,on(a,on(b,void)))),
X).
How far can you get with this formulation? Currently, I found a path of length 48 ... 55 ... Shouldn't it be finite? And: Is it possible to rule out such long paths for such trivial problems? Any toddler can keep the search space small... These are all fundamental questions, but they are independent of the programming problem as such.
Or, see it from another angle: The set of solutions for X is pretty large. So if we are exploring this set, where shall we start? What does it mean to be the best solution? The one that when uploaded on Utube produces the most upvotes? So what we are doing here is completely uninformed search. You would need to inform the program what kind of preference you have. It cannot guess it reasonably. OK, one heuristics would be the term size of a solution. length/2 did that.
Note that I did not dare to touch your clean code. Yes, I could have improved it somewhat, say by using path/4, but not by much. Rather stick to your highly clean style and rather do more querying instead! That is what Prolog is excellent at!
Other improvements: Use a list to represent the stack, this makes the state much more appealing.
1 That's the year Prolog was discovered/conceived/delivered.
Ohh... a block world problem!
It is simply because you do two things:
Depth first search through state space.
Failure to test whether a state has already been visited.
(Additionally, the solution you give is not a reachable state, the second line has a void on a wrong position, plus the path is reversed).
In fact, you construct the path through state path on return only in the third argument here: path(X,Y,[Z|ZS]).
You have to check on each state expansion whether a new state might already be on the path. Otherwise the program may cycle forever (depending on how it hits the move/2 move-generating predicate ... actually a nice exercise select a move/2 probabilistically... maybe later). In the code below, the check is done by fail_if_visited/2.
Additionally, a depth-first search according to the above will find a solution path, but the will likely not be a short path and not the solution sought.
You really need breadth-first search (or rather, Iterative Deepening). As Prolog doesn't allow to switch out the search algorithm (why not? it's been over 40 years), you have to roll one yourself.
Observe:
% ===
% Transform a state into a string
% ===
express(state(A,B,C),S) :-
express_pos(A,SA),
express_pos(B,SB),
express_pos(C,SC),
atomic_list_concat(["[",SA,",",SB,",",SC,"]"],S).
express_pos(on(Top,Rest),S) :-
express_pos(Rest,S2),
atomic_list_concat([Top,S2],S).
express_pos(void,"").
% ===
% Transform a path into a string
% (The path is given in the reverse order; no matter)
% ===
express_path(Path,PathStr) :-
express_path_states(Path,StateStrs),
atomic_list_concat(StateStrs,"<-",PathStr).
express_path_states([S|Ss],[StateStr|SubStateStrs]) :-
express_path_states(Ss,SubStateStrs),
express(S,StateStr).
express_path_states([],[]).
% ===
% For debugging
% ===
debug_proposed(Current,Next,Moved,Path) :-
express(Current,CurrentStr),
express(Next,NextStr),
length(Path,L),
debug(pather,"...Proposed at path length ~d: ~w -> ~w (~q)",[L,CurrentStr,NextStr,Moved]).
debug_accepted(State) :-
express(State,StateStr),
debug(pather,"...Accepted: ~w",[StateStr]).
debug_visited(State) :-
express(State,StateStr),
debug(pather,"...Visited: ~w",[StateStr]).
debug_moved(X) :-
debug(pather,"...Already moved: ~w",[X]).
debug_final(State) :-
express(State,StateStr),
debug(pather,"Final state reached: ~w",[StateStr]).
debug_current(State,Path) :-
express(State,StateStr),
express_path(Path,PathStr),
length(Path,L),
debug(pather,"Now at: ~w with path length ~d and path ~w",[StateStr,L,PathStr]).
debug_path(Path) :-
express_path(Path,PathStr),
debug(pather,"Path: ~w",[PathStr]).
% ===
% Moving blocks between three stacks, also recording the move
% ===
move(state(on(X, A), B, C),
state(A, on(X, B), C),
moved(X,"A->B")).
move(state(on(X, A), B, C),
state(A, B, on(X, C)),
moved(X,"A->C")).
move(state(A, on(X, B), C),
state(on(X, A), B, C),
moved(X,"B->A")).
move(state(A, on(X, B), C),
state(A, B, on(X, C)),
moved(X,"B->C")).
move(state(A, B, on(X, C)),
state(on(X, A), B, C),
moved(X,"C->A")).
move(state(A, B, on(X, C)),
state(A, on(X, B), C),
moved(X,"C->B")).
move(_,_,_,_) :- debug(pather,"No more moves",[]).
% ===
% Finding a path from an Initial State I to a Final State F.
% You have to remember the path taken so far to avoid cycles,
% instead of trying to reach the final state while the path-so-far
% is sitting inaccessible on the stack, from whence it can only be
% be reconstructed on return-fro-recursion.
% ===
fail_if_visited(State,Path) :-
(memberchk(State,Path)
-> (debug_visited(State),fail)
; true).
fail_if_moved(moved(X,_),LastMoved) :-
(LastMoved = moved(X,_)
-> (debug_moved(X),fail)
; true).
path2(F,F,Path,Path,_) :-
debug_final(F).
path2(I,F,PathToI,FullPath,LastMoved) :-
dif(I,F), % I,F are sure different (program will block if it can't be sure)
debug_current(I,PathToI),
move(I,Next,Moved), % backtrackably pattern-match yourself an acceptable next state based on I
ground(Next), % fully ground, btw
debug_proposed(I,Next,Moved,PathToI),
fail_if_moved(Moved,LastMoved), % don't want to move the same thing again
fail_if_visited(Next,PathToI), % maybe already visited?
debug_accepted(Next), % if we are here, not visited
PathToNext = [Next|PathToI],
path2(Next,F,PathToNext,FullPath,Moved). % recurse with path-so-far (in reverse)
% ---
% Top call
% ---
path(I,F,Path) :-
PathToI = [I],
path2(I,F,PathToI,FullPath,[]), % FullPath will "fish" the full path out of the depth of the stack
reverse(FullPath,Path), % don't care about efficiency of reverse/2 at all
debug_path(Path).
% ===
% Test
% ===
:- begin_tests(pather).
test(one, true(Path = [state(void, void, on(c,on(a,on(b,void)))),
state(void, on(c,void), on(void(a,on(b,void)))),
state(on(a,void), on(c,void), on(b,void)),
state(on(b,on(a,void)), on(c,void), void),
state(on(c,on(b,on(a,void))), void, void)]))
:- I = state(on(c,on(b,on(a,void))), void, void),
F = state(void, void, on(c,on(a,on(b,void)))),
path(I,F,Path).
:- end_tests(pather).
rt :- debug(pather),run_tests(pather).
At the end we get:
% ...Accepted: [c,,ab]
% Now at: [c,,ab] with path length 24 and path [c,,ab]<-[,c,ab]<-[,ac,b]<-[b,ac,]<-[ab,c,]<-[ab,,c]<-[b,a,c]<-[,a,bc]<-[a,,bc]<-[a,b,c]<-[,ab,c]<-[c,ab,]<-[ac,b,]<-[ac,,b]<-[c,a,b]<-[,ca,b]<-[b,ca,]<-[cb,a,]<-[cb,,a]<-[b,c,a]<-[,bc,a]<-[a,bc,]<-[ba,c,]<-[cba,,]
% ...Proposed at path length 24: [c,,ab] -> [,c,ab] (moved(c,"A->B"))
% ...Already moved: c
% ...Proposed at path length 24: [c,,ab] -> [,,cab] (moved(c,"A->C"))
% ...Already moved: c
% ...Proposed at path length 24: [c,,ab] -> [ac,,b] (moved(a,"C->A"))
% ...Visited: [ac,,b]
% ...Proposed at path length 24: [c,,ab] -> [c,a,b] (moved(a,"C->B"))
% ...Visited: [c,a,b]
% ...Proposed at path length 23: [,c,ab] -> [,,cab] (moved(c,"B->C"))
% ...Accepted: [,,cab]
% Final state reached: [,,cab]
% Path: [cba,,]<-[ba,c,]<-[a,bc,]<-[,bc,a]<-[b,c,a]<-[cb,,a]<-[cb,a,]<-[b,ca,]<-[,ca,b]<-[c,a,b]<-[ac,,b]<-[ac,b,]<-[c,ab,]<-[,ab,c]<-[a,b,c]<-[a,,bc]<-[,a,bc]<-[b,a,c]<-[ab,,c]<-[ab,c,]<-[b,ac,]<-[,ac,b]<-[,c,ab]<-[,,cab]
ERROR: /home/homexercises/pather.pl:146:
test one: wrong answer (compared using =)
ERROR: Expected: [state(void,void,on(c,on(a,on(b,void)))),state(void,on(c,void),on(void(a,on(b,void)))),state(on(a,void),on(c,void),on(b,void)),state(on(b,on(a,void)),on(c,void),void),state(on(c,on(b,on(a,void))),void,void)]
ERROR: Got: [state(on(c,on(b,on(a,void))),void,void),state(on(b,on(a,void)),on(c,void),void),state(on(a,void),on(b,on(c,void)),void),state(void,on(b,on(c,void)),on(a,void)),state(on(b,void),on(c,void),on(a,void)),state(on(c,on(b,void)),void,on(a,void)),state(on(c,on(b,void)),on(a,void),void),state(on(b,void),on(c,on(a,void)),void),state(void,on(c,on(a,void)),on(b,void)),state(on(c,void),on(a,void),on(b,void)),state(on(a,on(c,void)),void,on(b,void)),state(on(a,on(c,void)),on(b,void),void),state(on(c,void),on(a,on(b,void)),void),state(void,on(a,on(b,void)),on(c,void)),state(on(a,void),on(b,void),on(c,void)),state(on(a,void),void,on(b,on(c,void))),state(void,on(a,void),on(b,on(c,void))),state(on(b,void),on(a,void),on(c,void)),state(on(a,on(b,void)),void,on(c,void)),state(on(a,on(b,void)),on(c,void),void),state(on(b,void),on(a,on(c,void)),void),state(void,on(a,on(c,void)),on(b,void)),state(void,on(c,void),on(a,on(b,void))),state(void,void,on(c,on(a,on(b,void))))]
done
% 1 test failed
% 0 tests passed
false.
A path of length 23 successfully reaches the final state, but is "too long" according to the sought solution. Even with the heuristic "do not move a block twice" expressed in fail_if_moved/2.
Addendum: Probabilistic search
Using a Randomized Algorithm is amazingly rewarding:
Rip out the move/3 predicate from above and replace it by:
move(From,To,Moved) :-
random_permutation([0,1,2,3,4,5],ONs), % permute order numbers
!, % no backtracking past here!
move_randomly(ONs,From,To,Moved). % try to match a move
move_randomly([ON|___],From,To,Moved) :- move(ON,From,To,Moved).
move_randomly([__|ONs],From,To,Moved) :- move_randomly(ONs,From,To,Moved).
move_randomly([],_,_,_) :- debug(pather,"No more moves",[]).
move(0,state(on(X, A), B, C),
state(A, on(X, B), C),
moved(X,"0: A->B")).
move(1,state(on(X, A), B, C),
state(A, B, on(X, C)),
moved(X,"1: A->C")).
move(2,state(A, on(X, B), C),
state(on(X, A), B, C),
moved(X,"2: B->A")).
move(3,state(A, on(X, B), C),
state(A, B, on(X, C)),
moved(X,"3: B->C")).
move(4,state(A, B, on(X, C)),
state(on(X, A), B, C),
moved(X,"4: C->A")).
move(5,state(A, B, on(X, C)),
state(A, on(X, B), C),
moved(X,"5: C->B")).
Evidently this is not the paradigm of efficient Prolog, but who cares:
A solution of length 5 was found within 7 tries only!
Path: [cba,,]<-[ba,c,]<-[a,c,b]<-[,c,ab]<-[,,cab] (Length 5)
Related
Replacing substring in second occurrence in prolog
First of all, this is not a homework. I'm studying Computer Sciences in my home, to learn a little more alone. I'm doing an excercise. It says like this: Construct a predicate called replaceAtomsString/4 so that given a string s as the first parameter, a number N as the second parameter, and a pair of atoms [g, h] (list) as the third parameter, unify in a fourth parameter the replacement in the Nth apparition of g in s replacing it by h. Example: replaceAtomsString (sAbbbsAbbasA, 2, [sA, cc], X) should result in X = sAbbbccbbasA So, my first approach was trying to build a list with the string, just like prolog do with every string. After all, i've built this code: substitute(X, S, T, Y) :- append(S, Xt, X), % i.e. S is the first part of X, the rest is Xt !, substitute(Xt, S, T, Yt), append(T, Yt, Y). substitute([Xh|Xt], S, T, [Xh|Yt]) :- substitute(Xt, S, T, Yt). But it returns false on every attempt. Any ideas?
Since you need substantial work to get your code done, here is how to perform the task using the available libraries. sub_atom/5 it's a rather powerful predicate to handle atoms. Coupled with call_nth/2, the solution is straightforward and more general than what would result coding the loop around N. replaceAtomsString(S,N,[G,H],X) :- call_nth(sub_atom(S,Before,_,After,G),N), sub_atom(S,0,Before,_,Left), sub_atom(S,_,After,0,Right), atomic_list_concat([Left,H,Right],X). Example running your query, but leaving N to be computed: ?- replaceAtomsString(sAbbbsAbbasA, N, [sA, cc], X). N = 1, X = ccbbbsAbbasA ; N = 2, X = sAbbbccbbasA ; N = 3, X = sAbbbsAbbacc ; false.
G-machine, (non-)strict contexts - why case expressions need special treatment
I'm currently reading Implementing functional languages: a tutorial by SPJ and the (sub)chapter I'll be referring to in this question is 3.8.7 (page 136). The first remark there is that a reader following the tutorial has not yet implemented C scheme compilation (that is, of expressions appearing in non-strict contexts) of ECase expressions. The solution proposed is to transform a Core program so that ECase expressions simply never appear in non-strict contexts. Specifically, each such occurrence creates a new supercombinator with exactly one variable which body corresponds to the original ECase expression, and the occurrence itself is replaced with a call to that supercombinator. Below I present a (slightly modified) example of such transformation from 1 t a b = Pack{2,1} ; f x = Pack{2,2} (case t x 7 6 of <1> -> 1; <2> -> 2) Pack{1,0} ; main = f 3 == transformed into ==> t a b = Pack{2,1} ; f x = Pack{2,2} ($Case1 (t x 7 6)) Pack{1,0} ; $Case1 x = case x of <1> -> 1; <2> -> 2 ; main = f 3 I implemented this solution and it works like charm, that is, the output is Pack{2,2} 2 Pack{1,0}. However, what I don't understand is - why all that trouble? I hope it's not just me, but the first thought I had of solving the problem was to just implement compilation of ECase expressions in C scheme. And I did it by mimicking the rule for compilation in E scheme (page 134 in 1 but I present that rule here for completeness): so I used E[[case e of alts]] p = E[[e]] p ++ [Casejump D[[alts]] p] and wrote C[[case e of alts]] p = C[[e]] p ++ [Eval] ++ [Casejump D[[alts]] p] I added [Eval] because Casejump needs an argument on top of the stack in weak head normal form (WHNF) and C scheme doesn't guarantee that, as opposed to E scheme. But then the output changes to enigmatic: Pack{2,2} 2 6. The same applies when I use the same rule as for E scheme, i.e. C[[case e of alts]] p = E[[e]] p ++ [Casejump D[[alts]] p] So I guess that my "obvious" solution is inherently wrong - and I can see that from outputs. But I'm having trouble stating formal arguments as to why that approach was bound to fail. Can someone provide me with such argument/proof or some intuition as to why the naive approach doesn't work?
The purpose of the C scheme is to not perform any computation, but just delay everything until an EVAL happens (which it might or might not). What are you doing in your proposed code generation for case? You're calling EVAL! And the whole purpose of C is to not call EVAL on anything, so you've now evaluated something prematurely. The only way you could generate code directly for case in the C scheme would be to add some new instruction to perform the case analysis once it's evaluated. But we (Thomas Johnsson and I) decided it was simpler to just lift out such expressions. The exact historical details are lost in time though. :)
How to count the number of occurrences of a type passed to a function in haskell
How would one count the number of times a data type was passed into a function and a total of the values? I am new to FP and not sure if this is permitted by mutability laws or referential transparency. The context is working with stacks and trying to work out if you passed in a series of instructions to the stack you could work out the frequency particular instruction was passed in and the total value of all those type, as a sort of counter... I have searched around to no avail and starting to think my approach may be fundamentally flawed so any advice would be appreciated, but i thought i would put it out there as i'm interested to know, i was working along the lines of; > data Value > = Numeric Int > | Logical Bool > deriving (Eq, Show, Read) ... > data Instruction > = Push Value > | Pop > | Fetch Int > | Store Int ... > step inst c= > case (inst) of > (Push, stack) -> (c', x : stack) > (Pop, _ : stack) -> (c', stack) > where > c = c' + 1 ...
Instead of explicitly managing the stack, you can use the State monad from Control.Monad.State. For details of the inner workings you should read the docs. step :: Instruction -> State [Value] () step (Push v) = do stack <- get put (v:stack) step Pop = do (_:stack) <- get put stack You can also store the number of each instruction in the state: step :: Instruction -> State (Int, Int, Int, Int, [Value]) () step (Push v) = do (a, b, c, d, stack) <- get put (a+1, b, c, d, v:stack) step Pop = do (a, b, c, d, (_:stack)) <- get put (a, b+1, c, d, stack) Working with a 5-tuple is somewhat cumbersome so you may want to define your own datatype for this. In this model, the first Int is the number of Pushes, the second, the number of Pops, etc.
So you need to apply a whole sequence of stack operations and to get a resulting stack and operations calling statistics. For accumulating them in a pure way, you need to carry them along with you chain of operations. You can actually do it in some ways: 1) add the stats explicitly to each function call and combine them manually; 2) or wrap them into a monad so calls could be automatically chained with >>= or sequence. The last one suggest some particular variants. 2.1) Use State, as user2407038 proposed earlier. It hides an additional argument which carry stats so it should look like an imperative state which can be manipulated via put, get and modify. 2.2) Use Writer, which can be considered a «fat free» State where you can only add «something» (e.g. which operation was called) to your carried stats — which is actually what you need (as I can understand). Computations will be simpler because instead of all those put-s, get-s and modify-es you'll have single tell. But you'll need to make your Stats type an instance of Monoid(which is quite easy and linear, although). 2.3) Use ST, where types can be quite frightening, but you can use mutable infringement counters for performance. I wouldn't recommend that without real necessity, however.
You could assign a number to each instruction, and add an extra argument to the function, so that when when the number and the instruction match the count gets incremented. The input and output would be the program, the stack(s), and the counter. step i1 (push x : insts, stack, c) = if i1 == 0 then step i1 (insts, x : stack, c + 1) else step i1 (insts, x : stack, c) step i1 (pop : insts, _ : stack, c) = if i1 == 1 then step i1 (insts, stack, c + 1) else step i1 (insts, stack, c)
Prolog importing facts from a formatted text file
I have the following input in a text file input.txt atom1,atom2,atom3 relation(atom1 ,[10,5,2]) relation(atom2 ,[3,10,2]) relation(atom3 ,[6,5,10]) First line includes the list of atoms used in relation predicates in the file and each remaining line represents a relation predicate in order of the first line list.relation(atom1, [x,y,z]) means atom1 has a relation value of 10 with first atom, 5 with the second and 2 with the third I need to read this file and add represent relation values for each atom seperately.For example , these are the relation values which will be added for atom1 : assert(relation(atom1, atom1,10)). assert(relation(atom1, atom2, 5)). assert(relation(atom1, atom3, 2)). I have read some prolog io tutorials and seen some recommendations on using DCG but I'm a beginner prolog programmer and having trouble to choose the method for the solving problem. So I'm here to ask help from experienced prolog programmers.
Since you didn't stated what Prolog you're using, here is a snippet written in SWI-Prolog. I attempted to signal non ISO builtins by means of SWI-Prolog docs reference. parse_input :- open('input.txt', read, S), parse_line(S, atoms(Atoms)), repeat, ( parse_line(S, a_struct(relation(A, L))) -> store(Atoms, A, L), fail ; true ), close(S). :- meta_predicate(parse_line(+, //)). parse_line(S, Grammar) :- % see http://www.swi-prolog.org/pldoc/doc_for?object=read_line_to_codes/2 read_line_to_codes(S, L), L \= end_of_file, phrase(Grammar, L). % match any sequence % note - clauses order is mandatory star([]) --> []. star([C|Cs]) --> [C], star(Cs). % --- DCGs --- % comma sep atoms atoms(R) --> star(S), ( ",", {atom_codes(A, S), R = [A|As]}, atoms(As) ; {atom_codes(A, S), R = [A]} ). % parse a struct X, % but it's far easier to use a builtin :) % see http://www.swi-prolog.org/pldoc/doc_for?object=atom_to_term/3 a_struct(X, Cs, []) :- atom_codes(A, Cs), atom_to_term(A, X, []). % storage handler :- dynamic(relation/3). store(Atoms, A, L) :- nth1(I, L, W), nth1(I, Atoms, B), assertz(relation(A, B, W)). with the sample input.txt, I get ?- parse_input. true . ?- listing(relation). :- dynamic relation/3. relation(atom1, atom1, 10). relation(atom1, atom2, 5). relation(atom1, atom3, 2). relation(atom2, atom1, 3). relation(atom2, atom2, 10). relation(atom2, atom3, 2). relation(atom3, atom1, 6). relation(atom3, atom2, 5). relation(atom3, atom3, 10). HTH
Prolog find all paths Implementation
I've been tasked to implement a version of findall in Prolog without using any Prolog built-ins except for not and cut - so basically in pure Prolog. I'm trying to search a tree for all direct descendants and return the results in a list parent(a, b). parent(b, c). parent(b, d). parent(e, d). What I have so far is: find(X, L) :- find2(X, [], L). find2(X, Acc, L) :- parent(Y, X), find2(Y, [Y|Acc], L). find2(_, Acc, Acc). What I want to be getting when I enter for example: find(a,X). would be: X = [b, c, d] (Order not important) However instead I am getting: X = [b, c] ; X = [b, d] ; X = [b] ; X = []. I'm new to Prolog so any help on this would be much appreciated. Thanks
Besides asserting data as you go, you can also use an extra-logical predicate such as nb_setarg/3. Then once a parent is found, you fail back past nb_setarg and find another parent. All previously found solutions should stay in the term you did nb_setarg on, then after all results are exhausted, the nb_setarg term is the answer. The SWI-Prolog example is good, but its just a counter. Try doing it with a list (or better yet: difference list) that builds as you go.
Take a look at this solution. Note that this solution uses dynamic predicate named queue in order to cache all solutions until all possibilities are exhausted. Once no more solution exists, implementation retracts all facts and composes the list. This is of course a bit simplified solution, imagine what would happen if two findall would be active at the same time. It is also a bit fragile on exact semantics of assert and retract if particular prolog implementation
Thanks for you help everyone. I managed to solve it in the end by adding a predicate which checked each item against the current list, and failed if it was already present: find(X, Loa) :- find(X, [], Loa), !. find(X, Acc, Loa) :- dec(X, Y), uList(Y, Acc, AccNew), find(X, AccNew, Loa). find(_, Acc, Acc). dec(X,Y) :- parent(X,Y). dec(X,Y) :- parent(X,Z), dec(Z,Y). uList(X, [], [X]) :- !. uList(H, [H|_], _) :- !, fail. uList(X, [H|T], L) :- uList(X, T, Rtn), L = [H|Rtn].