I'm trying to write a program in Prolog, which will insert an element into a certain position, so e.g.
?- ins(a, [1,2,3,4,5], 3, X).
X = [1,2,a,3,4,5].
I have the following code:
ins(X,[H|T],P,OUT) :-
length([T3],P),
concatenate(X,[H],T),
ins(...).
The problem is that it is inserting element X in given index from back (I even know where the problem is -> the length([T3],P) which is obviously the length of the list from back not from head) . I was trying to remember how much elements did I cut off and insert X when "number of cut off elements" = P, but I can't really write that in Prolog. Any ideas?
% ins(Val,List,Pos,Res)
ins(Val,[H|List],Pos,[H|Res]):- Pos > 1, !,
Pos1 is Pos - 1, ins(Val,List,Pos1,Res).
ins(Val, List, 1, [Val|List]).
The predicate fails if Pos = 0 or Pos > length(List) + 1
Let's state what you want here. For example you could say: "I want to split my input List after Position - 1 elements so that I can insert a new element there".
A direct traduction with append/3 (DCG would be better btw):
ins(Element, List, Position, Result) :-
PrefixLength is Position - 1,
length(Prefix, PrefixLength),
append(Prefix, Suffix, List),
append(Prefix, [Element], Temp),
append(Temp, Suffix, Result).
Or you could say: "I want to go through the elements of my List Position - 1 times without touching anything and then insert Element and then not touch anything again".
This time the direct traduction would be:
ins2(Element, List, 1, [Element|List]).
ins2(Element, [Head|Tail], Position, [Head|Result]) :-
Position > 1,
NewPosition is Position - 1,
ins2(Element, Tail, NewPosition, Result).
you could too state that: "My input List is a list equal to my Result one except it hasn't my Element as Positionth element." and realize that if you use swi-prolog, a predicate solves this instantly:
ins3(Element, List, Position, Result) :-
nth1(Position, Result, Element, List).
Bottom line is: state what the problem is clearly and the solution should appear in simple terms.
TL;DR: To insert item E at position I1 into list Es0, we do not need to write recursive code.
Instead, we can delegate the work (and the worries about getting it right, too!) to versatile auxiliary predicates, all of which are part of the Prolog prologue. To define ins_/4 we write:
ins_(E, Es0, I1, Es) :-
maplist(any_thing, Es, [_|Es0]),
append(Prefix, Suffix, Es0),
length([_|Prefix], I1),
append(Prefix, [E|Suffix], Es).
any_thing(_, _). % auxiliary predicate (used above)
Note that maplist(any_thing, Es, [_|Es0]) is equivalent to same_length(Es, [_|Es0]).
Sample queries1,2,3 using GNU Prolog version 1.4.4 (64-bit):
?- ins_(X, [a,b,c,d,e], N1, Xs).
N1 = 1, Xs = [X,a,b,c,d,e]
; N1 = 2, Xs = [a,X,b,c,d,e]
; N1 = 3, Xs = [a,b,X,c,d,e]
; N1 = 4, Xs = [a,b,c,X,d,e]
; N1 = 5, Xs = [a,b,c,d,X,e]
; N1 = 6, Xs = [a,b,c,d,e,X]
; false.
?- ins_(X, [a,b,c,d,e], 3, Xs).
Xs = [a,b,X,c,d,e]
; false.
?- ins_(X, Xs0, 3, [a,b,c,d,e]).
X = c, Xs0 = [a,b,d,e]
; false.
Let's not forget about the most general query!
?- ins(X, Es0, I1, Es).
Es0 = [], I1 = 1, Es = [X]
;
Es0 = [A], I1 = 1, Es = [X,A]
; Es0 = [A], I1 = 2, Es = [A,X]
;
Es0 = [A,B], I1 = 1, Es = [X,A,B]
; Es0 = [A,B], I1 = 2, Es = [A,X,B]
; Es0 = [A,B], I1 = 3, Es = [A,B,X]
;
Es0 = [A,B,C], I1 = 1, Es = [X,A,B,C]
; Es0 = [A,B,C], I1 = 2, Es = [A,X,B,C]
; Es0 = [A,B,C], I1 = 3, Es = [A,B,X,C]
; Es0 = [A,B,C], I1 = 4, Es = [A,B,C,X]
;
Es0 = [A,B,C,D], I1 = 1, Es = [X,A,B,C,D]
; ...
Fair enumeration of all solutions, OK!
EDIT:
I repeated the most general query with ins3/4 as defined
by #m09 in his answer on SWI-Prolog 7.3.11 and SICStus Prolog 4.3.2 (both feature the library predicate nth1/4).
I was surprised to see the underlying implementations of nth1/4 exhibit different procedural semantics (w.r.t. "fair enumeration"). See for yourself!
% SICStus Prolog 4.3.2 % SWI Prolog 7.3.11
% %
?- ins3(X, Es0, I1, Es). % ?- ins3(X, Es0, I1, Es).
I1 = 1, Es0 = [], Es = [X] % I1 = 1, Es = [X|Es0]
; % ; I1 = 2, Es0 = [_A|_Z],
I1 = 1, Es0 = [_A], Es = [X,_A] % Es = [_A,X|_Z]
; I1 = 2, Es0 = [_A], Es = [_A,X] % ; I1 = 3, Es0 = [_A,_B|_Z],
; % Es = [_A,_B,X|_Z]
I1 = 1, Es0 = [_A,_B], Es = [X,_A,_B] % ; I1 = 4, Es0 = [_A,_B,_C|_Z],
; I1 = 2, Es0 = [_A,_B], Es = [_A,X,_B] % Es = [_A,_B,_C,X|_Z],
; I1 = 3, Es0 = [_A,_B], Es = [_A,_B,X] % ; I1 = 5, Es0 = [_A,_B,_C,_D|_Z],
; % Es = [_A,_B,_C,_D,X|_Z]
... % ...
Footnote 1: All sample queries shown above terminate universally.
Footnote 2: The answers given by the GNU Prolog toplevel have been pretty-printed a little.
Footnote 3: The code presented above is used as-is, no additional library predicates are required.
ins(Element,List,Nth,Result) :-
length([_|L0],Nth),
append(L0,[_|R],List),
append(L0,[Element|R],Result).
Related
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!
Fairly new to Prolog, I'm trying to implement a recursive rule duplicate_nth(N,L1,L2) which takes a 1-based index N, a list L1 and duplicates the Nth value of L1 and returns it in a list L2.
Sample use:
?- duplicate_nth(1, [2,3,4], X).
X = [2,2,3,4]. % expected result
My current code is:
duplicate_nth(N,[H|T],L2) :-
N = 1, % Is `N` equal to 1?
append([H],[H|T],L2). % If so, prepend `H` to `[H|T]`
duplicate_nth(N,H|T,L2) :-
N > 1,
This works if N = 1. It will not, however, work if N > 1 and I am unsure of how to proceed.
No need to worry about implementing recursive predicate(s) the right way!
Simply delegate the "recursive part" to same_length/2, append/3, and length/2:
duplicate_nth(N,Xs0,Xs1) :- % index `N` is 1-based
same_length([_|Xs0],Xs1),
Suffix = [X|_],
append(Prefix,Suffix,Xs0),
length([_|Prefix],N),
append(Prefix,[X|Suffix],Xs1).
Sample query:
?- N = 1,
Xs0 = [a,b,c,d],
duplicate_nth(N,Xs0,Xs1).
N = 1, Xs0 = [a,b,c,d], Xs1 = [a,a,b,c,d] % only solution
; false.
Let's generalize above query and see the solution set grow!
?- Xs0 = [a,b,c,d],
duplicate_nth(N,Xs0,Xs1).
N = 1, Xs0 = [a,b,c,d], Xs1 = [a,a,b,c,d] % (same solution as before)
; N = 2, Xs0 = [a,b,c,d], Xs1 = [a,b,b,c,d] % (new solution)
; N = 3, Xs0 = [a,b,c,d], Xs1 = [a,b,c,c,d] % (new solution)
; N = 4, Xs0 = [a,b,c,d], Xs1 = [a,b,c,d,d] % (new solution)
; false.
Note that duplicate_nth/3 also works when used "in the other direction".
?- Xs1 = [a,b,b,c,d,d,e],
duplicate_nth(N,Xs0,Xs1).
N = 2, Xs1 = [a,b,b,c,d,d,e], Xs0 = [a,b,c,d,d,e]
; N = 5, Xs1 = [a,b,b,c,d,d,e], Xs0 = [a,b,b,c,d,e]
; false.
Last, let's run the most general query!
?- duplicate_nth(N,Xs0,Xs).
N = 1, Xs0 = [_A], Xs = [_A,_A]
; N = 1, Xs0 = [_A,_B], Xs = [_A,_A,_B]
; N = 2, Xs0 = [_A,_B], Xs = [_A,_B,_B]
; N = 1, Xs0 = [_A,_B,_C], Xs = [_A,_A,_B,_C]
; N = 2, Xs0 = [_A,_B,_C], Xs = [_A,_B,_B,_C]
; N = 3, Xs0 = [_A,_B,_C], Xs = [_A,_B,_C,_C]
; N = 1, Xs0 = [_A,_B,_C,_D], Xs = [_A,_A,_B,_C,_D]
...
Fair enumeration "out-of-the-box"? Perfect!
Nearly all functions on lists have the same structure. A base case:
function(FixedValues,List1,List2) :-
!.
Where List2 is written in function of List1 and FixedValues is a set of parameters that is grounded.
And an inductive case:
function(Values,[H|T],[H2|T2]) :-
other_function(H,H2), %transform H into H2
ValuesNext is Values-1, %or some other function
function(ValuesNext,T,T2).
The ! is a "cut" and prevents Prolog to execute other predicate definitions if the conditions of the first one are met.
Applied to this problem, try:
duplicate_nth(1,[H|T],[H,H|T]) :- %base case
!.
duplicate_nth(N,[H|T],[H|T2]) :- %inductive case
N1 is N-1,
duplicate_nth(N1,T,T2).
In most cases you need to make the program safer, since N might be out of bounds (less than or equal to zero, or larger than the length of the list.)
In that case, add:
duplicate_nth(_,[],[]).
i m new in prolog that s why may be the question is easy for you but i couldnt find the answer. Can someone please help me.
I just want
a count function s.t
count([c,c,a,a,b,b,d,a,c,b,d,d,a], O).
it will returns the number of occurences of the list members.
O = [[a, 4], [b, 3], [c, 3], [d, 3]]
The following is based on my previous answer to "Remove duplicates in list (Prolog)" and on this previous answer to the question "Prolog union for A U B U C".
list_item_subtracted_count0_count/5 is derived from list_item_subtracted/3.
list_counts/2 is derived from list_setB/2, which were both defined here.
list_item_subtracted_count0_count([], _, [], N,N).
list_item_subtracted_count0_count([A|As], E, Bs1, N0,N) :-
if_(A = E,
( Bs1 = Bs , N1 is N0+1 ),
( Bs1 = [A|Bs], N1 = N0 )),
list_item_subtracted_count0_count(As, E, Bs, N1,N).
list_counts([], []).
list_counts([X|Xs], [X-N|Ys]) :-
list_item_subtracted_count0_count(Xs, X, Xs0, 1,N),
list_counts(Xs0, Ys).
Here's the query the OP gave:
?- list_counts([c,c,a,a,b,b,d,a,c,b,d,d,a], Xss).
Xss = [c-3,a-4,b-3,d-3]. % succeeds deterministically
Note the order of pairs X-N in Counts corresponds to the first occurrence of X in Xs:
?- list_counts([a,b,c,d], Xss).
Xss = [a-1,b-1,c-1,d-1].
?- list_counts([d,c,b,a], Xss).
Xss = [d-1,c-1,b-1,a-1].
Last, let's consider all possible lists Es—enumerated fairly with ascending lengths:
?- length(Es, N), list_counts(Es, Xss).
N = 0, Es = [], Xss = []
; N = 1, Es = [A], Xss = [A-1]
; N = 2, Es = [A,A], Xss = [A-2]
; N = 2, Es = [A,B], Xss = [A-1,B-1], dif(B,A)
; N = 3, Es = [A,A,A], Xss = [A-3]
; N = 3, Es = [A,A,B], Xss = [A-2,B-1], dif(B,A)
; N = 3, Es = [A,B,A], Xss = [A-2,B-1], dif(B,A)
; N = 3, Es = [B,A,A], Xss = [B-1,A-2], dif(A,B), dif(A,B)
; N = 3, Es = [A,B,C], Xss = [A-1,B-1,C-1], dif(C,A), dif(C,B), dif(B,A)
...
co(X,L) :- co(X,[],L).
co([],A,A).
co([X|Xs], A, L) :- p(X-Z,A,R), !, Z1 is Z+1, co(Xs, [X-Z1|R], L).
co([X|Xs], A, L) :- co(Xs, [X-1|A], L).
p(X-Y,[X-Y|R],R):- !.
p(X,[H|Y], [H|Z]) :- p(X,Y,Z).
I did not use very meaningful names on purpose. Try to understand what each one of the predicates does.
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
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]].