Найти максимальное целое число в списке в prolog


Я пытаюсь найти максимальное число в списке. Я знаю, что есть несколько решений, доступных в интернете, но я чувствую, что лучший способ научиться-это реализовать их самостоятельно.

Я написал следующий код:

max([X],X).
max([H|T],Res):-
    (  H >= Res
    -> max(T,Res1), Res1 = H
    ;  max(T,Res)
    ).
Может ли кто-нибудь указать мне на мою ошибку? Я не в состоянии понять это.
2 2

2 ответа:

Вы не гарантируете, что Res будет создан экземпляр. Для этого вам не обязательно нужен вспомогательный предикат. Вы можете сделать рекурсивный вызов перед проверкой, если Res больше, чем H, где Res - Самое большое целое число T.

Вы можете использовать ->, но вам это не нужно. Но если вы этого не сделаете, то потребуется еще немного отступлений.

Если вы попытаетесь остаться больше на вашем маршруте с рекурсией после проверки, вам понадобится вспомогательный предикат, как lurker имеет предложенный.

Edit: поскольку ответ принят сейчас, вот три предложения, реализованные:

max1([H|T], Y):-  % with the -> operator, call first
    max1(T,X),
    (H > X ->
     H = Y;
     Y = X).
max1([X],X).


max2([H|T], Y):-  % without the -> operator, call first (this might be very inefficient)
    max2(T,Y),
    H < Y.
max2([H|T], H):-
    max2(T,Y),
    H >= Y.
max2([X],X).


max3([H|T], Y) :- max_(T,H,Y).            % with helper predicate
max_([H|T],HighestNow,Highest):-          % call after the test
    (H > HighestNow -> max_(T,H, Highest)
     ;
     max_(T,HighestNow,Highest)).
max_([],X,X).

Рассмотрим код, представленный в моем ответе на соответствующий вопрос " нахождение максимума в списке - прологе".

Код в упомянутом ответе основан на мета-предикате foldl/4.

Здесь я покажу, как это сделать с метапредикатами combine/3 и reduce/3. Первый, combine/3:
:- meta_predicate combine(3,?,?).
combine( _ ,[]    ,[]).
combine(P_3,[X|Xs],Ys) :-
   list_prev_combined_(Xs,X,Ys,P_3).

:- meta_predicate list_combined_(?,?,3).
list_combined_([]    ,[], _ ).
list_combined_([X|Xs],Ys,P_3) :-
   list_prev_combined_(Xs,X,Ys,P_3).

:- meta_predicate list_prev_combined_(?,?,?,3).    
list_prev_combined_([]     ,X ,[X]   , _ ).
list_prev_combined_([X1|Xs],X0,[Y|Ys],P_3) :-
   call(P_3,X0,X1,Y),
   list_combined_(Xs,Ys,P_3).

Основываясь на combine/3, мы можем определить reduce/3 следующим образом:

:- meta_predicate reduce(3,?,?).
reduce(P_3,[X|Xs],V) :- 
   list_aka_prev_reduced_(Xs,Xs,X,V,P_3).

:- meta_predicate list_aka_prev_reduced_(?,?,?,?,3).
list_aka_prev_reduced_([]   ,_ ,V ,V, _ ).
list_aka_prev_reduced_([_|_],Xs,X0,V,P_3) :-
   list_prev_combined_(Xs,X0,Ys,P_3),
   reduce(P_3,Ys,V).

Относительно формы их соответствующих деревьев доказательств, foldl/4 аналогично списки, в то время как combine/3 и reduce/3 подобны сбалансированным двоичным деревьям.

Рассмотрим следующие запросы:

:- use_module(library(lambda)).

?- foldl(\X^Y^f(X,Y)^true, [1,2,3,4,5,6,7], 0,S).
S = f(7,f(6,f(5,f(4,f(3,f(2,f(1,0))))))).

?- combine(\X^Y^f(X,Y)^true, [1,2,3,4,5,6,7], S).
S = [f(1,2),f(3,4),f(5,6),7].

?- reduce(\X^Y^f(X,Y)^true, [1,2,3,4,5,6,7], S).
S = f(f(f(1,2),f(3,4)),f(f(5,6),7)).

reduce/3 основан на combine/3 и применяет его до тех пор, пока все элементы не будут объединены в один:

?- combine(\X^Y^f(X,Y)^true, [1,2,3,4,5,6,7], S).
S = [f(1,2),f(3,4),f(5,6),7].

?- combine(\X^Y^f(X,Y)^true, [f(1,2),f(3,4),f(5,6),7], S).
S = [f(f(1,2),f(3,4)),f(f(5,6),7)].

?- combine(\X^Y^f(X,Y)^true, [f(f(1,2),f(3,4)),f(f(5,6),7)], S).
S = [f(f(f(1,2),f(3,4)),f(f(5,6),7))].

?- reduce(\X^Y^f(X,Y)^true, [1,2,3,4,5,6,7], S).
S =  f(f(f(1,2),f(3,4)),f(f(5,6),7)).

Будем использовать его для получения максимального целого числа Max в списке [1,5,2,4,3,8,7,2]:

:- use_module(library(clpfd)).

?- reduce(\X^Y^XY^(XY #= max(X,Y)), [1,5,2,4,3,8,7,2], Max).
Max = 8.

℅ If you can't use clpfd, simply use is/2 instead of (#=)/2:
?- reduce(\X^Y^XY^(XY is max(X,Y)), [1,5,2,4,3,8,7,2], Max).
Max = 8.