2

如何编写谓词 minmax(L, X, Y) 来找出整数 L 列表中 X 的最小值和 Y 的最大值。

例子:

?- minmax([1, -10, 1, 0, 7, 7], X, Y).
X = -10, Y = 7.
4

4 回答 4

3

让我们定义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.

如果您只关心处理整数,请使用

:- 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'

我们期望得到一个错误,我们得到一个错误......

请注意,感谢,我们也可以运行更一般的查询!

?- list_zmin_zmax([A,B], Min,Max).
A #>= Min, Max #>= A, Min #= min(A,B),
B #>= Min, Max #>= B, Max #= max(A,B).
于 2015-08-05T07:30:38.960 回答
2

如前所述,您需要遍历列表,不断累积最小值和最大值。因此,假设您必须从头开始编写此代码,您需要做的第一件事是将问题分解为简单的步骤:

  1. 您需要一种方法来比较两个对象并确定哪个更低或更高。
  2. 您需要一种迭代循环并跟踪所看到的最小值和最大值的方法。

这导致 amin/3max/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 )
  .

简单的!

于 2012-10-03T17:12:49.373 回答
1

从列表中取出第一个值,然后检查列表中的每个其他元素,选择较低/较高的值作为临时最小值/最大值。

当在列表末尾时,您同时拥有...

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.
于 2012-10-03T13:53:31.393 回答
1

这是一些令人费解且有点复杂的事情。

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 中工作)。试着找出用什么来代替点...

于 2012-10-03T16:54:27.927 回答