3

During the execution of the following code a column of three lists and two sliders are displaying in output. The first list updates if either a or b changes. The second list updates only if the value of a is changed. Similarly, the third list updates only if the value of b is changed.

a = 0;
b = 0;
Column[
{
  Dynamic[{a, b}, TrackedSymbols :> {a,b}],
  Dynamic[{a, b}, TrackedSymbols :> {a}],
  Dynamic[{a, b}, TrackedSymbols :> {b}],
  Slider[Dynamic[a], {0, 1}],
  Slider[Dynamic[b], {0, 1}]
}
]

Next I would like to replace {a,b} by a list x with two elements:

x = {0, 0};
Column[
{
  Dynamic[{x[[1]], x[[2]]}, TrackedSymbols :> {x}],
  Dynamic[{x[[1]], x[[2]]}, TrackedSymbols :> {x[[1]]}],
  Dynamic[{x[[1]], x[[2]]}, TrackedSymbols :> {x[[2]]}],
  Slider[Dynamic[x[[1]]], {0, 1}],
  Slider[Dynamic[x[[2]]], {0, 1}]
}
]

It is easy to check that now only the first list updates. Please help me to solve this problem.

m_goldberg
  • 107,779
  • 16
  • 103
  • 257
Vahagn Poghosyan
  • 532
  • 2
  • 10
  • 4
    My first thought is that x[[1]] is not a Symbol. The dynamic updating system probably only tracks symbols, not parts of them, so probably you can't do it the second way. – Michael E2 Sep 17 '13 at 15:09
  • I believe TrackedSymbols must be taken strictly; i.e., it accepts only symbols, not expressions. – m_goldberg Sep 17 '13 at 16:00
  • I presume you are aware that you can write {a, b} = {0, 0} in your first example, so I ask: why doesn't that work for you? – m_goldberg Sep 17 '13 at 16:10
  • @m_goldberg, thank you for your comment. I want to write a code, which manipulates a big list (say 20 elements). I want to display the list and update it only when its first element is changes. Moreover, I don't want to use additional "tester" variables like Dynamic[x[[1]], (x[[1]] = tester = #1) &] ... TrackedSymbols :> {tester} – Vahagn Poghosyan Sep 17 '13 at 16:53

2 Answers2

3

You can create your own list of symbols with Unique.

