在Prolog中交换列表的连续项

时间:2013-04-14 23:50:01

标签: prolog logic-programming

我正在尝试编写可以交换列表中两个元素的Prolog代码,但前提是它们彼此连续。也就是说,

conseq_swap(d, e, [a, g, d, e, f], X).

应该给:

X = [a, g, e, d, f].

(d和e是连续的。)

然而,

conseq_swap(a, e, [a, g, d, e, f], X).

应该总是失败(a和e不连续。)

我可以假设一个项目只出现在列表中一次。

我有以下代码,实际上工作正常:

swap_conseq(X, Y, MainList, SwappedList) :-
   indexOf(MainList, X, Xpos),
   indexOf(MainList, Y, Ypos),
   Diff is Ypos - Xpos,
   Diff is 1,
   Xpos < Ypos,
   swap_ordered(X, Y, Xpos, Ypos, MainList, SwappedList).

swap_conseq(X, Y, MainList, SwappedList) :-
   indexOf(MainList, X, Xpos),
   indexOf(MainList, Y, Ypos),
   Diff is Xpos - Ypos,
   Diff is 1,
   Ypos < Xpos,
   swap_ordered(Y, X, Ypos, Xpos, MainList, SwappedList).

swap_ordered(Min, Max, Minpos, Maxpos, MainList, SwappedList) :-
   compute_lists(MainList, Min, Minpos, Pre, _),
   compute_lists(MainList, Max, Maxpos, _, Post),
   append(Pre, [Max, Min], Temp),
   append(Temp, Post, SwappedList).

indexOf([Element|_], Element, 1):- !.
indexOf([_|Tail], Element, Index):-
   indexOf(Tail, Element, Index1),
   !,
   Index is Index1+1.

compute_lists(MainList, X, Xpos, A, B) :-
   L is Xpos - 1,
   append(A, [X | B], MainList),
   length(A, L).

然而,仅仅通过查看代码,我可以说这是一种可怕的方式来做到这一点 - 重复,低效 - 只有像我这样的Prolog新手才能写出来。

非常感谢任何关于如何改进这一点的建议!

3 个答案:

答案 0 :(得分:3)

这是conseq_swap/4逻辑纯实现:

conseq_swap(E1,E2,Xs,Ys) :-       % use aux predicate w/dif-argument-order
   list_item1_item2_swapped(Xs,E1,E2,Ys).  

conseq_swap/4的参数顺序相比较 list_item1_item2_swapped/4已被更改,因此 first argument indexing已启用。这有助于防止不必要的选择点。

list_item1_item2_swapped([],_,_,[]).  
list_item1_item2_swapped([X|Xs],E1,E2,Ys) :-
   list_prev_item1_item2_swapped(Xs,X,E1,E2,Ys).

我们使用一种通常称为&#34; lagging&#34; 的技术来引用一个引用前一个列表元素的额外参数。要查看此技术的另一种用法,请查看@mat's answer到某些other question有关在Prolog列表上实现谓词的信息。

实际关系由list_prev_item1_item2_swapped/5

定义
list_prev_item1_item2_swapped([X|Xs],X0,X0,X,[X,X0|Xs]). % stop swapping
list_prev_item1_item2_swapped([X|Xs],X0,E1,E2,[X0|Ys]) :-
   dif(X0-X,E1-E2),               % state logically pure "not-equal"
   list_prev_item1_item2_swapped(Xs,X,E1,E2,Ys).

完成!现在让我们使用SWI-Prolog 7.1.37:

运行一些查询
?- conseq_swap(a,e,[a,g,d,e,f],X).
false.                            % fails, as OP said it should

?- conseq_swap(d,e,[a,g,d,e,f],X).
X = [a,g,e,d,f] ;                 % succeeds, as OP said it should
false.

?- conseq_swap(d,e,[A,G,D,E,F],X), A=a,G=g,D=d,E=e,F=f.
A = a, G = g, D = d, E = e, F = f, X = [a,g,e,d,f] ;   % succeeds
false.

?- conseq_swap(d,e,[a,g,d,e,d,e,f],X).
X = [a,g,e,d,d,e,f] ;             % succeeds; only the 1st (d,e) pair is swapped
false.

编辑2015-04-24

这是以前给出的代码的更直接,有些非优化的变体。

的效率低,但希望人类更容易阅读 。 和其他变种一样纯洁。

