C. E.
C. E.

Reputation: 10607

Dynamic in Mathematica without duplication of code

I'm trying to create three separate graphs, this code will give you the idea:

f[t_] := Sin[10 t] + Cos[15 t];
Slider[Dynamic[dx], {0.01, 1}]
var = Dynamic[Fourier[Table[f[t], {t, 0, 100, dx}]]];
ListLinePlot[Abs[var]]
ListLinePlot[Re[var]]
ListLinePlot[Im[var]]

This won't work because var hasn't been evaluated an so ListLinePlot/Abs/Re/Im does not recognize it as a list of numbers. Dynamic has to wrap ListLinePlot.

Wrapping ListLinePlot and everything else with Dynamic works. But then I would have to calculate Fourier[Table[... once for each graph. Per principle, I don't want to have this duplication of code.

This is a way that avoids duplication of code but is not as semantic as my proposed not working example above, plus it puts all series in one graph and not in three separate:

Dynamic[
 ListLinePlot[
  (#[Fourier[
       Table[f[t], {t, 0, 100, dx}]
       ]]) & /@ {Abs,Re,Min}, DataRange -> {0, 100}
  ]
 ]

Hopefully you can see now what I am trying to achieve. Something like my first piece of code except it should work. How can I do that?

Upvotes: 2

Views: 294

Answers (3)

Szabolcs
Szabolcs

Reputation: 25703

In most cases you only need to wrap Dynamic around the expression that needs to be recomputed. As you noticed, if you wrap Dynamic around the contents of var, it will not work because ListPlot will see a Dynamic head, not the list, when you pass var to it. What needs to be recomputed in this case is the complete ListPlot.

The correct solution is to use a delayed definition for var (i.e. := instead of =) and wrap Dynamic around ListPlot:

f[t_] := Sin[10 t] + Cos[15 t];
Slider[Dynamic[dx], {0.01, 1}]

var := Fourier[Table[f[t], {t, 0, 100, dx}]];

Dynamic@ListLinePlot[Abs[var]]
Dynamic@ListLinePlot[Re[var]]
Dynamic@ListLinePlot[Im[var]]

People often get confused with Dynamic because it sometimes shows up deep within in expression, e.g. in your Slider example. But there Dynamic has a different function: setting a value.

Generally, unless used to set a value, Dynamic always needs to be the outermost head in an expression. (There are some exceptions, notably when we're handling expressions that directly correspond to what is shown on screen, and are handled by the front end, such as graphics primitives: Slider[Dynamic[x], {0, 5}], Graphics[{Disk[], Dynamic@Disk[{x, 0}]}] will work.)

Dynamic affects only the way expressions are displayed in the front end, not how the kernel sees them. Here's an example:

x=1
arr = {Dynamic[x], 2, 3}

The Front End will display arr as {1, 2, 3}, but the kernel still sees it as {Dynamic[x], 2, 3}. So if we calculate Total[arr], the front end will display it as 1 + 5 but the kernel will see if as Dynamic[x] + 5. I hope this clarifies the situation a bit.

Note: I did not want to use Manipulate in this solution because the OP didn't use it either. Manipulate is just a high level convenience function and everything it does can be achieved with Dynamic and some controls such as Slider.

Upvotes: 4

Mr.Wizard
Mr.Wizard

Reputation: 24336

You probably want something like this:

f[t_] := Sin[10 t] + Cos[15 t]

DynamicModule[{var},
 Manipulate[
  var = Fourier[Table[f[t], {t, 0, 100, dx}]];
  {ListLinePlot[Abs[var]],
   ListLinePlot[Re[var]],
   ListLinePlot[Im[var]]},
  {dx, 0.01, 1}
]]

Mathematica graphics

Upvotes: 2

celtschk
celtschk

Reputation: 19731

Untested:

f[t_] := Sin[10 t] + Cos[15 t];
Slider[Dynamic[dx], {0.01, 1}]
Dynamic[var = Fourier[Table[f[t], {t, 0, 100, dx}]]];
Dynamic[ListLinePlot[Abs[var]]]
Dynamic[ListLinePlot[Re[var]]]
Dynamic[ListLinePlot[Im[var]]]

I think this should calculate Fourier just once. From my understanding, the ListLinePlots should be triggered by the change of var after evaluating Fourier (note that the assignment of var is inside the Dynamic).

Upvotes: 0

Related Questions