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

Prolog 序言:交换列表中的第一个和最后一个元素,prolog,dcg,Prolog,Dcg,我正试图写一个程序来交换第一个和最后一个元素 该函数有两个参数。显示为新交换列表的列表和变量 我以为我是在偷懒,但事实证明这对我来说同样困难 我打算抓住头部,把它放在一边-抓住尾部的最后一个元素,把它放在一边-抓住尾部,去掉最后一个元素,也把它放在一边,然后将所有3个元素附加在一起,形成一个列表 我很难去掉尾巴的最后一个部分 我有这样的想法: swap( [H|T], Y ) :- % GET HEAD, LAST OF TAIL, AND TAIL WITH LAST ELEM REM

我正试图写一个程序来交换第一个和最后一个元素

该函数有两个参数。显示为新交换列表的列表和变量

我以为我是在偷懒,但事实证明这对我来说同样困难

我打算抓住头部,把它放在一边-抓住尾部的最后一个元素,把它放在一边-抓住尾部,去掉最后一个元素,也把它放在一边,然后将所有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。非常感谢您的帮助。

可以是:

swap(A, B) :-
    append([First | Mid], [Last], A),
    append([Last | Mid], [First], B).
如果需要,使用一个元素和空列表成功的其他事实:

swap([X], [X]).
swap([], []).
可能只是:

swap(A, B) :-
    append([First | Mid], [Last], A),
    append([Last | Mid], [First], B).
如果需要,使用一个元素和空列表成功的其他事实:

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

这里有一个替代版本,使用内置的reverse/2代替append/3

它可能比Sergey用append/3显示的版本效率稍低。与append/3版本一样,如果您希望先交换\u后交换[X],[X]。和/或先交换后交换[],[]。事实上,您必须将它们添加为事实/谓词。但长度为2或更大的列表不需要它们

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

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

然后,第二个倒转将第一个列表的头,first,用作新列表的头,即[first | RevMid]。如果我们颠倒这个列表,我们将以正确的顺序得到中间的列表,首先在列表的末尾打上一记耳光,这个列表被称为B,所以我们得到了原始的第一个元素作为这个列表的最后一个元素,以及所有正确的中间元素。剩下的就是让第一个列出最后一个元素作为head,它作为[last | B]出现在子句的head中

举个例子,让我们以查询swap_first_last[a,b,c,d,e],s为例:

将[First | A]与[A,b,c,d,e]统一,First=A和A=[b,c,d,e] 查询反向[b,c,d,e],[Last | RevMid],它产生[Last | T]=[e,d,c,b],或者,Last=e和RevMid=[d,c,b]。 查询反向[a |[d,c,b]],b是反向[a,d,c,b],b产生,b=[b,c,d,a] 将[Last | B]实例化为[e |[B,c,d,a]]或[e,B,c,d,a]
这里有一个替代版本,使用内置的reverse/2代替append/3

它可能比Sergey用append/3显示的版本效率稍低。与append/3版本一样,如果您希望先交换\u后交换[X],[X]。和/或先交换后交换[],[]。事实上,您必须将它们添加为事实/谓词。但长度为2或更大的列表不需要它们

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

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

然后,第二个倒转将第一个列表的头,first,用作新列表的头,即[first | RevMid]。如果我们颠倒这个列表,我们将以正确的顺序得到中间的列表,首先在列表的末尾打上一记耳光,这个列表被称为B,所以我们得到了原始的第一个元素作为这个列表的最后一个元素,以及所有正确的中间元素。剩下的就是让第一个列出最后一个元素作为head,它作为[last | B]出现在子句的head中

举个例子,让我们以查询swap_first_last[a,b,c,d,e],s为例:

将[First | A]与[A,b,c,d,e]统一,First=A和A=[b,c,d,e] 查询反向[b,c,d,e],[Last | RevMid],它产生[Last | T]=[e,d,c,b],或者,Last=e和RevMid=[d,c,b]。 查询反向[a |[d,c,b]],b是反向[a,d,c,b],b产生,b=[b,c,d,a] 将[Last | B]实例化为[e |[B,c,d,a]]或[e,B,c,d,a] 使用:

但是,与其他建议类似,如果只有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).
使用:

但是,与其他建议类似,如果只有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).

这里有一个递归解决方案,它不使用append/3或reverse/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).
此解决方案通过递归向下传递原始第一个元素,直到它可以成为新的最后一个元素,并实例化原始的最后一个元素以成为新的第一个元素

与其他函数一样,对于swap_fl_recursive[X],[X]为true或swap_fl_recursive[],[]。事实上,这些需要作为事实/规则添加

append/3版本的计时/推断:

反向/2版本的计时/推断:

此答案中显示的递归版本的计时/推断:

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

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

这里有一个递归解决方案,它不使用append/3或reverse/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).
此解决方案通过递归向下传递原始第一个元素,直到它可以成为新的最后一个元素,并实例化原始的最后一个元素以成为新的第一个元素

与其他函数一样,对于swap_fl_recursive[X],[X]为true或swap_fl_recursive[],[]。事实上,这些需要作为事实/规则添加

append/3版本的计时/推断:

反向/2版本的计时/推断:

此答案中显示的递归版本的计时/推断:

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

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

@GeorgeCostanza,代码中的注释表明您正在按程序思考,例如,就好像您在用C/C++编程一样。在Prolog中,它是关系匹配和模式匹配。在Sergey的解决方案中,他知道一个名为append/3的内置函数,它定义了3个列表之间的关系,其中第3个列表是前两个粘贴在一起的列表。swap使用这种关系,并简单地定义B与append的关系,知道A与append的关系。如果A是第一个元素、中间列表和最后一个元素,那么B是最后一个元素、相同的中间列表和第一个元素。@Sergey Dymchenko谢谢。现在开始更好地理解prolog了。@GeorgeCostanza,代码中的注释表明您正在按程序进行思考,例如,就好像您在用C/C++编程一样。在Prolog中,它是关系匹配和模式匹配。在Sergey的解决方案中,他知道一个名为append/3的内置函数,它定义了3个列表之间的关系,其中第3个列表是前两个粘贴在一起的列表。swap使用这种关系,并简单地定义B与append的关系,知道A与append的关系。如果A是第一个元素、中间列表和最后一个元素,那么B是最后一个元素、相同的中间列表和第一个元素。@Sergey Dymchenko谢谢。现在开始更好地理解prolog了。您的计时只是表明所有版本都需要0.000秒。推断计数有助于比较不同的增长率,但未考虑实际成本。想想timelengthL,10000000,lengthL,N。这是7个推论…@错,是的,我知道,这是一个很好的观点。我并不是真的想把时间安排搞得这么重要,只是想说明推论数量的差异。我确实意识到,推理计数并不是衡量绝对效率的一个很好的指标。我应该说得更清楚。你的计时只是表明所有版本都需要0.000秒。推断计数有助于比较不同的增长率,但未考虑实际成本。想想timelengthL,10000000,lengthL,N。这是7个推论…@错,是的,我知道,这是一个很好的观点。我并不是真的想把时间安排搞得这么重要,只是想说明推论数量的差异。我确实意识到,推理计数并不是衡量绝对效率的一个很好的指标。我应该说得更清楚一些。