10

In this example, I want a series of four buttons to change the value of a variable that is used dynamically to drive a plot. I am trying to figure out why using Table around the buttons causes a problem.

This works:

{Button["1", freq = 1], Button["2", freq = 2], Button["3", freq = 3], 
 Button["4", freq = 4]}
 Dynamic[Plot[Sin[freq * x], {x, 0, 2 \[Pi]}]]

Why doesn't this work:

Table[Button[ToString[i], freq2 = i], {i, 4}]
Dynamic[Plot[Sin[freq2 * x], {x, 0, 2 \[Pi]}]]
ebergerson
  • 341
  • 1
  • 8

3 Answers3

14

The second one does not work, because Button has HoldRest property:

In[1]:= Attributes[Button]
Out[1]= {HoldRest, Protected, ReadProtected}

Thus, i in your buttons are interpreted as literal i, not numbers.

The easiest way to solve this is using a Map:

Map[(Button[ToString[#], freq2 = #]) &, Range[4]]
Dynamic[Plot[Sin[freq2*x], {x, 0, 2 \[Pi]}]]

When each value is applied to the pure function, # will be replaced with the actual value.

PS

Just for fun, you can confirm these by using Shift-Ctrl-E (or Shift-Cmd-E on Mac) on your button cell (exposing underlying FE language). This is what you see when expanding the button cell from the original code:

cell

As you can see, the action assigns i to freq (all in $CellContext), not actual value (1, this case).

Yu-Sung Chang
  • 7,061
  • 1
  • 39
  • 31
7

Look at the FullForm of the two lists:

{Button["1", freq = 1], Button["2", freq = 2], Button["3", freq = 3], Button["4", freq = 4]} //FullForm
(* List[Button["1",Set[freq,1]],Button["2",Set[freq,2]],Button["3",Set[freq,3]],Button["4",Set[freq,4]]] *)

Table[Button[ToString[i], freq2 = i], {i, 4}] // FullForm
(* List[Button["1",Set[freq2,i]],Button["2",Set[freq2,i]],Button["3",Set[freq2,i]],Button["4",Set[freq2,i]]] *)

In the second case you Set your freq2 equal to the symbolic i, not to the value of the iteration variable. That's because Button has the attribute HoldRest.

A possible way out would be to inject the value in the variable using With, i.e.

Table[With[{j = i}, Button[ToString[i], freq2 = j]], {i, 4}] //FullForm
(* List[Button["1",Set[freq2,1]],Button["2",Set[freq2,2]],Button["3",Set[freq2,3]],Button["4",Set[freq2,4]]] *)

Moreover, this is referenced in the documentation for Button, at Scope $\rightarrow$ Button Control.

3

As already stated this is an evaluation problem because of the Hold attribute that Button has, and the mechanism of Table which is akin to Block. You need a way to get the value into the expression. In this case I would use Function and Array:

Array[Button[#, freq2 = #] &, 4]

Dynamic @ Plot[Sin[freq2*x], {x, 0, 2 π}]

(ToString is unnecessary.)

See the answers to this question for other options:

Function in Table

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371