在 Prolog 中插入一个 cut 使关系子句绑定但双向
Inserting a cut in Prolog to make a relation clause bound but bidirectional
考虑以下 Prolog 程序:
transform([], []).
transform([X | Xs],[Y | Ys]):-
isSpecial(X),
isSpecial(Y),
transformSpecial(X,Y),
transform(Xs,Ys).
transform([X | Xs],[ X | Ys]):-
transform(Xs,Ys).
isSpecial(foo).
isSpecial(bar).
isSpecial(foobar).
transformSpecial(X,Y):-
isSpecial(X),
isSpecial(Y),
not(X = Y).
有些情况我会调用tranform([foo, 1, 2], X)
,有些情况我想调用transform(X, [foo, 1, 2])
。
在这两种情况下,我都希望 X 与 [bar, 1, 2]
和 [foobar, 1, 2]
统一,但不与 [foo, 1, 2]
统一。也就是说,我希望一旦谓词正确识别出第二个子句适用,它就应该坚持只使用第二个子句回溯。
我应该在哪里插入剪切才能实现此行为?
您的程序目前过于笼统。毕竟,您的查询有三种解决方案,但只针对第一种。最后一个错了
?- transform([foo, 1, 2], Xs).
Xs = [bar, 1, 2]
; Xs = [foobar, 1, 2]
; Xs = [foo, 1, 2]. % wrong
现在的削减可能会减少解决方案的数量。但大多数时候这是非常错误的。你应该 "insert the cut to achieve this behavior?" 的问题只有答案:没有地方。您需要做更多的工作才能实现这一目标。
本质上,您所描述的是逐元素转换。所以也许我们坚持一次描述一个元素。
el_transformed(X, Y) :-
isSpecial(X),
isSpecial(Y),
dif(X,Y).
el_transformed(X, X). % too general!
像以前一样使用maplist(el_transformed, [foo, 1, 2], Xs)
...
请注意,此版本与您的原始代码一样错误。但是现在,添加额外条件很简单:
el_transformed(X, Y) :-
isSpecial(X),
isSpecial(Y),
dif(X,Y).
el_transformed(X, X) :-
dif(X, foo),
dif(X, bar),
dif(X, foobar).
有一个很大的缺点:这个版本在某些情况下不是很确定:
?- el_transformed(foobar, bar).
true
; false.
如果您想从中获得更多,请考虑使用 library(reif)
两者都可用
为了
SICStus 和
SWI.
el_transformed(X, Y) :-
if_(( ( X = foo ; X = bar ; X = foobar ),
( Y = foo ; Y = bar ; Y = foobar ) ),
dif(X,Y),
X = Y).
有了这个公式,我们就不需要重复了。要检查代码是否正确,让我们尝试最一般的查询:
?- el_transformed(X, Y).
X = foo, Y = bar
; X = foo, Y = foobar
; X = bar, Y = foo
; X = bar, Y = foobar
; X = foobar, Y = foo
; X = foobar, Y = bar
; X = Y, dif(Y,foobar), dif(Y,bar), dif(Y,foo).
您正在查看所有可能发生的情况!没有其他情况需要考虑。
所以作为一个经验法则:每当你想要一个应该工作的谓词 "bidirectionally" 时,请考虑将其写成 "omnidirectional"!
也许您不喜欢明确提及 X = foo ; X = bar ; X = foobar
,在这种情况下,我们将不得不深入挖掘,将 isSpecial/1
具体化为 isSpecial_t/2
:
isSpecial_t(X, T) :-
call(
( X = foo
; X = bar
; X = foobar
), T).
el_transformed(X, Y) :-
if_( ( isSpecial_t(X), isSpecial_t(Y) ), dif(X, Y) , X = Y ).
这是另一种重用 isSpecial/1
的现有定义的方法。它最大的缺点是它做了很多不容易看到的假设。所以即使是一个小的扩展也可能使它无效。它使用 ("instantiated when").
el_transformed(X, Y) :-
iwhen(( nonvar(X) ; nonvar(Y) ), iel_transformed(X, Y) ).
iel_transformed(X, Y) :-
( nonvar(X)
-> ( isSpecial(X) -> isSpecial(Y), X \= Y ; X = Y )
; ( isSpecial(Y) -> isSpecial(X), X \= Y ; X = Y )
).
非常棘手 - 我什至在第一次尝试时犯了 a 两个错误......而且没有削减,切碎!
对于最一般的查询,我们现在会遇到实例化错误。不是很满意,但总比错误的结果好。
一般来说,在一般情况下,您必须寻找 constructive negation 的实现来处理这个...
考虑以下 Prolog 程序:
transform([], []).
transform([X | Xs],[Y | Ys]):-
isSpecial(X),
isSpecial(Y),
transformSpecial(X,Y),
transform(Xs,Ys).
transform([X | Xs],[ X | Ys]):-
transform(Xs,Ys).
isSpecial(foo).
isSpecial(bar).
isSpecial(foobar).
transformSpecial(X,Y):-
isSpecial(X),
isSpecial(Y),
not(X = Y).
有些情况我会调用tranform([foo, 1, 2], X)
,有些情况我想调用transform(X, [foo, 1, 2])
。
在这两种情况下,我都希望 X 与 [bar, 1, 2]
和 [foobar, 1, 2]
统一,但不与 [foo, 1, 2]
统一。也就是说,我希望一旦谓词正确识别出第二个子句适用,它就应该坚持只使用第二个子句回溯。
我应该在哪里插入剪切才能实现此行为?
您的程序目前过于笼统。毕竟,您的查询有三种解决方案,但只针对第一种。最后一个错了
?- transform([foo, 1, 2], Xs).
Xs = [bar, 1, 2]
; Xs = [foobar, 1, 2]
; Xs = [foo, 1, 2]. % wrong
现在的削减可能会减少解决方案的数量。但大多数时候这是非常错误的。你应该 "insert the cut to achieve this behavior?" 的问题只有答案:没有地方。您需要做更多的工作才能实现这一目标。
本质上,您所描述的是逐元素转换。所以也许我们坚持一次描述一个元素。
el_transformed(X, Y) :-
isSpecial(X),
isSpecial(Y),
dif(X,Y).
el_transformed(X, X). % too general!
像以前一样使用maplist(el_transformed, [foo, 1, 2], Xs)
...
请注意,此版本与您的原始代码一样错误。但是现在,添加额外条件很简单:
el_transformed(X, Y) :-
isSpecial(X),
isSpecial(Y),
dif(X,Y).
el_transformed(X, X) :-
dif(X, foo),
dif(X, bar),
dif(X, foobar).
有一个很大的缺点:这个版本在某些情况下不是很确定:
?- el_transformed(foobar, bar).
true
; false.
如果您想从中获得更多,请考虑使用 library(reif)
两者都可用
为了
SICStus 和
SWI.
el_transformed(X, Y) :-
if_(( ( X = foo ; X = bar ; X = foobar ),
( Y = foo ; Y = bar ; Y = foobar ) ),
dif(X,Y),
X = Y).
有了这个公式,我们就不需要重复了。要检查代码是否正确,让我们尝试最一般的查询:
?- el_transformed(X, Y).
X = foo, Y = bar
; X = foo, Y = foobar
; X = bar, Y = foo
; X = bar, Y = foobar
; X = foobar, Y = foo
; X = foobar, Y = bar
; X = Y, dif(Y,foobar), dif(Y,bar), dif(Y,foo).
您正在查看所有可能发生的情况!没有其他情况需要考虑。
所以作为一个经验法则:每当你想要一个应该工作的谓词 "bidirectionally" 时,请考虑将其写成 "omnidirectional"!
也许您不喜欢明确提及 X = foo ; X = bar ; X = foobar
,在这种情况下,我们将不得不深入挖掘,将 isSpecial/1
具体化为 isSpecial_t/2
:
isSpecial_t(X, T) :-
call(
( X = foo
; X = bar
; X = foobar
), T).
el_transformed(X, Y) :-
if_( ( isSpecial_t(X), isSpecial_t(Y) ), dif(X, Y) , X = Y ).
这是另一种重用 isSpecial/1
的现有定义的方法。它最大的缺点是它做了很多不容易看到的假设。所以即使是一个小的扩展也可能使它无效。它使用
el_transformed(X, Y) :-
iwhen(( nonvar(X) ; nonvar(Y) ), iel_transformed(X, Y) ).
iel_transformed(X, Y) :-
( nonvar(X)
-> ( isSpecial(X) -> isSpecial(Y), X \= Y ; X = Y )
; ( isSpecial(Y) -> isSpecial(X), X \= Y ; X = Y )
).
非常棘手 - 我什至在第一次尝试时犯了 a 两个错误......而且没有削减,切碎!
对于最一般的查询,我们现在会遇到实例化错误。不是很满意,但总比错误的结果好。
一般来说,在一般情况下,您必须寻找 constructive negation 的实现来处理这个...