Prolog 序言:交换列表中的第一个和最后一个元素
我正试图写一个程序来交换第一个和最后一个元素 该函数有两个参数。显示为新交换列表的列表和变量 我以为我是在偷懒,但事实证明这对我来说同样困难 我打算抓住头部,把它放在一边-抓住尾部的最后一个元素,把它放在一边-抓住尾部,去掉最后一个元素,也把它放在一边,然后将所有3个元素附加在一起,形成一个列表 我很难去掉尾巴的最后一个部分 我有这样的想法:Prolog 序言:交换列表中的第一个和最后一个元素,prolog,dcg,Prolog,Dcg,我正试图写一个程序来交换第一个和最后一个元素 该函数有两个参数。显示为新交换列表的列表和变量 我以为我是在偷懒,但事实证明这对我来说同样困难 我打算抓住头部,把它放在一边-抓住尾部的最后一个元素,把它放在一边-抓住尾部,去掉最后一个元素,也把它放在一边,然后将所有3个元素附加在一起,形成一个列表 我很难去掉尾巴的最后一个部分 我有这样的想法: swap( [H|T], Y ) :- % GET HEAD, LAST OF TAIL, AND TAIL WITH LAST ELEM REM
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个推论…@错,是的,我知道,这是一个很好的观点。我并不是真的想把时间安排搞得这么重要,只是想说明推论数量的差异。我确实意识到,推理计数并不是衡量绝对效率的一个很好的指标。我应该说得更清楚一些。