conseq_swap(X0,X1,[X0,X1|Xs],[X1,X0|Xs]).
conseq_swap(E0,E1,[X0,X1|Xs],[X0|Ys]) :-
   dif(X0-X1,E0-E1),
   conseq_swap(E0,E1,[X1|Xs],Ys).

相同的查询,相同的答案:

?- conseq_swap(a,e,[a,g,d,e,f],X).
false.

?- conseq_swap(d,e,[a,g,d,e,f],X).
X = [a,g,e,d,f] ;
false.

?- conseq_swap(d,e,[A,G,D,E,F],X), A=a,G=g,D=d,E=e,F=f.
A = a, G = g, D = d, E = e, F = f, X = [a,g,e,d,f] ;
false.

?- conseq_swap(d,e,[a,g,d,e,d,e,f],X).
X = [a,g,e,d,d,e,f] ;
false.

答案 1 :(得分:2)

(请按repeatLudwig阅读答案。这些都是很好的答案)

已修改以删除旧解决方案中的两个假设

conseq_swap(E1,E2,[E1,E2|R],[E2,E1|R]).
conseq_swap(E1,E2,[E2,E1|R],[E1,E2|R]).

conseq_swap(E1,E2,[A|RI],[A|RO]) :- conseq_swap(E1,E2,RI,RO).

删除了裁剪((!)/0)。

?- conseq_swap(a,e,[a,g,d,e,f],X).
false.

?- conseq_swap(d,e,[a,g,d,e,f],X).
X = [a, g, e, d, f] ;
false.

?- conseq_swap(d,e,[D,A,G,E,F],X), A=a,G=g,D=d,E=e,F=f.
false.

?- conseq_swap(d,e,[A,G,D,E,F],X), A=a,G=g,D=d,E=e,F=f.
A = a,
G = g,
D = d,
E = e,
F = f,
X = [a, g, e, d, f] ;
false.

对于有许多可能的交换对的情况,它只输出所有交换方式一次。 该问题假设无论如何只能有一对。

?- conseq_swap(d,e,[a,g,d,e,d,e,f],X).
X = [a, g, e, d, d, e, f] ;
X = [a, g, d, d, e, e, f] ;
X = [a, g, d, e, e, d, f] ;
false.

修改以删除假设2

如果您希望查询conseq_swap(a, e, [a, g, d, e, f], X).彻底失败,请删除旧解决方案中的前两行,这样可以在未执行交换时将原始列表作为输出结束。

旧解决方案(与相关代码输出相同)

这是使用以下假设编写的旧解决方案:

  1. 输入不包含任何无界变量。
  2. 当找不到满足条件的对时,按原样输出输入列表,类似于问题中的代码。
  3. % Empty list gives empty list
    conseq_swap(_,_,[],[]). 
    
    % List with single element gives back the same list
    conseq_swap(_,_,[A],[A]) :- !.
    
    % If we found the 2 items that need to be swapped, we can swap them.
    % We don't check for the rest of the list, due to the
    % assumption.
    % The cut at the end signals that the rule below do not need to be checked.
    conseq_swap(E1,E2,[E1,E2|R],[E2,E1|R]) :- !.
    conseq_swap(E1,E2,[E2,E1|R],[E1,E2|R]) :- !.
    
    % We recursively check the rest of the list and append the result.
    conseq_swap(E1,E2,[A|RI],[A|RO]) :- conseq_swap(E1,E2,RI,RO).
    

答案 2 :(得分:2)

另一种解决方案(在SWI-Prolog中)将是:

conseq_swap(D, E, L, Z) :- 
            append([A,[D,E],B], L),
            append([A,[E,D],B], Z).

相同的查询,相同的答案:

?- conseq_swap(a,e,[a,g,d,e,f],X).
false.

?- conseq_swap(d,e,[a,g,d,e,f],X).
X = [a, g, e, d, f] ;
false.

?- conseq_swap(d,e,[A,G,D,E,F],X), A=a,G=g,D=d,E=e,F=f.
A = a,
G = g,
D = d,
E = e,
F = f,
X = [a, g, e, d, f] ;
false.

?- conseq_swap(d,e,[a,g,d,e,d,e,f],X).
X = [a, g, e, d, d, e, f] ;
X = [a, g, d, e, e, d, f] ;
false.

(对不起,如果我不能写得那么像“重复”,但我是意大利人)。