List 删除列表的中间元素
我想编写一个Prolog程序,将中间元素从奇数列表中删除到另一个列表中 例如,如果我们给出:List 删除列表的中间元素,list,prolog,List,Prolog,我想编写一个Prolog程序,将中间元素从奇数列表中删除到另一个列表中 例如,如果我们给出:delete_mid([1,2,3,4,5],L),那么它将产生:L=[1,2,4,5]作为答案。我想你需要谓词。只需找到中间元素的索引,然后使用nth0/4将其删除 delete_middle(Ls, Ls1) :- length(Ls, L), divmod(L, 2, Q, 1), % constrain remainder to be 1: fails on even list
delete_mid([1,2,3,4,5],L)
,那么它将产生:L=[1,2,4,5]
作为答案。我想你需要谓词。只需找到中间元素的索引,然后使用nth0/4
将其删除
delete_middle(Ls, Ls1) :-
length(Ls, L),
divmod(L, 2, Q, 1), % constrain remainder to be 1: fails on even list
nth0(Q, Ls, _, Ls1).
生成变体:唯一的问题是divmod
divmod1(Dividend, Divisor, Quotient, Remainder) :-
( var(Dividend)
-> Dividend is Divisor*Quotient+Remainder
; divmod(Dividend, Divisor, Quotient, Remainder)
).
delete_middle(Ls, Ls1) :- % Reversed the clauses.
nth0(Q, Ls, _, Ls1),
divmod1(L, 2, Q, 1),
length(Ls, L).
使用
nth0/4
的解决方案是有效的,但是我们如何声明性地解决这个问题呢
middle_less(InList,MiddlelessList,Middle) :-
append([Prefix,[Middle],Suffix],InList),
length(Prefix,Len),
length(Suffix,Len),
append(Prefix,Suffix,MiddlelessList).
这基本上是Prolog形式的问题陈述
它还起作用:
:- begin_tests(middleless).
test("empty list",fail) :- middle_less([],_,_).
test("1-element list",[true([MLL,M] == [[],a]),nondet]) :-
middle_less([a],MLL,M).
test("2-element list",fail) :-
middle_less([a,b],_,_).
test("3-element list",[true([MLL,M] == [[a,c],b]),nondet]) :-
middle_less([a,b,c],MLL,M).
:- end_tests(middleless).
因此:
?- run_tests.
% PL-Unit: middleless .... done
% All 4 tests passed
true.
但有1001个元素的列表:
?- length(L,1001),time(middle_less(L,MLL,M)).
% 757,517 inferences, 0.110 CPU in 0.111 seconds (99% CPU, 6862844 Lips)
有一天,编译器自动将
middle\u less
的规范变形为一个有效的解决方案。我很惊讶,也有点难过,到目前为止,两个答案都没有采用最明显的方法;当然你们在学校里也听说过(我怀疑这可能是OP希望做的)
然而,立即解释或执行有点困难,因此首先,这里有一个谓词来查找中间元素:
list_mid([H|T], Mid) :-
list_mid_1(T, T, H, Mid).
list_mid_1([], _, Mid, Mid).
list_mid_1([_,_|Fast], [S|Slow], _, Mid) :-
list_mid_1(Fast, Slow, S, Mid).
我希望名字是显而易见的
?- list_mid([], Mid).
false.
?- list_mid([x], Mid).
Mid = x.
?- list_mid([a,x,b], Mid).
Mid = x.
?- list_mid([a,a,x,b,b], Mid).
Mid = x.
?- list_mid([a,a,x,b], Mid).
false.
似乎有效。现在,我可以尝试添加一部分,它保留了它目前丢弃的东西
我很忙,所以这需要一段时间。同时,这正是我的想法。我没有看到,而是写了这样一句话:
delete_mid([H|T], L) :-
delete_mid_1(T, T, H, L).
delete_mid_1([], Rest, _, Rest).
delete_mid_1([_,_|Fast], [H|Slow], Prev, [Prev|Back]) :-
delete_mid_1(Fast, Slow, H, Back).
它不像劳布萨格的解决方案那样整洁,但在其他方面似乎是相同的解决方案。对于测试用例,它通过@false终止
我认为
list\u middle/2
谓词就足够了;我再一次感到惊讶和有点悲伤,因为只有劳布萨格看到了(或者已经知道了)
这不是一个直截了当的回答,也不是一个更为理想的答案
delete_middle1(Ls, Ls1) :- delete_middle1_(Ls, Ls, [], Ls1).
delete_middle1_([X | Cs], [_, _ | Ds], Acc, L) :-
delete_middle1_(Cs, Ds, [X | Acc], L).
delete_middle1_([_ | Cs], [_], Acc, L) :- revappend(Acc, Cs, L).
revappend([], L, L).
revappend([X | L1], L2, L3) :- revappend(L1, [X | L2], L3).
这种方法在处理链表和指针时效果很好。当一个指针位于末端时,另一个指针将接近中间。然后我们可以删除元素
delete_middle([], [], _MiddleDeletedPrefix) -->
[_Middle].
delete_middle([L | Left], [R | ReversedRight], [L | MiddleDeletedPrefix]) -->
[L],
delete_middle(Left, ReversedRight, MiddleDeletedPrefix),
[R].
delete_middle(List, MiddleDeleted) :-
phrase(delete_middle(Left, ReversedRight, MiddleDeleted), List),
reverse(ReversedRight, Right),
append(Left, Right, MiddleDeleted).
基于TA_intern提出的“找到中间点”算法:
%! list_without_middle(SOURCEs,TARGETs)
list_without_middle(SOURCEs,TARGETs)
:-
list_middle(SOURCEs,_MIDDLE_,PREFIXs,SUFFIXs) ,
lists:append(PREFIXs,SUFFIXs,TARGETs)
.
%! list_middle(LISTs,MIDDLE,PREFIXs,SUFFIXs)
list_middle([ITEM|LISTs],MIDDLE,PREFIXs,SUFFIXs)
:-
list_middle(LISTs,LISTs,ITEM,MIDDLE,PREFIXs,SUFFIXs)
.
%! list_middle(FASTs,SLOWs,ITEM,MIDDLE,PREFIXs,SUFFIXs)
list_middle([],SLOWs,ITEM,ITEM,[],SLOWs) .
list_middle([_,_|FASTs],[ITEM|SLOWs],PREVIOUS_ITEM,MIDDLE,[PREVIOUS_ITEM|PREFIXs],SUFFIXs)
:-
list_middle(FASTs,SLOWs,ITEM,MIDDLE,PREFIXs,SUFFIXs)
.
此解决方案在“取出”中间项后保留一个计数器,以将尾部与适当长度的列表统一:
without_middle(Ls, Ls1):-
without_middle(Ls, 0, Ls1).
without_middle([_Mid|Tail], Len, Tail):-
length(Tail, Len).
without_middle([Item|Tail], Len, [Item|NTail]):-
succ(Len, Len1),
without_middle(Tail, Len1, NTail).
这种细微的变化更直接地嵌入了第二部分的计数+长度+统一,从而为大型列表提供了更好的性能结果:
without_middle(Ls, Ls1):-
without_middle(Ls, [], Ls1).
without_middle([_Mid|Tail], Tail, Tail).
without_middle([Item|Tail], RTail, [Item|NTail]):-
without_middle(Tail, [_|RTail], NTail).
样本测试用例:
?- without_middle([a,b,c,d,e,f,g], L).
L = [a, b, c, e, f, g] ;
false.
?- without_middle([a,b,c,d,e,f], L).
false.
?- without_middle(L, []).
L = [_552] ;
false.
?- dif(A,B), without_middle([A|_], [B|_]).
false.
现在我也想加入(这个问题的答案8)
想法:我看到TA_实习生关于获取中间元素(
list_mid
)的想法:这是天才。但是等等。。。这是可以改进的
进一步解释算法:谓词可以用来生成一个类似于(奇数)输入列表的列表,而不需要中间元素。或者,如果该属性成立,它可以测试两个列表 “genius”部分是不需要计算长度或计数器,因为它实际上使用输入列表的副本作为计数器。对其原理进行了解释和说明 第1行和第2行创建对同一列表的两个引用。计数器列表称为快速,元素列表称为慢速。为什么?因为在每个递归步骤中,您都会从快速列表中删除两个元素(
[H,fast]
),但从元素列表中只删除一个元素([H,Slow]
)。当快速列表中只剩下一个元素时([\u]
),您可以点击慢速列表中的中间元素。因此,将其拆下,并将其余部分放在返回轨道上。继续递归时,将从慢速列表中删除的所有元素(H
)作为返回列表的开头,递归将填充其余元素
等等,您得到了元素列表的精确副本,只是缺少中间的元素。使用
append/3
:
del_mid([_], []). % if input only has one element => output is []
del_mid([H|T], [H|X]) :-
append(M, [Litem], T), % M = list without first and last (Litem) element
del_mid(M, R), % Apply on M; if M is only one item => R will be []
append(R, [Litem], X). % X = R + [last item] => which gets added as result's tail
一些例子:
?- del_mid([], X).
false.
?- del_mid([a], X).
X = [] ;
false.
?- del_mid([a,b], X).
false.
?- del_mid([a,b,c], X).
X = [a, c] ;
false.
?- del_mid([a,b,c,d,e,f,g], X).
X = [a, b, c, e, f, g] ;
false.
以下是我的prolog解决方案:
delMidNumber(K,L):-
len(K,N),
(N mod 2 =:= 1 ->
N1 is N//2,
nth0(N1,K,E1),
del(E1,K,L); write('List has even length'),!).
len([],0).
len([H|T],N):-
len(T,N1),
N is N1+1.
del(E,[E|T],T).
del(E,[H|T],[H|T1]):-
del(E,T,T1).
谓词delMidNumber接受两个参数1-奇数列表。2-将形成的新列表。谓词首先计算列表的长度,然后检查列表的长度是否为奇数,然后将长度除以2。然后在nth0中使用结果,以给出该索引上的元素。然后,我们只需使用del谓词来删除该中间数元素。如果长度为偶数,则会写入长度为偶数的消息,然后剪切(停止)
新版本,现在更具确定性:
delete_mid(List, MiddleDeleted) :-
List = [_ | Tail],
gallop(Tail, MiddleDeleted, List, MiddleDeleted).
gallop([], [], [_Middle | Xs], Xs).
gallop([_,_ | Fast1], [_,_ | Fast2], [X | Xs], [X | Ys]) :-
gallop(Fast1, Fast2, Xs, Ys).
与以前的答案相比,新的答案是,它以双倍的速度运行两个列表,同时也复制前缀。它至少需要对前两个参数进行浅层索引才能确定,但SWI Prolog做到了这一点:
?- delete_mid([1, 2, 3, 4, 5], MiddleDeleted).
MiddleDeleted = [1, 2, 4, 5].
?- delete_mid(Xs, []).
Xs = [_2008].
?- delete_mid(Xs, [a, b]).
Xs = [a, _2034, b].
?- dif(A, B), delete_mid([A | _], [B | _]).
false.
下面是另一个尝试:
delit(In, Del):-
delit(In, In, Del, Del).
delit(Fast, [H|Slow], DelFast, Del):-
( Fast = [_],
DelFast = []
-> Del = Slow
; Fast = [_,_|Faster],
DelFast = [_,_|DelFaster],
Del = [H|Deler],
delit(Faster, Slow, DelFaster, Deler)
).
?- delit([1, 2, 3, 4, 5], Del).
Del = [1, 2, 4, 5].
?- delit([1, 2, 3, 4], Del).
false.
?- delit(L, []).
L = [_15594].
?- dif(A,B), delit([A|_], [B|_]).
false.
?- delit(L, [1,2]).
L = [1, _18882, 2].
?- delit(L, [1,2, 4, 5]).
L = [1, 2, _19916, 4, 5].
请注意:False在以前的版本中暗示了这个谓词不需要的行为。在理解并通过添加另一个快速计数器解决问题后,我现在有了一个类似于Isabelles代码的实现
还有一个不同的版本,它带有一个计数器谓词,用于“计算”奇数列表Div 2中的元素数
count([],0).
count([_|L],s(S)):-
count(L,S).
middel([_|Rest],0,Rest).
middel([H|List], s(s(S)), [H|Ret]):-
middel(List, S, Ret).
middel(In, Del):-
count(In, s(Cnt)),
count(Del, Cnt),
!,
middel(In, Cnt, Del).
?- middel([1, 2, 3, 4, 5], Del).
Del = [1, 2, 4, 5].
?- middel([1, 2, 4, 5], Del).
false.
?- middel(L, []).
L = [_12056].
?- dif(A,B), middel([A|_], [B|_]).
false.
?- middel(L, [1,2]).
L = [1, _15112, 2].
?- middel(L,[1,2,3,4]).
L = [1, 2, _20964, 3, 4].
那么元素数为偶数的列表呢?@Raubsauger:问题被限制在
奇数列表中。在偶数长度的列表中,您将什么定义为中间元素?忽略删除或删除其中两个。@DavidTonhofer如果长度尚未限制为奇数,则将divmod中的余数设置为1将解决此问题。@false:重新排列子句。首先放置nth0谓词,然后它终止。@false这是Prolog的问题-你永远不知道谓词将抛出什么,但你也不愿意添加一系列必须的内容,以限制使用实际已知的工作案例。find\u mid/2
的第一个参数真的是一个find吗?我刚刚发布了它。:-)@false不,不是,它显然应该是list\u middle/2
。啊,我在寻找这样的解决方案。(还有,老布什在普雷兹上学的时候,这所学校不在迪杰克斯特拉
del_mid([_], []). % if input only has one element => output is []
del_mid([H|T], [H|X]) :-
append(M, [Litem], T), % M = list without first and last (Litem) element
del_mid(M, R), % Apply on M; if M is only one item => R will be []
append(R, [Litem], X). % X = R + [last item] => which gets added as result's tail
?- del_mid([], X).
false.
?- del_mid([a], X).
X = [] ;
false.
?- del_mid([a,b], X).
false.
?- del_mid([a,b,c], X).
X = [a, c] ;
false.
?- del_mid([a,b,c,d,e,f,g], X).
X = [a, b, c, e, f, g] ;
false.
delMidNumber(K,L):-
len(K,N),
(N mod 2 =:= 1 ->
N1 is N//2,
nth0(N1,K,E1),
del(E1,K,L); write('List has even length'),!).
len([],0).
len([H|T],N):-
len(T,N1),
N is N1+1.
del(E,[E|T],T).
del(E,[H|T],[H|T1]):-
del(E,T,T1).
?-delMidNumber([1,3,2,4,5],L).
L = [1, 3, 4, 5]
?-delMidNumber([1,3,4,5],L).
List has even length
delete_mid(List, MiddleDeleted) :-
List = [_ | Tail],
gallop(Tail, MiddleDeleted, List, MiddleDeleted).
gallop([], [], [_Middle | Xs], Xs).
gallop([_,_ | Fast1], [_,_ | Fast2], [X | Xs], [X | Ys]) :-
gallop(Fast1, Fast2, Xs, Ys).
?- delete_mid([1, 2, 3, 4, 5], MiddleDeleted).
MiddleDeleted = [1, 2, 4, 5].
?- delete_mid(Xs, []).
Xs = [_2008].
?- delete_mid(Xs, [a, b]).
Xs = [a, _2034, b].
?- dif(A, B), delete_mid([A | _], [B | _]).
false.
delit(In, Del):-
delit(In, In, Del, Del).
delit(Fast, [H|Slow], DelFast, Del):-
( Fast = [_],
DelFast = []
-> Del = Slow
; Fast = [_,_|Faster],
DelFast = [_,_|DelFaster],
Del = [H|Deler],
delit(Faster, Slow, DelFaster, Deler)
).
?- delit([1, 2, 3, 4, 5], Del).
Del = [1, 2, 4, 5].
?- delit([1, 2, 3, 4], Del).
false.
?- delit(L, []).
L = [_15594].
?- dif(A,B), delit([A|_], [B|_]).
false.
?- delit(L, [1,2]).
L = [1, _18882, 2].
?- delit(L, [1,2, 4, 5]).
L = [1, 2, _19916, 4, 5].
count([],0).
count([_|L],s(S)):-
count(L,S).
middel([_|Rest],0,Rest).
middel([H|List], s(s(S)), [H|Ret]):-
middel(List, S, Ret).
middel(In, Del):-
count(In, s(Cnt)),
count(Del, Cnt),
!,
middel(In, Cnt, Del).
?- middel([1, 2, 3, 4, 5], Del).
Del = [1, 2, 4, 5].
?- middel([1, 2, 4, 5], Del).
false.
?- middel(L, []).
L = [_12056].
?- dif(A,B), middel([A|_], [B|_]).
false.
?- middel(L, [1,2]).
L = [1, _15112, 2].
?- middel(L,[1,2,3,4]).
L = [1, 2, _20964, 3, 4].