Prolog: predicate for maximum without accumulator

后端 未结 2 483
死守一世寂寞
死守一世寂寞 2021-01-18 06:17

Is it possible to create a predicate max/2 without an accumulator so that max(List, Max) is true if and only if Max is the ma

相关标签:
2条回答
  • 2021-01-18 06:23

    Neither the standard predicate is/2 nor a CLP(FD) predicate (#=)/2 can do mathematics here. So that in the end, for certain applications such as exact geometry, they might not be suitable.

    To make a point lets consider an example and the alternative of a computer algebra system (CAS). I am doing the demonstration with the new Jekejeke Minlog 0.9.2 prototype which provides CAS from within Prolog.

    As a preliminary we have two predicates eval_keys/2 and min_key/2, their code is found at the appendix in this post. Lets illustrate what this predicates do, first with integer. The first predicate just makes the keys of a pair list evaluated:

    Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.25-3-gc3a87c2)
    Copyright (c) 1990-2016 University of Amsterdam, VU Amsterdam
    
    ?- eval_keys([(1+3)-foo,2-bar],L).
    L = [4-foo,2-bar]
    

    The second predicate picks this first value where the key is minimum:

    ?- min_key([4-foo,2-bar],X).
    X = bar
    

    Now lets look at other key values, we will use square roots, which belong to the domain of algebraic numbers. Algebraic numbers are irrational and thus never fit into a float. We therefore get for the new example the outcome foo:

    ?- eval_keys([(sqrt(98428513)+sqrt(101596577))-foo,sqrt(400025090)-bar],L).
    L = [20000.62724016424-foo, 20000.627240164245-bar].
    
    ?- min_key([20000.62724016424-foo, 20000.627240164245-bar], X).
    X = foo.
    

    CLP(FD) has only integers and there is no direct way to represent algebraic numbers. On the other hand many CAS systems support radicals. Our Prototype even supports comparison of them so that we can obtain the exact result bar:

    Jekejeke Prolog 2, Runtime Library 1.2.2
    (c) 1985-2017, XLOG Technologies GmbH, Switzerland
    
    ?- eval_keys([(sqrt(98428513)+sqrt(101596577))-foo,sqrt(400025090)-bar],L).
    L = [radical(0,[98428513-1,101596577-1])-foo,
       radical(0,[400025090-1])-bar]
    
    ?- min_key([radical(0,[98428513-1,101596577-1])-foo,
       radical(0,[400025090-1])-bar],X).
    X = bar
    

    That bar is the exact result can be seen for example by using a multi-precision calculator. If we double the precision we indeed see that the last square root is the smaler one and not the sum of the square roots:

    ?- use_module(library(decimal/multi)).
    % 7 consults and 0 unloads in 319 ms.
    Yes
    
    ?- X is mp(sqrt(98428513)+sqrt(101596577), 32).
    X = 0d20000.627240164244658958331341095
    
    ?- X is mp(sqrt(400025090), 32).
    X = 0d20000.627240164244408966171597261
    

    But a CAS need not to proceed this way. For example our Prolog implementation uses a Swinnerton-Dyer polynomial inspired method to compare radical expressions, which works purely symbolic.

    Appendix Test Code:

    % :- use_module(library(groebner/generic)). /* to enable CAS */
    
    eval_keys([X-A|L], [Y-A|R]) :- Y is X, eval_keys(L, R).
    eval_keys([], []).
    
    min_key([X-A|L], B) :- min_key(L, X, A, B).
    
    min_key([X-A|L], Y, _, B) :- X < Y, !, min_key(L, X, A, B).
    min_key([_|L], X, A, B) :- min_key(L, X, A, B).
    min_key([], _, A, A).
    
    0 讨论(0)
  • 2021-01-18 06:39

    Yes, you can calculate the maximum after the recursive step. Like:

    max([M],M).          % the maximum of a list with one element is that element.
    max([H|T],M) :-
        max(T,M1),       % first calculate the maximum of the tail.
        M is max(H,M1).  % then calculate the real maximum as the max of
                         % head an the maximum of the tail.
    

    This predicate will work on floating points for instance. Nevertheless it is better to use an accumulator since most Prolog interpreters use tail call optimization (TCO) and predicates with accumulators tend to work with tail calls. As a result predicates with TCO will usually not get a stack overflow exception if you want to process huge lists.

    As @Lurker says, is only works in case that the list is fully grounded: it is a finite list and all elements grounded. You can however use Prolog's constraint logic programming package clp(fd):

    :- use_module(library(clpfd)).
    
    max([M],M).          % the maximum of a list with one element is that element.
    max([H|T],M) :-
        max(T,M1),       % first calculate the maximum of the tail.
        M #= max(H,M1).  % then calculate the real maximum as the max of
                         % head an the maximum of the tail.
    

    You can then for instance call:

    ?- max([A,B,C],M),A=2,B=3,C=1.
    A = 2,
    B = M, M = 3,
    C = 1 ;
    false.
    

    So after the max/2 call, by grounding A, B and C, we obtain M=3.

    0 讨论(0)
提交回复
热议问题