【问题标题】:How to write a predicate minmax(L, X, Y)?如何编写谓词 minmax(L, X, Y)?
【发布时间】:2015-08-05 22:20:14
【问题描述】:

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

例子:

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

【问题讨论】:

    标签: prolog


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

      这导致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”谓词来执行实际工作。通常,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 )
        .
      

      简单!

      【讨论】:

        【解决方案3】:

        这里有些复杂而且有点复杂。

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

        【讨论】:

          【解决方案4】:

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

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

          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.
          

          【讨论】:

          • 使用library(aggregate)的版本让我印象深刻。但是,它在极端情况下表现出虚假行为:目标minmax([1+1],1+1,2) 成功,但目标minmax([1+1],2,2)minmax([1+1],1+1,1+1)minmax([1+1],2,1+1) 全部失败。
          • @repeat:我最初发布的答案对初学者最有启发性。扩展行为需要“评估”每个成员,并在需要的地方获取结果:, (member(M, L), E is M), 。但是 aggregate/3 它的目的是解决在普通 Prolog 中不容易解决的问题,并且其实现起来并不明显。图书馆相当复杂。
          • 好的。实际上,只有第一个列表项需要显式评估,其他列表项不需要,因为is 的每个结果都是一个数字……除此之外,如果重新评估复杂表达式,它还可以提高运行时间避免。
          • 有时我喜欢在“调用者”端使用算术表达式,但我仍然必须找到一种适当的方法来记录这些“经常隐藏”的特性。你有什么想法吗?
          • @repeat:不,抱歉。 Prolog 的“功能方面”在历史上相当薄弱。我尝试了自己,在我的“升降机”中重写了术语(也许是从旧的 SWI-Prolog 邮件列表中整理出来的 interesting post
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-04
          • 1970-01-01
          • 1970-01-01
          • 2013-06-11
          • 2015-02-25
          • 1970-01-01
          相关资源
          最近更新 更多