1
votes

Suppose I have a predicate that sometimes gives me multiple outputs. Like this -

foo(Number, Out) :- Number < 10, Out = less_than_ten.
foo(Number, Out) :- Number > 0, Out = more_than_zero.

How might I get hold of all the values for Out that foo gives in another predicate, bearing in mind it can sometimes give one and sometimes give multiple (e.g. in a list)?

Edit - not quite sure the answers I've got answer my question so I'll be more specific. Taking the above predicate, I could run the query foo(5, Out). This satisfies both rules, so if I run it in SWI-prolog I'll get this -

?- foo(5, Out).
Out = less_than_ten

Then I can enter a semi-colon to get prolog to backtrack and look for the other solution -

?- foo(5, Out).
Out = less_than_ten ;
Out = more_than_zero.

So if I was executing this predicate within another, how do I get all the valid values for Out, given Number = 5?

2

2 Answers

3
votes

If you are only considering integers, you can opt to use CLP(FD). Then your predicate foo might look something like this:

:- use_module(library(clpfd)).

foo(Nums) :-
   Nums ins 1..9.     % the numbers in the list Nums are 0 < X < 10

You can use this predicate to test if a list of numbers is in your desired range:

   ?- foo([1,2,3,4,5]).
yes
   ?- foo([0,1,2,3,4,5]).
no
   ?- foo([1,2,3,4,5,10]).
no

If you want to use it to generate lists of integers in that range, you have make sure, that Nums is a list in order to avoid an instantiation error. You can do that by prefixing a goal length/2 in your query:

   ?- length(Nums,_), foo(Nums).
Nums = [] ? ;          % <- no number
Nums = [_A],           % <- one number
_A in 1..9 ? ;
Nums = [_A,_B],        % <- two numbers
_A in 1..9,
_B in 1..9 ? ;
Nums = [_A,_B,_C],     % <- three numbers
_A in 1..9,
_B in 1..9,
_C in 1..9 ?
.
.
.

These answers consist of residual goals (see the CLP(FD) documentation for details). If you want to see actual numbers, you have to add a goal to label the list:

   ?- length(Nums,_), foo(Nums), label(Nums).
Nums = [] ? ;
Nums = [1] ? ;
Nums = [2] ? ;
Nums = [3] ? ;
.
.
.
Nums = [1,1] ? ;
Nums = [1,2] ? ;
Nums = [1,3] ? ;
.
.
.
Nums = [9,9] ? ;
Nums = [1,1,1] ? ;
Nums = [1,1,2] ? ;
.
.
.
0
votes

Just found the predicate that answers this question. Prolog has a built in predicate bagof(Template, Goal, Bag), which unifies the Template argument of the Goal predicate with the Bag argument (not sure that's the correct prolog lingo!). So in the example in the question, use bagof(Out, foo(5, Out), Outputs)., and this will unify a list of the solutions for Out with Outputs. Running the query in SWI-prolog:

4 ?- bagof(Out, foo(5, Out), Outputs).
Outputs = [less_than_ten, more_than_zero].

A useful guide for different ways to find all solutions to a goal.