假设我有一个谓词foo/2
,它定义了它的第一个和第二个参数之间的关系。
什么是最惯用和最有效的方法来改变这样的实现foo/2
:
如果它的两个论点都成立,它就像以前一样(如果关系成立则成功,否则失败)。
如果两个参数之一(或两者)是自由的,它会“约束”这两个参数,以便当它们接地时,将检查关系。
换句话说,如何正确实现dif/2
任何类型的用户定义关系所表现出的行为?
listing(dif/2).
帮助不大。
假设我有一个谓词foo/2
,它定义了它的第一个和第二个参数之间的关系。
什么是最惯用和最有效的方法来改变这样的实现foo/2
:
如果它的两个论点都成立,它就像以前一样(如果关系成立则成功,否则失败)。
如果两个参数之一(或两者)是自由的,它会“约束”这两个参数,以便当它们接地时,将检查关系。
换句话说,如何正确实现dif/2
任何类型的用户定义关系所表现出的行为?
listing(dif/2).
帮助不大。
不同的 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 = 乙?; 不
首先,最重要的是,
...而不是实施者。这常常被忽略——在现有的约束实现中也是如此。它显示了。因此,这是需要考虑的最突出的方面。
显然这应该成立。产生干净的错误总是更好,主要是实例化错误,最好永远挣扎,甚至永远循环而不是错误地失败。如果所有其他方法都失败了,您可以使用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/2
或copy_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).