8
$\begingroup$

Here is a code sample that creates a control object which calculates the expressions inside Maniplate again and again and never stop (it's proved by the code in the note):

Sample 1:

Manipulate[date = Table[0, {num}];
 For[i = 1, i <= num, i++, date[[i]] = Sin[i]];(*Print@i;*)
 ListPlot[{date}], {{num, 2}, 1, 20, 1}]

While has the same problem:

Sample 2:

Manipulate[date = Table[0, {num}]; i = 1;
 While[i <= num, date[[i]] = Sin[i]; i++];
 ListPlot[{date}], {{num, 2}, 1, 20, 1}]

I know if I choose Do, the problem will no longer exist:

Sample 3:

Manipulate[date = Table[0, {num}];
 Do[date[[i]] = Sin[i], {i, 1, num}];
 ListPlot[{date}], {{num, 2}, 1, 20, 1}]

Still, I know I can solve the problem if I throw the expression into Module if what I'm using is For:

Sample 4:

Manipulate[Module[{i}, date = Table[0, {num}];
  For[i = 1, i <= num, i++, date[[i]] = Sin[i]];
  ListPlot[{date}]], {{num, 2}, 1, 20, 1}]

And…yeah, it doesn't work for the code above with While…:

Sample 5:

Manipulate[Module[{j}, date = Table[0, {num}]; j = 1;
  While[j <= num, date[[j]] = Sin[j]; j++];
  ListPlot[{date}]], {{num, 2}, 1, 20, 1}]
(*This still creates a problematic control object*)

How to solve the problem with While? what actually happens inside Manipulate? what's the exact reason for the endless loop?


OK, the problem with sample 5 turns out to be a collaboration of Sample 4 and Sample 5, before I run sample 5, a control object has been already created by Sample 4 (let's call it Object 4 below, and so we have Object 5 for Sample 5).I think the process should be like this: when date = Table[0, {num}]; in Sample 5 is executed, it's tracked by Object 4, and then For[i = 1, i <= num, i++, date[[i]] = Sin[i]]; in Sample 4 is executed, so it is tracked by Object 5…so this story tells us how important a good programing habit is…

$\endgroup$
11
  • 2
    $\begingroup$ Might be similar to this question $\endgroup$
    – cormullion
    Sep 18, 2012 at 6:55
  • $\begingroup$ @cormullion Yeah, quite similar and the solution with TrackedSymbols:> in your link works! But I still wonder why the solution with Module doesn't work for While $\endgroup$
    – xzczd
    Sep 18, 2012 at 7:14
  • $\begingroup$ @xzczd: why do you say that it wouldn't work for While? For me it does: Manipulate[Module[{i = 1}, date = Table[0, {num}]; While[i <= num, date[[i]] = Sin[i]; i++]; ListPlot[{date}]], {{num, 2}, 1, 20, 1}] won't result in an endless loop... $\endgroup$ Sep 18, 2012 at 12:45
  • $\begingroup$ @AlbertRetey Oh, I should have add the sample…in fact my i=1 is set in the expressions:Manipulate[Module[{j}, date = Table[0, {num}]; j = 1; While[j <= num, date[[j]] = Sin[j]; j++]; ListPlot[{date}]], {{num, 2}, 1, 20, 1}] $\endgroup$
    – xzczd
    Sep 18, 2012 at 12:54
  • 1
    $\begingroup$ @xzczd: just tried this one, doesn't loop endless for me either (8.0.4 on Windows 7 64bit). Which version are you using? Are you sure you didn't got fooled by another Manipulate shown at the same time? $\endgroup$ Sep 18, 2012 at 18:12

3 Answers 3

10
$\begingroup$

If you do not tell Manipulate which symbols to track, the default is Full

enter image description here

Hence use TrackedSymbols to explicitly tell it which symbols to track

Manipulate[
 date = Table[0, {num}]; i = 1;
 While[i <= num, date[[i]] = Sin[i]; i++];
 ListPlot[{date}],
 {{num, 2}, 1, 20, 1},
 TrackedSymbols :> {num}
 ]

Another solution is to use Module, and add i and date as the local symbols to the internal module. Now Manipulate will not track these as they are not its own symbols.

Manipulate[
 Module[{date, i},
  date = Table[0, {num}]; i = 1;
  While[i <= num, date[[i]] = Sin[i]; i++];
  ListPlot[{date}]
  ],
 {{num, 2}, 1, 20, 1}
 ]
$\endgroup$
3
  • $\begingroup$ Oh, I forgot date!…But in the sample with For, it still works fine though I only add i as the local symbol, why? $\endgroup$
    – xzczd
    Sep 18, 2012 at 10:44
  • $\begingroup$ Hehe, yeah, that's a good resource, and as I said in the comment below, this question is more for curiosity :D, and, I've added the most puzzled code to my question: that's indeed a sample that can't be solved without adding both i and date as the local symbols. $\endgroup$
    – xzczd
    Sep 18, 2012 at 13:18
  • $\begingroup$ Albert found the truth for While…see my edit for the question for more details. $\endgroup$
    – xzczd
    Sep 19, 2012 at 2:29
7
$\begingroup$

You can achieve the desired result more succinctly, without the need for the procedural For or While or Do which are rarely used in Mathematica, by using the functional programming features and built in list comprehension of functions such as Sin.

The functional style often removes completely the need for local or global variables to hold results for other pieces of code to work on.

It does this by wrapping layers of functions, like a coding onion, around the the key pieces of data, in this case the number of points you want to plot, num. Each function then works on the return value of the enclosed function.

The benefits of this are more concise code and fewer bugs because there is no "state", in the code which can become polluted by other parts of the code.

   Manipulate[ListPlot[Sin[Range[num]]], {num, 2, 20}]

Or even more concisely:

   Manipulate[ListPlot@Sin@Range@num, {num, 2, 20}]

where the code begins to read like a simple list of things to do num rather than a multi-line coding structure like For etc.

$\endgroup$
2
  • $\begingroup$ Hehe, yeah, I know, in fact the original question isn't raised by me but I found myself unable to tell the exact reason for the problem so I turn to here…still thanks for your vivid explain for the functional programming feature of Mma! $\endgroup$
    – xzczd
    Sep 18, 2012 at 10:50
  • $\begingroup$ No problem, I hope the onion didn't bring tears to your eyes :) $\endgroup$ Sep 18, 2012 at 10:52
3
$\begingroup$

Nasser has given two perfect solutions to the original problem. But there is still an open question: Why is there an infinite loop in some cases but not the others. I don't have an answer to that and this answer will just show how to test the different behaviour of the various cases.

As I have noted in one of the comments, there is an obvious problem if you show more than one of the following Manipulates at the same time. This is due to the global symbols used and relatively easy to understand: the change of e.g. the global date in one of the Manipulate will cause the other Manipulate to update which will change date again and cause the first to update and so on. For that reason, it is definitely a good idea to localize all variables within a Manipulate, which also would have solved the original problem in the first place (see Nassers second solution)...

Despite of that solution, it's still somewhat unclear when indefinite loops occur and when not. Let me start with the original Manipulate, where I have added a PlotLabel so we can easily see when an update occurs:

Manipulate[date = Table[0, {num}]; i = 1;
 While[i <= num, date[[i]] = Sin[i]; i++];
 ListPlot[{date}, PlotLabel -> DateString[]], {{num, 2}, 1, 20, 1}]

when evaluating it, it is obvious that the redefinitions of the global symbols in its body cause it to permanently update. We now know how to solve it (make sure to delete the output of the first before trying this):

Manipulate[date = Table[0, {num}]; i = 1;
 While[i <= num, date[[i]] = Sin[i]; i++];
 ListPlot[{date}, PlotLabel -> DateString[]], {{num, 2}, 1, 20, 1}, 
 TrackedSymbols :> {num}]

This will now only update when num is changed -- which is what we usually want. Note that even if we redefine e.g. date={}, the Manipulate won't update as the tracking of date is suppressed. Of course we can also track date (but not i), as here:

Manipulate[date = Table[0, {num}]; i = 1;
 While[i <= num, date[[i]] = Sin[i]; i++];
 ListPlot[{date}, PlotLabel -> DateString[]], {{num, 2}, 1, 20, 1}, 
 TrackedSymbols :> {num, date}]

This will now update when you do e.g. date={} (date will be redefined when the body of the Manipulate is evaluated so the plot will be the same, but you'll see the PlotLabel change). But also note that this doesn't cause an infinite loop, although the global symbol date is redefined within the body (and num+1 times, to be precise).

On the other hand, if we track i but not date, this causes an infinite loop (again, make sure to delete all the other outputs to exclude the mentioned problem):

Manipulate[date = Table[0, {num}]; i = 1;
 While[i <= num, date[[i]] = Sin[i]; i++];
 ListPlot[{date}, PlotLabel -> DateString[]], {{num, 2}, 1, 20, 1}, 
 TrackedSymbols :> {num, i}]

Why the tracking of i causes the infinite loop but that of date doesn't isn't something that I think can be understood from how the language works but seems to be detail of how the updating is internally implemented.

Of course the best solution is to localize all variables. There are cases where it isn't possible to work entirely with variables which are local within the most inner enclosing Dynamic or Manipulate. For these cases the correct automatic detection of when updates are necessary and when not seems to be difficult and doesn't always work as "expected". So it is, especially for larger graphical interfaces, often necessary to set the TrackedSymbols explicitly. I think that's something everyone working on anything but the most trivial Manipulates should be aware of...

$\endgroup$

Your Answer

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

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