Reputation: 199
I need to swap two elements in list with specified indices using predicate:
swap(List, index1, index2).
I tried this way:
change_value([X|List], 0, Y, [Y|List2]) :-
copy_rest([X|List], List2).
change_value([], P, Y, []).
change_value([X|List], Pos, Y, [X|List2]) :-
X \== Y,
Pos > 0,
NewPos is Pos - 1,
change_value(List, NewPos, Y, List2).
copy_rest([], []).
copy_rest([X|List], [X|List2]) :-
copy_rest(List, List2).
Is there any better solution?
Thank you very much!
Upvotes: 1
Views: 4695
Reputation: 4438
Using DCG, for performance (due to difference lists):
list_i_j_nth1_swap_using_dcg(I, J, Lst, LstSwap) :-
integer(I),
integer(J),
Min is min(I, J),
Max is max(I, J),
Min >= 1,
Min < Max,
phrase(list_i_j_nth1_swap(Min, Max), Lst, LstSwap).
list_i_j_nth1_swap(1, Max), [ElemJ] -->
!, [ElemI], { Max0 is Max - 1 }, list_i_j_nth1_swap_j(Max0, ElemI, ElemJ).
list_i_j_nth1_swap(Min, Max), [E] -->
[E], { Min0 is Min - 1, Max0 is Max - 1 }, list_i_j_nth1_swap(Min0, Max0).
list_i_j_nth1_swap_j(1, ElemI, ElemJ), [ElemI] -->
!, [ElemJ].
list_i_j_nth1_swap_j(Max, ElemI, ElemJ), [E] -->
[E], { Max0 is Max - 1 }, list_i_j_nth1_swap_j(Max0, ElemI, ElemJ).
Result in swi-prolog:
?- numlist(1, 10, L), time(list_i_j_nth1_swap_using_dcg(2, 4, L, S)).
% 9 inferences, 0.000 CPU in 0.000 seconds (88% CPU, 208792 Lips)
L = [1,2,3,4,5,6,7,8,9,10],
S = [1,4,3,2,5,6,7,8,9,10].
?- time(list_i_j_nth1_swap_using_dcg(2, 9, L, [1,9,3,4,5,6,7,8,2,10])).
% 14 inferences, 0.000 CPU in 0.000 seconds (90% CPU, 272368 Lips)
L = [1,2,3,4,5,6,7,8,9,10].
?- numlist(1, 10000000, L), time(list_i_j_nth1_swap_using_dcg(3, 5, L, S)).
% 10 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 320092 Lips)
S = [1,2,5,4,3,6,7,8,9,10...
?- numlist(1, 10000000, L), time(list_i_j_nth1_swap_using_dcg(2000, 1000, L,
S)).
% 2,005 inferences, 0.319 CPU in 0.325 seconds (98% CPU, 6290 Lips)
Upvotes: 0
Reputation: 18726
No need to write recursive code!
Simply use the builtin predicates length/2
, same_length/2
, and append/3
like so:
list_i_j_swapped(As,I,J,Cs) :-
same_length(As,Cs),
append(BeforeI,[AtI|PastI],As),
append(BeforeI,[AtJ|PastI],Bs),
append(BeforeJ,[AtJ|PastJ],Bs),
append(BeforeJ,[AtI|PastJ],Cs),
length(BeforeI,I),
length(BeforeJ,J).
Done! Let's put it to use!
?- list_i_j_swapped([e0,e1,e2,e3,e4,e5],5,1,Ys). Ys = [e0,e5,e2,e3,e4,e1] ; false.
OK! Does it work in the "other direction", too?
?- list_i_j_swapped(Xs,5,1,[e0,e5,e2,e3,e4,e1]). Xs = [e0,e1,e2,e3,e4,e5] ; false.
Alright! What about the following quite general query?
?- list_i_j_swapped([A,B,C],I,J,Ys).
I = 0, J = 0, Ys = [A,B,C]
; I = 0, J = 1, Ys = [B,A,C]
; I = 0, J = 2, Ys = [C,B,A]
; I = 1, J = 0, Ys = [B,A,C]
; I = 1, J = 1, Ys = [A,B,C]
; I = 1, J = 2, Ys = [A,C,B]
; I = 2, J = 0, Ys = [C,B,A]
; I = 2, J = 1, Ys = [A,C,B]
; I = J, J = 2, Ys = [A,B,C]
; false.
It worked! At last, we run the most general query:
?- list_i_j_swapped(Xs,I,J,Ys).
I = 0, J = 0, Xs = [_A] , Ys = [_A]
; I = 0, J = 0, Xs = [_A,_B] , Ys = [_A,_B]
; I = 0, J = 1, Xs = [_A,_B] , Ys = [_B,_A]
; I = 1, J = 0, Xs = [_A,_B] , Ys = [_B,_A]
; I = 1, J = 1, Xs = [_A,_B] , Ys = [_A,_B]
; I = 0, J = 0, Xs = [_A,_B,_C], Ys = [_A,_B,_C]
...
Fair enumeration out-of-the-box? What's not to like?
Upvotes: 4