From: ap on
Dear group,

I just wanted to check that I'm correct in thinking that I cannot use
Nest or Fold to iterate a function, where that function has a
HoldFirst attribute set that allows it to modify the global variable
(parameter) passed to it.
As a test case / example I define a function which modifies a global
variable (a list) by setting the 2nd element to a random integer, and
returns the modified list, as follows:

SetAttributes[f,HoldFirst]
f[var_] := {var[[2]] = RandomInteger[ {0,Length[var]} ], var}[[2]]

Then if I simply do

mylist=Range[10]
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

I get

f[mylist]
{1, 8, 3, 4, 5, 6, 7, 8, 9, 10}

as expected.
However, if I try to Nest this function to apply it iteratively (as an
alternative to a For or Do loop), as follows:

Nest[f, mylist, 3]

I get

Set::setps: {1,2,3,4,5,6,7,8,9,10} in the part assignment is not a
symbol. >>

In other words, Nest seems not to respect the HoldFirst attribute of
f. Instead, the list is passed by value and the resulting attempted
assignment of the random integer fails. The same behaviour results
from Fold if I slightly modify f to make it a function of two
variables.

Is this correct? And if so, is the best alternative simply to use a
loop? That's easy enough, but I was just trying to be as 'functional'
and hence efficient as possible, as this kind of modification of a
global variable is a key step in my algorithm and needs to be as fast
as possible. I have an alternative implementation where instead of
trying to modify the list I Join existing parts of it with the
relevant modified element, but this appears to be ~25% slower than a
simple Do loop to modify the list.

From: Bill Rowe on
On 4/17/10 at 6:03 AM, arno.proeme(a)gmail.com (ap) wrote:

>I just wanted to check that I'm correct in thinking that I cannot
>use Nest or Fold to iterate a function, where that function has a
>HoldFirst attribute set that allows it to modify the global variable
>(parameter) passed to it. As a test case / example I define a
>function which modifies a global variable (a list) by setting the
>2nd element to a random integer, and returns the modified list, as
>follows:

>SetAttributes[f,HoldFirst] f[var_] := {var[[2]] = RandomInteger[
>{0,Length[var]} ], var}[[2]]

>Then if I simply do

