简体   繁体   English

在Prolog中交换列表的连续项

[英]Swapping consecutive items of a list in Prolog

I'm trying to write Prolog code that can swap two elements of a list, but only if they are consecutive to each other . 我正在尝试编写可以交换列表中两个元素的Prolog代码,但前提是它们彼此连续 That is, 那是,

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

should give: 应该给:

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

(d and e are consecutive.) (d和e是连续的。)

However, 然而,

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

should always fail (a and e are not consecutive.) 应该总是失败(a和e不连续。)

I can assume that an item appears in the list only once. 我可以假设一个项目只出现在列表中一次。

I have the following code, which is actually working fine: 我有以下代码,实际上工作正常:

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

However, just by looking at the code, I can tell that this is a horrible way to do this - repetitive, inefficient - something only a Prolog newbie like me could write. 但是,仅通过查看代码,我就能知道这是一种可怕的方式-重复,效率低下-只有像我这样的Prolog新手才能编写。

Any suggestions on how to improve this would be greatly appreciated! 任何建议,以改善这一点将不胜感激!

Here's a logically pure implementation of conseq_swap/4 : 这是一个逻辑上纯粹conseq_swap/4

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

Compared to conseq_swap/4 the argument order of list_item1_item2_swapped/4 is altered, so first argument indexing is enabled. 相比于conseq_swap/4的参数顺序list_item1_item2_swapped/4被改变,所以第一个参数的索引被启用。 This can help prevent unneeded choice-points. 这可以帮助防止不必要的选择点。

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

We thread an extra argument that refers to the previous list element through, employing a technique commonly called "lagging" . 我们使用通常称为“滞后”的技术来引用一个引用前一个列表元素的额外参数。 To see another use of this technique in action, look at @mat's answer to some other question about implementing predicates on Prolog lists. 要查看此技术的另一种用法,请查看@mat关于在Prolog列表上实现谓词的其他问题 的答案

The actual relation is defined by list_prev_item1_item2_swapped/5 : 实际关系由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).

Done! 完成! Now let's run some queries with SWI-Prolog 7.1.37: 现在让我们用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.

Edit 2015-04-24 编辑2015-04-24

Here's a more direct, somewhat de-optimized variant of the code given before. 这是以前给出的代码的更直接,有些非优化的变体。

It is less efficient, but hopefully a bit easier to read by humans . 低效率的,但希望有点容易被人类阅读 As pure as the other variant. 像其他变种一样纯洁。

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

Same queries, same answers: 相同的查询, 相同的答案:

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

(Do read answers by repeat and Ludwig . Those are good answers) (请通过repeatLudwig阅读答案。这些都是很好的答案)

Modified to remove both assumptions in old solution 修改以删除旧解决方案中的两个假设

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

Cuts ( (!)/0 ) are removed. 剪切( (!)/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.

For the case where there are many possible pairs to swap, it output all ways of swapping only once. 对于有许多可能的交换对的情况,它仅输出一次交换的所有方式。 The question assumes that there can only be one pair anyway. 这个问题假定反正只能有一对。

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

Modified to remove assumption 2 修改以删除假设2

If you want the query conseq_swap(a, e, [a, g, d, e, f], X). 如果要查询conseq_swap(a, e, [a, g, d, e, f], X). to fail outright, remove the first two lines in the old solution, which allows the original list to end up as output when no swapping is performed. 彻底失败,删除旧解决方案中的前两行,这允许原始列表在没有执行交换时最终作为输出。

Old solution (same output as the code in question) 旧解决方案(与相关代码相同的输出)

This is the old solution written with the following assumptions: 这是使用以下假设编写的旧解决方案:

  1. The input does not contain any unbounded variable. 输入不包含任何无界变量。
  2. When no pair satisfying the condition is found, output the input list as-is , similar to what the code in the question does. 当找不到满足条件的对时, 按原样输出输入列表 ,类似于问题中的代码。
% 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).

Another solution (in SWI-Prolog) would be: 另一个解决方案(在SWI-Prolog中)将是:

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

Same queries, same answers: 相同的查询,相同的答案:

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

(Sorry if I'm not able to write so much like "repeat", but I'm italian). (对不起,如果我不能写得那么像“重复”,但我是意大利人)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM