Re: Nest and Fold don't respect HoldFirst?
- To: mathgroup at smc.vnet.net
- Subject: [mg109273] Re: Nest and Fold don't respect HoldFirst?
- From: Rui <rui.rojo at gmail.com>
- Date: Tue, 20 Apr 2010 05:50:11 -0400 (EDT)
- References: <hqc0vu$ftg$1@smc.vnet.net>
On Apr 17, 7: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. I think this is my first answer in the group. This is my humble opinion on this matter: * Not sure functional tend to lead to efficient. * Yeah, I agree, Nest seems to not respect the HoldFirst. fHF[_fHF] := "Hold"; fHF[_] := "NoHold"; f[_f] := "Hold"; f[_] := "NoHold"; SetAttributes[fHF, HoldFirst]; Now if I do In: {f[f[f[0]]], fHF[fHF[fHF[0]]]} Out: {"NoHold", "Hold"} In: Composition[fHF, fHF, fHF][0] Out: Hold In: Nest[fHF, 0, 3] Out: NoHold * In your case, f[f[MyList]] doesn't work either, so you're not yet in the point of blaming Nest. In the case of f[f[MyList]]: What Mathematica does is: - Evaluates the outermost "f" to "f", nothing changes - Sees the HoldFirst, so doesn't evaluate f[MyList] just like you told it to - Replaces var in your definition by f[MyList] and, among other things, tries to do f[MyList][[2]]= something, which is an error and not what you want. *The best way that I've read so far is doing it like Bob's f2 f2[var_] := ReplacePart[var, 2 -> RandomInteger[{0, Length[var]}]]; This looks like "pass by value", but I think it's not, in the sense that var is just an alias of the list, so when you call f[f[...]] you apply ReplacePart to the same piece of data without making copies. So overall I'm guessing it's quite efficient. However, I guess that in the last assignment MyList=Nest[f, 3, MyList], it's probably making all the useless assignments instead of just the second element, but I doubt that makes a difference compared to other things I've read. For example, I'm not confident on the efficiency of your way of returning var by creating a list and extracting the second element. Create + extract seem worse than just using CompoudExpresion ( ; ) Meaning: f[var_] := (var[[2]] = RandomInteger[ {0,Length[var]} ]; var) So IIIi don't think Bill is right when he says you're making local copies. The MapAt choice I think it will take you ages. Bob's f does however create a local variable, so probably takes a little longer too.