Swap two items depending on the index

Viewed 113

I have a predicate like this:

swap(List, Index1, Index2, New_List).
List = [3,8,1,6].
Index1 = 1.
Index2 = 3.

With the swap, New_List will be:

New_List = [3,6,1,8].

I'm trying to implement some code, but I don't understand why It gives me: False.

Could you help me to understand why my code is wrong, and how can I fix it?

Code:

swap([],_,_,[]).

swap(List, Index1, Index2, New_List) :-
    swap(List, 0, Index1, Index2, New_List),
    print(New_List).

swap([],_,_,_,[]).
swap(List, Index, Index1, Index2, [H|R]) :-
    Index \== Index1,
    nth0(Index, List, H, _Rest),
    Index_New is Index+1,
    swap(List, Index_New, Index1, Index2, R).

swap(List, Index, Index1, Index2, [H|R]) :-
    Index == Index1,
    nth0(Index2, List, H, _Rest),
    Index_New is Index+1,
    swap(List, Index_New, Index1, Index2, R).

swap(List, Index, Index1, Index2, [H|R]) :-
    Index == Index2,
    nth0(Index1, List, H, _Rest),
    Index_New is Index+1,
    swap(List, Index_New, Index1, Index2, R).

Thank you.

3 Answers

You can use tracing to see exactly what happens. Like this:

?- trace.
true.

[trace]  ?- swap([3,8,1,6], 1, 3, R).
   Call: (10) swap([3, 8, 1, 6], 1, 3, _13242) ? /* Press 'h' */ Options:
+:                  spy        -:              no spy
/c|e|r|f|u|a goal:  find       .:              repeat find
a:                  abort      A:              alternatives
b:                  break      c (ret, space): creep
[depth] d:          depth      e:              exit
f:                  fail       [ndepth] g:     goals (backtrace)
h (?):              help       i:              ignore
l:                  leap       L:              listing
n:                  no debug   p:              print
r:                  retry      s:              skip
u:                  up         w:              write
m:                  exception details
C:                  toggle show context
   Call: (10) swap([3, 8, 1, 6], 1, 3, _13242) ? creep
   Call: (11) swap([3, 8, 1, 6], 0, 1, 3, _13242) ? creep
   Call: (12) 0\==1 ? creep
   Exit: (12) 0\==1 ? creep
   Call: (12) lists:nth0(0, [3, 8, 1, 6], _13830, _13990) ? creep

You can use 'c' to "creep" through the rest of the evaluation of your predicate and try to understand how it is different from what you expected to happen.

For example, I don't understand why you need the extra argument, the 0, and so on, if you already chose to use nth0/4. I am using the nth0/4 that I find in SWI-Prolog.


This should be enough:

swap(L, A, B, Result) :-
    nth0(B, L, Y, L0),
    nth0(A, L0, X, L1),
    A < B,
    nth0(A, L2, Y, L1),
    nth0(B, Result, X, L2).

The order of the indexes with regard to each other is important. Also the order of the swapping is important, if you don't want to recalculate the indexes. You can even enumerate the two indexes:

?- swap([a,b,c], A, B, R).
A = 0,
B = 1,
R = [b, a, c] ;
A = 0,
B = 2,
R = [c, b, a] ;
A = 1,
B = 2,
R = [a, c, b] ;
false.

This still does not terminate for this query:

?- swap(List, A, B, Swapped).

This would be a pretty hairy thing to get absolutely right. Does it need to work in this mode? EDIT: see the answer by Isabelle Newbie.

As discussed in the comments to TA_intern's answer, here is a small tweak:

swap(L, A, B, Result) :-
    same_length(L, Result),   % added this goal
    nth0(B, L, Y, L0),
    nth0(A, L0, X, L1),
    A < B,
    nth0(A, L2, Y, L1),
    nth0(B, Result, X, L2).

This allows the most general query to generate answers:

?- swap(List, X, Y, Xs), numbervars(List-Xs).
List = [A, B],
X = 0,
Y = 1,
Xs = [B, A] ;
List = [A, B, C],
X = 0,
Y = 1,
Xs = [B, A, C] ;
List = [A, B, C],
X = 0,
Y = 2,
Xs = [C, B, A] ;
List = [A, B, C],
X = 1,
Y = 2,
Xs = [A, C, B] .

If your Prolog doesn't provide a same_length predicate, you can define it as:

my_same_length([], []).
my_same_length([_X | Xs], [_Y | Ys]) :-
    my_same_length(Xs, Ys).

For example:

?- my_same_length(Xs, Ys), numbervars(Xs-Ys).
Xs = Ys, Ys = [] ;
Xs = [A],
Ys = [B] ;
Xs = [A, B],
Ys = [C, D] ;
Xs = [A, B, C],
Ys = [D, E, F] ;
Xs = [A, B, C, D],
Ys = [E, F, G, H] .

It is not necessary for this swap predicate to use anything more fancy than same_length. If either L or Result is bound to a proper list, same_length will succeed deterministically iff it can ensure that the other argument is a proper list of the same length; otherwise, it will fail. The length of this list is then related to the numerical arguments by nth0, same_length doesn't need to care about numbers at all.

Here's my approach:

replace(I,L,E,K):-
    nth0(I,L,_,R),
    nth0(I,K,E,R).

swap(List,A,B,NewList):-
    nth0(A,List,EA),
    nth0(B,List,EB),
    replace(A,List,EB,R1),
    replace(B,R1,EA,NewList).

?- swap([1,4,2,5],1,3,L).
L = [1, 5, 2, 4]
Related