Clear[x];
nVars = 20;
x = Table[Unique[x], {nVars}];
vars = Map[Hold, OwnValues@x, {-1}][[-1, -1]]
(# = 0.) & /@ x; 

(* {Hold[x$363], Hold[x$364], ...} *)

It's convenient to store the held variables, since once they are initialized, it's tricky to get the symbols unevaluated. We can get at the symbols via various tricks:

vars[[2]] /. Hold[var_] :> (TrackedSymbols :> {var}
(* --> TrackedSymbols :> {x$364} *)

Dynamic @@ vars[[3]]]
(* --> Dynamic[x$364] *)

Here's an example like the one in the question.

Grid[Transpose@
  {Table[Dynamic[x, #] &[vars[[i]] /. Hold[var_] :> (TrackedSymbols :> {var})],
     {i, Length@x}],
    Table[Slider[Dynamic @@ vars[[i]]], {i, Length@x}]}
]

Here's the output for nVars = 4, where each slider has been clicked once in turn:

Screenshot of output

Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • Thank you for your smart trick. I would like to ask you, what is the role of the Refresh function ? Namely, why you don't use shorter possibility like this

    Table[Slider[Dynamic @@ vars[[i]]], {i, Length@x}]

    Table[Dynamic[x, Evaluate[vars[[i]] /. Hold[var_]:> (TrackedSymbols :> {var})]], {i, Length@x}]

    – Vahagn Poghosyan Sep 17 '13 at 22:28
  • Actually what confuses me is why I seem to have to inject TrackedVariables in there twice. Doing it once does not always work. In any case, your example does not work for me from a fresh kernel in V9.0.1. For the first Dynamic[x,...], I get symbols x$363 etc. that are never updated. Of all the variations I tried, only the one with Refresh worked in all cases. I'm afraid I have not completely figured out why yet. – Michael E2 Sep 17 '13 at 22:55
  • It is amasing, but I got such problem with your version. By the way, if I evaluate the code twice, the symbols are starting to uptate. I use also V9.0.1. In my example the sliders are constructed first. Maybe they initialize the sybols and the updating becomes possible ? – Vahagn Poghosyan Sep 17 '13 at 23:11
  • @VahagnPoghosyan Oops, I forgot to copy the initialization line -- your comment made me notice. Now it should work. I'm not sure why the symbols need initialization. For instance {Dynamic@y, Slider[Dynamic@y]} does not need to have y initialized beforehand. – Michael E2 Sep 18 '13 at 01:11
  • I must be missing something; why don't you merely define vars = Hold /@ x ? – Mr.Wizard Sep 18 '13 at 02:28
  • @Mr.Wizard It started as extra protection, since I was fiddling with things out of order. If the x$nnn are already initialized, Hold /@ x will hold their values, not the symbols. If you're willing to assume that the lines will be executed exactly as presented, then vars = Hold /@ x works fine. But if in the OP's application, things might happen in a different order, then this more robust definition seems a better way to go. – Michael E2 Sep 18 '13 at 02:47
  • I'm not sure why the symbols need initialization. For instance {Dynamic@y, Slider[Dynamic@y]} does not need to have y initialized beforehand.


    @MichaelE2 I think it comes from the fact that vars[[i]] represents held variable. Unfortunately it is not possible to use variables in the form Symbol["x"<>ToString[i]]. Always we get message "Tag Symbol in Symbol["x1"] is Protected".

    – Vahagn Poghosyan Sep 18 '13 at 08:13
  • @VahagnPoghosyan The expression returned to the front end has Dynamic[$CellContext`x$179095] in it -- the vars[[i]] are replaced in the kernel before they are sent back to the front end. The returned output looks the same to me for the y slider as for the x sliders. – Michael E2 Sep 18 '13 at 10:48
  • @MichaelE2 The mechanism of the calculations is very strange, but I solved this problem via Evaluate function:

    Table[Dynamic[Evaluate[x], Evaluate[vars[[i]] /. Hold[var_] :> (TrackedSymbols :> {var})]], {i, Length@x}]

    – Vahagn Poghosyan Sep 18 '13 at 10:58
  • @VahagnPoghosyan Yes, that is equivalent to Dynamic[x, #]&[vars[[i]] /. Hold[var_] :> (TrackedSymbols :> {var})], which I tried. I think my inclusion of Refresh may be a red-herring, as you questioned earlier -- now removed. I can't find the conditions that led to an error. All the ways seem to need variables in x to be initialized for them to work. – Michael E2 Sep 18 '13 at 12:59
  • @MichaelE2 Very good ! Now it works for all initial states of the kernel ! – Vahagn Poghosyan Sep 18 '13 at 13:11
2

Using the ideas/tricks/solutions given in comments, I arrived to the following compact code, which uses only the variables x, x1, x2, ...

nVars = 10;
Clear @@ Names["x" ~~ DigitCharacter ...]
x = Table[ToExpression["x" <> ToString[i], InputForm], {i, 1, nVars}]

Column[Table[
ToExpression["x" <> ToString[i], InputForm, Hold] /. Hold[var_] :>
    Dynamic[Evaluate[x], TrackedSymbols :> {var}]
, {i, 1, nVars}]]

Column[Table[
ToExpression["x" <> ToString[i], InputForm, Hold] /. Hold[var_] :> Slider[Dynamic[var]]
, {i, 1, nVars}]]
Vahagn Poghosyan
  • 532
  • 2
  • 10
  • Feel free to unaccept mine and accept your own. I think it is better, and future visitors would appreciate having the best answer marked. Unless someone else posts an even better solution. – Michael E2 Sep 18 '13 at 13:08
  • @MichaelE2, this answer is based on the ideas of your answer and comments. Both answers are acceptable for me. – Vahagn Poghosyan Sep 18 '13 at 13:15