Creating second list with increment from other list in prolog - list

I'm trying to create list2 from list1, but every member should have Z increment.
if Z=1 then: [1,2,3] -> [2,3,4]
addZ(Z,[X | Xs],[Y | Ys]):-
Y is X+Z,
addZ(Z,Xs,Ys).
If I try addZ(1,[1,2,3],X). I just get no as answer.

After adding the base case, as recommended by #DavidTonhofer, you can change the order of the first two arguments of the predicate to avoid spurious choice points (since Prolog apply first argument indexing to quickly narrow down applicable clauses):
addZ([], _, []).
addZ([X|Xs], Z, [Y|Ys]):-
Y is X + Z,
addZ(Xs, Z, Ys).
Example:
?- addZ([1,2,3], 1, Z).
Z = [2, 3, 4].
?-

As always the most fun starts when you use CLP, like
:- use_module(library(clpz)).
addZ(_, [], []).
addZ(I, [X|Xs], [Y|Ys]) :-
Y #= I + X,
addZ(I, Xs, Ys).
And now you can do stuff like:
?- addZ(1, [1, 2, 3], C).
C = [2,3,4].
?- addZ(1, [1, 2, X], [X, Y, 9]).
false.
?- addZ(4, [1, 2, X], [A, Y, 9]).
A = 5, Y = 6, X = 5.
?- addZ(I, [1, 2, X], [5|Ys]).
I = 4, Ys = [6,_A], clpz:(4+X#=_A).

Here's another solution using maplist/3 and library yall:
addZ(Z, Xs, Ys):-
maplist({Z}/[X, Y]>>(Y is X+Z), Xs, Ys).
Sample runs
?- addZ(1, [1,2,3], X).
X = [2, 3, 4].
?- addZ(5, [1,2,3], X).
X = [6, 7, 8].

Related

Set difference of two compatible lists in Prolog

I'm currently trying to implement an automated theorem prover in prolog and have stumbled across a problem.
If I have a list of lists such as:
[[1,2],[-1,3],[4,5,7],[-2,4]]
How would I get the "set difference" of two compatible list items:
What I mean by compatible is, if the negation of a certain number exists in another list, then replace those two lists with the set difference, ie:
[1,2] and [-1,3] are compatible because -1 is present in the second clause and thus it should return the set difference of [2,3] and the new list should be [[2,3],[4,5,7],[-2,4]].
Currently I have the following step predicate:
memberlist(X,[[X|_]|_]).
memberlist(X,[[_|T1]|T2]) :-
memberlist(X,[T1|T2]).
memberlist(X,[[]|T2]) :-
memberlist(X,T2).
step([]).
step([_|T]) :-
memberlist(neg X,T),
write(X),
nl,
step(T).
step([_|T]) :-
step(T).
So it simply checks each list and checks if the negation of a variable exists and if it does simply write it out. I've already added code which deals with negative numbers, so X will match a -X, X being any integer.
I'm quite stuck at this point, and any help would be greatly appreciated.
Another possible solution:
shrink([L1|R1], [L3|R2]) :-
select(L2, R1, R2),
difference(L1, L2, L3).
shrink([L1|R1], [L1|S]) :-
shrink(R1, S).
difference(L1, L2, L3) :-
select(X, L1, R1),
compatible(X, Y),
select(Y, L2, R2),
union(R1, R2, L3).
compatible(neg(P), P) :- !.
compatible(P, neg(P)).
Some examples:
?- shrink([[1,2], [neg(1),3], [4,5,6], [neg(2),4]], S).
S = [[2, 3], [4, 5, 6], [neg(2), 4]] ;
S = [[1, 4], [neg(1), 3], [4, 5, 6]] ;
false.
?- shrink([[a,neg(b)], [a,b]], S).
S = [[a]] ;
false.
?- shrink([[rainning], [neg(rainning)]], S).
S = [[]] ;
false.
?- shrink([[rainning], [neg(rainning), wet_grass], [neg(wet_grass), green_grass]], S).
S = [[wet_grass], [neg(wet_grass), green_grass]] ;
S = [[rainning], [neg(rainning), green_grass]] ;
false.
?- shrink([[neg(green_grass)], [rainning], [neg(rainning), wet_grass], [neg(wet_grass), green_grass]], A), shrink(A, B), shrink(B, C).
A = [[neg(wet_grass)], [rainning], [neg(rainning), wet_grass]],
B = [[neg(rainning)], [rainning]],
C = [[]] ;
A = [[neg(wet_grass)], [rainning], [neg(rainning), wet_grass]],
B = [[neg(wet_grass)], [wet_grass]],
C = [[]] ;
A = [[neg(green_grass)], [wet_grass], [neg(wet_grass), green_grass]],
B = [[neg(wet_grass)], [wet_grass]],
C = [[]] ;
A = [[neg(green_grass)], [wet_grass], [neg(wet_grass), green_grass]],
B = [[neg(green_grass)], [green_grass]],
C = [[]] ;
A = [[neg(green_grass)], [rainning], [neg(rainning), green_grass]],
B = [[neg(rainning)], [rainning]],
C = [[]] ;
A = [[neg(green_grass)], [rainning], [neg(rainning), green_grass]],
B = [[neg(green_grass)], [green_grass]],
C = [[]] ;
false.
An alternative formulation.
memberlist will give you the triples p(X, Y, Z) where Z and neg(Z) are in X and Y.
collapse will take such a triple and remove X, Y from Xs and add X+Y-Z-neg(Z) to it.
memberlist([X|Xs], p(X, Y, Z)) :-
member(Z, X), member(Y, Xs), member(neg(Z), Y).
memberlist([X|Xs], p(X, Y, Z)) :-
member(neg(Z), X), member(Y, Xs), member(Z, Y).
memberlist([_|Xs], A) :-
memberlist(Xs, A).
collapse(Xs, Ys) :-
memberlist(Xs, p(A, B, I)), % A and B have some I and neg(I) in them
select(A, Xs, XsA), % remove A
select(B, XsA, XsAB), % remove B
append(A, B, AB), select(I, AB, ABI), select(neg(I), ABI, ABII),
Ys = [ABII|XsAB].
Your example
?- collapse([[1, 2], [neg(1), 3], [4, 5, 7], [neg(2), 4]], X).
X = [[2, 3], [4, 5, 7], [neg(2), 4]] ;
X = [[1, 4], [neg(1), 3], [4, 5, 7]] ;
false.

Append elements of list to other list in Prolog [duplicate]

I need to find the combinations in a list of lists. For example, give the following list,
List = [[1, 2], [1, 2, 3]]
These should be the output,
Comb = [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3]]
Another example:
List = [[1,2],[1,2],[1,2,3]]
Comb = [[1,1,1],[1,1,2],[1,1,3],[1,2,1],[1,2,2],[1,2,3]....etc]
I know how to do it for a list with two sublists but it needs to work for any number of sublists.
I'm new to Prolog, please help.
This answer hunts the bounty offered "for a pure solution that also takes into account for Ess".
Here we generalize this previous
answer like so:
list_crossproduct(Xs, []) :-
member([], Xs).
list_crossproduct(Xs, Ess) :-
Ess = [E0|_],
same_length(E0, Xs),
maplist(maybelonger_than(Ess), Xs),
list_comb(Xs, Ess).
maybelonger_than(Xs, Ys) :-
maybeshorter_than(Ys, Xs).
maybeshorter_than([], _).
maybeshorter_than([_|Xs], [_|Ys]) :-
maybeshorter_than(Xs, Ys).
list_crossproduct/2 gets bidirectional by relating Xs and Ess early.
?- list_comb(Xs, [[1,2,3],[1,2,4],[1,2,5]]).
nontermination % BAD!
?- list_crossproduct(Xs, [[1,2,3],[1,2,4],[1,2,5]]).
Xs = [[1],[2],[3,4,5]] % this now works, too
; false.
Sample query having multiple answers:
?- list_crossproduct(Xs, [[1,2,3],[1,2,4],[1,2,5],X,Y,Z]).
X = [1,2,_A],
Y = [1,2,_B],
Z = [1,2,_C], Xs = [[1],[2],[3,4,5,_A,_B,_C]]
; X = [1,_A,3],
Y = [1,_A,4],
Z = [1,_A,5], Xs = [[1],[2,_A],[3,4,5]]
; X = [_A,2,3],
Y = [_A,2,4],
Z = [_A,2,5], Xs = [[1,_A],[2],[3,4,5]]
; false.
For completeness, here is the augmented version of my comment-version. Note nilmemberd_t/2 which is inspired by memberd_t/2.
nilmemberd_t([], false).
nilmemberd_t([X|Xs], T) :-
if_(nil_t(X), T = true, nilmemberd_t(Xs, T)).
nil_t([], true).
nil_t([_|_], false).
list_comb(List, []) :-
nilmemberd_t(List, true).
list_comb(List, Ess) :-
bagof(Es, maplist(member,Es,List), Ess).
Above version shows that "only" the first clause was missing in my comment response. Maybe even shorter with:
nilmemberd([[]|_]).
nilmemberd([[_|_]|Nils]) :-
nilmemberd(Nils).
This should work for Prologs without constraints. With constraints, bagof/3 would have to be reconsidered since copying constraints is an ill-defined terrain.
Here's a way to do it using maplist/3 and append/2:
list_comb([], [[]]).
list_comb([Xs|Xss], Ess) :-
Xs = [_|_],
list_comb(Xss, Ess0),
maplist(aux_x_comb(Ess0), Xs, Esss1),
append(Esss1, Ess).
aux_x_comb(Ess0, X, Ess1) :-
maplist(head_tail_list(X), Ess0, Ess1).
head_tail_list(X, Xs, [X|Xs]).
Sample query:
?- list_comb([[a,b],[f,g],[x,y,z]], Ess).
Ess = [[a,f,x],[a,f,y],[a,f,z],
[a,g,x],[a,g,y],[a,g,z],
[b,f,x],[b,f,y],[b,f,z],
[b,g,x],[b,g,y],[b,g,z]].
Here's how it works!
As an example, consider these goals:
list_comb([[a,b],[f,g],[x,y,z]], Ess)
list_comb([ [f,g],[x,y,z]], Ess0)
How can we get from Ess0 to Ess?
We look at the answers to the
latter query:
?- list_comb([[f,g],[x,y,z]], Ess0).
Ess0 = [[f,x],[f,y],[f,z], [g,x],[g,y],[g,z]].
... place a before [f,x], ..., [g,z] ...
?- maplist(head_tail_list(a),
[[f,x],[f,y],[f,z],
[g,x],[g,y],[g,z]], X).
X = [[a,f,x],[a,f,y],[a,f,z],
[a,g,x],[a,g,y],[a,g,z]].
... then do the same for b.
maplist(aux_x_comb) helps us handle all items:
?- maplist(aux_x_comb([[f,x],[f,y],[f,z],
[g,x],[g,y],[g,z]]),
[a,b], X).
X = [[[a,f,x],[a,f,y],[a,f,z],
[a,g,x],[a,g,y],[a,g,z]],
[[b,f,x],[b,f,y],[b,f,z],
[b,g,x],[b,g,y],[b,g,z]]].
To get from a list of lists to a list use append/2.
I hope this explanation was more eludicating than confusing:)
A twist in #false's approach:
%list_comb( ++LL, -Ess)
list_comb( LL, Ess):-
is_list( LL),
maplist( is_list, LL),
findall( Es, maplist( member, Es, LL), Ess).
Testing:
41 ?- list_comb( [[1,2],[1],[1]], X).
X = [[1, 1, 1], [2, 1, 1]].
42 ?- list_comb( [[1,2],[1],[1,2,3]], X).
X = [[1, 1, 1], [1, 1, 2], [1, 1, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3]].
43 ?- list_comb( [[1,2],[],[1,2,3]], X).
X = [].
44 ?- list_comb( [[1,2],t,[1,2,3]], X).
false.
45 ?- list_comb( t, X).
false.

