如何写一个谓词minmax(L,X,Y)?

时间:2012-10-03 11:44:28

标签: prolog

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

示例:

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

4 个答案:

答案 0 :(得分: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.

OK!那么支持非整数?

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

答案 1 :(得分:2)

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

  1. 您需要一种比较两个对象并确定哪个更低或更高的方法。
  2. 您需要一种迭代循环并跟踪您看到的最小值和最大值的方法。
  3. 这会导致min/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谓词接受非空列表,将worker谓词与列表头部一起使用的最小/最大累加器播种,然后使用列表尾部调用worker谓词。

    您的工作者谓词可能如下所示:

    % 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 )
      .
    

    容易!

答案 2 :(得分: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).

我会让你在递归调用之前编写测试(即填充点!)

编辑只是为了指出库(aggregate),可以在几个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.

答案 3 :(得分: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)。尝试找出代替点...的内容。