5

假设我有一个谓词foo/2,它定义了它的第一个和第二个参数之间的关系。

什么是最惯用和最有效的方法来改变这样的实现foo/2

  • 如果它的两个论点都成立,它就像以前一样(如果关系成立则成功,否则失败)。

  • 如果两个参数之一(或两者)是自由的,它会“约束”这两个参数,以便当它们接地时,将检查关系。

换句话说,如何正确实现dif/2任何类型的用户定义关系所表现出的行为?

listing(dif/2).帮助不大。

4

2 回答 2

3

不同的 Prolog 实现提供了不同的功能来实现这一点。该机制被称为coroutineing延迟目标约束,您的 Prolog 系统手册将提供更多信息。

这里有两种变体,可在SICStus Prolog和其他一些系统中使用。

block/1指示

在 SICStus Prolog(可能还有其他一些系统)中,一种将用户定义的谓词提升到这种受约束版本的方法是通过声明block性声明可用的。

有趣的是,这不需要对谓词本身进行任何更改!

假设你有一个不纯的版本dif/2,使用非单调(\=)/2谓词:

麦迪夫(X,Y):-
    X \= Y。

然后您可以将其转换为延迟版本,例如:

:- 阻止 madif(-, ?),
         麦迪夫(?,-)。

麦迪夫(X,Y):-
    X \= Y。

示例查询和答案:

| ?- madif(a, b)。
是的
| ?- madif(a, X)。
用户:madif(a,X)?;
不
| ?- madif(a, X), X = b。
X = 乙?;
不
| ?- madif(X, Y)。
用户:马迪夫(X,Y)?;

根据需要,目标的评估被延迟,直到两个参数都被实例化。

when/2

使用 SICStus Prolog(和其他提供此功能的系统)实现此目的的第二种方法是使用when/2. 这需要更改谓词本身。

例如,使用when/2,您可以madif/2这样实现:

麦迪夫(X,Y):-
    当((地面(X),
          地面(Y)),X \= Y)。

示例查询和答案:

| ?- madif(X, a)。
序言:trig_ground(X,[],[X],_A,_A),
序言:何时(_A,(地面(X),地面(a)),用户:(X\=a))?;
不
| ?- madif(X, a), X = b。
X = 乙?;
于 2017-02-19T09:24:57.917 回答
3

首先,最重要的是,

站在用户的角度

...而不是实施者。这常常被忽略——在现有的约束实现中也是如此。它显示了。因此,这是需要考虑的最突出的方面。

正确性

显然这应该成立。产生干净的错误总是更好,主要是实例化错误,最好永远挣扎,甚至永远循环而不是错误地失败。如果所有其他方法都失败了,您可以使用freeze(_, G_0). 请注意,您确实需要一个有效的顶层才能真正看到这些挣扎的目标。SICStus 有这样一个顶层1,在 SWI 中,您需要包装查询call_residue_vars(Query_0, Vs)以查看所有附加的约束。

一致性

接下来,您要确保您的约束尽可能地确保一致性。有许多一致性的概念,例如域一致性和边界一致性。要考虑您的精确要求difgrn/2并将其与内置的进行比较dif/2

difgrn(X, Y) :-
   when((ground(X), ground(Y)), X \== Y).

| ?- difgrn(X, X).
prolog:trig_ground(X,[],[X],_A,_B),
prolog:trig_ground(X,[],[X],_A,_C),
prolog:trig_and(_C,[],_A,_B,_A),
prolog:when(_A,(ground(X),ground(X)),user:(X\==X)) ? ;
no
| ?- dif(X, X).
no


| ?- difgrn([], [_]).
prolog:trig_ground(_A,[],[_A],_B,_C),
prolog:trig_and(_C,[],_B,1,_B),
prolog:when(_B,(ground([]),ground([_A])),user:([]\==[_A]))

| ?- dif([], [_]).
yes

全力实施dif/2的一种方法是使用非常特殊的条件(?=)/2

difwh(X,Y) :- when(?=(X,Y), X\==Y).

这应该尽可能地回答你的问题:

换句话说,如何正确实现 dif/2 表现出的行为,但具有任何类型的用户定义关系?

但不幸的是,这并没有延伸到其他任何东西。

如果考虑各种约束之间的一致性,情况会变得更加复杂。想想X in 1..2, dif(X, 1), dif(X, 2)

答案预测

(因为没有更好的词。)有时您希望在顶层很好地看到您的约束 - 最好的方法是将它们表示为目标,这些目标本身将重新建立表示答案所需的确切状态。见上面的trig_ground答案,当然可以美化一点。

可变预测

与答案预测相同,但可以在任何时间点通过frozen/2copy_term/3

包含检查

这对于诊断目的和循环检查很有用。对于纯粹的句法术语,有subsumes_term/2哪些忽略约束。执行有效测试的先决条件是将每个涉及的变量与实际约束联系起来。考虑目标freeze(X, Y = a)并想象一些包含检查Y作为参数。如果Y不再附加到信息中(通常与当前的实现一样freeze/2),您将得出错误的结论,即它Y包含b.

请注意,对于 的实际示例dif/2,这是有史以来的第一个约束(1972,Prolog 0)。Michel van Caneghem 在 L'anatomie de Prolog、InterÉditions 1986 和 Lee Naish 在关于 MU-Prolog 的论文中给出了更详细的描述。


1 半真半假。因为library(clpfd)你需要assert(clpfd:full_answer).

于 2017-02-20T19:24:08.257 回答