Combinations of multiple lists - Prolog

I need to find the combinations in a list of lists. For example, give the following list,
List = [[1, 2], [1, 2, 3]]
These should be the output,
Comb = [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3]]
Another example:
List = [[1,2],[1,2],[1,2,3]]
Comb = [[1,1,1],[1,1,2],[1,1,3],[1,2,1],[1,2,2],[1,2,3]....etc]
I know how to do it for a list with two sublists but it needs to work for any number of sublists.
I'm new to Prolog, please help.
This answer hunts the bounty offered "for a pure solution that also takes into account for Ess".
Here we generalize this previous
answer like so:
list_crossproduct(Xs, []) :-
member([], Xs).
list_crossproduct(Xs, Ess) :-
Ess = [E0|_],
same_length(E0, Xs),
maplist(maybelonger_than(Ess), Xs),
list_comb(Xs, Ess).
maybelonger_than(Xs, Ys) :-
maybeshorter_than(Ys, Xs).
maybeshorter_than([], _).
maybeshorter_than([_|Xs], [_|Ys]) :-
maybeshorter_than(Xs, Ys).
list_crossproduct/2 gets bidirectional by relating Xs and Ess early.
?- list_comb(Xs, [[1,2,3],[1,2,4],[1,2,5]]).
nontermination % BAD!
?- list_crossproduct(Xs, [[1,2,3],[1,2,4],[1,2,5]]).
Xs = [[1],[2],[3,4,5]] % this now works, too
; false.
Sample query having multiple answers:
?- list_crossproduct(Xs, [[1,2,3],[1,2,4],[1,2,5],X,Y,Z]).
X = [1,2,_A],
Y = [1,2,_B],
Z = [1,2,_C], Xs = [[1],[2],[3,4,5,_A,_B,_C]]
; X = [1,_A,3],
Y = [1,_A,4],
Z = [1,_A,5], Xs = [[1],[2,_A],[3,4,5]]
; X = [_A,2,3],
Y = [_A,2,4],
Z = [_A,2,5], Xs = [[1,_A],[2],[3,4,5]]
; false.
For completeness, here is the augmented version of my comment-version. Note nilmemberd_t/2 which is inspired by memberd_t/2.
nilmemberd_t([], false).
nilmemberd_t([X|Xs], T) :-
if_(nil_t(X), T = true, nilmemberd_t(Xs, T)).
nil_t([], true).
nil_t([_|_], false).
list_comb(List, []) :-
nilmemberd_t(List, true).
list_comb(List, Ess) :-
bagof(Es, maplist(member,Es,List), Ess).
Above version shows that "only" the first clause was missing in my comment response. Maybe even shorter with:
nilmemberd([[]|_]).
nilmemberd([[_|_]|Nils]) :-
nilmemberd(Nils).
This should work for Prologs without constraints. With constraints, bagof/3 would have to be reconsidered since copying constraints is an ill-defined terrain.
Here's a way to do it using maplist/3 and append/2:
list_comb([], [[]]).
list_comb([Xs|Xss], Ess) :-
Xs = [_|_],
list_comb(Xss, Ess0),
maplist(aux_x_comb(Ess0), Xs, Esss1),
append(Esss1, Ess).
aux_x_comb(Ess0, X, Ess1) :-
maplist(head_tail_list(X), Ess0, Ess1).
head_tail_list(X, Xs, [X|Xs]).
Sample query:
?- list_comb([[a,b],[f,g],[x,y,z]], Ess).
Ess = [[a,f,x],[a,f,y],[a,f,z],
[a,g,x],[a,g,y],[a,g,z],
[b,f,x],[b,f,y],[b,f,z],
[b,g,x],[b,g,y],[b,g,z]].
Here's how it works!
As an example, consider these goals:
list_comb([[a,b],[f,g],[x,y,z]], Ess)
list_comb([ [f,g],[x,y,z]], Ess0)
How can we get from Ess0 to Ess?
We look at the answers to the
latter query:
?- list_comb([[f,g],[x,y,z]], Ess0).
Ess0 = [[f,x],[f,y],[f,z], [g,x],[g,y],[g,z]].
... place a before [f,x], ..., [g,z] ...
?- maplist(head_tail_list(a),
[[f,x],[f,y],[f,z],
[g,x],[g,y],[g,z]], X).
X = [[a,f,x],[a,f,y],[a,f,z],
[a,g,x],[a,g,y],[a,g,z]].
... then do the same for b.
maplist(aux_x_comb) helps us handle all items:
?- maplist(aux_x_comb([[f,x],[f,y],[f,z],
[g,x],[g,y],[g,z]]),
[a,b], X).
X = [[[a,f,x],[a,f,y],[a,f,z],
[a,g,x],[a,g,y],[a,g,z]],
[[b,f,x],[b,f,y],[b,f,z],
[b,g,x],[b,g,y],[b,g,z]]].
To get from a list of lists to a list use append/2.
I hope this explanation was more eludicating than confusing:)
A twist in #false's approach:
%list_comb( ++LL, -Ess)
list_comb( LL, Ess):-
is_list( LL),
maplist( is_list, LL),
findall( Es, maplist( member, Es, LL), Ess).
Testing:
41 ?- list_comb( [[1,2],[1],[1]], X).
X = [[1, 1, 1], [2, 1, 1]].
42 ?- list_comb( [[1,2],[1],[1,2,3]], X).
X = [[1, 1, 1], [1, 1, 2], [1, 1, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3]].
43 ?- list_comb( [[1,2],[],[1,2,3]], X).
X = [].
44 ?- list_comb( [[1,2],t,[1,2,3]], X).
false.
45 ?- list_comb( t, X).
false.

