Prolog:没有累加器的谓词最大值

时间:2017-05-11 12:10:58

标签: list prolog integer-arithmetic

是否可以在没有累加器的情况下创建谓词max/2 ,以便max(List, Max)为真,当且仅当MaxList的最大值时1}}(整数列表)?

2 个答案:

答案 0 :(得分:4)

是的,您可以在递归步骤之后计算最大。像:

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.

例如,这个谓词将适用于浮点数。然而,使用累加器是更好,因为大多数Prolog解释器使用尾调用优化(TCO),而带累加器的谓词倾向于使用尾调用。因此,如果要处理大量列表,使用TCO的谓词通常不会出现堆栈溢出异常。

作为@Lurker saysis仅在列表完全接地的情况下有效:它是有限列表,所有元素都接地。但是,您可以使用Prolog的约束逻辑编程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.

然后您可以调用:

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

在<{em> max/2来电之后ABC,我们获得M=3

答案 1 :(得分:1)

标准谓词既不是/ 2也不是CLP(FD)谓词(#=)/ 2都可以在这里做数学。因此,最终,对于某些应用,例如精确几何,它们可能不合适。

为了说明一点,我们可以考虑一个例子和计算机代数系统(CAS)的替代方案。我正在使用新的Jekejeke Minlog 0.9.2原型进行演示,该原型从Prolog中提供CAS。

作为初步,我们有两个谓词eval_keys / 2和min_key / 2,它们的代码可以在本文的附录中找到。让我们说明这个谓词的作用,首先是整数。第一个谓词只是评估了一对列表的键:

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]

第二个谓词选择密钥最小的第一个值:

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

现在让我们看一下其他键值,我们将使用平方根,它们属于代数数域。代数数字是不合理的,因此永远不适合浮动。因此,我们得到新的例子结果 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)只有整数,没有直接的方法来表示代数数字。另一方面,许多CAS系统支持激进分子。我们的Prototype甚至支持对它们进行比较,以便我们可以获得确切的结果 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

该条形图可以通过使用多精度计算器看到确切的结果。如果我们将精度加倍,我们确实看到最后一个平方根是较小的根而不是平方根的总和:

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

但CAS不需要这样做。例如,我们的Prolog implementation使用Swinnerton-Dyer多项式启发方法来比较激进的表达式,这些表达式纯粹是符号化的。

附录测试代码:

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