Some newbie questions (e.g. how to get at most N solutions, what is template? what is @, :, +, - in arguments)

I’m a beginner in SWI-Prolog and I am trying to get multiple solutions from a query.

For example,

test(1, 1).
test(1, 2).
test(2, 1).
test(2, 2).

?- test(X, Y).
X = Y, Y = 1 ;
X = 1,
Y = 2 ;
X = 2,
Y = 1 ;
X = Y, Y = 2.

However, I have to press semicolon to get the next solution and it’s inconvenient. I searched the Internet and found an approach. It seems that findnsols could help.

I blindly tried the approach to get at most 2 solutions, it works.

?- findnsols(2, [X, Y], test(X, Y), Xs), !.
Xs = [[1, 1], [1, 2]].

So far so good!

Then I tried to read the document of findnsols from SWI-Prolog -- Finding all Solutions to a Goal , but it make me some confusion.

findnsols (+N, @Template, :Goal, -List)

My questions are

  1. Is my approach above correct? i.e. using cut at the end of query to get at most N solutions.

  2. What exactly Template is?

  3. What’s the meaning of +, -, :, @ at the predicate declares in the document?

Thanks.

Further read the document carefully, it seems that I should use once instead of !.

?- once(findnsols(2, [X, Y], test(X, Y), Xs)).
Xs = [[1, 1], [1, 2]].

"Those who cannot learn from history are doomed to repeat it.“
by George Santayana

The SWI-Prolog Manual was originally written as a manual so one should read the first part. With it now being accessible as a web site and being able to link directly to topics, many don’t know about and read the introduction.

This document is a reference manual . That means that it documents the system, but it does not explain the basics of the Prolog language and it leaves many details of the syntax, semantics and built-in primitives undefined where SWI-Prolog follows the standards. This manual is intended for people that are familiar with Prolog. For those not familiar with Prolog, we recommend to start with a Prolog textbook such as Bratko, 1986, Sterling & Shapiro, 1986 or Clocksin & Melish, 1987. For more advanced Prolog usage we recommend O’Keefe, 1990.

In other words reading the manual first is like jumping into the deep end of the pool and not knowing how to swim.

Instead try, Learn Prolog Now! and check out Useful Prolog references.


  1. Is my approach above correct? i.e. using cut at the end of query to get at most N solutions.

If you are trying to group the results based on a count then findnsols/4 is what I would use.

In the example below see how it groups the answers by the count.

Click triangle to expand
Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.0)

?- [user].
|: test(1,1).
|: test(1,2).
|: test(2,1).
|: test(2,2).
|: 
% user://1 compiled 0.00 sec, 4 clauses
true.

?- test(X,Y).
X = Y, Y = 1 ;
X = 1,
Y = 2 ;
X = 2,
Y = 1 ;
X = Y, Y = 2.

?- findnsols(2, [X, Y], test(X, Y), Xs).
Xs = [[1, 1], [1, 2]] ;
Xs = [[2, 1], [2, 2]].

What is normally done is to use Goal to access/filter the data (think SQL rows) then use Template to select the parts you want to keep (think SQL fields).

I can’t recall personally ever using findnsols/4 for a real problem. I know there are real use cases for when to use it but your example explanation doesn’t give me enough details to give relevant feedback. Also if a cut (!) is not your friend then be very careful using them, they might do what you don’t expect.


  1. What exactly Template is?

Something created to confuse new people? :thinking: Just like C++ was created to make life harder for those who use C.

Seriously, the way to think of predicates that use Template is to think of the Goal you want to run first, then use the Template to extract the data you want to collect as an item, then put all of the items into the Bag/List/Set as a list/set.

The way I learned about them was looking at lots of examples.


  1. What’s the meaning of + , - , : , @ at the predicate declares in the document?

See: Type, mode and determinism declaration headers

Yes I know I gave you a link to the manual but you asked about a topic not noted in many other places. :slightly_smiling_face:


Thanks for telling me these useful information, especially about @, :, +, -. They are very google unfriend and can not be found by pldoc’s search engine.

When searching for them in the SWI-Prolog manual just use mode instead of the characters, it works much better.

It really depends what you are doing with the solutions that you find. The idea of the toplevel (the Prolog REPL or interactive prompt or whatever you want to call it) is that you can interact with your program, as a human.

If you are instead writing software components that “talk to each other” then the answer is “it depends”.

I can’t recall personally ever using findnsols/4 for a real problem. I know there are real use cases for when to use it but your example explanation doesn’t give me enough details to give relevant feedback.

