Swap two elements from list with specified indices - list

I need to swap two elements in list with specified indices using predicate:
swap(List, index1, index2).
I tried this way:
change_value([X|List], 0, Y, [Y|List2]) :-
copy_rest([X|List], List2).
change_value([], P, Y, []).
change_value([X|List], Pos, Y, [X|List2]) :-
X \== Y,
Pos > 0,
NewPos is Pos - 1,
change_value(List, NewPos, Y, List2).
copy_rest([], []).
copy_rest([X|List], [X|List2]) :-
copy_rest(List, List2).
Is there any better solution?
Thank you very much!

No need to write recursive code!
Simply use the builtin predicates length/2, same_length/2, and append/3 like so:
list_i_j_swapped(As,I,J,Cs) :-
same_length(As,Cs),
append(BeforeI,[AtI|PastI],As),
append(BeforeI,[AtJ|PastI],Bs),
append(BeforeJ,[AtJ|PastJ],Bs),
append(BeforeJ,[AtI|PastJ],Cs),
length(BeforeI,I),
length(BeforeJ,J).
Done! Let's put it to use!
?- list_i_j_swapped([e0,e1,e2,e3,e4,e5],5,1,Ys).
Ys = [e0,e5,e2,e3,e4,e1]
; false.
OK! Does it work in the "other direction", too?
?- list_i_j_swapped(Xs,5,1,[e0,e5,e2,e3,e4,e1]).
Xs = [e0,e1,e2,e3,e4,e5]
; false.
Alright! What about the following quite general query?
?- list_i_j_swapped([A,B,C],I,J,Ys).
I = 0, J = 0, Ys = [A,B,C]
; I = 0, J = 1, Ys = [B,A,C]
; I = 0, J = 2, Ys = [C,B,A]
; I = 1, J = 0, Ys = [B,A,C]
; I = 1, J = 1, Ys = [A,B,C]
; I = 1, J = 2, Ys = [A,C,B]
; I = 2, J = 0, Ys = [C,B,A]
; I = 2, J = 1, Ys = [A,C,B]
; I = J, J = 2, Ys = [A,B,C]
; false.
It worked! At last, we run the most general query:
?- list_i_j_swapped(Xs,I,J,Ys).
I = 0, J = 0, Xs = [_A] , Ys = [_A]
; I = 0, J = 0, Xs = [_A,_B] , Ys = [_A,_B]
; I = 0, J = 1, Xs = [_A,_B] , Ys = [_B,_A]
; I = 1, J = 0, Xs = [_A,_B] , Ys = [_B,_A]
; I = 1, J = 1, Xs = [_A,_B] , Ys = [_A,_B]
; I = 0, J = 0, Xs = [_A,_B,_C], Ys = [_A,_B,_C]
...
Fair enumeration out-of-the-box? What's not to like?

