List Prolog查找列表中的中间元素

List Prolog查找列表中的中间元素,list,prolog,List,Prolog,我试图使用prolog谓词并找到给定列表的中间元素。我的想法是使用递归删除列表的第一个和最后一个元素。不幸的是,我不知道如何正确处理递归调用 delete_last(L, L1) :- append(L1, [_], L). delete_first(L,L1) :- append([_],L1,L). check_len(L) :- length(L,LEN), \+ 1 is LEN. delete_both([],_):- false. delete_bot

我试图使用prolog谓词并找到给定列表的中间元素。我的想法是使用递归删除列表的第一个和最后一个元素。不幸的是,我不知道如何正确处理递归调用

delete_last(L, L1) :-  
 append(L1, [_], L).
delete_first(L,L1) :-
    append([_],L1,L).
check_len(L) :-
   length(L,LEN), \+ 1 is LEN.


delete_both([],_):-
    false.
delete_both([_,_],_) :-
    false.
delete_both([X],X):-
    true, write('MidElement').

delete_both(L,L2) :-
    delete_first(LT,L2), delete_last(L,LT),check_len(LT) 
    ->write('here should be recursive call only when length is more than one'). 

如果您能提供任何帮助,我将不胜感激。

您可以通过以下方式加强现有的资源:

delete_last(L, L1) :-
    append(L1, [_], L).
delete_first([_|L], L).

% No need to check length of 1, since we only need to check
% if L = [X] in the caller, so we'll eliminate this predicate
%check_len(L) :-
%    length(L, 1).           % No need for an extra variable to check length is 1

% Clauses that yield false are not needed since clauses already fail if not true
% So you can just remove those
%
delete_both([X], X) :-
    write('MidElement').

% Here you need to fix the logic in your main clause
% You are deleting the first element of the list, then the last element
% from that result and checking if the length is 1.

delete_both(L, X) :-
    delete_first(L, L1),    % Remove first and last elements from L
    delete_last(L1, LT),
    (   LT = [X]            % Check for length of 1
    ->  true
    ;   delete_both(LT, X)  % otherwise, X is result of delete_both(LT, X)
    ).
结果如下:

| ?- delete_both([a,b,c,d,e], X).

X = c

yes
| ?- delete_both([a,b,c,d,e,f], X).

no
| ?- middle([a,b,c,d,e], X).

X = c ? ;

(1 ms) no
| ?- middle(L, a).

L = [a] ? ;

L = [_,a,_] ? ;

L = [_,_,a,_,_] ?
...

DCG解决方案在这里也很有效:

% X is the middle if it is flanked by two sequences of the same length
%
middle(X) --> seq(N), [X], seq(N).

seq(0) --> [].
seq(N) --> [_], { N #= N1 + 1 }, seq(N1).

middle(List, X) :- phrase(middle(X), List).
结果如下:

| ?- delete_both([a,b,c,d,e], X).

X = c

yes
| ?- delete_both([a,b,c,d,e,f], X).

no
| ?- middle([a,b,c,d,e], X).

X = c ? ;

(1 ms) no
| ?- middle(L, a).

L = [a] ? ;

L = [_,a,_] ? ;

L = [_,_,a,_,_] ?
...

另一种可能的解决方案是使用SWI Prolog的
append/2
谓词,它附加一个列表列表(假设您使用的是SWI):



在上述所有解决方案中,如果列表中的元素数为偶数,则谓词将失败。因为这是您最初的解决方案所做的,所以我假设这是必需的。如果对偶数列表有特定要求,则需要明确说明。

如果您检查列表的长度,计算中间元素的位置,然后遍历列表以获取该位置的元素,则可以节省大量键入。使用SWI Prolog,这将是:

?- length(List, Len),
   divmod(Len, 2, N, 1),
   nth0(N, List, a).
List = [a],                                 Len = 1, N = 0 ;
List = [_G2371, a, _G2377],                 Len = 3, N = 1 ;
List = [_G2371, _G2374, a, _G2380, _G2383], Len = 5, N = 2 . % and so on

此解决方案确保列表的长度为奇数。如果您需要自己定义它,您可以看到。或者,如果列表不必有奇数和长度,只需使用
N is Len div 2
。如果由于某种原因不允许您使用
nth0/3
,它仍然比您尝试执行的谓词更容易实现。

您是否支持所有大小的列表(偶数和奇数)?如果是这样,请查看相关问题“如何获取列表方案和prolog的第一个、中间和最后一个元素?”(),尤其是这个答案。嗯!