From: Rui on
On Apr 17, 7: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.

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.