简体   繁体   中英

Print sequence of numbers in Prolog

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.

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, eg 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.

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]

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM