10
$\begingroup$

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]}]]
$\endgroup$
1

3 Answers 3

14
$\begingroup$

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).

$\endgroup$
1
  • $\begingroup$ Thank you, that makes perfect sense. $\endgroup$
    – ebergerson
    Jul 17, 2012 at 19:10
7
$\begingroup$

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.

$\endgroup$
1
  • $\begingroup$ Yes, I see here, as above, the i was not being evaluated. Thank you for your help. $\endgroup$
    – ebergerson
    Jul 17, 2012 at 19:10
3
$\begingroup$

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

$\endgroup$
1
  • $\begingroup$ Thank you. Nice to see the alternative Array to Map above. I would not have thought of the alternative. $\endgroup$
    – ebergerson
    Jul 17, 2012 at 19:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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