List 删除列表的中间元素

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

我想编写一个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
    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].