>mylist=Range[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

>I get

>f[mylist] {1, 8, 3, 4, 5, 6, 7, 8, 9, 10}

>as expected. However, if I try to Nest this function to apply it
>iteratively (as an alternative to a For or Do loop), as follows:

>Nest[f, mylist, 3]

>I get

>Set::setps: {1,2,3,4,5,6,7,8,9,10} in the part assignment is not a
>symbol. >>

I get the same error when I use your code and do:

f[Range(a)10]

So, problem is Nest evaluates the first time to a list of
numbers and operates on this list rather than the global
variable that has been modified. And since your f will not work
with this type of list the error message gets generated. That is
I see the problem as being using the syntax
var[[2]]=RandomInteger to change the second value.

>In other words, Nest seems not to respect the HoldFirst attribute of
>f. Instead, the list is passed by value and the resulting attempted
>assignment of the random integer fails. The same behaviour results
>from Fold if I slightly modify f to make it a function of two
>variables.

This sounds like another way to describe the situation. And when
I think about this, I don't see how Nest could work differently.
If Nest doesn't evaluate its argument, how can the next
iteration evaluate correctly?

I don't understand why you create a local copy of the global
variable you want to change. Instead of writing f as you did why
not write it as:

f:=(myList[[2]]=RandomInteger[Length(a)myList];myList)

that is write f to operate directly on the global variable to be
modified rather than to operate on a local copy of that
variable. If I write f in this fashion then

Nest[f,myList,3]

works as expected with no error messages.

But frankly, the entire idea of modify global variables in this
manner strikes me as being unwise. I see result as being code
that will be difficult to maintain and debug.


From: Ray Koopman on
On Apr 17, 3:02 am, ap <arno.pro...(a)gmail.com> wrote:
> Dear group,
>
> I just wanted to check that I'm correct in thinking that I cannot use
> Nest or Fold to iterate a function, where that function has a
> HoldFirst attribute set that allows it to modify the global variable
> (parameter) passed to it.
> As a test case / example I define a function which modifies a global
> variable (a list) by setting the 2nd element to a random integer, and
> returns the modified list, as follows:
>
> SetAttributes[f,HoldFirst]
> f[var_] := {var[[2]] = RandomInteger[ {0,Length[var]} ], var}[[2]]
>
> Then if I simply do
>
> mylist=Range[10]
> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
>
> I get
>
> f[mylist]
> {1, 8, 3, 4, 5, 6, 7, 8, 9, 10}
>
> as expected.
> However, if I try to Nest this function to apply it iteratively (as an
> alternative to a For or Do loop), as follows:
>
> Nest[f, mylist, 3]
>
> I get
>
> Set::setps: {1,2,3,4,5,6,7,8,9,10} in the part assignment is not a
> symbol. >>
>
> In other words, Nest seems not to respect the HoldFirst attribute of
> f. Instead, the list is passed by value and the resulting attempted
> assignment of the random integer fails. The same behaviour results
> from Fold if I slightly modify f to make it a function of two
> variables.
>
> Is this correct? And if so, is the best alternative simply to use a
> loop? That's easy enough, but I was just trying to be as 'functional'
> and hence efficient as possible, as this kind of modification of a
> global variable is a key step in my algorithm and needs to be as fast
> as possible. I have an alternative implementation where instead of
> trying to modify the list I Join existing parts of it with the
> relevant modified element, but this appears to be ~25% slower than a
> simple Do loop to modify the list.

Use MapAt:

In[1]:= g[var_] := MapAt[Random[Integer,{0,Length(a)var}]&,var,2]

In[2]:= mylist = Range(a)10

Out[2]= {1,2,3,4,5,6,7,8,9,10}

In[3]:= g[mylist]

Out[3]= {1,9,3,4,5,6,7,8,9,10}

In[4]:= Nest[g,mylist,3]

Out[4]= {1,6,3,4,5,6,7,8,9,10}

From: Bob Hanlon on

mylist = Range[10];

SetAttributes[{f, f1, f2}, HoldFirst];

f[var_] := Module[{x = var},
{x[[2]] = RandomInteger[{0, Length[x]}], x}[[2]]];

Nest[f, mylist, 3]

{1,4,3,4,5,6,7,8,9,10}

f1[var_] := Module[{x = var},
x[[2]] = RandomInteger[{0, Length[x]}];
x];

Nest[f1, mylist, 3]

{1,1,3,4,5,6,7,8,9,10}

f2[var_] := ReplacePart[var,
2 -> RandomInteger[{0, Length[var]}]];

Nest[f2, mylist, 3]

{1,6,3,4,5,6,7,8,9,10}


Bob Hanlon

---- ap <arno.proeme(a)gmail.com> wrote:

=============
Dear group,

I just wanted to check that I'm correct in thinking that I cannot use
Nest or Fold to iterate a function, where that function has a
HoldFirst attribute set that allows it to modify the global variable
(parameter) passed to it.
As a test case / example I define a function which modifies a global
variable (a list) by setting the 2nd element to a random integer, and
returns the modified list, as follows:

SetAttributes[f,HoldFirst]
f[var_] := {var[[2]] = RandomInteger[ {0,Length[var]} ], var}[[2]]

Then if I simply do

mylist=Range[10]
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

I get

f[mylist]
{1, 8, 3, 4, 5, 6, 7, 8, 9, 10}

as expected.
However, if I try to Nest this function to apply it iteratively (as an
alternative to a For or Do loop), as follows:

Nest[f, mylist, 3]

I get

Set::setps: {1,2,3,4,5,6,7,8,9,10} in the part assignment is not a
symbol. >>

In other words, Nest seems not to respect the HoldFirst attribute of
f. Instead, the list is passed by value and the resulting attempted
assignment of the random integer fails. The same behaviour results
from Fold if I slightly modify f to make it a function of two
variables.

Is this correct? And if so, is the best alternative simply to use a
loop? That's easy enough, but I was just trying to be as 'functional'
and hence efficient as possible, as this kind of modification of a
global variable is a key step in my algorithm and needs to be as fast
as possible. I have an alternative implementation where instead of
trying to modify the list I Join existing parts of it with the
relevant modified element, but this appears to be ~25% slower than a
simple Do loop to modify the list.


From: Leonid Shifrin on
Hi Arno,

My understanding is that indeed, Nest and Fold do not respect the Hold
Attributes in the way you outlined. Here is my guess for what's happening:
the behavior that you observed is probably just a consequence of the way
they (Nest and Fold) work (are implemented): first your function is computed
(iterated) by Nest or Fold, and then that result is copied to whatever
internal storage Nest or Fold is using to keep the intermediate result of a
given iteration. In other words, what you get as an input for the next
iteration is already a result stored in that internal storage rather than
your initial variable. And when this result is supplied to you for the next
iteration, it gets evaluated, so probably what you get is a fully evaluated
copy rather than a reference to that storage. This copy is immutable, so you
get the error when attempting to modify it. This scenario is just my
speculation, any part of it may be wrong.

Now, one of the main goals of functional style is to work on immutable
structures, so this behavior seems consistent with that ideology. Whenever
you use Hold attributes to implement pass-by-reference and in-place
modifications, you already step outside of the functional programming
paradigm. So I would not hesitate to use loops - in your situation this
seems totally appropriate.

As far as efficiency is concerned, functional programming is efficient in
Mathematica simply because it allows to work with large pieces at once,
pushing more operations into the kernel and by-passing the main evaluation
loop for many small parts of your expression. In most cases, loops are
associated with breaking expression into pieces, and that is what makes them
inefficient. In some cases however, loops can be used to the same effect,
and then they are no less efficient than functional programming. For your
problem in particular, loops should be totally fine. In the case of lists,
you may also save a little (like 25 %) by wrapping your values in
HoldComplete rather than List, since then no UpValues lookup will take
place.

Hope this helps.

Regards,
Leonid


On Sat, Apr 17, 2010 at 3:03 AM, ap <arno.proeme(a)gmail.com> wrote:

> Dear group,
>
> I just wanted to check that I'm correct in thinking that I cannot use
> Nest or Fold to iterate a function, where that function has a
> HoldFirst attribute set that allows it to modify the global variable
> (parameter) passed to it.
> As a test case / example I define a function which modifies a global
> variable (a list) by setting the 2nd element to a random integer, and
> returns the modified list, as follows:
>
> SetAttributes[f,HoldFirst]
> f[var_] := {var[[2]] = RandomInteger[ {0,Length[var]} ], var}[[2]]
>
> Then if I simply do
>
> mylist=Range[10]
> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
>
> I get
>
> f[mylist]
> {1, 8, 3, 4, 5, 6, 7, 8, 9, 10}
>
> as expected.
> However, if I try to Nest this function to apply it iteratively (as an
> alternative to a For or Do loop), as follows:
>
> Nest[f, mylist, 3]
>
> I get
>
> Set::setps: {1,2,3,4,5,6,7,8,9,10} in the part assignment is not a
> symbol. >>
>
> In other words, Nest seems not to respect the HoldFirst attribute of
> f. Instead, the list is passed by value and the resulting attempted
> assignment of the random integer fails. The same behaviour results
> from Fold if I slightly modify f to make it a function of two
> variables.
>
> Is this correct? And if so, is the best alternative simply to use a
> loop? That's easy enough, but I was just trying to be as 'functional'
> and hence efficient as possible, as this kind of modification of a
> global variable is a key step in my algorithm and needs to be as fast
> as possible. I have an alternative implementation where instead of
> trying to modify the list I Join existing parts of it with the
> relevant modified element, but this appears to be ~25% slower than a
> simple Do loop to modify the list.
>
>