I have some experience in miniKanren.

They provided a interface run, for example

(define (test x y)
  (conde
   ((== x 1) (== y 1))
   ((== x 1) (== y 2))
   ((== x 2) (== y 1))
   ((== x 2) (== y 2))))

(run 2 (x y)
     (test x y))
;; => '((1 1) (1 2))

This could give me at most N solutions from the goal. For small programs, it is convenient, because you don’t need to press ; every time. I am looking for the equivalent in swi-prolog.

You need to know to look for “mode”. This together with the 0'<char> notation are the two things that are impossible to find if you don’t already know what you are looking for.

Perhaps this

?- bagof(item(X,Y),test(X,Y),Items).
Items = [item(1, 1), item(1, 2), item(2, 1), item(2, 2)].

or this

?- bagof(item(Y),test(1,Y),Items).
Items = [item(1), item(2)].

EDIT

An answer by false from StackOverflow seems to be what you seek.

findfirstn(N, Template, Goal_0, Instances) :-
   findall(Template, call_firstn(Goal_0, N), Instances).

call_firstn(Goal_0, N) :-
   N + N mod 1 >= 0, % ensures that N >=0 and N is an integer
   call_nth(Goal_0, Nth),
   ( Nth == N -> ! ; true ).

Complete example

:- module(examples,
    [
        findfirstn/4
    ]).

test(1,a).
test(2,b).
test(3,c).
test(4,d).

findfirstn(N, Template, Goal_0, Instances) :-
    findall(Template, call_firstn(Goal_0, N), Instances).

 call_firstn(Goal_0, N) :-
    N + N mod 1 >= 0, % ensures that N >=0 and N is an integer
    call_nth(Goal_0, Nth),
    ( Nth == N -> ! ; true ).

Example run

Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.0)

?- working_directory(_,'C:/Users/Groot').
true.

?- [examples].
true.

?- findfirstn(2,item(X,Y),test(X,Y),Items).
Items = [item(1, a), item(2, b)].

?- findfirstn(3,item(X,Y),test(X,Y),Items).
Items = [item(1, a), item(2, b), item(3, c)].

?- findfirstn(1,item(X,Y),test(X,Y),Items).
Items = [item(1, a)].

?- findfirstn(5,item(X,Y),test(X,Y),Items).
Items = [item(1, a), item(2, b), item(3, c), item(4, d)].

EDIT

Knew this predicate was somewhere just could not remember the name because I never use it, limit/2. (source)

?- bagof(test(X,Y),limit(2,examples:test(X,Y)),Items).
Items = [test(1, a), test(2, b)].

?- bagof(test(X,Y),limit(10,examples:test(X,Y)),Items).
Items = [test(1, a), test(2, b), test(3, c), test(4, d)].

This doesn’t exactly give you the first n answers that @chansey97 is looking for. findnsols/4 exists for a reason. In what particular situations you need it is a different question altogether.

Agree. See edit I made above.

I don’t get it, this is exactly what the findnsols/4 predicate does for you (together with the cut or wrapped inside once/1.

So you could trivially define your helper predicate as

once( findnsols(N, Template, Goal, List) )

it seems? Or is there something big I am missing?

I don’t know but if false wrote it I would trust what he did more than most others. It might be that the code is just more portable.

OK good old argument from authority :slight_smile:

Interestingly false’s solution that you paste here uses call_nth/2. False remarks in their answer:

A full implementation of call_nth/2 that does not leak and still is reentrant cannot be defined in ISO Prolog directly. You need to resort to all kinds of low-level operations with often incomplete semantics that are better hidden from the regular programmer.

so much for portability. (the “portability” is shifted from findnsols/4 to call_nth/2)

Yes, stackoverflow has many different answers (someone use limit, someone use call_nth, etc), but none of the answers was accepted. I was very confused, when I first saw them.

I currently use findnsols + once approach first, because the program is more short.

Thanks all!

1 Like

Prolog is an old language with many implementations, relatively small ecosystem (compared to the giants), turf wars, old scars, the full package. You will rarely get a definitive answer but this is a good thing.

2 Likes

@chansey97

You might find this of value, someday.

Protocol/1 (Used to capture screen to file)

I also found Prolog’s argument mode indicators very confusing when I first encountered them. The documentation is at SWI-Prolog -- Manual

Making them even more confusing is they similar, but not the same, as meta predicates whose documentation is at SWI-Prolog -- Manual

I’ve put some notes for myself at Prolog - Frontier Software

1 Like