tags:

views:

547

answers:

4

Is there a easy way to make a query in prolog only return each result once?

for instance I'm trying something like:

deadly(Xn) :- scary(X), Xn is X - 1, Xp is X + 1, not(safe(Xn)), safe(Xp).
deadly(Xp) :- scary(X), Xn is X - 1, Xp is X + 1, not(safe(Xp)), safe(Xn).

deadly(X).

and getting

X = 5

X = 5

X = 5

X = 5

....

Not to usefull for me.

+1  A: 

It's hard to say without more of your code, but you're probably looking for the cut operator (!). If you want to post the definition of foo, I (or someone else who follows) may be able to give a detailed/specific answer.

MarkusQ
every time I've used ! it has totally stopped evaluation and I do want every value that prolog is finding, but only once per value. (I'll try and cut down my case)
BCS
It's a matter of when and where to use it. Saying "never come back here again" has a wildly different meaning coming before or after the first capture of the data.
MarkusQ
+2  A: 

If I remember correctly there is a predicate solutions (or similar, it's been a while since I programmed Prolog) which collects unique solutions in a list.

Edit: setof/3 is the one I was thinking of. Thanks, Kaarel.

starblue
findall/3, bagof/3, setof/3
Kaarel
+2  A: 

One thing that you can do is to apply setof/3 to the predicate that generates the solutions. But note that setof/3 is implemented by applying sort/2 to the result delivered by bagof/3 (at least this is the case in SWI-Prolog). So, if your solution generator goes on forever, then setof/3 will never be applied...

So I would say that try to program so that duplicates are not generated, i.e. by using the cut (!) where it makes sense.

Kaarel
If I remembercorrectly, setof is only useful if the query has a finite solution size, and it knows that it's done. Otherwise, it could just keep getting a 5, saying, Nope, already got that, and looking again, and getting ANOTHER 5... infinite loop.
Brian Postow
Thanks. I've updated the answer a bit.
Kaarel
+1  A: 

Another approach is to memoize solutions.

:- dynamic seen/1.

% Call this always before calling deadly_wrapper/1
clear_seen :-
    retractall(seen(_)).

% This wrapper calls deadly/1 but remembers
% the solution using assert/1, and fails
% if the solution has been "seen" before.
deadly_wrapper(X) :-
    deadly(X),
    (
        seen(X)
    ->  
        fail
    ;
        assert(seen(X))
    ).  


% This is for testing.
deadly(1).
deadly(1).
deadly(1).
deadly(5).
deadly(1).
deadly(1).

In case your Prolog supports tabling, then it gets even simpler. Example file:

:- table deadly/1.

deadly(1).
deadly(1).
deadly(5).
deadly(1).
deadly(5).

Example execution with XSB:

$ xsb
[xsb_configuration loaded]
[sysinitrc loaded]

XSB Version 3.2 (Kopi Lewak) of March 15, 2009
[x86_64-unknown-linux-gnu; mode: optimal; engine: slg-wam;
 scheduling: local; word size: 64]

| ?- [deadly_tab].
[Compiling ./deadly_tab]
[deadly_tab compiled, cpu time used: 0.0100 seconds]
[deadly_tab loaded]

yes
| ?- deadly(X).

X = 5;

X = 1;

no
| ?-
Kaarel
I would say "memoize", rather than "memorize".
Matthew Schinckel