3

我正在上编程范式课程。目前我们正在学习 Prolog,而我陷入了不同的范式。我试图从命令式转换的函数之一是一个相对简单的函数。

foo(A,B,C)
    if(A > B) C is 1
    if(A < B) C is -1
    if(A = B) C is 0

我可以很容易地在 Prolog 中做到这一点。

foo(A,B,C) :- sub(A-B,C).

sub(E, C) :- E = 0, C is 0.
sub(E, C) :- E > 0, C is 1.
sub(E, C) :- E < 0, C is -1.

问题是,我只能在整个谓词中使用一个“是”(不能定义一个较小的谓词来调用 Is 并调用它来代替或任何东西),并且我不能使用 Prolog 的 if/else 构造。我无法弄清楚如何以声明的方式考虑这个问题。

我想也许我可以做一些类似 C is (AB)/abs(AB) 的事情,但这会在 A=B 上中断,并且需要 2 个“is”语句。我只是卡住了。

4

5 回答 5

1

为了以声明方式进行编程,我们必须考虑我们要声明的内容,然后才能描述它。为此,我将尝试编写谓词,以便它表达为什么 的值C根据 和 的相对大小在三种可能的状态之间A交替B。这是一个与您描述的形式相同的谓词foo

comparison(A, B, less)     :-  A < B.
comparison(A, B, equal)    :-  A =:= B. %% =:=/2 is "True if expression A evaluates to a number equal to B".
comparison(A, B, greater)  :-  A > B.

您不需要前端谓词foo/3。您可能会这样阅读这些行:“The comparisonof Aand Bis <Value>if Ais <relation>about to B”。该程序由描述规则的三个规则组成comparison/3。“3”表示该规则定义了作为参数提供给它的 3 个元素之间的关系。

你可以查询这个程序:

?- comparison(1,3, X).
X = less ;
false.
于 2014-03-08T07:37:09.003 回答
0

你可以有多个规则吗?

sub(0,0).
sub(E,C) :- C is E/abs(E).

A-B如果您传入 0 ,这将始终与第一条规则匹配,C然后将与 0 统一。从技术上讲,您只需要一个is,并且您正在使用基本案例来定义它。为了使其独立于规则顺序,您也可以通过在子句E \= 0中,但我不知道这是否会验证这一点is,因为\=(不统一)本质上是同一件事(没有双关语),但有任何基本术语不仅仅是int。

于 2014-03-07T20:46:12.080 回答
0

也许我误解了,但你为什么不能这样做:

foo(A, B, 1) :- A > B.
foo(A, B, -1) :- A < B.
foo(A, B, 0) :- A =:= B.

即直接将'C'的值作为输出参数嵌入。

?- foo(1,3,C).
C = -1 .

?- foo(2,2,C).
C = 0.

如果您已经拥有不需要计算的 C 的值,则无需将其声明为变量?

于 2014-03-08T08:17:21.847 回答
0

出于效率原因,Prolog 中的算术以“传统的”命令式方式处理。但是当我们可以限制计算领域时,我们可以使用一些库来提高语言的水平,并重新获得一些失去的声明性......

例如,在 SWI-Prolog 中,library( clpfd ) - 仅限于整数域 - 将允许写入

:- [library(clpfd)].

foo(A,B,C) :-
    A #> B #<==> C #= 1,
    A #< B #<==> C #= -1,
    A #= B #<==> C #= 0.

这是A,B,C之间相当普遍的关系。例如

1 ?- foo(1,2,C).
C = -1.

2 ?- foo(2,1,C).
C = 1.

3 ?- foo(2,X,1).
X in inf..1,
X+1#=_G219,
_G219 in inf..2.

4 ?- foo(X,Y,1).
X#\=Y,
X+1#=_G519,
X#>=_G531,
Y#=<_G519+ -1,
Y+1#=_G531.

查询 3 和 4 显示了库提供的通用性(我读inf为 -infinity),显示了从实例化模式推导出的剩余约束——当然还有我们自己的方程。

无论如何,我会以这种方式将您的代码翻译成普通的 Prolog

foo(A,B,C) :-
    A > B, C is 1 ;
    A < B, C is -1 ;
    A =:= B, C is 0.

这需要将 A,B 实例化为数值表达式

注意我保持不变的是/2。这样,如果 C 被实例化为算术表达式,它将按预期工作......

于 2014-03-08T11:33:58.497 回答
0

你为什么不写一个明显的单行字?

foo(A,B,C) :- C is truncate(sign(A-B)).

它们都是 ISO 序言的一部分:

  • sign/1
    如果 Expr < 0 则将 [s] 计算为 -1,如果 Expr > 0 则为 1,如果 Expr = 0 则为 0。如果 Expr 计算为浮点数,则返回值为浮点数(例如,-1.0、0.0 或 1.0)。特别要注意,sign(-0.0) 的计算结果为 0.0。

  • truncate/1
    将 [s] Expr 截断为整数。如果 Expr >= 0 这与 floor(Expr) 相同。对于 Expr < 0,这与 ceil(Expr) 相同。也就是说, truncate/1 向零舍入。

这要求A两者B都绑定到数值(浮点或整数,或混合)。

如果你想在任何类型的序言词中概括这一点,那么就像这样:

foo(A,B,  1 ) :- A @> B .
foo(A,B,  0 ) :- A == B .
foo(A,B, -1 ) :- A @< B .

您会发现使用这些运算符有时会产生...奇怪的结果(例如,您会发现-1.0 @< 1is true。如果您想在有数字时强制执行更“正常”的语义,那么您会想要当两个术语都是数字时劫持案例:

foo(A,B,  C ) :- number(A), number(B), ! , C is truncate(sign(A-B)) .
foo(A,B,  1 ) :- A @> B .
foo(A,B,  0 ) :- A == B .
foo(A,B, -1 ) :- A @< B .
于 2014-03-10T23:57:18.060 回答