4
$\begingroup$

(This question might be an application of this other question, but there might be another solution specific to this application that I'm not aware of.)

I am currently in a situation in which (in a For loop) I am appending the current value to one of several lists, chosen randomly. (The lists themselves contain nonrandom values.) The basic structure of

RandomChoice[{AppendTo[listA,value], AppendTo[listB,value], 
    AppendTo[listC,value], AppendTo[listD,value],...}]

works, but it seems highly repetitive to have a random choice of appending the same value. The obvious thing to do would be to reverse the two functions, giving

AppendTo[RandomChoice[{listA, listB, listC, listD,...}],value]

but then AppendTo tries to append value to the RandomChoice function instead of its output. However, if I surround the RandomChoice with an Evaluate, it instead comes up with an error of "{1,2} is not a variable with a value, so its value cannot be changed". But if Evaluate is not the outermost function within the AppendTo, it tries to append value to that outermost function (e.g. ReleaseHold). Surrounding each list with Defer produces some results that, to me, are truly weird, returning an error of "listB [or whichever one was randomly chosen] is not a variable with a value, so its value cannot be changed" -- even though I have previously defined those lists as variables with values! Using Map and Apply also don't work, as the results come in completely unevaluated, leaving me with the first situation. These pretty much exhaust all possibilities that I know of. Are there any possibilities that I'm not aware of? (I'm looking for something which can be done in a single expression, if it's at all possible.)

(Using v10.2)

$\endgroup$
  • 3
    $\begingroup$ What if you did something like AppendTo[lists[[RandomInteger[{1, Length@lists}]]], value], where lists = {listA, listB, listC, listD,...}? $\endgroup$ – march Jan 7 '16 at 4:44
  • $\begingroup$ It errors with "{listA,listB,listC,listD} in the part assignment is not a symbol". $\endgroup$ – 404UserNotFound Jan 7 '16 at 19:18
  • $\begingroup$ Once listA, listB, etc. are defined, you define lists = {listA, listB, listC, listD,...}, and then it should work fine. $\endgroup$ – march Jan 7 '16 at 19:21
2
$\begingroup$

I agree with the comment by @march that using an indexed list would simplify things nicely. However, if we wish to press on with named lists then one way would be to create a list of functions that express the possible AppendTo operations and then to evaluate a randomly chosen function from that list.

For example, starting from:

listA = {1, 1, 1};
listB = {2, 2, 2};
listC = {3, 3, 3};
listD = {4, 4, 4};

We can generate a list of appending functions:

fns = Cases[Hold[listA, listB, listC, listD], l_ :> (AppendTo[l, #]&)]

(* { AppendTo[listA, #1]&, AppendTo[listB, #1]&,
     AppendTo[listC, #1]&, AppendTo[listD, #1]& } *)

We choose one of those functions randomly...

f = RandomChoice[fns]

(* AppendTo[listC, #1] & *)

... and then use it to update the corresponding list:

f[999]

(* {3, 3, 3, 999} *)

We can then verify that the selected list, and only the selected list, has been changed:

listA
(* {1, 1, 1} *)

listB
(* {2, 2, 2} *)

listC
(* {3, 3, 3, 999} *)

listD
(* {4, 4, 4} *)

If we are only interested in the result of the random append operation without actually updating the corresponding variable, then all we need to do is use Append instead of AppendTo:

Append[RandomChoice[{listA, listB, listC, listD}], 999]

(* {3, 3, 3, 999} *)
$\endgroup$
  • $\begingroup$ This works pretty much like I wanted, although I would use f:=... rather than just f=... for my specific case. The one thing that I'm confused about is: how does the Hold get broken? $\endgroup$ – 404UserNotFound Jan 7 '16 at 19:30
  • $\begingroup$ The Cases expression transforms each element of the Hold expression into a function. It is at that point that each list variable gets notionally moved out of the Hold and into a Function. A function definition is also a held context, so the list variable is still not evaluated. It is only when the function is called that the list variable finally appears in a context in which it is evaluated. $\endgroup$ – WReach Jan 7 '16 at 20:49
  • $\begingroup$ I don't know your full context, but if you are using f := ..., then I'd probably recommend that you use f[] := ... instead. This is a matter to style rather than necessity. I'd be suprised, for example, if {f, f} returned a list with different elements. But getting different elements from {f[], f[]} is unsurprising. Again, this is just a question of convention rather than a language requirement. You might also consider something like f[n_] := RandomChoice[fns][n]. $\endgroup$ – WReach Jan 7 '16 at 20:59
3
$\begingroup$

With, for instance,

listA = {1, 2}; listB = {3, 4}; value = 5;

the command

RandomChoice[{AppendTo[listA, value], AppendTo[listB, value]}]

appends value to all lists and then selects one of them, as can be seen from.

{listA, listB}
(* {{1, 2, 5}, {3, 4, 5}} *)

As an alternative, use

RandomChoice[{listA, listB} = Map[Join[#, {value}] &, {listA, listB}]]

which is more compact for a large set of lists. It too appends value to each list before selecting one.

However, if you actually wish to append value only to the randomly selected list without changing the values of the original lists, use the suggestion by march in a comment above.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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