0

I am trying to implement a simple version of findall in Prolog without using the built-in findall or similar built-in predicates -- just as a learning exercise.

For example, suppose there is a relation a/1 in my database,

a(1).
a(11).
a(13).

and I want a relation like find_the_as(List) to give me List = [1,11,13].

I can do this with a "fail" based loop using assert and retract, but I am thinking there must be a recursive way to do this using an accumulator (and I am just not seeing it).

Thanks in advance!

for example:

a(1).
a(11).
a(13).

%
% collect all the N such that a(N) 
% into a List
%  
collect_n_such_that_a_of_n(List) :- 
   retractall(the_n_such_that_a_of_n(_)),
   assert(the_n_such_that_a_of_n([])),
   (
      a(N),
      the_n_such_that_a_of_n(As),
      retractall(the_n_such_that_a_of_n(_)),
      assert(the_n_such_that_a_of_n([N|As])),
      fail
   ) ; true,
   the_n_such_that_a_of_n(List).
   
PaulM
  • 305
  • 1
  • 8

2 Answers2

0

Using this example:

a(1).
a(11).
a(13).

find(Input,Input) :- \+ (a(X), \+ memberchk(X,Input)).
find(Input,Output) :- a(X), \+ memberchk(X,Input), !, find([X|Input],Output). 

find_the_as(List) :- find([], List).

then the query:

find_the_as(List).

returns:

List = [13, 11, 1]

The example uses:

  • memberchk/2 - True if the first argument is a member of the list represented by the second argument.
  • \+/1 - True if the goal represented by its argument cannot be proven.

Unlike with findall, the resulting list does not contain duplicates. eg. if you add an extra a(11) then the resulting list will still only contain one 11.

Unlike with findall, the elements in the resulting list are in reverse order to which they were found (i.e. [13, 11, 1] rather than [1, 11, 13]).

14241354
  • 156
  • 4
0

Using call_nth/2 (beware of procedures which have side effects):

findall_recursive(Template, Goal, Bag):-
  findall_recursive(Template, Goal, Bag, 1).
  
findall_recursive(Template, Goal, Bag, N) :-
    copy_term(Goal-Template, CGoal-CTemplate),
    (   call_nth(CGoal, N)
    ->  succ(N, N1),
        Bag=[CTemplate|Bag1],
        findall_recursive(Template, Goal, Bag1, N1)
    ;   Bag=[]
    ).

Sample run having these facts:

a(1).
a(11).
a(13).
a(11).

?- findall_recursive(X, a(X), Bag).
Bag = [1, 11, 13, 11].
gusbro
  • 22,357
  • 35
  • 46