在Prolog中插入一个剪切,使关系子句绑定但双向



考虑以下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], unexpected.   % wrong

现在的削减可能会减少解决方案的数量。 但大多数时候,这非常错误。你的问题应该在哪里"插入切割以实现这种行为?"只有答案:没有地方。 您需要做更多的事情才能实现这一目标。

从本质上讲,您所描述的是一种元素转换。所以也许我们坚持一次描述一个元素。

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).

您正在查看可能发生的所有可能的情况! 没有进一步的案例需要考虑。

因此,根据经验:每当你想要一个应该"双向"工作的谓词时,请考虑将其写成"全向"!


也许你不喜欢明确提到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定义的方法。它最大的缺点是它做出了许多不容易看到的假设。因此,即使是很小的扩展也可能使其无效。它使用iwhen/2("实例化时间"(。

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 )
).

非常棘手 - 我什至在第一次尝试时犯了两个错误......没有切,切碎!

对于最一般的查询,我们现在得到一个实例化错误。不是很满意,但总比不正确的结果好。

一般来说,你必须寻找建设性否定的实现来在一般情况下处理这个问题......

最新更新