2
votes

I'm big newbie to Prolog and I'm trying to write simple generator which finds the integers below 5.

gen(0).
gen(X):- X<5

When I run the program with gen(X) it only prints X = 0 and prompts me to enter something and when I press enter the '?-' is shown again.

How to make him generate the numbers from zero to five ? I'm using SWI-Prolog. Thanks

4

4 Answers

4
votes

If you don't want to use between/3 you can write your generator easily using an auxiliary procedure gen/3:

gen(Num):-
  gen(0, 5, Num).

gen(Cur, Top, Cur):- Cur < Top.
gen(Cur, Top, Next):-
  Cur < Top,
  succ(Cur, Cur1),  % or Cur1 is Cur+1
  gen(Cur1, Top, Next).

The auxiliary procedure gen/3 keeps track of the current number generated, and the top number, and binds the third argument with the currently generated number.

The first clause of gen/3 succeeds, binding the output number with the input number, when the input number is below top.

The second clause increments the current number and recursively calls itself to get the next numbers upon backtracking.

3
votes

The builtin predicate devoted to enumerate integers is between(Low,High,Num).

Using that you would write gen(X) :- between(0,4,X).

I've reimplemented between in this way

gen(X) :- between_(0, 4, X).

/* between_(I,J,K) is true if K is an integer between I and J inclusive.    */
between_(I,J,I) :- I =< J.
between_(I,J,K) :- I < J, I1 is I+1, between_(I1,J,K).

The interesting part of the question is to understand why the naive implementation with just an argument loops forever...

gen(0).
gen(X) :- gen(Y), X is Y + 1, X < 5.

?- gen(X).
X = 0 ;
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
^CAction (h for help) ? goals
[374,153] 3<5
[374,152] gen(3)
[374,151] gen(_G1013)
[374,150] gen(_G1013)
[374,149] gen(_G1013)
2
votes

well, since gen/1 should generate the integers from 0 to 5 (technically, if it was integers bellow 5 we should produce negatives too) you can just do:

gen(0).
gen(1).
gen(2).
gen(3).
gen(4).
gen(5).

which technically fulfils chack's restrictions xD

for a predicate gen/2 in which the first argument defines the max number we can do:

gen(N,R):-
    N>0,
    R is N-1.
gen(N,R):-
    N>1,
    NN is N-1,
    gen(NN,R).

but this has the advantage of using the first argument. a rephrase of the rules to avoid the first solution would be that we should be able to replace every occurrence of 5 with some other number without changing the rules and the program should still work; we should also add that we cannot use lists (unless they have exactly 1 element) or tupples or basically any term with two variables as argument as well as any kind of encoding (2^X * 3*Y for example). Inspired by the first program:

gen(X):-
    clause(g(_),_) ->
    g(X) ; (generate(5),!, g(X)).

generate(-1):-
    compile_predicates([g/1]).
generate(N):-
    assert(g(N)),
    NN is N-1,
    generate(NN).

compile_predicates/1 is just for the speed improvement; note that if we change the max number we should restart the VM. another option would be to avoid compiling g/1 and add a retract_all(g(_)) before we call generate/1. Of course the obvious problem is that it's not pure prolog since we rely on meta-predicates.

well, with all these restrictions it appears impossible indeed

-1
votes

What about using the inbuilt function numlist?

?- numlist(0,4,X).
X = [0, 1, 2, 3, 4].