Prolog在List中查找中间元素

时间:2016-03-08 00:48:34

标签: 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_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'). 

如果有任何帮助,我将不胜感激。

2 个答案:

答案 0 :(得分:3)

如果你检查了列表的长度,计算了中间元素的位置,那么它将节省大量的输入,然后遍历 em>用于获取该位置元素的列表。使用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

此解决方案可确保列表具有奇数长度。如果您需要自己定义,可以看到documentation of divmod/4。或者,如果列表不必具有奇数,长度,则只需使用N is Len div 2。如果由于某种原因您不允许使用nth0/3,那么实施它仍然比您尝试的更容易。

答案 1 :(得分:2)

你可以按照以下方式收紧你所拥有的东西:

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

<小时/> 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).

结果:

| ?- 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):

middle(L, X) :-
    same_length(Left, Right),
    append([Left, [X], Right], L).

same_length([], []).
same_length([_|T1], [_|T2]) :- same_length(T1, T2).

<小时/> 在所有上述解决方案中,如果列表具有偶数个元素,则谓词将失败。由于这是您的原始解决方案所做的,我认为这是必需的。如果对偶数列表有特定要求,则需要明确说明。