Related
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.
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.
This is the code for deleting or removing an element from a given list:
remove_elem(X,[],[]).
remove_elem(X,L1,L2) :-
L1 = [H|T],
X == H,
remove_elem(X,T,Temp),
L2 = Temp.
remove_elem(X,L1,L2) :-
L1 = [H|T],
X \== H,
remove_elem(X,T,Temp),
L2 = [H|Temp].
How can I modify it, so that I can delete every occurrence of a sub list from a list?
When I tried to put a list in an element, it only deletes the element and only once.
It should be this:
?- remove([1,2],[1,2,3,4,1,2,5,6,1,2,1],L).
L = [3,4,5,6,1]. % expected result
Inspired by #CapelliC's implementation I wrote the following code based on
and_t/3:
append_t([] ,Ys,Ys, true).
append_t([X|Xs],Ys,Zs,Truth) :-
append_aux_t(Zs,Ys,Xs,X,Truth).
append_aux_t([] ,_ ,_ ,_,false). % aux pred for using 1st argument indexing
append_aux_t([Z|Zs],Ys,Xs,X,Truth) :-
and_t(X=Z, append_t(Xs,Ys,Zs), Truth).
One append_t/4 goal can replace two prefix_of_t/3 and append/3 goals.
Because of that, the implementation of list_sublist_removed/3 gets a bit simpler than before:
list_sublist_removed([] ,[_|_] ,[]).
list_sublist_removed([X|Xs],[L|Ls],Zs) :-
if_(append_t([L|Ls],Xs0,[X|Xs]),
(Zs = Zs0 , Xs1 = Xs0),
(Zs = [X|Zs0], Xs1 = Xs)),
list_sublist_removed(Xs1,[L|Ls],Zs0).
Still deterministic?
?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],[1,2],L).
L = [3,4,5,6,1].
Yes! What about the following?
?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],X,[3,4,5,6,1]).
X = [1,2] ; % succeeds with useless choice-point
false.
Nope. So there is still room for potential improvement...
This logically pure implementation is based on the predicates if_/3 and (=)/3.
First, we build a reified version of prefix_of/2:
prefix_of_t([],_,true).
prefix_of_t([X|Xs],Zs,T) :-
prefix_of_t__aux(Zs,X,Xs,T).
prefix_of_t__aux([],_,_,false).
prefix_of_t__aux([Z|Zs],X,Xs,T) :-
if_(X=Z, prefix_of_t(Xs,Zs,T), T=false).
Then, on to the main predicate list_sublist_removed/3:
list_sublist_removed([],[_|_],[]).
list_sublist_removed([X|Xs],[L|Ls],Zs) :-
if_(prefix_of_t([L|Ls],[X|Xs]), % test
(Zs = Zs0, append([L|Ls],Xs0,[X|Xs])), % case 1
(Zs = [X|Zs0], Xs0 = Xs)), % case 2
list_sublist_removed(Xs0,[L|Ls],Zs0).
A few operational notes on the recursive clause of list_sublist_removed/3:
First (test), we check if [L|Ls] is a prefix of [X|Xs].
If it is present (case 1), we strip it off [X|Xs] yielding Xs0 and add nothing to Zs.
If it is absent (case 2), we strip X off [X|Xs] and add X to Zs.
We recurse on the rest of [X|Xs] until no more items are left to process.
Onwards to some queries!
The use case you gave in your question:
?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],[1,2],L).
L = [3,4,5,6,1]. % succeeds deterministically
Two queries that try to find the sublist that was removed:
?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],Sub,[ 3,4,5,6,1]).
Sub = [1,2] ? ;
no
?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],Sub,[1,3,4,5,6,1]).
no
Next, let's find a suitable Ls in this query:
?- list_sublist_removed(Ls,[1,2],[3,4,5,6,1]).
% a lot of time passes ... and nothing happens!
Non-termination! This is unfortunate, but within expectations, as the solution set is infinite. However, by a-priori constraining the length of Ls, we can get all expected results:
?- length(Ls,_), list_sublist_removed(Ls,[1,2],[3,4,5,6,1]).
Ls = [ 3,4,5,6,1] ?
; Ls = [1,2, 3,4,5,6,1] ?
; Ls = [3, 1,2, 4,5,6,1] ?
; Ls = [3,4, 1,2, 5,6,1] ?
; Ls = [3,4,5, 1,2, 6,1] ?
; Ls = [3,4,5,6, 1,2, 1] ?
; Ls = [3,4,5,6,1, 1,2 ] ?
; Ls = [1,2, 1,2, 3,4,5,6,1] ? ...
<rant>
So many years I study Prolog, still it deserves some surprises... your problem it's quite simple to solve, when you know the list library, and you have a specific mode (like the one you posted as example). But can also be also quite complex to generalize, and it's unclear to me if the approach proposed by #repeat, based on #false suggestion (if_/3 and friends) can be 'ported' to plain, old Prolog (a-la Clocksin-Mellish, just to say).
</rant>
A solution, that has been not so easy to find, based on old-school Prolog
list_sublist_removed(L, S, R) :-
append([A, S, B], L),
S \= [],
list_sublist_removed(B, S, T),
append(A, T, R),
!
; L = R.
some test:
?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],[1,2],L).
L = [3, 4, 5, 6, 1].
?- list_sublist_removed([1,2,3,4,1,2,5,6,1,2,1],X,[3, 4, 5, 6, 1]).
X = [1, 2].
?- length(X,_), list_sublist_removed(X,[1,2],[3, 4, 5, 6, 1]).
X = [3, 4, 5, 6, 1] ;
X = [3, 4, 5, 6, 1, 2, 1] ...
I have written a program that can take a list and change it into a tree.
build_tree([X,Y],'Tree'(X,Y)) :- !.
build_tree([X|Y],'Tree'(X,Z)) :- build_tree(Y, Z).
If I want to reverse the process and take the tree and change it back into a list, how would I do this?
Note that your tree->list conversion isn't a function since trees may correspond to multiple lists:
?- build_tree([1, 2, 3], T).
T = 'Tree'(1, 'Tree'(2, 3)).
?- build_tree([1, 'Tree'(2, 3)], T).
T = 'Tree'(1, 'Tree'(2, 3)).
If you want a predicate that can generate all lists from a tree, remove the cut from build_tree and apply it with a variable first argument. If you want a deterministic conversion, write a new predicate tree_to_list.
Just curious, how would that deterministic version play out? Assuming there was only one possible list, from the tree, for example:
('Tree'('Tree'(nil, 2, nil), 5, 'Tree'(nil, 6, nil)).
Which gives: L = [5, 2, 6]
If you remove the cut from the first rule, your code it's ready to work in 'backward' mode:
?- build_tree([1,2,3,4],T).
T = 'Tree'(1, 'Tree'(2, 'Tree'(3, 4))) ;
false.
?- build_tree(X,$T).
X = [1, 'Tree'(2, 'Tree'(3, 4))] ;
X = [1, 2, 'Tree'(3, 4)] ;
X = [1, 2, 3, 4] ;
false.
flatten(leaf, []).
flatten(node(L, E, R), Ls) :-
flatten(L, Ls1),
append(Ls1, [E], Ls2),
flatten(R, Ls3),
append(Ls2, Ls3, Ls).
if you consider tree ass node(leaf,Element,leaf) for example
flatten(node(node(leaf,2,leaf),3,node(leaf,5,leaf)),X).
gives X=[2,3,5].
and if you wanna have bst
List to Tree.
insert(E,leaf,node(leaf,E,leaf)).
insert(E,node(L,N,R),T) :-
E >= N,
T=node(L,N,R1),
insert(E,R,R1).
insert(E,node(L,N,R),T) :-
E < N,
T=node(L1,N,R),
insert(E,L,L1).
list_to_tree(List,Tree) :-
list_to_tree(List,leaf,Trea2),
Tree=Trea2.
list_to_tree([],Tree,Tree).
list_to_tree([H|T],Tree,St):-
insert(H,Tree,R),
list_to_tree(T,R,St).
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]