Using DCG, for performance (due to difference lists):
list_i_j_nth1_swap_using_dcg(I, J, Lst, LstSwap) :-
integer(I),
integer(J),
Min is min(I, J),
Max is max(I, J),
Min >= 1,
Min < Max,
phrase(list_i_j_nth1_swap(Min, Max), Lst, LstSwap).
list_i_j_nth1_swap(1, Max), [ElemJ] -->
!, [ElemI], { Max0 is Max - 1 }, list_i_j_nth1_swap_j(Max0, ElemI, ElemJ).
list_i_j_nth1_swap(Min, Max), [E] -->
[E], { Min0 is Min - 1, Max0 is Max - 1 }, list_i_j_nth1_swap(Min0, Max0).
list_i_j_nth1_swap_j(1, ElemI, ElemJ), [ElemI] -->
!, [ElemJ].
list_i_j_nth1_swap_j(Max, ElemI, ElemJ), [E] -->
[E], { Max0 is Max - 1 }, list_i_j_nth1_swap_j(Max0, ElemI, ElemJ).
Result in swi-prolog:
?- numlist(1, 10, L), time(list_i_j_nth1_swap_using_dcg(2, 4, L, S)).
% 9 inferences, 0.000 CPU in 0.000 seconds (88% CPU, 208792 Lips)
L = [1,2,3,4,5,6,7,8,9,10],
S = [1,4,3,2,5,6,7,8,9,10].
?- time(list_i_j_nth1_swap_using_dcg(2, 9, L, [1,9,3,4,5,6,7,8,2,10])).
% 14 inferences, 0.000 CPU in 0.000 seconds (90% CPU, 272368 Lips)
L = [1,2,3,4,5,6,7,8,9,10].
?- numlist(1, 10000000, L), time(list_i_j_nth1_swap_using_dcg(3, 5, L, S)).
% 10 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 320092 Lips)
S = [1,2,5,4,3,6,7,8,9,10...
?- numlist(1, 10000000, L), time(list_i_j_nth1_swap_using_dcg(2000, 1000, L,
S)).
% 2,005 inferences, 0.319 CPU in 0.325 seconds (98% CPU, 6290 Lips)

Related

List consisting of each element of another List repeated twice Prolog

I have to write a predicate: double(X,Y) to be true when Y is the list consisting of each element of X
repeated twice (e.g. double([a,b],[a,a,b,b]) is true).
I ended with sth like this:
double([],[]).
double([T],List) :- double([H|T],List).
double([H|T],List) :- count(H, List, 2).
Its working fine for lists like [a,a,b] but it shouldnt... please help.
And i need help with another predicate: repeat(X,Y,N) to be true when Y is the list consisting of each element of X
repeated N times (e.g. repeat([a,b], [a,a,a,b,b,b],3) is true).
double([],[]).
double([I|R],[I,I|RD]) :-
double(R,RD).
Here's how you could realize that "repeat" predicate you suggested in the question:
:- use_module(library(clpfd)).
Based on if_/3 and (=)/3 we define:
each_n_reps([E|Es], N) :-
aux_n_reps(Es, E, 1, N).
aux_n_reps([], _, N, N). % internal auxiliary predicate
aux_n_reps([E|Es], E0, N0, N) :-
if_(E0 = E,
( N0 #< N, N1 #= N0+1 ), % continue current run
( N0 #= N, N1 #= 1 )), % start new run
aux_n_reps(Es, E, N1, N).
Sample queries1 using SICStus Prolog 4.3.2:
?- each_n_reps(Xs, 3).
Xs = [_A,_A,_A]
; Xs = [_A,_A,_A,_B,_B,_B] , dif(_A,_B)
; Xs = [_A,_A,_A,_B,_B,_B,_C,_C,_C], dif(_A,_B), dif(_B,_C)
...
How about fair enumeration?
?- length(Xs, _), each_n_reps(Xs, N).
N = 1, Xs = [_A]
; N = 2, Xs = [_A,_A]
; N = 1, Xs = [_A,_B] , dif(_A,_B)
; N = 3, Xs = [_A,_A,_A]
; N = 1, Xs = [_A,_B,_C] , dif(_A,_B), dif(_B,_C)
; N = 4, Xs = [_A,_A,_A,_A]
; N = 2, Xs = [_A,_A,_B,_B], dif(_A,_B)
; N = 1, Xs = [_A,_B,_C,_D], dif(_A,_B), dif(_B,_C), dif(_C,_D)
...
How can [A,B,C,D,E,F] be split into runs of equal length?
?- each_n_reps([A,B,C,D,E,F], N).
N = 6, A=B , B=C , C=D , D=E , E=F
; N = 3, A=B , B=C , dif(C,D), D=E , E=F
; N = 2, A=B , dif(B,C), C=D , dif(D,E), E=F
; N = 1, dif(A,B), dif(B,C), dif(C,D), dif(D,E), dif(E,F).
Footnote 1: Answers were reformatted to improve readability.
Ok for repeat/3 i have sth like this:
repeat1([],[],0).
repeat1([A|B],[X|T],Y):- repeat1(B,T,Z), Y is 1+Z.
repeat1([A1|B],[X1|T], Z) :- A1\=A, X1\=X, repeat1(B,T,Z).

Prolog: Arrangements of k elements with sum of elements S

I am trying to compute arrangements of K elements in Prolog, where the sum of their elements is equal to a given S. So, I know that arrangements can be computed by finding the combinations and then permute them. I know how to compute combinations of K elements, something like:
comb([E|_], 1, [E]).
comb([_|T], K, R) :-
comb(T, K, R).
comb([H|T], K, [H|R]) :-
K > 1,
K1 is K-1,
comb(T, K1, R).
The permutations of a list, having the property that the sum of their elements is equal to a given S, I know to compute like this:
insert(E, L, [E|L]).
insert(E, [H|T], [H|R]) :-
insert(E, T, R).
perm([], []).
perm([H|T], P) :-
perm(T, R),
insert(H, R, P).
sumList([], 0).
sumList([H], H) :-
number(H).
sumList([H|Tail], R1) :-
sumList(Tail, R),
R1 is R+H.
perms(L, S, R) :-
perm(L, R),
sumList(R, S1),
S = S1.
allPerms(L, LP) :-
findall(R, perms(L,R), LP).
The problem is that I do not know how to combine them, in order to get the arrangements of K elements, having the sum of elements equal to a given S. Any help would be appreciated.
Use clpfd!
:- use_module(library(clpfd)).
Using SWI-Prolog 7.3.16 we query:
?- length(Zs,4), Zs ins 1..4, sum(Zs,#=,7), labeling([],Zs).
Zs = [1,1,1,4]
; Zs = [1,1,2,3]
; Zs = [1,1,3,2]
; Zs = [1,1,4,1]
; Zs = [1,2,1,3]
; Zs = [1,2,2,2]
; Zs = [1,2,3,1]
; Zs = [1,3,1,2]
; Zs = [1,3,2,1]
; Zs = [1,4,1,1]
; Zs = [2,1,1,3]
; Zs = [2,1,2,2]
; Zs = [2,1,3,1]
; Zs = [2,2,1,2]
; Zs = [2,2,2,1]
; Zs = [2,3,1,1]
; Zs = [3,1,1,2]
; Zs = [3,1,2,1]
; Zs = [3,2,1,1]
; Zs = [4,1,1,1].
To eliminate "redundant modulo permutation" solutions use chain/2:
?- length(Zs,4), Zs ins 1..4, chain(Zs,#=<), sum(Zs,#=,7), labeling([],Zs).
Zs = [1,1,1,4]
; Zs = [1,1,2,3]
; Zs = [1,2,2,2]
; false.
I use SWI-Prolog.
You can write that
:- use_module(library(lambda)).
arrangement(K, S, L) :-
% we have a list of K numbers
length(L, K),
% these numbers are between 1 (or 0) and S
maplist(between(1, S), L),
% the sum of these numbers is S
foldl(\X^Y^Z^(Z is X+Y), L, 0, S).
The result
?- arrangement(5, 10, L).
L = [1, 1, 1, 1, 6] ;
L = [1, 1, 1, 2, 5] ;
L = [1, 1, 1, 3, 4] ;
L = [1, 1, 1, 4, 3] .
You can use also a CLP(FD) library.
Edited after the remark of #repeat.
This response is similar to response of #repeat
predicates that below are implemented using the SICStus 4.3.2 tool
after simple modification of gen_list(+,+,?)
edit Code
gen_list(Length,Sum,List) :- length(List,Length),
domain(List,0,Sum),
sum(List,#=,Sum),
labeling([],List),
% to avoid duplicate results
ordered(List).
Test
| ?- gen_list(4,7,L).
L = [0,0,0,7] ? ;
L = [0,0,1,6] ? ;
L = [0,0,2,5] ? ;
L = [0,0,3,4] ? ;
L = [0,1,1,5] ? ;
L = [0,1,2,4] ? ;
L = [0,1,3,3] ? ;
L = [0,2,2,3] ? ;
L = [1,1,1,4] ? ;
L = [1,1,2,3] ? ;
L = [1,2,2,2] ? ;
no
I don't think that permutations could be relevant for your problem. Since the sum operation is commutative, the order of elements should be actually irrelevant. So, after this correction
sumList([], 0).
%sumList([H], H) :-
% number(H).
sumList([H|Tail], R1) :-
sumList(Tail, R),
R1 is R+H.
you can just use your predicates
'arrangements of K elements'(Elements, K, Sum, Arrangement) :-
comb(Elements, K, Arrangement),
sumList(Arrangement, Sum).
test:
'arrangements of K elements'([1,2,3,4,5,6],3,11,A).
A = [2, 4, 5] ;
A = [2, 3, 6] ;
A = [1, 4, 6] ;
false.
You already know how to use findall/3 to get all lists at once, if you need them.

Prolog add N to 0 to the list

I want to add to Result list N to 0 digits.
The sample query
?- add(5,R).
should return the answer:
R = [5,4,3,2,1,0].
I already tried the following code but it did not work.
add(0, 0).
add(N, [R]) :-
N1 is N-1,
add(N1, [R|N]).
You're so close!
add(0, [0]).
add(N, [N|R]) :-
N > 0,
N1 is N-1,
add(N1, R).
So, what's different here?
add(0, [0]) has [0] instead of 0 because you're building a list, not an integer; otherwise you get the rather awkward looking [5,4,3,2,1|0] result.
N > 0 as a guard, to ensure that we don't loop crawling through negative numbers forever once we hit the base case.
The work is being done in the head of the second clause of add/2 instead of the body of it. To wit, our pattern is add(N, [N|R]) instead of add(N, [R]). This is because this term adds N to the head of the list rather than adding it before recurring.
Similarly, you have a simple inversion in [R|N]; this would build lists kind of backwards.
All in all, I think you were very close. A little more experimenting at the prompt may have been sufficient to fix it. Have you tried using trace/0 yet?
We show an analog of this answer (which dealed with consecutive integers ascending from 0).
:- use_module(library(clpfd)).
:- set_prolog_flag(toplevel_print_anon, false).
Based on equidistant_stride/2 we query:
?- N = 10, Zs = [N|_Zs0], length(_Zs0, N), equidistant_stride(Zs, -1).
N = 10, Zs = [10,9,8,7,6,5,4,3,2,1,0].
Let's re-run1 the runtime measurements we did in this previous answer!
?- between(1,6,E),
N is 10^E,
garbage_collect,
call_time(numlist(0, N, _), T1_in_ms),
garbage_collect,
call_time((_Zs = [N|_Z], length(_Z, N), equidistant_stride(_Zs, -1)), T2_in_ms).
N = 10, T1_in_ms = 0, T2_in_ms = 0
; N = 100, T1_in_ms = 1, T2_in_ms = 0
; N = 1000, T1_in_ms = 1, T2_in_ms = 1
; N = 10000, T1_in_ms = 3, T2_in_ms = 12
; N = 100000, T1_in_ms = 14, T2_in_ms = 32
; N = 1000000, T1_in_ms = 90, T2_in_ms = 280.
Edit
Past revisions of this answer inadvertently sk(r)ewed runtime measurements in favor of clpfd. How?
It's simple: A reverse/2 goal followed numlist/3, even though it is useless in this setting.
This should be better now: Thx 2 #JanWielemaker 4 reporting!
Footnote 1: Using SWI-Prolog version 7.3.11 (64-bit).
Use clpfd!
:- use_module(library(clpfd)).
:- set_prolog_flag(toplevel_print_anon, false).
We define n_to_0/2 like this:
n_to_0(N,[Z|Zs]) :-
length(Zs,N),
[Z|Zs] ins 0..N,
chain([Z|Zs],#>).
Sample query as given by the OP:
?- n_to_0(5,Zs).
Zs = [5,4,3,2,1,0].
How about the most general query using n_to_0/2?
?- n_to_0(N,Zs).
N = 0, Zs = [0]
; N = 1, Zs = [1,0]
; N = 2, Zs = [2,1,0]
; N = 3, Zs = [3,2,1,0]
; N = 4, Zs = [4,3,2,1,0]
; N = 5, Zs = [5,4,3,2,1,0]
; N = 6, Zs = [6,5,4,3,2,1,0]
...
Edit
#JanWielemaker pointed out that n_to_0/2 (as defined above) is abysmally slow—particularly when comparing it to its non-clpfd counterpart:
Thanks a lot for reporting!
See for yourself...
?- between(1, 3, E),
N is 10^E,
call_time((numlist(0, N, _Zs0), reverse(_Zs0, _)), T1_in_ms),
call_time(n_to_0(N, _), T2_in_ms).
E = 1, N = 10, T1_in_ms = 0, T2_in_ms = 1
; E = 2, N = 100, T1_in_ms = 0, T2_in_ms = 104
; E = 3, N = 1000, T1_in_ms = 0, T2_in_ms = 29701
...
Check out this new, improved, clpfd-based answer!

Fill list in SWI-Prolog

I am trying to fill a list of given length N with numbers 1,2,3,...,N.
I thought this could be done this way:
create_list(N,L) :-
length(L,N),
forall(between(1,N,X), nth1(X,L,X)).
However, this does not seem to work. Can anyone say what I am doing wrong?
First things first: Use clpfd!
:- use_module(library(clpfd)).
In the following I present zs_between_and/3, which (in comparison to my previous answer) offers some more features.
For a start, let's define some auxiliary predicates first!
equidistant_stride([] ,_).
equidistant_stride([Z|Zs],D) :-
equidistant_prev_stride(Zs,Z,D).
equidistant_prev_stride([] ,_ ,_). % internal predicate
equidistant_prev_stride([Z1|Zs],Z0,D) :-
Z1 #= Z0+D,
equidistant_prev_stride(Zs,Z1,D).
Let's run a few queries to get a picture of equidistant_stride/2:
?- Zs = [_,_,_], equidistant_stride(Zs,D).
Zs = [_A,_B,_C], _A+D#=_B, _B+D#=_C.
?- Zs = [1,_,_], equidistant_stride(Zs,D).
Zs = [1,_B,_C], _B+D#=_C, 1+D#=_B.
?- Zs = [1,_,_], equidistant_stride(Zs,10).
Zs = [1,11,21].
So far, so good... moving on to the actual "fill list" predicate zs_between_and/3:
zs_between_and([Z0|Zs],Z0,Z1) :-
Step in -1..1,
Z0 #= Z1 #<==> Step #= 0,
Z0 #< Z1 #<==> Step #= 1,
Z0 #> Z1 #<==> Step #= -1,
N #= abs(Z1-Z0),
( fd_size(N,sup)
-> true
; labeling([enum,up],[N])
),
length(Zs,N),
labeling([enum,down],[Step]),
equidistant_prev_stride(Zs,Z0,Step).
A bit baroque, I must confess...
Let's see what features were gained---in comparison to my previous answer!
?- zs_between_and(Zs,1,4). % ascending consecutive integers
Zs = [1,2,3,4]. % (succeeds deterministically)
?- zs_between_and(Zs,3,1). % descending consecutive integers (NEW)
Zs = [3,2,1]. % (succeeds deterministically)
?- zs_between_and(Zs,L,10). % enumerates fairly
L = 10, Zs = [10] % both ascending and descenting (NEW)
; L = 9, Zs = [9,10]
; L = 11, Zs = [11,10]
; L = 8, Zs = [8,9,10]
; L = 12, Zs = [12,11,10]
; L = 7, Zs = [7,8,9,10]
...
?- L in 1..3, zs_between_and(Zs,L,6).
L = 3, Zs = [3,4,5,6]
; L = 2, Zs = [2,3,4,5,6]
; L = 1, Zs = [1,2,3,4,5,6].
Want some more? Here we go!
?- zs_between_and([1,2,3],From,To).
From = 1, To = 3
; false.
?- zs_between_and([A,2,C],From,To).
A = 1, From = 1, C = 3, To = 3 % ascending
; A = 3, From = 3, C = 1, To = 1. % descending
I don't have a prolog interpreter available right now, but wouldn't something like...
isListTo(N, L) :- reverse(R, L), isListFrom(N, R).
isListFrom(0, []).
isListFrom(N, [H|T]) :- M is N - 1, N is H, isListFrom(M, T).
reverse can be done by using e.g. http://www.webeks.net/prolog/prolog-reverse-list-function.html
So tracing isListTo(5, [1, 2, 3, 4, 5])...
isListTo(5, [1, 2, 3, 4, 5])
<=> isListFrom(5, [5, 4, 3, 2, 1])
<=> 5 is 5 and isListFrom(4, [4, 3, 2, 1])
<=> 4 is 4 and isListFrom(3, [3, 2, 1])
<=> 3 is 3 and isListFrom(2, [2, 1])
<=> 2 is 2 and isListFrom(1, [1])
<=> 1 is 1 and isListFrom(0, [])
QED
Since PROLOG will not only evaluate truth, but find satisfying solutions, this should work. I know this is a vastly different approach from the one you are trying, and apologize if your question is specifically about doing loops in PROLOG (if that is the case, perhaps re-tag the question?).
Here's a logically pure implementation of predicate zs_from_to/3 using clpfd:
:- use_module(library(clpfd)).
zs_from_to([],I0,I) :-
I0 #> I.
zs_from_to([I0|Is],I0,I) :-
I0 #=< I,
I1 #= I0 + 1,
zs_from_to(Is,I1,I).
Let's use it! First, some ground queries:
?- zs_from_to([1,2,3],1,3).
true.
?- zs_from_to([1,2,3],1,4).
false.
Next, some more general queries:
?- zs_from_to(Zs,1,7).
Zs = [1,2,3,4,5,6,7]
; false.
?- zs_from_to([1,2,3],From,To).
From = 1, To = 3.
Now, let's have some even more general queries:
?- zs_from_to(Zs,From,2).
Zs = [], From in 3..sup
; Zs = [2], From = 2
; Zs = [1,2], From = 1
; Zs = [0,1,2], From = 0
; Zs = [-1,0,1,2], From = -1
; Zs = [-2,-1,0,1,2], From = -2
...
?- zs_from_to(Zs,0,To).
Zs = [], To in inf.. -1
; Zs = [0], To = 0
; Zs = [0,1], To = 1
; Zs = [0,1,2], To = 2
; Zs = [0,1,2,3], To = 3
; Zs = [0,1,2,3,4], To = 4
...
What answers do we get for the most general query?
?- zs_from_to(Xs,I,J).
Xs = [], J#=<I+ -1
; Xs = [I], I+1#=_A, J#>=I, J#=<_A+ -1
; Xs = [I,_A], I+1#=_A, J#>=I, _A+1#=_B, J#>=_A, J#=<_B+ -1
; Xs = [I,_A,_B], I+1#=_A, J#>=I, _A+1#=_B, J#>=_A, _B+1#=_C, J#>=_B, J#=<_C+ -1
...
Edit 2015-06-07
To improve on above implementation of zs_from_to/3, let's do two things:
Try to improve determinism of the implementation.
Extract a more general higher-order idiom, and implement zs_from_to/3 on top of it.
Introducing the meta-predicates init0/3 and init1/3:
:- meta_predicate init0(2,?,?).
:- meta_predicate init1(2,?,?).
init0(P_2,Expr,Xs) :- N is Expr, length(Xs,N), init_aux(Xs,P_2,0).
init1(P_2,Expr,Xs) :- N is Expr, length(Xs,N), init_aux(Xs,P_2,1).
:- meta_predicate init_aux(?,2,+). % internal auxiliary predicate
init_aux([] , _ ,_ ).
init_aux([Z|Zs],P_2,I0) :-
call(P_2,I0,Z),
I1 is I0+1,
init_aux(Zs,P_2,I1).
Let's see init0/3 and init1/3 in action!
?- init0(=,5,Zs). % ?- numlist(0,4,Xs),maplist(=,Xs,Zs).
Zs = [0,1,2,3,4].
?- init1(=,5,Zs). % ?- numlist(1,5,Xs),maplist(=,Xs,Zs).
Zs = [1,2,3,4,5].
Ok, where do we go from here? Consider the following query:
?- init0(plus(10),5,Zs). % ?- numlist(0,4,Xs),maplist(plus(10),Xs,Zs).
Zs = [10,11,12,13,14].
Almost done! Putting it together, we define zs_from_to/2 like this:
z_z_sum(A,B,C) :- C #= A+B.
zs_from_to(Zs,I0,I) :-
N #= I-I0+1,
init0(z_z_sum(I0),N,Zs).
At last, let's see if determinism has improved!
?- zs_from_to(Zs,1,7).
Zs = [1,2,3,4,5,6,7]. % succeeds deterministically
If I understood correctly, the built-in predicate numlist/3 would do.
http://www.swi-prolog.org/pldoc/man?predicate=numlist/3

Appending a list to a list of lists recursively

The problem I'm trying to solve is as follows:
I'm given a sorted list where I must pair the first and last items in the list. Then I must pair the 2nd and (last-1) items in the list until the list is either empty or 1 element remains. Then I must return a list of the pairs.
The steps I decided to take for this problem was to first check if the list's length was greater than 1. If it wasn't, then that means we have a list of 0 or 1 elements.
Then I get the first and last items in the given list, delete them from the list, pair them, and then recursively call the same predicate on the new list. Once I've gone all the way down to 0/1 items, I then pop back up and append them to my return list.
The problem I'm having is that when I try to append the pair L = [first,last] to my return list, it errors out. My code is listed below.
T is my input list.
first/2 just gets the first item in the list. pair/3 strips away some info from P1 and P2 and then creates L = [P1,P2].
getMatches(T,K,ReturnList) :-
( length(T,Val),
Val > 1,
first(T,P1),
last(T, P2),
delete(T,P1,G),
delete(G,P2,H),
pair(P1,P2,L),
getMatches(H,K,ReturnList),
append(L,K,ReturnList)
; first(T,_),
K = []
).
An example use:
If T = [1, 2, 3, 4, 5] then
ReturnList = [[1,5], [2, 4]] should hold.
We define list_pairs/2 based on the commonly available list predicate append/3.
list_pairs([] , []).
list_pairs([_], []).
list_pairs([A,B|Xs0], [A-Z|Yss]) :-
append(Xs, [Z], [B|Xs0]),
list_pairs(Xs, Yss).
Note we do not represent a pair of X and Y as a list [X,Y], but rather as a compound X-Y. This convention is idiomatic, widespread, and more efficient, too!
Here's the most general query of list_pairs/2:
?- list_pairs(Es, Pss).
Es = [] , Pss = []
; Es = [_] , Pss = []
; Es = [A,B] , Pss = [A-B]
; Es = [A,_,B] , Pss = [A-B]
; Es = [A,B,C,D] , Pss = [A-D,B-C]
; Es = [A,B,_,C,D] , Pss = [A-D,B-C]
; Es = [A,B,C,D,E,F] , Pss = [A-F,B-E,C-D]
; Es = [A,B,C,_,D,E,F] , Pss = [A-F,B-E,C-D]
; Es = [A,B,C,D,E,F,G,H] , Pss = [A-H,B-G,C-F,D-E]
; Es = [A,B,C,D,_,E,F,G,H], Pss = [A-H,B-G,C-F,D-E]
...
This is a follow-up to this earlier answer and improves it by
avoiding the creation of useless choice-points and by
reducing the Big-O complexity from O(N2) to O(N).
list_pairs_lin([], []).
list_pairs_lin([X|Xs], XYs) :-
reverse(Xs, Ys),
ahead_keys_values_pairs(Xs, [X|Xs], Ys, XYs).
ahead_keys_values_pairs([], _, _, []).
ahead_keys_values_pairs([_|Fs0], [X|Xs], [Y|Ys], [X-Y|XYs]) :-
maybe_ahead(Fs0, Fs),
ahead_keys_values_pairs(Fs, Xs, Ys, XYs).
maybe_ahead([], []).
maybe_ahead([_|Xs], Xs).
Let's run some queries with SWI-Prolog 7.3.15!
Do we still get sound answers when using list_pairs_lin/2?
?- length(Es, N), numlist(1, N, Es), list_pairs_lin(Es, Pss).
N = 1, Es = [1] , Pss = []
; N = 2, Es = [1,2] , Pss = [1-2]
; N = 3, Es = [1,2,3] , Pss = [1-3]
; N = 4, Es = [1,2,3,4] , Pss = [1-4,2-3]
; N = 5, Es = [1,2,3,4,5] , Pss = [1-5,2-4]
; N = 6, Es = [1,2,3,4,5,6] , Pss = [1-6,2-5,3-4]
; N = 7, Es = [1,2,3,4,5,6,7] , Pss = [1-7,2-6,3-5]
; N = 8, Es = [1,2,3,4,5,6,7,8] , Pss = [1-8,2-7,3-6,4-5]
; N = 9, Es = [1,2,3,4,5,6,7,8,9], Pss = [1-9,2-8,3-7,4-6]
...
Yes! What about complexity?
?- set_prolog_flag(toplevel_print_anon, false).
true.
?- numlist(1, 5000, _Xs), time(list_pairs(_Xs,_)).
% 6,252,500 inferences, 2.302 CPU in 2.301 seconds (100% CPU, 2716404 Lips)
true ; % succeeds, but leaves behind useless choicepoint
% 2,503 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 1716259 Lips)
false. % terminates universally
?- numlist(1, 5000, _Xs), time(list_pairs_lin(_Xs,_)).
% 10,003 inferences, 0.003 CPU in 0.003 seconds (100% CPU, 3680523 Lips)
true. % succeeds deterministically
getMatches(List, ReturnList) :- % getMatches/2
getMatches(List, [], Answer),
reverse(Answer, ReturnList),
!.
getMatches(List, ListAns, ListAns) :- % getMatches/3
length(List, L),
L < 2.
getMatches([H | Tail], List, Ans) :-
last(Tail, Last),
delete(Tail, Last, NewTail),
append([[H, Last]], List, NewList),
getMatches(NewTail, NewList, Ans).
And
?- getMatches([1,2,3,4,5],X).
X = [[1, 5], [2, 4]].