有谁知道谓词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/2
或nb_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).