与 STO 检测统一
Posted
技术标签:
【中文标题】与 STO 检测统一【英文标题】:Unification with STO detection 【发布时间】:2015-08-12 21:28:19 【问题描述】:在 ISO Prolog 中,统一仅针对 NSTO(不受发生检查)的情况定义。背后的想法是涵盖那些主要用于程序中并且实际上被所有 Prolog 系统支持的统一案例。更具体地说,ISO/IEC 13211-1:1995 内容如下:
7.3.3 接受发生检查 (STO) 而不是接受发生检查 (NSTO)
一组方程(或两项)是“服从于发生- 检查”(STO)如果有办法通过 Herbrand 算法的步骤使得 7.3.2 g 发生。
一组方程(或两项)“不受制于 发生检查”(NSTO)如果没有办法继续 通过 Herbrand 算法的步骤,使得 发生 7.3.2 g。
...
这一步 7.3.2 g 内容如下:
g) 如果存在 X = t 这样的方程X 是一个变量,而 t 是一个非变量项 包含这个变量,然后退出失败(not 统一,阳性发生-检查)。
完整的算法称为 Herbrand 算法,也就是通常所说的 Martelli-Montanari unification algorithm - 它本质上是通过以非确定性方式重写方程组来进行的。
请注意,新方程是通过以下方式引入的:
d) 如果存在 f(a1,a2, ...aN 形式的方程) = f(b1,b2, ...bN) 然后用方程组替换它ai = bi.
这意味着具有相同函子但元数不同的两个复合术语永远不会有助于 STO 性。
这种不确定性是 STO 测试难以实施的原因。毕竟,仅测试是否需要发生检查是不够的,但要证明对于所有可能的算法执行方式,这种情况永远不会发生。
这里有一个案例来说明情况:
?- A/B+C*D = 1/2+3*4.
统一可以从A = 1
开始,也可以从其他任何对开始,并以任何顺序继续。为确保 NSTO 属性,必须确保没有可能产生 STO 情况的路径。考虑一个对于当前实现来说没有问题但仍然是 STO 的案例:
?- 1+A = 2+s(A).
Prolog 系统首先将这个方程改写为:
?- 1 = 2, A = s(A).
现在,他们选择任何一个
1 = 2
导致算法失败退出,或者
A = s(A)
应用步骤 g 并检测到 STO-ness。
我的问题是双重的。首先,它是关于unify_sto(X,Y)
(仅使用第 1 部分的defined built-ins)在 ISO Prolog 中的实现,以下内容适用:
如果统一是STO,那么unify_sto(X,Y)
会报错,否则
如果unify_sto(X,Y)
成功,那么X = Y
也成功
如果unify_sto(X,Y)
失败,那么X = Y
也会失败
我的第二个问题是关于在这种情况下发出的具体错误。请参阅 ISO 的 error classes。
这是一个简单的开始步骤:所有成功案例都包含在unify_with_occurs_check(X,Y)
的成功中。剩下要做的是区分 NSTO 故障和 STO 错误情况。那是事情开始变得困难......
【问题讨论】:
我遗漏了一些东西:如果 A=B 是没有检查的统一,而 unify_with_occurs_check 是有检查的统一,则 NSTO 的情况不是:unify_sto(A,B) := A=B, \+unify_with_occurs_check (A,B), raise_erro ? unify_sto(,) :- raise ?A = B
对于A = s(A)
之类的情况未定义,而unify_with_occurs_check(A, s(A))
定义明确:它失败了。我不清楚你对unify_sto/2
的定义是什么。太多的未定义。
【参考方案1】:
第三次尝试。这主要是先前答案中的错误修复(已经有很多修改)。 编辑:06/04/2015
当创建一个更通用的术语时,如果它们中的任何一个是变量,我都会将两个子术语保持原样。现在我通过调用term_general/2
为这种情况下的“其他”子项建立一个更通用的术语。
unify_sto(X,Y):-
unify_with_occurs_check(X,Y) -> true ;
(
term_general(X, Y, unify(X,Y), XG, YG),
\+unify_with_occurs_check(XG,YG),
throw(error(type_error(acyclic, unify(X,Y)),_))
).
term_general(X, Y, UnifyTerm, XG, YG):-
(var(X) -> (XG=X, term_general(Y, YG)) ;
(var(Y) -> (YG=Y, term_general(X, XG)) ;
((
functor(X, Functor, Len),
functor(Y, Functor, Len),
X=..[_|XL],
Y=..[_|YL],
term_general1(XL, YL, UnifyTerm, NXL, NYL)
) ->
(
XG=..[Functor|NXL],
YG=..[Functor|NYL]
) ;
( XG=_, YG=_ )
))).
term_general1([X|XTail], [Y|YTail], UnifyTerm, [XG|XGTail], [YG|YGTail]):-
term_general(X, Y, UnifyTerm, XG, YG),
(
\+(unify_with_occurs_check(XG,YG)) ->
throw(error(type_error(acyclic,UnifyTerm),_)) ;
term_general1(XTail, YTail, UnifyTerm, XGTail, YGTail)
).
term_general1([], [], _, [], []).
term_general(X, XG):-
(var(X) -> XG=X ;
(atomic(X) -> XG=_ ;
(
X=..[_|XL],
term_general1(XL, XG)
))).
term_general1([X|XTail], [XG|XGTail]):-
term_general(X, XG),
term_general1(XTail, XGTail).
term_general1([], _).
到目前为止,这个问题中提到的单元测试在这里:
unit_tests:-
member([TermA,TermB], [[_A+_B,_C+_D], [_E+_F, 1+2],
[a(_G+1),a(1+_H)], [a(1), b(_I)],
[A+A,a(B)+b(B)], [A+A,a(B,1)+b(B)]]),
(unify_sto(TermA, TermB)->Unifies=unifies ; Unifies=does_not_unify),
writeln(test(TermA, TermB, Unifies)),
fail.
unit_tests:-
member([TermA,TermB], [[A+A,B+a(B)], [A+A,A+b(A)],
[A+A,a(_)+b(A)], [1+A,2+s(A)],
[a(1)+X,b(1)+s(X)]]),
catch(
(
(unify_sto(TermA, TermB)->true;true),
writeln(test_failed(TermA, TermB))
), E, writeln(test_ok(E))),
fail.
unit_tests.
【讨论】:
unify_sto(A+A, a(B)+b(B)).
表示错误,但应该失败
我修复了这个案例的代码,要么 subterm 是一个变量。在这种情况下,我将两个子项保持原样,而不是为另一个(可能是非变量)子项建立一个更通用的术语。
手指交叉:看来你明白了!【参考方案2】:
再来一次尝试:
unify_sto(X,Y):-
unify_with_occurs_check(X,Y) -> true ;
(
term_general(X, Y, XG, YG),
\+(unify_sto1(XG,YG)),
throw(error(type_error(acyclic,unify(X,Y)),_))
).
unify_sto1(X, Y):-
unify_with_occurs_check(X,Y).
unify_sto1(X, Y):-
X\=Y.
term_general(X, Y, XG, YG):-
((var(X) ; var(Y)) -> (XG=X, YG=Y) ;
((
functor(X, Functor, Len),
functor(Y, Functor, Len),
X=..[_|XL],
Y=..[_|YL],
term_general1(XL, YL, NXL, NYL)
) ->
(
XG=..[Functor|NXL],
YG=..[Functor|NYL]
) ;
( XG=_, YG=_ )
)).
term_general1([X|XTail], [Y|YTail], [XG|XGTail], [YG|YGTail]):-
term_general(X, Y, XG, YG),
term_general1(XTail, YTail, XGTail, YGTail).
term_general1([], [], [], []).
它首先尝试 unify_with_occurs_check,如果不成功,则继续构建两个更通用的术语,遍历每个术语的结构。
如果任一术语是变量,则两个术语都保持原样。 如果这两个术语是同一个原子,或者它们都是具有相同原子的复合术语 functor 和 arity [*],它遍历它的参数,使更多 他们的总称。 否则,它会为每个术语分配一个新的变量。然后它再次尝试 unify_with_occurs_check 更通用的术语 测试非循环统一并相应地抛出错误。
[*] 对复合术语中的 arity 的测试是贪婪地完成的,因为 。 (已编辑:在调用 term_general1 之前添加了两个 term_general1/4
将失败递归,因为 OP 声明只使用在此 @987654321 的第 1 部分中定义的内置谓词@with 不包括 length/2
.functor/3
调用来测试函子 和 arity,以便在它们的 arity 不匹配时不尝试检查内部术语)
例如:
?- unify_sto(s(1)+A,A+s(B)).
A = s(1),
B = 1
?- unify_sto(1+A,2+s(A)).
ERROR: Type error: `acyclic' expected, found `unify(1+_G5322,2+s(_G5322))'
?- unify_sto(a(1)+X,b(1)+s(X)).
ERROR: Type error: `acyclic' expected, found `unify(a(1)+_G7068,b(1)+s(_G7068))'
编辑 06/02/2015:
上述解决方案对查询失败:
unify_sto(A+A,a(A)+b(A)).
是否不会产生统一错误。
以下是对成对处理每个子项并在发现错误时立即产生错误的算法的改进:
unify_sto(X,Y):-
unify_with_occurs_check(X,Y) -> true ;
(
term_general(X, Y, unify(X,Y), XG, YG),
\+unify_with_occurs_check(XG,YG),
throw(error(type_error(acyclic,unify(X,Y)),_))
).
unify_sto1(X, Y):-
unify_with_occurs_check(X,Y).
unify_sto1(X, Y):-
X\=Y.
term_general(X, Y, UnifyTerm, XG, YG):-
((var(X) ; var(Y)) -> (XG=X, YG=Y) ;
((
functor(X, Functor, Len),
functor(Y, Functor, Len),
X=..[Functor|XL],
Y=..[Functor|YL],
term_general1(XL, YL, UnifyTerm, NXL, NYL)
) ->
(
XG=..[Functor|NXL],
YG=..[Functor|NYL]
) ;
( XG=_, YG=_ )
)).
term_general1([X|XTail], [Y|YTail], UnifyTerm, [XG|XGTail], [YG|YGTail]):-
term_general(X, Y, UnifyTerm, XG, YG),
\+(unify_with_occurs_check(XG,YG))-> throw(error(type_error(acyclic,UnifyTerm),_)) ;
term_general1(XTail, YTail, UnifyTerm, XGTail, YGTail).
term_general1([], [], _, [], []).
在原始答案中产生错误结果的查询的测试用例:
?- unify_sto(A+A,a(A)+b(A)).
ERROR: Type error: `acyclic' expected, found `unify(_G6902+_G6902,a(_G6902)+b(_G6902))'
?- unify_sto(A+A, a(_)+b(A)).
ERROR: Type error: `acyclic' expected, found `unify(_G5167+_G5167,a(_G5173)+b(_G5167))'
【讨论】:
我没有得到你的反例,你能用一个查询来举例说明吗?正如我在答案中所说,我的代码隐含地采用了arities(我在解释为什么不使用length/2
来获取arities 时写了“贪婪”的东西)。当 term_general1/4
对具有不同参数的术语失败时,会考虑复合术语的参数。
@false:我只是修改了一点答案,以便使用functor/3
在一个术语中“go”之前测试函子 和 arity。跨度>
反例:unify_sto(A+A,a(A)+b(A)).
显然是 STO,但默默失败
现在应该修复了。这是 term_general 的“基本情况”。我删除了最后一个unify_with_occurs_check/2
,当其中一个术语不是复合词时显然需要它。
新的反例unify_sto(A+A, a(_)+b(A)).
失败但应该是STO【参考方案3】:
这是我的尝试:
unify_sto(X,Y):-
unify_with_occurs_check(X,Y) -> true ;
(
term_general(X, XG),
term_general(Y, YG),
\+(unify_sto1(XG,YG)),
throw(error(type_error(acyclic,unify(X,Y)),_))
).
unify_sto1(X, Y):-
unify_with_occurs_check(X,Y).
unify_sto1(X, Y):-
X\=Y.
term_general(X, Y):-
(var(X) -> Y=X ;
(atomic(X) -> Y=_ ;
(
X=..[Functor|L],
term_general1(L, NL),
Y=..[Functor|NL]
))).
term_general1([X|XTail], [Y|YTail]):-
term_general(X, Y),
term_general1(XTail, YTail).
term_general1([], []).
它首先尝试unify_with_occurs_check
,如果不成功,则继续为每个参数构建一个更通用的术语,然后尝试统一这样的术语并测试它是否是循环的。如果它是循环的,则抛出 acyclic 类型的 type_error
。
例如:
?- unify_sto(1+A,2+s(A)).
ERROR: Unhandled exception: error(type_error(acyclic,unify(1+_G3620,2+s(_G3620))))
【讨论】:
反例:unify_sto(a(1)+X,b(1)+s(X)).
是 STO,但上述定义静默失败。
s(X) 进行尝试。如果您重新尝试,请写下另一个答案。【参考方案4】:
这是我用来测试@gusbro 版本的版本。这个想法是从字面上使用 Martelli-Montanari。通过重写方程列表[X1=Y1,X2=Y2|Etc]
,立即应用某些重写规则 - 使用!为承诺。对于某些规则,我不太确定,所以我让它们像原始算法一样不确定。
注意rewrite_sto/1
将失败或产生错误。我们对成功案例不感兴趣,无需任何搜索即可处理。另外,请注意,可以消除导致(立即)失败的方程式!这有点不直观,但我们这里只对寻找 STO 案例感兴趣。
unify_with_sto_check(X,Y) :-
( \+ unify_with_occurs_check(X, Y)
-> rewrite_sto([X=Y]) % fails or error
; X = Y
).
rewrite_sto(Xs0) :-
select(X=Y, Xs0,Xs),
( X == Y
; nonvar(X), nonvar(Y),
functor(X,F,A),
\+ functor(Y,F,A)
; var(X), var(Y),
X = Y
),
!,
rewrite_sto(Xs).
rewrite_sto(Xs0) :-
select(X=Y, Xs0, Xs1),
nonvar(X), nonvar(Y),
functor(X,F,A),
functor(Y,F,A),
!,
X =.. [_|XArgs],
Y =.. [_|YArgs],
maplist(\Xi^Yi^(Xi=Yi)^true, XArgs, YArgs, XYs),
append(XYs,Xs1,Xs),
rewrite_sto(Xs).
rewrite_sto(Xs0) :-
select(X=Y, Xs0,Xs),
( var(X), nonvar(Y) -> unify_var_term(X, Y)
; nonvar(X), var(Y) -> unify_var_term(Y, X)
; throw(impossible)
),
rewrite_sto(Xs).
unify_var_term(V, Term) :-
( unify_with_occurs_check(V, Term) -> true
; throw(error(type_error(acyclic_term, Term), _))
).
【讨论】:
【参考方案5】:在 SWI 序言中:
unify_sto(X,Y) :-
\+ unify_with_occurs_check(X,Y),
X = Y,
!,
writeln('Error: NSTO failure'),
fail.
unify_sto(X,Y) :-
X = Y.
给出以下结果:
[debug] ?- unify_sto(X,s(X)).
Error: NSTO failure
false.
[debug] ?- unify_sto(X,a).
X = a.
[debug] ?- unify_sto(b,a).
false.
【讨论】:
对于给出的示例错误地失败:unify_sto(1+A,2+s(A))
这应该是一个错误。
请查看您正在使用 SWI 特定 bips 的 ISO 内置插件。
此外,没有使用错误 - 这使您的程序难以测试。以上是关于与 STO 检测统一的主要内容,如果未能解决你的问题,请参考以下文章