Predicate to unzip a list

List1=[(x,1),(y,1),(z,1)]
I'm attempting to split this list:
into two lists:
List3=[x,y,z]
List4=[1,1,1]
So I have written this predicate to try to do it:
splt([], [], []).
splt([X|Xs], [Y|Ys], [X,Y|Zs]) :-
splt(Xs,Ys,Zs).
However instead of the desired result, the predicate returns:
1 ?- splt([(x,1),(y,2),(z,3)],L3,L4).
L3 = [_G1760, _G1769, _G1778],
L4 = [ (z, 1), _G1760, (y, 2), _G1769, (z, 3), _G1778].
First, the term you have chosen. This: (a, b), is most definitely not how you would usually represent a "tuple" in Prolog. You almost always use a-b for a "pair", and pairs are used throughout the standard libraries.
So your initial list would look like this: [x-1, y-1, z-1].
This should also explain why you are having your problem. You write (a, b), but your predicate says a, b, and you consume two elements when you expect to get one ,(a,b) term. So, to fix your current predicate you would write:
split([], [], []).
split([X|Xs], [Y|Ys], [(X,Y)|XYs]) :-
split(Xs, Ys, XYs).
?- split(Xs, Ys, [(x,1), (y,1), (z,1)]).
Xs = [x, y, z],
Ys = [1, 1, 1].
But instead, using a more conventional name, term order, and Prolog pairs:
zip([], [], []).
zip([X-Y|XYs], [X|Xs], [Y|Ys]) :-
zip(XYs, Xs, Ys).
?- zip([x-1, y-1, z-1], Xs, Ys).
Xs = [x, y, z],
Ys = [1, 1, 1].
And of course, SWI-Prolog at least has a library(pairs), and it comes with a pairs_keys_values/3:
?- pairs_keys_values([x-1, y-1, z-1], Xs, Ys).
Xs = [x, y, z],
Ys = [1, 1, 1].
I find comfortable using library(yall):
?- maplist([(X,Y),X,Y]>>true, [(x,1),(y,2),(z,3)],L3,L4).
L3 = [x, y, z],
L4 = [1, 2, 3].
or, maybe clearer
?- maplist([A,B,C]>>(A=(B,C)), [(x,1),(y,2),(z,3)],L3,L4).
L3 = [x, y, z],
L4 = [1, 2, 3].
You're matching the tuple as a whole, rather than it's component parts.
You should match on [(X1,Y1)|XS], instead of [X|XS] and [Y|Ys].
splt([],[],[]).
splt([(X1,Y1)|Xs],[X1|T1],[Y1|T2]):-
splt(Xs,T1,T2).
Here the first term is used as input, the second and third as output.
Ideone example, using SWI-Prolog, here.

