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

在Prolog中插入一个cut,使关系子句绑定但双向,prolog,prolog-cut,Prolog,Prolog Cut,考虑以下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(foob

考虑以下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).
有些情况下,我将调用
transform([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
现在的削减可能会减少解决方案的数量。但大多数情况下,这是非常错误的。你的问题是,你应该在哪里“插入切口以实现这种行为?”答案是:没有地方。要做到这一点,你需要做更多的工作

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

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.

如果您想从中得到更多,请考虑使用<代码>库(RIF)< /代码> 对于 和

有了这个提法,我们就不必重复了。要检查代码是否正确,让我们尝试最一般的查询:

?- 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).
您正在查看所有可能发生的情况!没有进一步的考虑。

这是一条经验法则:每当你想要一个“双向”的谓词时,就要把它写成“全向”而不是!p>


也许你不喜欢明确提到
X=foo;X=巴;X=foobar
,在这种情况下,我们必须通过具体化
isSpecial/1
isSpecial\u 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
定义。它最大的缺点是,它做出了许多不容易看到的假设。因此,即使是一个小的扩展也可能使其无效。它使用(“实例化时”)

非常棘手-我甚至在第一次尝试时犯了两个错误。。。不要割伤,切碎

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


通常情况下,您必须寻找的实现来处理这种情况…

关于在地图列表中转换的技巧是一个很好的技巧,我将使用它。然而,您的回答回避了我的主要问题:在您编写dif语句的地方,我需要一种自动的方式说“前面的任何条款都不适用”,因为在我的实际用例中,我有10条条款,而编写所有这些保障措施将使代码更加安全illegible@Jsevillamol:使用dif/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 ).
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 )
   ).