Reputation: 343
I wanted to create a sequence of numbers in Prolog. So, if the function is print(4,3,10)
, it will print 4 7 10 13 16 19 22 25 28 31. The second parameter determines the next number and the last parameter determines where the sequence should stop.
I have the code but it seems doesn't work.
print(A,B,C) :- C>=0.
print(A,B,C) :- D is A+B, E is C-1, print(D,B,E), write(D).
The result only shows true
.
Are there any solutions for this problem? Thanks.
Upvotes: 1
Views: 1914
Reputation: 60024
A DCG could be the simplest way to embed both of good suggestions by @tas (+1) in your code.
print(_,_,0) --> [].
print(A,B,C) --> {C>0, D is A+B, E is C-1}, [A], print(D,B,E).
Test:
?- phrase(print(4,3,10),S).
S = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
false.
Edit:
Higher order predicates like foldl/4 can also solve this problem quite compactly (predating the idea by @user27815: +1):
initial_step_size_sequence(Initial,Step,Size,Sequence):-
length(Sequence,Size),
foldl({Step}/[A,B,C]>>(A=B,C is B+Step),Sequence,Initial,_).
Note the 'assignment' to sequence elements A=B
(free vars, as created by length/2), and the usage of library(yall) to inline the predicate.
Upvotes: 3
Reputation: 8140
You're almost there. You have realized that the counter has to be decreased every time your predicate wrote a number, so why not stop if it becomes zero? If you change the first rule...
print(_A,_B,0).
print(A,B,C) :-
D is A+B,
E is C-1,
print(D,B,E),
write(D).
... your predicate already delivers answers:
?- seq_step_len(4,3,10).
3431282522191613107
true ;
ERROR: Out of local stack
Note that there's an underscore in front of the first two arguments of the non-recursive rule. This avoids singleton warnings when loading the source file. However, the sequence does not start with 4 but with 34, it doesn't end with 31 but with 7 and there's no space between the numbers. And then there's this error ERROR: Out of local stack
. The latter you can easily avoid by adding a goal C>0
to your recursive rule. The wrong start/end number can be avoided by writing A
instead of D
. To address the reversed sequence you can write A
before the recursion. And to add spaces between the number I'd suggest the use of format/2 instead of write/1
. Then print2/3
might look something like:
print2(_A,_B,0).
print2(A,B,C) :-
C>0, % <- new goal
format('~d ', [A]), % <- format/2 instead of write/1
D is A+B,
E is C-1,
print2(D,B,E).
This yields the desired results:
?- print2(4,3,10).
4 7 10 13 16 19 22 25 28 31
true ;
false.
And while you're at it, why not having the sequence in a list? Then you could actually use it, e.g. as a goal in another predicate. And it would also be nice to have a more descriptive name, that makes it more obvious which argument is what. So let's add an argument for the sequence, then the predicate might look something like this:
seq_start_end_len([],_A,_B,0).
seq_start_end_len([A|As],A,B,C) :-
C>0,
D is A+B,
E is C-1,
seq_start_end_len(As,D,B,E).
If you happen to use SWI-Prolog you might have to type w to see the entire list:
?- seq_start_end_len(Seq,4,3,10).
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28|...] [write]
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
false.
However, if you try to query this predicate with any of the last three arguments being a variable you'll run into an error:
?- seq_start_end_len(Seq,X,3,10).
ERROR: is/2: Arguments are not sufficiently instantiated
?- seq_start_end_len(Seq,4,X,10).
ERROR: is/2: Arguments are not sufficiently instantiated
?- seq_start_end_len(Seq,4,3,X).
Seq = [],
X = 0 ;
ERROR: >/2: Arguments are not sufficiently instantiated
This is due to the use of is/2
and >/2
. You can avoid these errors by using CLP(FD):
:- use_module(library(clpfd)). % <- new
seq_start_end_len([],_A,_B,0).
seq_start_end_len([A|As],A,B,C) :-
C#>0, % <- change
D #= A+B, % <- change
E #= C-1, % <- change
seq_start_end_len(As,D,B,E).
If you try one of the above queries now, you'll get a lot of residual goals as an answer:
?- seq_start_end_len(Seq,X,3,10).
Seq = ['$VAR'('X'), _G1690, _G1693, _G1696, _G1699, _G1702, _G1705, _G1708, _G1711, _G1714],
'$VAR'('X')+3#=_G1690,
_G1690+3#=_G1693,
_G1693+3#=_G1696,
_G1696+3#=_G1699,
_G1699+3#=_G1702,
_G1702+3#=_G1705,
_G1705+3#=_G1708,
_G1708+3#=_G1711,
_G1711+3#=_G1714,
_G1714+3#=_G1838 ;
false.
In order to get actual numbers you'll have to restrict the range of X
and label the variables in the sequence:
?- X in 1..4, seq_start_end_len(Seq,X,3,10), label(Seq).
X = 1,
Seq = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28] ;
X = 2,
Seq = [2, 5, 8, 11, 14, 17, 20, 23, 26, 29] ;
X = 3,
Seq = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30] ;
X = 4,
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
false.
?- X in 1..4, seq_start_end_len(Seq,4,X,10), label(Seq).
X = 1,
Seq = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13] ;
X = 2,
Seq = [4, 6, 8, 10, 12, 14, 16, 18, 20, 22] ;
X = 3,
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
X = 4,
Seq = [4, 8, 12, 16, 20, 24, 28, 32, 36, 40] ;
false.
?- X in 1..4, seq_start_end_len(Seq,4,3,X), label(Seq).
X = 1,
Seq = [4] ;
X = 2,
Seq = [4, 7] ;
X = 3,
Seq = [4, 7, 10] ;
X = 4,
Seq = [4, 7, 10, 13] ;
false.
With the CLP(FD) version you can also ask more general queries like What sequences of length 4 to 6 are there with the numbers ranging from 1 to 10?:
?- Len in 4..6, seq_start_end_len(Seq,S,E,Len), Seq ins 1..10, label(Seq).
Len = 4,
Seq = [1, 1, 1, 1],
S = 1,
E = 0 ;
Len = 4,
Seq = [1, 2, 3, 4],
S = E, E = 1 ;
Len = 4,
Seq = [1, 3, 5, 7],
S = 1,
E = 2 ;
.
.
.
Len = 6,
Seq = [9, 9, 9, 9, 9, 9],
S = 9,
E = 0 ;
Len = 6,
Seq = [10, 9, 8, 7, 6, 5],
S = 10,
E = -1 ;
Len = 6,
Seq = [10, 10, 10, 10, 10, 10],
S = 10,
E = 0 ;
false.
And you get all 80 possibilities. Note how nicely the name reflects the relational nature of the CLP(FD) predicate.
Upvotes: 6
Reputation: 4797
equal(X,X).
initial_step_size_sequence(Initial,Step,Size,Sequence):-
length([_H|List],Size),
maplist(equal(Step),List),
scanl(plus,List,Initial,Sequence).
Is one way to do it with higher order predicates. I have named the predicate so that vars are clear. Note you need a four place predicate not three like you suggest.
?- initial_step_size_sequence(4,3,10,S).
S = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31]
Upvotes: 3