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

我认为这可能是一个错误。但它可能不是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).

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

顶级

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

然后我们看到新的array/5复合项,每条forall的通道都有新的变量

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: ~qn",[Index,A]),
nb_setarg(Index, A, 0),
format("~q: ~qn",[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).

另一方面,实现如下:

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

以上不是用作循环的好谓词。

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

文档说:

谓词forall/2+ ( Cond, + Action)实现的,即没有实例化Action为假的Cond。使用双重否定意味着forall/2不会更改任何变量绑定。它证明了一种关系。forall/2控制结构可用于其副作用。

确实如此。

nb_setarg/3的描述也没有什么特别之处。

就好像nb_setarg/3在顶层做setarg/3一样?

跟踪不会显示任何内容:

^  Call: (13) format("~q: ~qn", [1, array(_30756, _32086, _32088, _32090, _32092)]) ? creep
1: array(_30756,_32086,_32088,_32090,_32092)
^  Exit: (13) format("~q: ~qn", [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: ~qn", [1, array(0, _32086, _32088, _32090, _32092)]) ? creep
1: array(0,_32086,_32088,_32090,_32092)
^  Exit: (13) format("~q: ~qn", [1, array(0, _32086, _32088, _32090, _32092)]) ? creep
Next "forall" passage: we are using a new compound term! 
^  Call: (13) format("~q: ~qn", [2, array(_32084, _30756, _32088, _32090, _32092)]) ? creep
2: array(_32084,_30756,_32088,_32090,_32092)
^  Exit: (13) format("~q: ~qn", [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: ~qn",[Index,A]),
nb_setarg(Index, A, 0),
format("~q: ~qn",[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).

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

问题归结为:

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

即,如果在目标位置上存在较早的跟踪分配, 回溯到此跟踪分配将位置变回之前 变量。在这种情况下,arg(I,Term,_)将目标与变量统一起来 在顶级查询词中。由于时间较旧,目标位置将变为 对查询变量的尾随引用。

nb_setarg/3有用,但一罐蠕虫。

我必须做很多研究才能找到[出了什么问题]。 跟踪所有尾随及其优化并非易事。 基本上,不要回溯到您开始使用nb_*之前,并且只 在同一位置使用nb_*谓词。

所以这个想法似乎是跟踪(即如果我理解正确的话,回溯时要回滚的修改堆栈)包含一个分配(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).

最新更新