Does anyone know the reason why predicate nb_setarg/3
does not work correctly when used with the predicate forall/3
in the toplevel of the SWI-Prolog interpreter (v. 8.2.1)?
How it works when used in a goal typed in toplevel:
?-
functor(A, array, 5),
forall(arg(Index, A, _),
nb_setarg(Index, A, 0)).
A = array(_26341340, _26341342, _26341344, _26341346, _26341348).
How it works when used in a rule:
new_array(A,N) :-
functor(A, array, N),
forall(
arg(Index, A, _),
nb_setarg(Index, A, 0)).
Then:
?-
new_array(A,5).
A = array(0, 0, 0, 0, 0).
I think this might be a bug. But it might not be a bug (just) in forall/2
or nb_setarg/3
. Because this works:
?- A = array(_, _, _, _, _), forall(arg(Index, A, _), nb_setarg(Index, A, 0)).
A = array(0, 0, 0, 0, 0).
while your example doesn't (SWI 7.6.4):
?- functor(A, array, 5), forall(arg(Index, A, _), nb_setarg(Index, A, 0)).
A = array(_2290, _2292, _2294, _2296, _2298).
I don't know, but the on the toplevel the compound term's modification is rolled back on backtracking for some reason (SWI-Prolog 8.3.14):
Toplevel
?-
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]))).
Then we see new array/5
compound terms with fresh variables on each passage of 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).
As a rule
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]))).
Then:
?- 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).
On the other hand, the implementation is as follows:
forall(Cond, Action) :-
\+ (Cond, \+ Action).
The above is not a good predicate to use as a loop.
However, the behaviour in the "rule setting" seems correct.
The documentation says:
The predicate
forall/2
is implemented as\+ ( Cond, \+ Action)
, i.e., There is no instantiation ofCond
for whichAction
is false. The use of double negation implies thatforall/2
does not change any variable bindings. It proves a relation. Theforall/2
control structure can be used for its side-effects.
Quite so.
There is nothing special in the description of nb_setarg/3
either.
It's as if nb_setarg/3
were working as setarg/3
on the toplevel?
The trace doesn't reveal anything:
^ 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) ?
As it is SWI-Prolog related, you may want to ask this on Discourse.
Update
Tried it online in GNU Prolog.
GNU Prolog demands that the index of arg/3
be instantiated and has no nb_setarg/3
(nor a forall/2
??).
But let's try the following in 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]))).
Doesn't work either.
Update: Trying something simpler & pared-down
As expected:
?-
A=p(1,2,3),nb_setarg(1,A,foo).
A = p(foo, 2, 3).
With double negation. Also keeps the non-backtrackably-set value:
?-
A=p(1,2,3),\+ \+ nb_setarg(1,A,foo).
A = p(foo, 2, 3).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With