List Prolog查找列表中的中间元素
我试图使用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
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的第一个、中间和最后一个元素?”(),尤其是这个答案。嗯!