Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected result for predicate nb_setarg/3

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).
like image 318
slago Avatar asked Dec 31 '22 18:12

slago


2 Answers

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).
like image 155
Isabelle Newbie Avatar answered Jan 19 '23 08:01

Isabelle Newbie


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 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.

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).
like image 25
David Tonhofer Avatar answered Jan 19 '23 07:01

David Tonhofer