谓词 nb_setarg/3 的意外结果

Unexpected result for predicate nb_setarg/3

有谁知道谓词 nb_setarg/3 在 SWI-Prolog 解释器 (v. 8.2.1) 的顶层与谓词 forall/3 一起使用时无法正常工作的原因?

在顶层输入的目标中使用时的工作原理:

?- 
functor(A, array, 5), 
forall(arg(Index, A, _), 
       nb_setarg(Index, A, 0)).

A = array(_26341340, _26341342, _26341344, _26341346, _26341348).

在规则中使用时的工作原理:

new_array(A,N) :- 
   functor(A, array, N),
   forall(
      arg(Index, A, _), 
      nb_setarg(Index, A, 0)).

然后:

?- 
new_array(A,5).
A = array(0, 0, 0, 0, 0).

我不知道,但是在顶层,由于某种原因(SWI-Prolog 8.3.14),复合项的修改在回溯时被回滚:

顶级

?- 
functor(A, array, 5), 
   forall( 
      arg(Index, A, _),
      (format("~q: ~q\n",[Index,A]),
       nb_setarg(Index, A, 0),
       format("~q: ~q\n",[Index,A]))).

然后我们在 forall

的每个段落中看到带有新变量的新 array/5 复合术语
1: array(_3228,_4334,_4336,_4338,_4340)
1: array(0    ,_4334,_4336,_4338,_4340)
2: array(_4332,_3228,_4336,_4338,_4340)
2: array(_4332,0,    _4336,_4338,_4340)
3: array(_4332,_4334,_3228,_4338,_4340)
3: array(_4332,_4334,    0,_4338,_4340)
4: array(_4332,_4334,_4336,_3228,_4340)
4: array(_4332,_4334,_4336,0,    _4340)
5: array(_4332,_4334,_4336,_4338,_3228)
5: array(_4332,_4334,_4336,_4338,0)
A = array(_4332, _4334, _4336, _4338, _4340).

一般

new_array(A,N) :- 
   functor(A, array, N), 
   forall( 
      arg(Index, A, _),
      (format("~q: ~q\n",[Index,A]),
       nb_setarg(Index, A, 0),
       format("~q: ~q\n",[Index,A]))).

然后:

?- new_array(A,5).
1: array(_2498,_2500,_2502,_2504,_2506)
1: array(0,_2500,_2502,_2504,_2506)
2: array(0,_2500,_2502,_2504,_2506)
2: array(0,0,_2502,_2504,_2506)
3: array(0,0,_2502,_2504,_2506)
3: array(0,0,0,_2504,_2506)
4: array(0,0,0,_2504,_2506)
4: array(0,0,0,0,_2506)
5: array(0,0,0,0,_2506)
5: array(0,0,0,0,0)
A = array(0, 0, 0, 0, 0).

另一方面,the implementation如下:

forall(Cond, Action) :-
    \+ (Cond, \+ Action).

上面的谓词不适合用作循环。

但是,“规则设置”中的行为似乎是正确的。

documentation 说:

The predicate forall/2 is implemented as \+ ( Cond, \+ Action), i.e., There is no instantiation of Cond for which Action is false. The use of double negation implies that forall/2 does not change any variable bindings. It proves a relation. The forall/2 control structure can be used for its side-effects.

确实如此。

nb_setarg/3的描述也没什么特别的

就好像 nb_setarg/3 在顶层作为 setarg/3 工作?

跟踪没有显示任何内容:

^  Call: (13) format("~q: ~q\n", [1, array(_30756, _32086, _32088, _32090, _32092)]) ? creep

1: array(_30756,_32086,_32088,_32090,_32092)

^  Exit: (13) format("~q: ~q\n", [1, array(_30756, _32086, _32088, _32090, _32092)]) ? creep
   Call: (13) setarg(1, array(_30756, _32086, _32088, _32090, _32092), 0) ? creep
   Exit: (13) setarg(1, array(0, _32086, _32088, _32090, _32092), 0) ? creep
^  Call: (13) format("~q: ~q\n", [1, array(0, _32086, _32088, _32090, _32092)]) ? creep

1: array(0,_32086,_32088,_32090,_32092)

^  Exit: (13) format("~q: ~q\n", [1, array(0, _32086, _32088, _32090, _32092)]) ? creep

Next "forall" passage: we are using a new compound term! 

^  Call: (13) format("~q: ~q\n", [2, array(_32084, _30756, _32088, _32090, _32092)]) ? creep

