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.
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.
To overcome said drawback, I also show an alternative solution.
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]].
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:
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.
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.