Print sequence of numbers in Prolog - 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, 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.

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.

Related

How do i get all combination in prolog?

this is my code, I don't understand why this does not look like what I thought
com([],[]).
com([H|T1],[H|T2]):-
com(T1,T2).
com([_|T1],T2):-
com(T1,T2).
three([X1,X2|T]) :-
X2 is X1 + 1,
(
T = []
; three([X2|T])
).
then I tried "findall(Tests, (com([6,7,8,8,9],Tests),three(Tests),Length(Test,N)), N>=3,Alltests)", this is to find all possible cases that continuous number with at least length 3,
'com' return all the tests
'three' is to find all continuous number with at least length 3.
but, the results were wrong.
[trace] findall(..,....,[[6, 7, 8, 9], [6, 7, 8], [6, 7, 8], [7, 8, 9]])
why I just got one [6,7,8,9] and [7,8,9], it should return both I think
findall(Tests, (com([6,7,8,8,9],Tests),three(Tests),Length(Test,N)), N>=3,Alltests)
You have Test instead of Tests here, so it's a different variable.
And Length with a capital needs to be length
And you have N>=3 outside the () so you are using findall/4 instead of findall/3.
With those changes, it seems to work for me:
?- findall(Tests, (com([6,7,8,8,9],Tests),three(Tests),length(Tests,N), N>=3),Alltests).
Alltests = [[6, 7, 8, 9], [6, 7, 8], [6, 7, 8, 9], [6, 7, 8], [7, 8, 9], [7, 8, 9]]
Everything seems to work alright:
?- com([6,7,8,8,9],Tests), three(Tests), length(Tests,N), N >= 3.
N = 4, Tests = [6,7,8,9]
; N = 3, Tests = [6,7,8]
; N = 4, Tests = [6,7,8,9]
; N = 3, Tests = [6,7,8]
; N = 3, Tests = [7,8,9]
; N = 3, Tests = [7,8,9]
; false.
Tip: always do the pure parts first; aggregate later.
Just wanted to give an easy way to define three/1
three(Lst) :-
LstDigits = [1, 2, 3, 4, 5, 6, 7, 8, 9],
between(3, 9, Len),
length(Lst, Len),
append([_, Lst, _], LstDigits).
Result in swi-prolog:
?- three(L).
L = [1,2,3] ;
L = [2,3,4] ;
L = [3,4,5] ;
L = [4,5,6] ;
L = [5,6,7] ;
L = [6,7,8] ;
L = [7,8,9] ;
L = [1,2,3,4] ;
L = [2,3,4,5] ;
etc.

Replicating findall in Prolog

I'm attempting to have some code that will return a single list of positions/indices where elements are found in some base list. After much searching, copying, tweaking, etc. The following code is what I have gotten to so far.
It works in SWISH, but requires that I hit next several times before I finally get a single list with all the positions of the searched for element.
How might I have it do all the answers before sending/printing back the resulting list?
pos([E|_],E,I,P) :- P = I.
pos([E|T],E,I,[P|Pt]) :- I1 is I+1, pos(T,E,I1,Pr), Pt = Pr, P = I.
pos([H|T],E,I,P) :- H\=E, I1 is I+1, pos(T,E,I1,Pr), P = Pr.
find(X,P):- a(L),pos(L,X,1,Pr), P = Pr.
a([2,1,4,5,3,2,6,2,1,21,2,1,4,7,4,3,5,2,4,6,8,2,1,37,3,2]).
Results:
?- find(2,X)
X = 1
X = [1|6]
X = [1, 6|8]
X = [1, 6, 8|11]
X = [1, 6, 8, 11|18]
X = [1, 6, 8, 11, 18|22]
X = [1, 6, 8, 11, 18, 22|26]
There are several possible solutions.
If you want to keep your code, you should write something like this (basically, you do need to call again the predicate when you find a match)
find_in([],_,_,[]).
find_in([El|TL],El,Pos,[Pos|T]):- !,
P1 is Pos + 1,
find_in(TL,El,P1,T).
find_in([_|T],El,Pos,L):-
P1 is Pos + 1,
find_in(T,El,P1,L).
find_pos(El,LPos):-
a(L),
find_in(L,El,1,LPos).
?- find_pos(2,X).
X = [1, 6, 8, 11, 18, 22, 26]
I've used the cut, but you can avoid it using \= (as you have done in your question).
If you can use built in predicates (nth0/3 or something similar and findall/3), there is a more compact solution:
find_pos(El,LPos):-
a(L),
findall(I,nth1(I,L,El),LPos).
?- find_pos(2,X).
X = [1, 6, 8, 11, 18, 22, 26]