2: array(_32084,_30756,_32088,_32090,_32092)

^  Exit: (13) format("~q: ~q\n", [2, array(_32084, _30756, _32088, _32090, _32092)]) ? creep
   Call: (13) setarg(2, array(_32084, _30756, _32088, _32090, _32092), 0) ? creep
   Exit: (13) setarg(2, array(_32084, 0, _32088, _32090, _32092), 0) ? 

由于它与 SWI-Prolog 相关,您可能想在 Discourse 上提问。

更新

GNU Prolog 在线试过。

GNU Prolog 要求实例化 arg/3 的索引并且没有 nb_setarg/3(也没有 forall/2??)。

但是让我们在 SWI-Prolog 中尝试以下操作:

functor(A, array, 5), 
   \+ ( 
      between(1,5,Index),arg(Index, A, _),
      \+
         (format("~q: ~q\n",[Index,A]),
          nb_setarg(Index, A, 0),
          format("~q: ~q\n",[Index,A]))).

也不行。

更新:尝试更简单和精简的东西

符合预期:

?- 
A=p(1,2,3),nb_setarg(1,A,foo).
A = p(foo, 2, 3).

带有双重否定。还保留不可回溯设置的值:

?- 
A=p(1,2,3),\+ \+ nb_setarg(1,A,foo).
A = p(foo, 2, 3).

我认为这可能是一个错误。但它可能不是 forall/2nb_setarg/3 中的错误(只是)。因为这有效:

?- A = array(_, _, _, _, _), forall(arg(Index, A, _), nb_setarg(Index, A, 0)).
A = array(0, 0, 0, 0, 0).

而您的示例没有 (SWI 7.6.4):

?- functor(A, array, 5), forall(arg(Index, A, _), nb_setarg(Index, A, 0)).
A = array(_2290, _2292, _2294, _2296, _2298).

Jan Wielemaker 在 issue #733 中说(“从 forall/2 调用,nb_setarg/3 应用于顶级目标中构造的复合项没有效果(它变成setarg/3?") 这是一个已知问题:

The problem boils down to:

?- functor(C, a, 2), 
    \+ \+ (arg(1, C, x), 
    nb_setarg(1, C, foo)).

C = a(_52710, _52712).

I.e., if there is an earlier trailed assignment on the target location, backtracking to before this trailed assignment turns the location back into a variable. In this case the arg(I,Term,_) unifies the target with the variable in the toplevel query term. As this is older, the target location becomes a trailed reference to the query variable.

nb_setarg/3 is useful, but a can of worms.

...

I'd have to do a lot of research to find [what is going wrong]. Tracking all the trailing and optimization thereof is non-trivial. Basically, do not backtrack to before where you started using nb_* and only use the nb_* predicates on the same location.

所以这个想法似乎是轨迹(即,如果我理解正确的话,在回溯时要回滚的修改堆栈)包含一个赋值(arg(1, C, x)),用于精确修改的位置nb_setarg/3 并且您回溯到 之前的作业 ,您的不可回溯作业丢失了。

有道理,这样看

A = array(_26341340, _26341342, _26341344, _26341346, _26341348).

实际上是正确的结果。

(在逻辑Prolog和赋值-Prolog之间切换让我头疼)。

我猜是这样做的:

A=array(_,_,_,_,_), 
forall(
   between(1,5,Index),   
   nb_setarg(Index, A, bar)).

functor(A, array, 5),
forall(
   between(1,5,Index),   
   nb_setarg(Index, A, bar)).

在尝试了以下查询之后,我怀疑谓词 functor/3(或其合理化版本 compound_name_arity/3)是 nb_setarg/3 所观察到的问题的真正根源。它似乎创建了一个包含新变量的术语,这些变量“不在顶级查询的范围内”(请注意,当变量名称明确出现在查询中时,结果符合预期)。

?- A =.. [array,X1,X2,X3], 
   forall( arg(I, A, _), 
           nb_setarg(I, A, 0) ).

A = array(0,0,0)
?- compound_name_arguments(A, array, [X1,X2,X3]), 
   forall( arg(I, A, _), 
           nb_setarg(I, A, 0) ).

A = array(0,0,0)
?- compound_name_arity(A, array, 3), 
   forall( arg(I, A, _), 
           nb_setarg(I, A, 0) ).

A = array(_20928, _20930, _20932)
?- functor(A, array, 3), 
   forall( arg(I, A, _), 
           nb_setarg(I, A, 0) ).

A = array(_22056, _22058, _22060).