I am wondering how can I move first N elements from a list and put them at the end. For example:
[1,2,3,4]
and I want to move first 2 elements , so the result will be [3,4,1,2]
.
rule(List1,N,List2) :- length(List1,Y), ...
I don't know how to start, any advice ?
You could opt to adopt a more general perspective on the task. If you think about it, taking the first N elements of a list and appending them at the end can be seen as a rotation to the left by N steps (just imagine the list elements arranged in a circle). The predicate name
rotate/3
in @Willem Van Onsem's answer also indicates this perspective. You can actually define such a predicate as a true relation, that is making it work in all directions. Additionally it would be desirable to abstain from imposing unnecessary restrictions on the arguments while retaining nice termination properties. To reflect the relational nature of the predicate, let's choose a descriptive name. As the third argument is the left rotation byN
steps of the list that is the first argument, let's maybe call itlist_n_lrot/3
and define it like so:Now let's step through the definition and observe some of its effects by example. The first rule of
list_n_lrot/3
is only included to deal with the special case of empty lists:If you don't want to include these cases in your solution just omit that rule. Throughout the predicates CLP(FD) is used for arithmetic constraints, so the second argument of
list_n_lrot/3
can be variable without leading to instantiation errors. The goallist_list_samelength/2
is a structural constraint to ensure the two lists are of same length. This helps avoiding an infinite loop after producing all answers in the case that only the third argument is ground (to see this, remove the first two goals oflist_n_lrot/3
and replace the third withlist_n_heads_tail(L,N,H,T)
and then try the query?- list_n_lrot(L,N,[1,2,3]).
). It's also the reason why the most general query is listing the solutions in a fair order, that is producing all possibilities for every list length instead of only listing the rotation by 0 steps:Finally, it also describes the actual length of the two lists, which is used in the next goal to determine the remainder of
N
modulo the length of the list. Consider the following: If you rotate a list of length N by N steps you arrive at the initial list again. So a rotation by N+1 steps yields the same list as a rotation by 1 step. Algebraically speaking, this goal is exploiting the fact that congruence modulo N is partitioning the infinite set of integers into a finite number of residue classes. So for a list of length N it is sufficient to produce the N rotations that correspond to the N residue classes in order to cover all possible rotations (see the query above for N=3). On the other hand, a given N > list length can be easily computed by taking the smallest non-negative member of its residue class. For example, given a list with three elements, a rotation by 2 or 5 steps respectively yields the same result:And of course you could also left rotate the list by a negative number of steps, that is rotating it in the other direction:
On a side note: Since this constitutes rotation to the right, you could easily define right rotation by simply writing:
The predicate
list_n_heads_tail/4
is quite similar tosplitAt/4
in Willem's post. However, due to the use of if_/3 the predicate succeeds deterministically (no need to hit;
after the only answer since no unnecessary choicepoints are left open), if one of the lists and the second argument oflist_n_lrot/3
are ground:You can observe another nice effect of using CLP(FD) with the second solution of the most general query:
This answer states, that for a list with one element any rotation by an arbitrary number of steps yields the very same list again. So in principle, this single general answer summarizes an infinite number of concrete answers. Furthermore, you can also ask questions like: What lists are there with regard to a rotation by 2 steps?
To finally come back to the example in your question:
Note how this more general approach to define arbitrary rotations on lists subsumes your use case of relocating the first N elements to the end of the list.
Since we are speaking of predicates - i.e. true relations among arguments - and Prolog library builtins are written with efficiency and generality in mind, you should know that - for instance - length/2 can generate a list, as well as 'measuring' its length, and append/3 can also split a list in two. Then, your task could be
Replace each dash with an appropriate variable, and you'll get
Try this
The first predicate moves one element to the end of the list, then the only thing I do is "repeat" that N times.
We can do this with a predicate that works in two phases:
N
items of the list; andLet is construct the two phases with separate predicate. For the collect phase, we could use the following predicate:
Now for the emit phase, we could use
append/3
. So then the full predicate is:This gives:
The algorithm works in O(n).