Re: Nest and Fold don't respect HoldFirst?

*To*: mathgroup at smc.vnet.net*Subject*: [mg109230] Re: Nest and Fold don't respect HoldFirst?*From*: Ray Koopman <koopman at sfu.ca>*Date*: Sun, 18 Apr 2010 05:57:58 -0400 (EDT)*References*: <hqc0vu$ftg$1@smc.vnet.net>

On Apr 17, 3:02 am, ap <arno.pro... at 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@var}]&,var,2] In[2]:= mylist = Range@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}