I have found a very nice Erlang-implementation for grouping list members by position.
everynth(List, N) ->
[ lists:reverse(Y)
|| Y <- lists:foldl(
fun(X, [H|T]) -> T++[[X|H]] end,
lists:duplicate(N, []),
List)
].
This sorts the provided list (List) by their index position into 3 groups:
everynth([1, 2, 3, 4, 5, 6, 7, 8, 9], 3).
gives
[[1,4,7],[2,5,8],[3,6,9]].
I would like to do the same in Prolog, but I don't know how to do it. Can you help me? Thank you.
Literal translation
We can implement this in Prolog in a way that quite closely corresponds to the Erlang version.
For example, using Ulrich Neumerkel's library(lambda):
everynth(Ls, N, Groups) :-
findall([], between(1,N,_), Groups0),
foldl(\L^[G0|Gs0]^Gs^append(Gs0,[[L|G0]],Gs), Ls, Groups0, Groups1),
maplist(reverse, Groups1, Groups).
Depending on your Prolog system, you may need to import one or two libraries to run this.
Among other similarities, this carries with it a quite unfortunate drawback. Exercise: Which? Hint: Imeanapartfromthename.
Alternative version
To overcome said drawback, I also show an alternative solution.
Building block
The key building block I shall use is with_remainder_mod/5, defined as:
with_remainder_mod(N, L, R-L, I0, I) :-
R #= I0 mod N,
I #= I0 + 1.
In essence, this equips each element with the residue of its index (mod N). For example:
?- foldl(with_remainder_mod(3), [a,b,c,d,e,f], Rs, 0, _).
Rs = [0-a, 1-b, 2-c, 0-d, 1-e, 2-f].
We are almost done, because we can now use the ISO standard predicate keysort/2, and then the widely available group_pairs_by_key/2 or similar library predicate:
?- foldl(with_remainder_mod(3), [a,b,c,d,e,f], Rs0, 0, _),
keysort(Rs0, Rs),
group_pairs_by_key(Rs, Gs).
Rs0 = [0-a, 1-b, 2-c, 0-d, 1-e, 2-f],
Rs = [0-a, 0-d, 1-b, 1-e, 2-c, 2-f],
Gs = [0-[a, d], 1-[b, e], 2-[c, f]].
Full solution
In total, a full solution could look like this:
with_remainder_mod(N, L, R-L, I0, I) :-
R #= I0 mod N,
I #= I0 + 1.
every_nth(Ls0, N, Groups) :-
foldl(with_remainder_mod(N), Ls0, Rs0, 0, _),
keysort(Rs0, Rs),
group_pairs_by_key(Rs, KeyGroups),
pairs_values(KeyGroups, Groups).
The time complexity is Θ(n·log n), where n is the length of the list.
Note that this solution uses:
declarative integer arithmetic via CLP(FD) constraints, to make this usable in all directions.
foldl/5 which is a very useful meta-predicate that already ships with several Prolog systems.
Again, depending on your Prolog system, you may need to import one or two libraries to use these facilities and other predicates like pairs_values/2 or equivalent ones.
Examples
Your example query and the answer:
?- every_nth([1,2,3,4,5,6,7,8,9], 3, Ls).
Ls = [[1, 4, 7], [2, 5, 8], [3, 6, 9]].
Much more general queries are of course also possible:
?- length(Es, _), every_nth(Es, 3, Ls).
Es = Ls, Ls = [] ;
Es = [X1],
Ls = [[X1]] ;
Es = [X1, X2],
Ls = [[X1], [X2]] ;
Es = [X1, X2, X3],
Ls = [[X1], [X2], [X3]] ;
Es = [X1, X2, X3, X4],
Ls = [[X1, X4], [X2], [X3]] ;
Es = [X1, X2, X3, X4, X5],
Ls = [[X1, X4], [X2, X5], [X3]] ;
etc.
Note that a version based on findall/3 cannot do this, because findall/3 creates fresh copies of variables.
Here is a benchmark that may be useful for you to decide between different solutions:
?- length(_, E),
portray_clause(2^E),
N #= 2^E, I #= N // 2,
length(Ls, N),
time(every_nth(Ls,I,_)),
false.
2^0.
% 150 inferences, 0.000 CPU in 0.000 seconds (80% CPU, 4545455 Lips)
2^1.
% 34 inferences, 0.000 CPU in 0.000 seconds (78% CPU, 2428571 Lips)
2^2.
% 62 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 4428571 Lips)
2^3.
% 118 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 6555556 Lips)
2^4.
% 230 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 8214286 Lips)
2^5.
% 454 inferences, 0.000 CPU in 0.000 seconds (94% CPU, 9659574 Lips)
2^6.
% 902 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 10488372 Lips)
2^7.
% 1,798 inferences, 0.000 CPU in 0.000 seconds (92% CPU, 9364583 Lips)
2^8.
% 3,590 inferences, 0.000 CPU in 0.000 seconds (99% CPU, 10316092 Lips)
2^9.
% 7,174 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 11297638 Lips)
2^10.
% 14,342 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 10491587 Lips)
etc.
Not really elegant but should work.
getEveryNth([], _, [], _).
getEveryNth([H|Ti], N, [H|To], N) :-
getEveryNth(Ti, 1, To, N).
getEveryNth([_|Ti], M, Lo, N) :-
M < N,
Mp1 is M+1,
getEveryNth(Ti, Mp1, Lo, N).
everyNthH(_, 0, [], _).
everyNthH([H|Ti], M, [[H|To] | Lo], N) :-
M > 0,
Mm1 is M-1,
getEveryNth(Ti, 1, To, N),
everyNthH(Ti, Mm1, Lo, N).
everyNth(L, N, LL) :-
everyNthH(L, N, LL, N).
From
everyNth([1, 2, 3, 4, 5, 6, 7, 8, 9], 3, L)
I get (L is unified with)
[[1,4,7],[2,5,8],[3,6,9]]
I had to do some try & test to find the proper relation between indexes, you can see from variables' names that everynth_starting/4 has been 'uninlined' to ease debugging.
everynth(L, P, Gs) :-
findall(G, (
between(1, P, S),
everynth_starting(L, P, S, G)
), Gs).
everynth_starting(L, P, S, G) :-
findall(E, (nth1(I, L, E), S mod P=:=I mod P), G).
nth1/3 acts as a generator for pairs index/element
Related
I am using constraint logic programming and I want to output a list with the product of the list (excluding the first element) is equal to the first element. For example,
[24 6 _ _] the output would be [24 6 1 4] or [24 6 4 1] or [24 6 2 2] etc.
I am trying to solve something similar to sudoku but I am stuck on this step.
Using foldl/2, you will need:
mul_(A, S0, S) :-
S #= S0 * A.
And you can do:
?- N #= 24, length(Vs, 3), Vs ins 1..N, Vs=[6|_], foldl(mul_, Vs, 1, N), label(Vs).
For this problem, clpfd is far slower than a sensible alternative:
product(1, []).
product(Prod, [N|Lst]) :-
between(1, Prod, N),
divmod(Prod, N, N1, 0),
product(N1, Lst).
Results in swi-prolog:
?- length(L, 20), time(bagof(L, product(24, L), Ls)), length(Ls, Sols).
% 602,066 inferences, 0.065 CPU in 0.066 seconds (100% CPU, 9196344 Lips)
Sols = 30800
Vs
?- N #= 24, length(L, 20), L ins 1..N,
time(bagof(L, (foldl(mul_, L, 1, N), label(L)), Ls)), length(Ls, Sols).
% 62,070,425 inferences, 2.783 CPU in 2.786 seconds (100% CPU, 22301363 Lips)
Sols = 30800
[-2,3,4,-6,7,-9, 10,12,13,-14,16,-17]
In this list, how to spilt list into to positive and negative lists use visual prolog?
Follow is my code, I just print positive or negative list, how to solve it?
As an example of simple list processing:
split_list(PosNegLst, PosLst, NegLst) :-
must_be(list, PosNegLst),
split_list_(PosNegLst, PosLst, NegLst).
split_list_([], [], []).
split_list_([Head|Tail], PosLst, [Head|NegLst]) :-
Head < 0, !,
split_list_(Tail, PosLst, NegLst).
split_list_([Head|Tail], [Head|PosLst], NegLst) :-
Head >= 0,
split_list_(Tail, PosLst, NegLst).
Result in swi-prolog:
?- time(split_list([5, -2, -3, 6, 0, 8], PosLst, NegLst)).
% 21 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 406677 Lips)
PosLst = [5,6,0,8],
NegLst = [-2,-3].
I've been trying to write a program that compares two lists hat are the same except the last two elements of List 2 are in reverse order, i.e [4,5,6] and [4,6,5], and that returns the last two elements that have been swapped.
For example:
SwapLastTwo([4, 5, 6] , [ 4, 6, 5], X).
should return
X = [6, 5]
So far my code looks like this:
lastTwoReversed([Z,A|T],[_,Y,X]) :-reverse([Z,A|T],[Y,X|_]).
My predicate so far only takes two arguments and checks, if are the same except the last two elements of List 2 are in reverse order and returns true, if the condsitions are met
I don't know how I can modify my predicate to incoparate the X as its third argument and make it return the swapped elements.
Try this:
lastTwoReversed(L1, L2, [X1,X2]) :-
reverse(L1, [X1,X2|Rest]),
reverse(L2, [X2,X1|Rest]).
Notice that by using the variable Rest in both subgoals, you establish that the lists must be the same, except for the last two items (which are swapped).
Example:
?- lastTwoReversed([1,2,3,4,5,6], [1,2,3,4,6,5], R).
R = [6, 5].
?- lastTwoReversed([1,2,3,4,5,6], [1,2,3,6,5], R).
false.
?- lastTwoReversed([1,2,3,4,5,6], [1,2,3,4,5,6], R).
false.
Using first-argument indexing, and improved performance by removing the unnecessary reverse(), and also same_length():
swap_last_2([Elem1, Elem2|Tail], SwappedLst, Last2) :-
% same_length would prevent an unwanted choicepoint on: swap_last_2(L, [a,b], SL).
%same_length([Elem1, Elem2|Tail], SwappedLst),
swap_last_2_(Tail, Elem1, Elem2, SwappedLst, Last2).
% Swap the last 2 elements
swap_last_2_([], Elem1, Elem2, [Elem2, Elem1], [Elem2, Elem1]).
swap_last_2_([Head|Tail], Elem1, Elem2, [Elem1|SwappedLst], Last2) :-
% Move the elements along
swap_last_2_(Tail, Elem2, Head, SwappedLst, Last2).
Result in swi-prolog:
?- swap_last_2([4, 5, 6], [4, 6, 5], X).
X = [6,5].
Performance comparison (this method is fastest):
?- cmp(1000, 1000).
% 7,011,001 inferences, 0.503 CPU in 0.497 seconds (101% CPU, 13941510 Lips)
% 2,001,001 inferences, 0.282 CPU in 0.278 seconds (101% CPU, 7098617 Lips)
% 2,007,001 inferences, 0.205 CPU in 0.203 seconds (101% CPU, 9782721 Lips)
% 1,002,001 inferences, 0.063 CPU in 0.063 seconds (101% CPU, 15819840 Lips)
I think the comparison made by #CapelliC [that has been deleted] is misleading. The OP asks for a predicate to compare two lists and check if they are the same, except for the last two elements that may appear swapped (which is different from a predicate that just swaps the last two elements of a single list). Thus, a fairer comparison should call the predicates to be compared with two instantiated lists as input.
My suggestion is to modify perf_belt/3 as follows:
/* File: last2swap_perf.pl
Author: Carlo
Created: Dec 12 2021
Purpose: compare answers to https://stackoverflow.com/q/70281408/874024
*/
:- module(last2swap_perf, [cmp/2]).
:- meta_predicate perf_belt(+,+,3).
% CapelliC's solutions
% append/2 based
last2swap_app2(A,B,[U,V]):-
append([C,[U,V]],A),
append([C,[V,U]],B).
% append/3 based
last2swap_app3(A,B,[U,V]):-
append(C,[U,V],A),
append(C,[V,U],B).
% slago' solution
lastTwoReversed(L1, L2, [X1,X2]) :-
reverse(L1, [X1,X2|Rest]),
reverse(L2, [X2,X1|Rest]).
% brebs' answer
swap_last_2([Elem1, Elem2|Tail], SwappedLst, Last2) :-
swap_last_2_(Tail, Elem1, Elem2, [], RevSwappedLst, Last2),
reverse(RevSwappedLst, SwappedLst).
% This swaps the last 2 elements
swap_last_2_([], Elem1, Elem2, SoFar, [Elem1, Elem2|SoFar], [Elem2, Elem1]).
swap_last_2_([Head|Tail], Elem1, Elem2, SoFar, SwappedLst, Last2) :-
% Move the elements along
swap_last_2_(Tail, Elem2, Head, [Elem1|SoFar], SwappedLst, Last2).
%! %%% minimal perf utility
perf_belt(NRep, LList, Pred) :- % <== NEW VERSION PROPOSED
make_lists(LList, List1, List2),
time( forall(between(1, NRep, _), % <== TWO INSTANTIATED LISTS AS INPUT
call(Pred, List1, List2, _)) ).
make_lists(N, List1, List2) :-
N1 is N - 1,
N2 is N - 2,
numlist(1, N2, Prefix),
append(Prefix, [N1, N], List1),
append(Prefix, [N, N1], List2).
cmp(NRep,LList) :-
perf_belt(NRep,LList,last2swap_app2),
perf_belt(NRep,LList,last2swap_app3),
perf_belt(NRep,LList,lastTwoReversed),
perf_belt(NRep,LList,swap_last_2).
Fairer results Running on SWI-Prolog, version 8.4.1, we can see that double reverse is not so bad.
?- cmp(100, 100).
% 71,101 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
% 20,101 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
% 20,701 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
% 20,401 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
true.
?- cmp(100, 1000).
% 701,101 inferences, 0.047 CPU in 0.047 seconds (100% CPU, 14956821 Lips)
% 200,101 inferences, 0.031 CPU in 0.031 seconds (100% CPU, 6403232 Lips)
% 200,701 inferences, 0.016 CPU in 0.016 seconds (100% CPU, 12844864 Lips)
% 200,401 inferences, 0.016 CPU in 0.016 seconds (100% CPU, 12825664 Lips)
true.
?- cmp(1000, 1000).
% 7,011,001 inferences, 0.438 CPU in 0.437 seconds (100% CPU, 16025145 Lips)
% 2,001,001 inferences, 0.219 CPU in 0.219 seconds (100% CPU, 9147433 Lips)
% 2,007,001 inferences, 0.141 CPU in 0.141 seconds (100% CPU, 14272007 Lips)
% 2,004,001 inferences, 0.141 CPU in 0.141 seconds (100% CPU, 14250674 Lips)
true.
I am beginner in Prolog programming. I got this program which remove same elements from list? but I want only same elements from list.
Sample query with expected answer:
?- set([1,2,3,4,2,3], Xs).
Xs = [2,3].
Prolog Code:
ismember(X, SET) :-
member(X, SET).
set([], []).
set([H|T], [H|Out]) :-
not(ismember(H,T)),
set(T, Out).
set([H|T], Out) :-
ismember(H, T),
set(T, Out).
Thank You!
The following is derived from this answer to the related question "Remove unique elements only".
Using tpartition/4 in tandem with
if_/3 and (=)/3, we define remove_uniq/2 like this:
remove_uniq([], []).
remove_uniq([E|Es], Xs0) :-
tpartition(=(E), Es, Ts, Fs),
if_(Ts = [],
Xs0 = Xs,
Xs0 = [E|Xs]),
remove_uniq(Fs, Xs).
Sample queries:
:- remove_uniq([1,2,3,4,2,3], [2,3]).
% ^ ^ ^ ^
% |-+--------- take leftmost occurrence
% v v v v
:- remove_uniq([1,2,3,4,3,2], [2,3]).
% ^ ^ ^ ^
% |-+--------- preserve the original order
% v v v v
:- remove_uniq([5,3,2,1,2,3,2,3], [3,2]).
quick and dirty
?- S=[1,2,3,4,2,3], setof(C, R^(select(C,S,R),memberchk(C,R)), L).
S = [1, 2, 3, 4, 2, 3],
L = [2, 3].
Is a specialization of 'generate and test' pattern.
How about performance ?
'slow quick and dirty'(S0, S) :-
setof(C, R^(select(C,S0,R),member(C,R)), S).
'better quick and dirty'(S0, S) :-
setof(C, R^(select(C,S0,R),memberchk(C,R)), S).
'still better quick and dirty'(S0, S) :-
setof(H, Done^H^R^(append(Done,[H|R],S0),memberchk(H,R)), S).
test(N) :-
findall(R, (between(1,N,_), random_between(10,100,R)), S),
time('slow quick and dirty'(S, Sa)),
time('better quick and dirty'(S, Sb)),
time('still better quick and dirty'(S, Sc)),
Sa = Sb, Sb = Sc,
time(remove_uniq(S, Sd)),
maplist(length, [Sa, Sb, Sc, Sd], Ls),
writeln(Ls).
25 ?- so:test(100).
% 10,225 inferences, 0.003 CPU in 0.003 seconds (100% CPU, 3506071 Lips)
% 282 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 226231 Lips)
% 254 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 347891 Lips)
% 22,697 inferences, 0.018 CPU in 0.028 seconds (65% CPU, 1272020 Lips)
[28,28,28,28]
true.
26 ?- so:test(1000).
% 1,011,929 inferences, 0.275 CPU in 0.276 seconds (100% CPU, 3674049 Lips)
% 3,015 inferences, 0.013 CPU in 0.013 seconds (98% CPU, 239535 Lips)
% 2,924 inferences, 0.013 CPU in 0.016 seconds (82% CPU, 216598 Lips)
% 351,724 inferences, 0.262 CPU in 0.272 seconds (96% CPU, 1343870 Lips)
[91,91,91,91]
true.
Out of curiosity, I've included remove_uniq/2, but, I think it's not strictly comparable, given the different semantic.
Using member/2 we have a quadratic complexity.
The runtime efficiency of memberchk (implemented as builtin, in C) just squeeze away the filter delay: efficiency become almost linear.
append/3 instead of select/3 allows a further small improvement.
I made a Prolog predicate posAt(List1,P,List2) that tests whether the element at position P of List1 and List2 are equal:
posAt([X|Z], 1, [Y|W]) :-
X = Y.
posAt([Z|X], K, [W|Y]) :-
K > 1,
Kr is K - 1,
posAt(X, Kr, Y).
When testing:
?- posAt([1,2,3], X, [a,2,b]).
I expected an output of X = 2 but instead I got the following error:
ERROR: >/2: Arguments are not sufficiently instantiated
Why am I getting this error?
A Prolog predicate is a relation between arguments, and your statement
the element at position P of List1 and List2 are equal
is clearly an example where multiple solutions are possible.
?- posAt([1,2,3],X,[1,5,3,7]).
X = 1.
So the answer from sharky, while clearly explains why the technical error arises, requires a small correction:
posAt([X0|_], Pos, Pos, [X1|_]) :-
X0 == X1.
Now it works as expected.
?- posAt([1,2,3],X,[1,5,3,7]).
X = 1 ;
X = 3 ;
false.
Writing simple predicates for list processing it's a very valuable apprenticeship practice, and the main way to effectively learn the language. If you are incline also to study the available library predicates, here is a version using nth1/3 from library(lists)
posAt(L0, P, L1) :-
nth1(P, L0, E),
nth1(P, L1, E).
This outputs:
?- posAt([1,2,3],X,[1,5,3,7]).
X = 1 ;
X = 3.
Could be interesting to attempt understanding why in this case SWI-Prolog 'top level' interpreter is able to infer the determinacy of the solution.
This occurs because, when the subgoal K > 1 is evaluated by Prolog, K is still an unbound variable and not a number. Standard Prolog can't (won't) evaluate the true/false value of numerical range restrictions such as this when they aren't ground (as opposed to constraint solvers like CLP, which permit this but work differently).
Consider this solution:
posAt(L0, Pos, L1) :-
posAt(L0, 1, Pos, L1).
posAt([X0|_], Pos, Pos, [X1|_]) :-
X0 == X1.
posAt([_|X0s], CurrPos, Pos, [_|X1s]) :-
NextPos is CurrPos + 1,
posAt(X0s, NextPos, Pos, X1s).
The first predicate posAt/3 sets up the initial condition: lists as position 1, and calls posAt/4 to iterate though the list.
The first clause of posAt/4 is a match condition: the elements in both lists at the same position are equal. In this case, the current position variable is unified with Pos, the result.
If the above clause failed because the list elements X0 and X1 were unequal, the list position CurrPos is incremented by one, and a recursive call to posAt/4 starts processing again at the next pair of items.
EDIT: Removed incorrect cut in first clause of posAt/4 (thanks to #chac for the pickup)
The general solution for such problems are constraints.
Use clpfd for integer arithmetic that works in all directions:
:- use_module(library(clpfd)).
posAt([X|_], 1, [X|_]).
posAt([_|X], K, [_|Y]) :-
K #> 1,
Kr #= K - 1,
posAt(X,Kr,Y).
With these simple changes, your example works exactly as expected:
?- posAt([1,2,3], X, [a,2,b]).
X = 2 ;
false.
TL;DR: The answers by #CAFEBABE, #CapelliC, #mat, and #sharky all fall short!
So... what exactly are the shortcomings of the answers proposed earlier?
#CAFEBABE's stated:
Nice about this solution is that the runtime is linear in the length of both lists.
Let's put that statement to the test!
?- numlist(1,1000,Zs), time(posAt__CAFEBABE1(Zs,1000,Zs)).
% 999,001 inferences, 0.090 CPU in 0.091 seconds (100% CPU, 11066910 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 4 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 66738 Lips)
false.
Bummer! The others do just fine:
?- numlist(1,1000,Zs), time(posAt__CapelliC1(Zs,1000,Zs)).
% 671 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 3492100 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...].
?- numlist(1,1000,Zs), time(posAt__mat1(Zs,1000,Zs)).
% 3,996 inferences, 0.001 CPU in 0.001 seconds (99% CPU, 3619841 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 5 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 154703 Lips)
false.
?- numlist(1,1000,Zs), time(posAt__sharky1(Zs,1000,Zs)).
% 1,000 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 2627562 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 4 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 97109 Lips)
false.
#CapelliC used nth1/3, which can (and does) cause problems with universal termination:
?- time((posAt__CapelliC1(_,N,[a,b,c]), false)).
**LOOPS**
D'oh! The others all do just fine:
?- time((posAt__CAFEBABE1(_,_,[a,b,c]), false)).
% 14 inferences, 0.000 CPU in 0.000 seconds (88% CPU, 1098470 Lips)
false.
?- time((posAt__mat1(_,_,[a,b,c]), false)).
% 2,364 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 2764075 Lips)
false.
?- time((posAt__sharky1(_,_,[a,b,c]), false)).
% 6 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 207247 Lips)
false.
#mat's code has complexity issues.
#CAFEBABE and #CapelliC do "a little better"—their codes are faster because they use rely on lower-level primitives (is)/2 and nth1/3.
?- numlist(1,1000,Zs), time((posAt__mat1(Zs,_,_), false)).
% 33,365,972 inferences, 1.643 CPU in 1.643 seconds (100% CPU, 20304661 Lips)
false.
?- numlist(1,1000,Zs), time((posAt__CAFEBABE1(Zs,_,_), false)).
% 1,001,002 inferences, 0.083 CPU in 0.083 seconds (100% CPU, 12006557 Lips)
false.
?- numlist(1,1000,Zs), time((posAt__CapelliC1(Zs,_,_), false)).
% 171,673 inferences, 0.030 CPU in 0.030 seconds (100% CPU, 5810159 Lips)
false.
The code by #sharky is clearly best in this respect:
?- numlist(1,1000,Zs), time((posAt__sharky1(Zs,_,_), false)).
% 1,003 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 1605658 Lips)
false.
#sharky's code uses the meta-logical built-in predicate (==)/2 which makes it lose logical soundness when used with insufficiently instantiated terms. This query should succeed:
?- posAt__sharky1([a], 1, Xs).
false.
The other codes all give a logically sound answer:
?- posAt__CAFEBABE1([a], 1, Xs).
Xs = [a|_G235] ;
false.
?- posAt__CapelliC1([a], 1, Xs).
Xs = [a|_G235].
?- posAt__mat1([a], 1, Xs).
Xs = [a|_G235] ;
false.
Going past the first answer, #CAFEBABE's code gets more than a little inefficient:
?- numlist(1,1000,Zs), time(posAt__CAFEBABE1(Zs,1,Zs)).
% 0 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 0 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 999,004 inferences, 0.076 CPU in 0.076 seconds (100% CPU, 13121058 Lips)
false.
Similar—but an order of magnitude smaller— issues show with #sharky's code:
?- numlist(1,1000,Zs), time(posAt__sharky1(Zs,1,Zs)).
% 1 inferences, 0.000 CPU in 0.000 seconds (75% CPU, 31492 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 1,003 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 1078556 Lips)
false.
The codes by #CapelliC and #mat are both doing alright:
?- numlist(1,1000,Zs), time(posAt__CapelliC1(Zs,1,Zs)).
% 7 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 306802 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...].
?- numlist(1,1000,Zs), time(posAt__mat1(Zs,1,Zs)).
% 0 inferences, 0.000 CPU in 0.000 seconds (80% CPU, 0 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 5 inferences, 0.000 CPU in 0.000 seconds (84% CPU, 345662 Lips)
false.
So... what do we do?
Why not follow this approach and combine #mat's and #sharky's code?
:- use_module(library(clpfd)).
posAt__NEW(L0, Pos, L1) :-
posAt__NEW_(L0, 1, Pos, L1).
posAt__NEW_([X|_], Pos, Pos, [X|_]).
posAt__NEW_([_|X0s], CurrPos, Pos, [_|X1s]) :-
CurrPos #< Pos,
NextPos #= CurrPos + 1,
posAt__NEW_(X0s, NextPos, Pos, X1s).
Let's re-run above sample queries with posAt__NEW/3:
?- numlist(1,1000,Zs), time(posAt__NEW(Zs,1000,Zs)).
% 4,997 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 18141619 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 9 inferences, 0.000 CPU in 0.000 seconds (71% CPU, 122877 Lips)
false.
?- time((posAt__NEW(_,_,[a,b,c]), false)).
% 440 inferences, 0.001 CPU in 0.001 seconds (98% CPU, 803836 Lips)
false.
?- numlist(1,1000,Zs), time((posAt__NEW(Zs,_,_), false)).
% 154,955 inferences, 0.014 CPU in 0.014 seconds (100% CPU, 11067900 Lips)
false.
?- posAt__NEW([a], 1, Xs).
Xs = [a|_G229] ;
false.
?- numlist(1,1000,Zs), time(posAt__NEW(Zs,1,Zs)).
% 1 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 121818 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 7 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 266748 Lips)
false.
Alright! At last, we make sure that the goal used in the 3rd query above has linear complexity:
?- numlist(1,100,Zs), time((posAt__NEW(Zs,_,_), false)).
% 15,455 inferences, 0.004 CPU in 0.004 seconds (100% CPU, 3545396 Lips)
false.
?- numlist(1,1000,Zs), time((posAt__NEW(Zs,_,_), false)).
% 154,955 inferences, 0.016 CPU in 0.017 seconds (98% CPU, 9456629 Lips)
false.
?- numlist(1,10000,Zs), time((posAt__NEW(Zs,_,_), false)).
% 1,549,955 inferences, 0.098 CPU in 0.099 seconds (99% CPU, 15790369 Lips)
false.
?- numlist(1,100000,Zs), time((posAt__NEW(Zs,_,_), false)).
% 15,499,955 inferences, 1.003 CPU in 1.007 seconds (100% CPU, 15446075 Lips)
false.
(This just popped up on my dashboard hence the late answer...)
I looked at the question and was thinking whether it is possible to provide a solution close to the original question. The problem, as already explained, is that the relation > needs its arguments instantiated. Actually similar for is. However, this can easily be fixed by reordering the goals:
posAt([X|_], 1, [X|_]).
posAt([_|X], K, [_|Y]) :-
posAt(X, Kr, Y),
K is Kr+1,
K > 1.
This solution is that the runtime is linear in the length of both lists, as long as K is ground. However, there is a problem, if the first elements do match in the list as nicely illustrated in the answer by repeat.
In fact the last element is superfluous, hence, equivalent.
posAt([X|_], 1, [X|_]).
posAt([_|X], K, [_|Y]) :-
posAt(X, Kr, Y),
K is Kr+1.
However, as proven by #repeat this code is horribly slow. this is probably due to the fact that the code is breaking tail recursion.
A logical clean solution would solve this problem. Here we would represent the natural numbers using the Peano Axiomes (s/1 successor or relation) and the solution would become
posAt([X|_], zero, [X|_]).
posAt([_|X], s(K), [_|Y]) :-
posAt(X, K, Y).
however, this is hard to time, hence, a hackish solution roughly equivalent
posAt([X|_], [a], [X|_]).
posAt([_|X], [a|L], [_|Y]) :-
posAt(X, L, Y).
Timing this solution gives
N=8000,numlist(1,N,_Zs), length(_LN,N),time(posAt(_Zs,_LN,_Zs)).
7,999 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 7342035 Lips)
N = 8000