recursionprologdifference-lists

Move every second element to the back of a list, recursively


I'm looking for a way to shuffle a list of numbers in a specific way.

shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) should return [1, 3, 5, 7, 9, 11, 2, 6, 10, 4, 12, 8]

The recursion would be something like this:

[1,3,5,7,9,11] with remainder [2,4,6,8,10,12]
[2,6,10] with remainder [4,8,12]
[4,12] with remainder [8]

and then you append the result lists and return the wanted answer.

My current code looks like this. How can I adapt it so that it produces the type of recursion I explained above? the mode is shuffle(+,?).

shuffle([], _).
shuffle(List, Shuffled) :- r(List, Shuffled).
r([], []).
r([X], [X]):- !.
r([X,A|Xs], [X|Ys]) :- r(Xs, Ys).

Solution

  • First, a predicate that gets half the work done: reorders the list so that every second element is picked out and appended to the back, keeping the order:

    untangle([], []).
    untangle([X|Xs], [X|Ys]) :-
        untangle_1([X|Xs], [X|Ys], Bs, Bs).
    
    % The rest of the Untangled is the list at the back;
    % the list at the back is now empty
    untangle_1([], Back, Back, []).
    % Keep elements in odd positions at the front
    untangle_1([X|Xs], [X|Untangled], Back, Bs) :-
        untangle_2(Xs, Untangled, Back, Bs).
    
    % Same as above
    untangle_2([], Back, Back, []).
    % Move elements in even positions to the back
    untangle_2([X|Xs], Untangled, Back, [X|Bs]) :-
        untangle_1(Xs, Untangled, Back, Bs).
    

    This is very similar to the interwine/3 defined in this answer. Instead of using two lists for the "unzipped" elements, it puts them at the front and back of the same list.

    Now what you need is shuffle the elements that would otherwise be appended to the back:

    shuffle([], []).
    shuffle([X|Xs], Shuffled) :-
        untangle_1([X|Xs], Shuffled, Back, Bs),
        shuffle(Bs, Back).
    

    Did I understand that correctly?

    ?- shuffle([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z], S), write(S).
    [a,c,e,g,i,k,m,o,q,s,u,w,y,b,f,j,n,r,v,z,d,l,t,h,x,p]
    S = [a, c, e, g, i, k, m, o, q|...].
    

    You will also notice that this shuffle/2 works in modes shuffle(+List, -Shuffled), shuffle(-List, +Shuffled), and shuffle(?List, ?Shuffled). To what I can see, it is identical in semantics (and almost identical in implementation) to the solution of false.