Prolog:交换列表中的第一个和最后一个元素

时间:2014-04-14 22:54:08

标签: prolog dcg

我正在尝试编写一个交换第一个和最后一个元素的程序。

该功能需要2个参数。列表和变量,显示为新交换的列表。

我以为我是以懒惰的方式做这件事,但事实证明这对我来说同样困难。

我要抓住头部,把它放在一边 - 抓住尾巴的最后一个元素,把它放在一边 - 取尾巴,取下最后一个元素,把它放在一边,然后将所有3个加在一起做成一个清单

我无法删除尾部的最后一个元素。

我有这样的事情:

swap( [H|T],  Y ) :-

  % GET HEAD, LAST OF TAIL, AND TAIL WITH LAST ELEM REMOVED

  % GET HEAD (NEW LAST ELEMENT)

   H = NEW_LASTELEMENT,

  % GET LAST ELEMENT (LAST OF TAIL, WILL BE NEW HEAD)

   last(T,X), X = NEWHEAD, 

  % CUT END OF TAIL OFF

   cutlast(T, Z), REST OF CODE . . .

  .



% CUT LAST
cutlast([H | T], [H | T2]) :- T = [_|_], cutlast(T, T2).

我从网上借用了cutlast谓词,但我不确定它是如何工作的。我已经测试了将参数传递给它一小时了,它们都会继续返回false。任何帮助表示赞赏。

4 个答案:

答案 0 :(得分:5)

可能只是:

swap(A, B) :-
    append([First | Mid], [Last], A),
    append([Last | Mid], [First], B).

如果需要,可以使用一个元素和空列表成功的其他事实:

swap([X], [X]).
swap([], []).

答案 1 :(得分:4)

这是一个不使用append/3reverse/2内置插件的递归解决方案。我认为我发现它比那些更有效(在推理数量方面):

swap_fl_recursive([First|A], [Last|B]) :-
    swap_fl_recursive_(A, First, Last, B).

swap_fl_recursive_([Last], First, Last, [First]).
swap_fl_recursive_([X|A], First, Last, [X|B]) :-
    swap_fl_recursive_(A, First, Last, B).

此解决方案将原始First元素向下传递到递归,直到它可以成为新的最后一个元素,然后将原始Last实例化为新的第一个元素。

与其他人一样,要使swap_fl_recursive([X], [X])为真,或swap_fl_recursive([], []).为真,则需要将其添加为事实/规则。

append/3版本的时间/推论:

?- numlist(1,10000,L), time(swap_fl_append(L, S)).
% 20,000 inferences, 0.021 CPU in 0.021 seconds (100% CPU, 950838 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
S = [10000, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 3 inferences, 0.000 CPU in 0.000 seconds (94% CPU, 38921 Lips)
false.

reverse/2版本的时间/推论:

?- numlist(1,10000,L), time(swap_fl_reverse(L, S)).
% 20,055 inferences, 0.024 CPU in 0.024 seconds (100% CPU, 841265 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
S = [10000, 2, 3, 4, 5, 6, 7, 8, 9|...].

递归版本的时间/推断(在本回答中显示):

?- numlist(1,10000,L), time(swap_fl_recursive(L, S)).
% 10,000 inferences, 0.009 CPU in 0.010 seconds (99% CPU, 1059142 Lips)
L = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
S = [10000, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 2 inferences, 0.002 CPU in 0.002 seconds (100% CPU, 821 Lips)
false.

这三种方法都是与列表长度成比例的线性时间。

答案 2 :(得分:3)

这是使用内置reverse/2代替append/3的替代版本。

swap_first_last([First|A], [Last|B]) :-
    reverse(A, [Last|RevMid]),
    reverse([First|RevMid], B).

它的效率可能略低于Sergey用append/3显示的版本。与append/3版本一样,如果您希望swap_first_last([X], [X]).和/或swap_first_last([], []).为true,则必须将它们添加为事实/谓词。但是长度为2或更长的列表并不需要它们。

此谓词表示, [Last|B][First|A]相同,如果[Last|RevMid]与列表A相反,则交换第一个和最后一个元素,{ {1}}与B 相反。

第一个[First|RevMid]反转第一个列表(reverse)的尾部,产生一个有自己头尾的列表A。此时,[Last|RevMid]代表第一个列表的最后一个元素,而Last代表"中间"列表,不包括第一个和最后一个元素,但的顺序相反

第二个RevMid然后获取第一个列表的头reverse,并将其用作新列表First的头部。如果我们撤销此列表,我们最终会得到"中间"以正确的顺序列出并在末尾打了[First|RevMid](此列表称为First)因此我们将原始的第一个元素作为此列表的最后一个元素以及所有正确的中间元素。剩下的就是让第一个列表最后一个元素(B)作为头部前置,它出现在子句的头部Last

举例来说,让我们进行查询[Last|B]

  1. swap_first_last([a,b,c,d,e], S).屈服,[First|A][a,b,c,d,e]
  2. 统一First = a
  3. 查询产生A = [b,c,d,e]reverse([b,c,d,e], [Last|RevMid])[Last|T] = [e,d,c,b]的{​​{1}}。
  4. 查询Last = e RevMid = [d,c,b]屈服,reverse([a|[d,c,b]], B)
  5. reverse([a,d,c,b], B)实例化为B = [b,c,d,a][Last|B]

答案 3 :(得分:3)

使用

swap_first_last(Xs,Ys) :-
   phrase(([First],seq(Seq),[Last]),  Xs),
   phrase(([Last], seq(Seq),[First]), Ys).

seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).

但是,如果只有Ys是列表,那么 - 与其他建议类似,不会终止。第一次尝试是将列表限制为相同的长度:

same_length([], []).
same_length([_|Xs], [_|Ys]) :-
   same_length(Xs, Ys).

swap_first_last(Xs,Ys) :-
   same_length(Xs, Ys),
   phrase(([First],seq(Seq),[Last]),  Xs),
   phrase(([Last], seq(Seq),[First]), Ys).

但是,如果第二个元素中的列表不同,那该怎么办?喜欢

?- Xs = [_,a|_], Ys = [_,b|_], swap_first_last(Xs, Ys).
Xs = [b, a],
Ys = [a, b] ;
**LOOPS**

我们的谓词仍然可以终止:

 swap_first_last(Xs, [Last|Ys]) :-
    phrase(([First],dseq(Ys,[First]),[Last]), Xs).

 dseq(Xs,Xs) --> [].
 dseq([X|Xs0],Xs) --> [X], dseq(Xs0,Xs).
相关问题