Mathematica Stack Exchange is a question and answer site for users of Mathematica. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

This code adds random digits to lists, and it works fine:

a = {{1}, {2}, {3}};
Do[
  j = RandomInteger[{1, Length[a]}];
  AppendTo[a[[j]], RandomInteger[9]];
  Print[a], {i, 5}];

(* {{1,7},{2},{3}}
   {{1,7},{2,2},{3}}
   {{1,7},{2,2},{3,9}}
   {{1,7},{2,2},{3,9,1}}
   {{1,7,2},{2,2},{3,9,1}}  *)

But if I replace the 'j' inside the [[]] with the definition j in the previous line, everything goes haywire:

a = {{1}, {2}, {3}};
Do[
  AppendTo[a[[RandomInteger[{1, Length[a]}]]], RandomInteger[9]];
  Print[a], {i, 5}];

(* {{1},{1,7},{3}}
   {{1},{1,7},{1,4}}
   {{1,7},{1,7},{1,4}}
   {{1,7},{1,7},{1,7,9}}
   {{1,7,9},{1,7},{1,7,9}} *)

Is this a bug or something I'm doing wrong?

share|improve this question
    
What exactly do you mean by "everything goes haywire"? It's hard to tell what's wrong, given the random numbers in the code (and therefore the outputs that are never the same). Can you elaborate? – march yesterday
    
@march Initially a = {{1}, {2}, {3}}; after the first step of appending it's {{1},{1,7},{3}} - you append 7 to {2} and get {1,7}. – corey979 yesterday
    
I believe I see the problem. RandomInteger[{1, Length[a]}] is evaluated twice in the second case. I will write a quick answer. – march yesterday
1  
Answers by @Szabolcs and @march dispel the mystery. Note also that this case is a good use case for BlockRandom; that is, as an alternative to your first approach you could also use Do[AppendTo[a[[BlockRandom[RandomInteger[{1, Length[a]}]]]], RandomInteger[9]]; Print[a], {i, 5}]; – kglr yesterday
4  
Related, possible duplicate: (107619) – Mr.Wizard 7 hours ago
up vote 10 down vote accepted

Here's the issue. In the second (non-working) code,

RandomInteger[{1, Length[a]}]

is evaluated twice, as we can see by Traceing the evaluation:

SeedRandom[2]
a = {{1}, {2}, {3}};
Trace[AppendTo[a[[RandomInteger[{1, Length[a]}]]], RandomInteger[9]], TraceInternal -> True]
  1. {RandomInteger[9], 8}
  2. AppendTo[a[[RandomInteger[{1, Length[a]}]]], 8]
  3. {{a, {{1}, {2}, {3}}}, {{{{a, {{1}, {2}, {3}}}, Length[{{1}, {2}, {3}}], 3}, {1, 3}}, RandomInteger[{1, 3}], 3}, {{1}, {2}, {3}}[[3]], {3}}
  4. a[[RandomInteger[{1, Length[a]}]]] = Append[{3}, 8]
  5. {Append[{3}, 8], {3, 8}}
  6. a[[RandomInteger[{1, Length[a]}]]] = {3, 8}
  7. {{{{a, {{1}, {2}, {3}}}, Length[{{1}, {2}, {3}}], 3}, {1, 3}}, RandomInteger[{1, 3}], 2}

We can see in Line 1 that RandomInteger[9] evaluates to 8, so we will be appending 8 to one of the lists. In Line 3, RandomInteger[{1, 3}] evaluates to 3, so we're going to append to a[[3]]. This happens on Line 5, where 8 is appended to {3} to make {3, 8}.

Now, the kicker: In Line 7, RandomInteger[{1, Length[a]}] is evaluated again, so it evaluates to a different number. In this case, it evaluates to 2, so instead of replacing a[[3]], we are replacing a[[2]] with {3, 8}. Hence the output after this evaluation is

a
(* {{1}, {3, 8}, {3}} *)

Now, the fix here is to do things the way you're doing it in the first code. I would probably wrap the entire thing in a Module with j as a local variable, but it's the same process.

share|improve this answer
    
Ooooooh... Interesting. So not a bug, but that second evaluation sure seems like an iffy design feature. Thank you for your insight and the time you put into writing this up. This was instructive and helpful. – Jerry Guern yesterday
1  
@JerryGuern. Well, I think Szabolcs has a pretty good explanation of why the double evaluation occurs, in terms of what AppendTo is likely literally doing behind the scenes. I think that that particular "design choice" for AppendTo makes some amount of sense. – march yesterday

No, not a bug.

Let's think about how AppendTo may be implemented (even though the actual implementation isn't inspectable).

SetAttributes[appendTo, HoldFirst]
appendTo[a_, val_] := (a = Append[a, val])

What happens if we evaluate the following?

appendTo[ a[[ RandomInteger[{1,3}] ]],  x ]

It simply does this:

a[[ RandomInteger[{1,3}] ]] = Append[a[[ RandomInteger[{1,3}] ]], x]

That's because RandomInteger[...] didn't get evaluated before substitution due to the HoldFirst. Now we have two of them. And they may evaluate to different values. So we may get things like

a[[1]] = Append[ a[[3]], x ]

if the first random number we get is 1 and the second is 3.

I hope this makes it clear. I have a vague feeling that the same has been asked before in a different form.

share|improve this answer
    
Would you kindly review the link I just added in the comments below the question, and vote to close this question as a duplicate if you feel that is appropriate? – Mr.Wizard 7 hours ago
    
@Mr.Wizard I knew there had to be a duplicate! – Szabolcs 6 hours ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.