I\'m working on a prolog algorithm that will perform a \"swap\" on a list.
Example:
Input: [1,2,3,4] -> Output: [3,4,1,2] Input: [1,2,3,4,5] -> Output: [4,5,3,1
Prolog doesn't do inline expression evaluation. Thus, calls such as trim(L2, Length/2 , B)
and trim(L, round(Length/2), A)
will not work as you expect. Expressions are only evaluated in specific when using certain operators such as is/2
, arithmetic comparisons, or their CLP(FD) counterparts. These expressions would need to be done as: L is Length // 2, trim(L2, L, B)
and R is round(Length/2), trim(L, R, A)
if done literally.
Your solution could be condensed, however, as follows.
swap(L, S) :-
same_length(L, S), % L and S are necessarily the same length
length(L, N),
M is N // 2, % integer divide ; half the original list length
length(Left, M), % Left is a list of half the length of L
% or half minus one if the length is odd
( (N mod 2) =:= 1 % If the original length is odd...
-> append(Left, [H|Right], L), % then L is Left + [H|Right]
append(Right, [H|Left], S) % So, S is Right + [H|Left]
; append(Left, Right, L), % otherwise, L is Left + Right
append(Right, Left, S) % So, S is Right + Left
).
round
is not a function, it's a predicate. I haven't looked at the rest of the code, but that line should be
round(Length/2, R), trim(L, R, A)
EDIT: BTW, you're overthinking it.
swap([], []).
swap([X], [X]).
swap([X, Y | A], [Y, X | B]) :- swap(A, B).
an idiomatic solution:
swap(L,S) :-
%maplist(when_, [
append([X,C,Y],L), (C=[];C=[_]), same_length(X,Y),
reverse(X,U), reverse(Y,V), append([U,C,V],S)
%])
.
?- swap([1,2,3,4,5],S).
S = [2, 1, 3, 5, 4] ;
false.
it's not a true relation, since it hangs if called in mode swap(-,+)
, but it behaves better after uncommenting the bracketing, and providing this snippet:
:- meta_predicate when_(0).
when_(P) :-
strip_module(P,_,Q), Q =.. [_|As],
or_list(As, Exp), display(Exp),
when(Exp, P).
or_list([A], ground(A)) :- !.
or_list([A|As], (ground(A);Exp)) :- or_list(As, Exp).
edit
after @false' comments, I'm trying to explain the motivation of this answer. The requested swap/2 'function' seems to be an ideal candidate to showcase the peculiarity of Prolog data model, albeit the requested explanation (about proper syntactic usage of integer arithmetic applied to lists) is not even hinted here.
From the very start of Prolog we have available an unique mixing of relational and functional (recursive) tools, that could make little sense to a newbie. At least, it still surprises me... and I like this fact.
Now, functional logic programming, for instance Curry, attempts a solution through 'narrowing'. From the linked page:
Narrowing is useful because it allows a function to be treated as a relation: its value can be computed "in both directions"
Now, when_/1 it's a simple minded approach to the same problem. Down to earth, the swap/2 has been described as a function, but could be implemented as a relation ?
@false suggestion, adding same_length(L,S)
as first goal, fixes the swap(-,+) mode, but loops on swap(-,-). The approach based on when_/1 instead reports the inability to ground the conjunction.
edit
Termination issues apart, my choice of goals order is really ineffective. Hinting this answer to another question, occurred to me that a big efficiency gain (at least, for the mode swap(+,-)) can be obtained putting constraints first:
3 ?- numlist(1,100,L), time((append([X,C,Y],L), (C=[];C=[_]), same_length(X,Y), append([Y,C,X], S))).
% 328,899 inferences, 0.125 CPU in 0.125 seconds (100% CPU, 2634422 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
X = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
C = [],
Y = [51, 52, 53, 54, 55, 56, 57, 58, 59|...],
S = [51, 52, 53, 54, 55, 56, 57, 58, 59|...]
.
4 ?- numlist(1,100,L), time(((C=[];C=[_]), same_length(X,Y), append([X,C,Y],L), append([Y,C,X], S))).
% 3,273 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 3210999 Lips)
Here's another solution that is based upon a modification to a predicate posted by @joel76 for splitting a list into two equal length lists. The modification I made enables the predicate to succeed with an odd-length list by including the "middle" list of 0 or 1 elements as an argument. It also uses same_length
to constrain the arguments to avoid a termination issue for certain arguments. I included a simple implementation of same_length/2
which not all Prologs have (it is included with SWI Prolog).
swap(L, S) :-
same_length(L, S),
div(L, Front, Middle, Back), % L is divided into two halfs & middle
append(Back, Middle, NewFront), % S is Back + Middle + Front
append(NewFront, Front, S).
% List L consists of Left + Middle + Right where Left and Right are equal length
% and Middle has maximum length of 1
%
div(L, Left, Middle, Right) :-
split(L, L, Left, Middle, Right).
split(L, [], [], [], L).
split([H|T], [_], [], [H], T).
split([H|T], [_, _|T1], [H|T2], M, Right) :-
split(T, T1, T2, M, Right).
% same_length/2 is pre-defined in SWI Prolog and succeeds if the arguments
% are lists of equal length
%
same_length([], []).
same_length([_|Xs], [_|Ys]) :- same_length(Xs, Ys).