如何编写谓词 minmax(L, X, Y) 来找出整数 L 列表中 X 的最小值和 Y 的最大值。
例子:
?- minmax([1, -10, 1, 0, 7, 7], X, Y).
X = -10, Y = 7.
如何编写谓词 minmax(L, X, Y) 来找出整数 L 列表中 X 的最小值和 Y 的最大值。
例子:
?- minmax([1, -10, 1, 0, 7, 7], X, Y).
X = -10, Y = 7.
让我们定义list_minnum_maxnum/3
如下list_minnum/2
:
list_minnum_maxnum([E|Es],Min,Max) :-
V is E,
list_minnum0_minnum_maxnum0_maxnum(Es,V,Min,V,Max).
list_minnum0_minnum_maxnum0_maxnum([] ,Min ,Min,Max ,Max).
list_minnum0_minnum_maxnum0_maxnum([E|Es],Min0,Min,Max0,Max) :-
V is E,
Min1 is min(Min0,V),
Max1 is max(Max0,V),
list_minnum0_minnum_maxnum0_maxnum(Es,Min1,Min,Max1,Max).
OP 给出的示例查询:
?- list_minnum_maxnum([1,-10,1,0,7,7], Min,Max).
Min = -10,
Max = 7.
请注意,此实现list_minnum_maxnum/3
适用于各种数字。
?- list_minnum_maxnum([1,-10,1,0,7.2,7,7], Min,Max).
Min = -10,
Max = 7.2.
如果您只关心处理整数,请使用clpfd!
:- use_module(library(clpfd)).
我们定义list_zmin_zmax/3
如下:
list_zmin_zmax([E|Es],Min,Max) :-
V #= E,
list_zmin0_zmin_zmax0_zmax(Es,V,Min,V,Max).
list_zmin0_zmin_zmax0_zmax([] ,Min ,Min,Max ,Max).
list_zmin0_zmin_zmax0_zmax([E|Es],Min0,Min,Max0,Max) :-
V #= E,
Min1 #= min(Min0,V),
Max1 #= max(Max0,V),
list_zmin0_zmin_zmax0_zmax(Es,Min1,Min,Max1,Max).
与以前相同的示例用法:
?- list_zmin_zmax([1,-10,1,0,7,7], Min,Max).
Min = -10,
Max = 7.
好的!对非整数的支持如何?
?- list_zmin_zmax([1,-10,1,0,7.2,7,7], Min,Max).
ERROR: Domain error: `clpfd_expression' expected, found `7.2'
我们期望得到一个错误,我们得到一个错误......
请注意,感谢clpfd,我们也可以运行更一般的查询!
?- list_zmin_zmax([A,B], Min,Max).
A #>= Min, Max #>= A, Min #= min(A,B),
B #>= Min, Max #>= B, Max #= max(A,B).
如前所述,您需要遍历列表,不断累积最小值和最大值。因此,假设您必须从头开始编写此代码,您需要做的第一件事是将问题分解为简单的步骤:
这导致 amin/3
和 max/3
,因此:
min(X,X,X).
min(X,Y,X) :- X < Y .
min(X,Y,Y) :- X > Y .
max(X,X,X).
max(X,Y,X) :- X > Y .
max(X,Y,Y) :- X < Y .
出于您的目的,如果您愿意,甚至可以将它们组合成一个谓词:
rank( X , X , X , X ) .
rank( X , Y , X , Y ) :- X < Y .
rank( X , Y , Y , X ) :- X > Y .
Prolog 中一个非常典型的编程模式是有一个简单的公共 API 谓词,它调用一个私有的“工人”谓词来完成实际的工作。通常,worker 谓词会携带临时的“累加器”变量来简化工作。您的公共谓词可能如下所示:
minmax([X|Xs],Min,Max) :- minmax_scan( Xs , X , X , Min , Max ).
在这里,您的公共 API 谓词接受一个非空列表,将工作谓词使用的最小/最大累加器播种到列表的头部,然后使用列表的尾部调用工作谓词。
您的工作者谓词可能如下所示:
% if the list is empty, we've solved the puzzle, right?
minmax_scan( [] , Min , Max , Min , Max ) .
% if the list is non-empty, we need to compare its head to
% the current value for min/max to determine the new values for min/max
% (which might be the same), and then recurse down on the tail of the list
minmax_scan( [X|Xs] , CurrMin , CurrMax , Min , Max ) :-
min( X , CurrMin , NextMin ) ,
max( X , CurrMax , NextMax ) ,
minmax_scan( Xs , NextMin , NextMax , Min , Max )
.
简单的!
从列表中取出第一个值,然后检查列表中的每个其他元素,选择较低/较高的值作为临时最小值/最大值。
当在列表末尾时,您同时拥有...
minmax([First|Rest], Min, Max) :-
minmax(Rest, First, First, Min, Max).
minmax([], Min, Max, Min, Max).
minmax([Value|Ns], MinCurr, MaxCurr, Min, Max) :-
....
minmax(Ns, MinNext, MaxNext, Min, Max).
我会让你在递归调用之前编写测试(即填充点!)
编辑只是指出库(聚合),在几个 Prolog 系统中可用:
1 ?- [user].
minmax(L, X, Y) :- aggregate( (min(E), max(E)), member(E, L), (X, Y) ).
|:
true.
2 ?- minmax([1, -10, 1, 0, 7, 7], X, Y).
X = -10,
Y = 7.
这是一些令人费解且有点复杂的事情。
is_minmax(A,B-D,C-E) :-
D is min(...),
E is max(...) .
pair(A,B,A-B).
minmax(L,MIN,MAX) :-
L=[A|_], length(L,N), N2 is N-1,
length(L2,N2), append(L2,[MIN],L22),
length(L3,N2), append(L3,[MAX],L33),
maplist(pair, [A|L2], L22, KL2),
maplist(pair, [A|L3], L33, KL3),
maplist(is_minmax, L, KL2, KL3).
(在 SWI Prolog 中工作)。试着找出用什么来代替点...
。