4

考虑以下 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]. 也就是说,我希望一旦谓词正确地识别出第二个子句适用,它应该只使用第二个子句回溯。

我应该在哪里插入切口以实现这种行为?

4

2 回答 2

5

您的程序目前过于笼统。毕竟,您的查询有三个解决方案,但只有第一个是预期的。最后一个是错误的。

?- transform([foo, 1, 2], Xs).
   Xs = [bar, 1, 2]
;  Xs = [foobar, 1, 2]
;  Xs = [foo, 1, 2].   % 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)使用 SICStusSWI

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/1isSpecial_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 ).
于 2018-05-25T08:52:10.877 回答
4

这是另一种重用现有定义的方法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 )
   ).

非常棘手 - 我第一次尝试时甚至犯两个错误......而且没有削减,切碎!

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

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

于 2018-05-25T09:52:05.950 回答