Search code examples
prolog

Swap two items depending on the index


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.


Solution

  • 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.