Assigning values in a predicate

Is there a way to assing 2 times a different value in a variable inside a predicate?For example can we somehow make
X is 10,
X is 3.
produce true?
Please don't do it like this. is/2 is for evaluating arithmetic expressions.
Without any context whatsoever it is difficult impossible to suggest what is the right way to do it. The traditional way is to have a predicate that looks like this:
ten_or_three(10).
ten_or_three(3).
You can do all kinds of Prolog-y things with a predicate like that.
?- ten_or_three(10).
true.
?- ten_or_three(4).
false.
?- length(L, 3), maplist(ten_or_three, L), sumlist(L, Sum).
L = [10, 10, 10],
Sum = 30 ;
L = [10, 10, 3],
Sum = 23 ;
L = [10, 3, 10],
Sum = 23 ;
L = [10, 3, 3],
Sum = 16 ;
L = [3, 10, 10],
Sum = 23 ;
L = [3, 10, 3],
Sum = 16 ;
L = [3, 3, 10],
Sum = 16 ;
L = [3, 3, 3],
Sum = 9.
For example,
p(X) :- X is 3;X is 5.
is true of X = 3 or 5.

Avoid findall overflow with n-fractions problem

I am trying to print all solutions of the n-fractions problem for n=4:
:- lib(ic).
fractions(Digits) :-
Digits = [A,B,C,D,E,F,G,H,I,J,K,L],
Digits #:: 1..9,
ic:alldifferent(Digits),
X #= 10*B+C,
Y #= 10*E+F,
Z #= 10*H+I,
V #= 10*K+L,
A*Y*Z*V + D*X*Z*V + G*X*Y*V + J*X*Y*Z #= X*Y*Z*V,
A*Y #=< D*X,
D*Z #=< G*Y,
G*V #=< J*Z,
search(Digits,0,input_order,indomain,complete,[]).
When I run the query:
?- findall(Digits,fractions(Digits),List).
I get the following exception:
*** Overflow of the local/control stack!
You can use the "-l kBytes" (LOCALSIZE) option to have a larger stack.
Peak sizes were: local stack 105728 kbytes, control stack 25344 kbytes
I am thinking if there is a way to loop inside the program and print one solution each time, or I can't do that because the problem has too many solutions?
As has been pointed out, your code fails because the alldifferent(Digits) constraint is too restrictive. The digits must be allowed to occur between 1 and 2 times. In eclipse-clp, you can use constraints such as atleast/3, atmost/3, occurrences/3 or gcc/2 to express this.
Slightly off-topic: as you are using ECLiPSe's ic-solver (which can handle continuous domains), you can actually use a model much closer to the original specification, without introducing lots of multiplications:
:- lib(ic).
:- lib(ic_global).
fractions4(Digits) :-
Digits = [A,B,C,D,E,F,G,H,I,J,K,L],
Digits #:: 1..9,
A/(10*B+C) + D/(10*E+F) + G/(10*H+I) + J/(10*K+L) $= 1,
( for(I,1,9), param(Digits) do
occurrences(I, Digits, NOcc), NOcc #:: 1..2
),
lex_le([A,B,C], [D,E,F]), % lex-ordering to eliminate symmetry
lex_le([D,E,F], [G,H,I]),
lex_le([G,H,I], [J,K,L]),
labeling(Digits).
Apart from the main equality constraint (using $= instead of #= because we don't want to require integrality here), I've used occurrences/3 for the occurrence restrictions, and lexicographic ordering constraints as a more standard way of eliminating symmetry. Result:
?- findall(Ds, fractions4(Ds), Dss), length(Dss, NSol).
Dss = [[1, 2, 4, 3, 5, 6, 8, 1, 4, 9, 2, 7], [1, 2, 6, 5, 3, 9, 7, 1, 4, 8, 2, 4], [1, 2, 6, 5, 3, 9, 7, 8, 4, 9, 1, 2], [1, 2, 6, 7, 3, 9, 8, 1, 3, 9, 5, 4], [1, 2, 6, 8, 7, 8, 9, 1, 3, 9, 5, 4], [1, 3, 4, 5, 4, 6, 8, 1, 7, 9, 2, 3], [1, 3, 4, 7, 5, 6, 8, 1, 7, 9, 2, 4], [1, 3, 4, 8, 1, 7, 8, 5, 2, 9, 2, ...], [1, 3, 5, 6, 2, 8, 7, 1, 4, 9, ...], [1, 3, 6, 5, 2, 4, 7, 1, 8, ...], [1, 3, 6, 5, 3, 6, 7, 8, ...], [1, 3, 6, 5, 4, 5, 8, ...], [1, 3, 6, 5, 6, 3, ...], [1, 3, 6, 6, 5, ...], [1, 3, 6, 7, ...], [1, 3, 9, ...], [1, 3, ...], [1, ...], [...], ...]
NSol = 1384
Yes (82.66s cpu)
An added advantage of this model is that it can be quite easily turned into a generic model for arbitrary N.
Simply your predicate fails. If you remove all the constraints except alldifferent/1 and search/6 (just to understand the problem) and call ?- fractions(Digits). you get false because it's impossible to have a list with 12 elements (Digits = [A,B,C,D,E,F,G,H,I,J,K,L]) with domain for each element Digits #:: 1..9 and constraint those elements to be all different (ic:alldifferent(Digits)). 9 options for 12 elements: unsolvable. If you expand the domain up to 12 (Digits #:: 1..12), you get a solution:
?- fractions(Digits).
Digits = [2, 3, 4, 9, 7, 10, 12, 8, 5, 11, 1, 6]
Yes (94.00s cpu, solution 1, maybe more)
Then you can apply findall/3 and see other solutions...
Many clpfd implementations offer global_cardinality constraints which I use in this example. In the following I use SICStus Prolog 4.5.0:
:- use_module(library(clpfd)).
fractions(Digits) :-
Digits = [A,B,C,D,E,F,G,H,I,J,K,L],
domain(Digits, 1, 9),
global_cardinality(Digits, [1-N1,2-N2,3-N3,4-N4,5-N5,6-N6,7-N7,8-N8,9-N9]),
domain([N1,N2,N3,N4,N5,N6,N7,N8,N9], 1, 2),
X #= 10*B+C,
Y #= 10*E+F,
Z #= 10*H+I,
V #= 10*K+L,
Z*V #= ZV,
X*Y #= XY,
A*Y*ZV + D*X*ZV + G*XY*V + J*XY*Z #= XY*ZV,
X #=< Y, X #= Y #=> A #=< D, % break some symmetries
Y #=< Z, Y #= Z #=> D #=< G,
Z #=< V, Z #= V #=> G #=< J.
Sample use:
| ?- n_fractions(4,Zs), labeling([enum],Zs).
Zs = [2,1,2,9,1,8,7,3,5,6,4,5] ? ;
Zs = [2,1,3,7,1,8,9,2,6,5,4,5] ? ;
Zs = [2,1,3,7,1,8,9,2,6,6,5,4] ? ;
...
no
Using prolog-findall for collecting all solutions works out all right, too:
?- findall(Zs,(n _fractions(4,Zs), labeling([enum],Zs)), Zss),
length(Zss, N_sols).
Zss = [[2,1,2,9,1,8,7,3,5|...],
[2,1,3,7,1,8,9,2,6|...],
[2,1,3,7,1,8,9,2|...],
[2,1,3,8,1,5,7|...],
[2,1,3,8,1,6|...],
[2,1,3,9,1|...],
[2,1,3,9|...],
[2,1,4|...],
[2,1|...],
[...|...]|...],
N_sols = 1384 ? ;
no

PROLOG List filter predicate not aswering true or false

I'm trying to make a predicate that takes two vectors/lists and uses the first one as a filter. For example:
?- L1=[0,3,0,5,0,0,0,0],L2=[1,2,3,4,5,6,7,8],filter(L1,L2,1).
L1 = [0, 3, 0, 5, 0, 0, 0, 0],
L2 = [1, 2, 3, 4, 5, 6, 7, 8] .
That's what I'm getting but I would want true or false if L2 has 3 as the second element, 5 as the fourth element, etc. The 0s are ignored, that's the "filter" condition.
What I know from the input is that L1 and L2 are always length=8 and only L1 has 0s.
My code is:
filter(_,_,9).
filter([Y|T],V2,Row):-
Y=:=0,
NewRow is Row + 1,
filter([Y|T],V2,NewRow).
filter([Y|T],V2,Row):-
Y=\=0,
nth(Row,[Y|T],X1),
nth(Row,V2,X2),
X1=:=X2,
NewRow is Row + 1,
filter([Y|T],V2,NewRow).
nth(1,[X|_],X).
nth(N,[_|T],R):- M is N-1, nth(M,T,R).
I know there are better ways of doing the function, for example comparing the first element of the first to the nth of the second and delete the head of the first with recursion but I just want to know why I'm not getting true or false, or any "return" value at all.
Can someone help me?, got it working
New code:
filter([],R,_,R).
filter([Y|T],V2,Row,R):-
Y=:=0,
NewRow is Row + 1,
filter(T,V2,NewRow,R).
filter([Y|T],V2,Row,R):-
Y=\=0,
nth(Row,V2,X2),
Y=:=X2,
NewRow is Row + 1,
filter(T,V2,NewRow,R).
Example of expected behaviour:
permutation([1,2,3,4,5,6,7,8],X),filter([1,2,3,4,0,0,0,0],X,1,R).
X = R, R = [1, 2, 3, 4, 5, 6, 7, 8] ;
X = R, R = [1, 2, 3, 4, 5, 6, 8, 7] ;
X = R, R = [1, 2, 3, 4, 5, 7, 6, 8] ;
X = R, R = [1, 2, 3, 4, 5, 7, 8, 6] .
Now i can get all the permutations that starts with 1,2,3,4.
If someone knows a better way to achieve the same, plz share, but i already got what i needed =).
seems like could be a perfect task for maplist/3
filter(L1, L2, _) :-
maplist(skip_or_match, L1, L2).
skip_or_match(E1, E2) :- E1 == 0 ; E1 == E2.
yields
?- permutation([1,2,3,4,5,6,7,8],X),filter([1,2,3,4,0,0,0,0],X,_).
X = [1, 2, 3, 4, 5, 6, 7, 8] ;
X = [1, 2, 3, 4, 5, 6, 8, 7] ;
X = [1, 2, 3, 4, 5, 7, 6, 8] ;
X = [1, 2, 3, 4, 5, 7, 8, 6] ;
...
We could do that more useful, using Prolog facilities - namely, use an anonymus variable to express don't care.
Then filter/N is a simple application of maplist:
?- permutation([1,2,3,4,5,6,7,8],X),maplist(=,[1,2,3,4,_,_,_,_],X).
X = [1, 2, 3, 4, 5, 6, 7, 8] ;
X = [1, 2, 3, 4, 5, 6, 8, 7] ;
X = [1, 2, 3, 4, 5, 7, 6, 8] ;
X = [1, 2, 3, 4, 5, 7, 8, 6] ;
...
Your code always tests the first item of the filtering list for being zero. For example, look at the case when you're checking second value:
filter([0,3,0,5,0,0,0,0], [1,2,3,4,5,6,7,8], 2).
This call will perform the following unifications:
# first case: obvious fail…
filter([0,3,0,5,0,0,0,0], [1,2,3,4,5,6,7,8], 2) =\= filter(_, _, 9).
# second case:
filter([0,3,0,5,0,0,0,0], [1,2,3,4,5,6,7,8], 2) = filter([Y|T],V2,Row).
# unification succeeds with substitutions:
Y = 0
T = [3,0,5,0,0,0,0]
V2 = [1,2,3,4,5,6,7,8]
Row = 2
# and what happens next?
Y =:= 0 # success!
You probably wanted here to check whether second element of [Y|T] is zero; instead, you're checking the first one. If you want to fix it without changing the rest of your code, you should instead perform comparisons to X1:
filter(V1,V2,Row):-
nth(Row, V1, X1),
X1 =:= 0,
NewRow is Row + 1,
filter(V1,V2,NewRow).
filter(V1,V2,Row):-
nth(Row,V1,X1),
X1=\=0,
nth(Row,V2,X2),
X1=:=X2,
NewRow is Row + 1,
filter(V1,V2,NewRow).
Also, there's one more thing that I think you might not be getting yet in Prolog. If a predicate fails, Prolog indeed prints false and stops computation. But if a predicate succeeds, there are two cases:
If there were no variables in your query, Prolog prints true.
If there were any variables in your query, Prolog does not print true. Instead, it prints values of variables instead. This also counts as true.
In your case Prolog actually “returns” true from your predicate—except that because you have used variables in your query, it printed their value instead of printing true.

Resources