How to access list permutations in prolog?

I want to access list permutation and pass it as argument to other functions.
This is the permutation code:
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]) :-
takeout(X,R,S),
write(S).
perm([X|Y],Z) :-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
To start with, let's redefine your predicates so they don't do any unnecessary I/O:
takeout(X,[X|R],R).
takeout(X,[F |R],[F|S]) :- takeout(X,R,S).
perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).
perm([],[]).
Now you have what could be considered a "pure" permutation function:
?- perm([1,2,3], X).
X = [1, 2, 3] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [1, 3, 2] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
So, suppose you have a max_heap function that takes a list of values and produces a tree. I'll let you worry about that, so let's just posit that it exists and is called max_heap/2 and let's further posit that you have a way to display this attractively called display_heap/1. To "take" the permutation and "send" it as a parameter to these functions, you're really saying in math-ese: suppose P is a permutation of X, let's make a max_heap with it and display it. Or, suppose P is a permutation of X, H is a max heap made from X, let's display H:
show_heaps(List) :- perm(List, P), max_heap(P, H), display_heap(H).
This says the same thing as my English sentence: suppose P is a permutation of the list, then H is a heap representation of it, then display it. Technically, display_heap/1 is still a predicate which could be true or false for a given heap. In practice, it will always be true, and if you run this you'll still have to hit ; repeatedly to say, give me another solution, unless you use a failure-driven loop or an extralogical predicate like findall/3 to cause all the solutions to be found.
Edit: Let's discuss failure-driven loops and findall/3. First let me add some new predicates, because I don't know exactly what you're doing, but it doesn't matter for our purposes.
double([X|Xs], [Y|Ys]) :- Y is X*2, double(Xs, Ys).
double([],[]).
showlist(Xs) :- print(Xs).
So now I have a predicate double/2 which doubles the values in the list and a predicate showlist/1 that prints the list on standard output. We can try it out like so:
?- perm([1,2,3], X), double(X, Y), showlist(Y).
[2,4,6]
X = [1, 2, 3],
Y = [2, 4, 6] ;
[4,2,6]
X = [2, 1, 3],
Y = [4, 2, 6] ;
[4,6,2]
X = [2, 3, 1],
Y = [4, 6, 2] ;
[2,6,4]
X = [1, 3, 2],
Y = [2, 6, 4] ;
[6,2,4]
X = [3, 1, 2],
Y = [6, 2, 4] ;
[6,4,2]
X = [3, 2, 1],
Y = [6, 4, 2] ;
false.
When you type ; you're saying, "or?" to Prolog. In other words, you're saying "what else?" You're telling Prolog, in effect, this isn't the answer I want, try and find me another answer I like better. You can formalize this process with a failure-driven loop:
?- perm([1,2,3], X), double(X, Y), showlist(Y), fail.
[2,4,6][4,2,6][4,6,2][2,6,4][6,2,4][6,4,2]
false.
So now you see the output from each permutation having gone through double/2 there, and then Prolog reported false. That's what one means by something like this:
show_all_heaps(List) :- perm(List, X), double(X, Y), showlist(Y), nl, fail.
show_all_heaps(_).
Look at how that works:
?- show_all_heaps([1,2,3]).
[2,4,6]
[4,2,6]
[4,6,2]
[2,6,4]
[6,2,4]
[6,4,2]
true.
The other option is using findall/3, which looks more like this:
?- findall(Y, (perm([1,2,3], X), double(X, Y)), Ys).
Ys = [[2, 4, 6], [4, 2, 6], [4, 6, 2], [2, 6, 4], [6, 2, 4], [6, 4, 2]].
Using this to solve your problem is probably beyond the scope of whatever homework it is you're working on though.
We can define list_permutation/2 based on same_length/2 and select/3 like this:
:- use_module(library(lists),[same_length/2,select/3]).
list_permutation(As,Bs) :-
same_length(As,Bs), % redundant goal helps termination
list_permutation_(As,Bs).
list_permutation_([],[]).
list_permutation_([A|As],Bs0) :-
select(A,Bs0,Bs),
list_permutation_(As,Bs).
Thanks to same_length/2, both of the following queries1,2 terminate universally:
?- list_permutation([1,2,3],Ys).
Ys = [1,2,3]
; Ys = [1,3,2]
; Ys = [2,1,3]
; Ys = [3,1,2]
; Ys = [2,3,1]
; Ys = [3,2,1]
; false.
?- list_permutation(Xs,[1,2,3]).
Xs = [1,2,3]
; Xs = [1,3,2]
; Xs = [2,1,3]
; Xs = [2,3,1]
; Xs = [3,1,2]
; Xs = [3,2,1]
; false.
So far, so good. But what does the answer sequence look like if there are duplicate list items?
?- list_permutation([1,1,1],Ys).
Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; Ys = [1,1,1]
; false.
5/6 answers are redundant! What can we do? We simply use selectd/3 instead of select/3!
list_permuted(As,Bs) :-
same_length(As,Bs),
list_permuted_(As,Bs).
list_permuted_([],[]).
list_permuted_([A|As],Bs0) :-
selectd(A,Bs0,Bs), % use selectd/3, not select/3
list_permuted_(As,Bs).
Let's re-run above query that gave us 5 redundant solutions before!
?- list_permuted([1,1,1],Ys).
Ys = [1,1,1]
; false.
?- list_permuted(Xs,[1,1,1]).
Xs = [1,1,1]
; false.
Better! All redundant answers are gone.
Let's compare the solution set for some sample case:
?- _Xs = [1,2,1,1,2,1,1,2,1],
setof(Ys,list_permutation(_Xs,Ys),Yss),
setof(Ys,list_permuted(_Xs,Ys),Yss),
length(Yss,N).
N = 84, Yss = [[1,1,1,1,1,1,2,2,2],[1,1,1,1,1,2,1,2,2],[...|...]|...].
OK! How about empirical runtime measurements with a problem of a slightly bigger size?
We use call_time/2 for measuring the runtime in milli-seconds T_ms.
?- call_time(\+ (list_permutation([1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 8110.
?- call_time(\+ (list_permuted( [1,2,1,1,1,2,1,1,1,2,1],_),false),T_ms).
T_ms = 140.
OK! And with proper compilation of if_/3 and (=)/3, list_permuted/2 is even faster!
Footnote 1: Using SICStus Prolog version 4.3.2 (x86_64-linux-glibc2.12).
Footnote 2: The answers given by the Prolog toplevel have been post-processed for the sake of readability.
If you just want to explore the permutations without the "False" in the end, this code might be helpful
takeout(X,[F |R],[F|S]) :- F\=X, takeout(X,R,S).
takeout(X,[X|R],R).
perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).
perm([],[]).
So, the output of perm([a,b],B) would be
B=[a,b]
B=[b,a]