Message Boards Message Boards

GROUPS:

Manipulate an array calculated outside Manipulate?

Posted 1 month ago
339 Views
|
8 Replies
|
1 Total Likes
|

I have an array named graphs which was calculated during a longer program. Actually, this is an array of Graphics like frames of a video, but here I just use a simple array with numbers. I want to show the array elements with Manipulate and possibly run them like a film.

Case 1: In the first naive approach, I just call Manipulate with the array and a StepManipulator-control for the frame number. The two symbols graphs within Manipulate are unevaluated or respectively unknown and highlighted red with a warning "'graphs' in ... occurs where it is probably not going to be evaluated before going out of scope". Obviously, the graphs within the Manipulate is not the same as outside.

Clear[graphs];

 Block[{graphs = {1, 20, 30, 40}},
  Print[Manipulate[
     graphs[[i]]
     , {{i, 1, "Step"}, 1, Length[graphs], 1, Appearance -> "Open"}
     , LabelStyle -> Directive[Black, Bold, 11], 
     ControlPlacement -> Bottom
     ]];
  ];

Case 1 graphs graphs

Case 2: In the second approach, I add an option SaveDefinitions -> True. Highlighting and warning remain, but now graphs takes on its proper value. And when it is changed at the beginning of the Block, this is reflected within Manipulate after the block is evaluated.

But graphs changes as well within the Manipulate in Case 1 above! Even when I reevaluate Case1 it does not get back its proper Case 1 value. Obviously, the SaveDefinitions has turned graphs into a global variable and the definition within Block is ignored.

(* Case 2 *)
Block[{graphs = {2, 20, 30, 40}},
 Print[Manipulate[
    graphs[[i]]
    , {{i, 1, "Step"}, 1, Length[graphs], 1, Appearance -> "Open"}
    , LabelStyle -> Directive[Black, Bold, 11], 
    ControlPlacement -> Bottom
    , SaveDefinitions -> True
    ]];
 ];

Case 2

graphs
{2, 20, 30, 40}

The documentation says "Manipulate is a scoping construct that implements lexical scoping" which might explain some of that unexpected behavior. But why does Case 2 work then? How does the outer graphs-value from Block get into Manipulate?

Anyway, I do not like that SaveDefinitions-option since it is needless and may lead to a huge notebook on disk.

Case x: I tried some other things with accessing graphs trough a trivial function defined inside or outside Manipulate. But this doesn't change anything.

Case 10: What works is to use an intermediate global array globGraphs fed from graphs.

(* Case 10 *)
Clear[globGraphs];
Block[{graphs = {10, 20, 30, 40}},
  globGraphs = graphs;
  Print[Manipulate[
    globGraphs[[i]]
    , {{i, 1, "Step"}, 1, Length[globGraphs], 1, Appearance -> "Open"}
    , LabelStyle -> Directive[Black, Bold, 11], 
    ControlPlacement -> Bottom
    ]];
  ];

Case 10

{graphs, globGraphs}
{{2, 20, 30, 40}, {10, 20, 30, 40}}

Now I have no highlighting and warnings anymore and everything works as expected. But that solution looks pretty strange.

Does anybody have a better solution?

Attachments:
8 Replies
Posted 1 month ago

I think that using "With" rather than "Block" to localize your "graphs" variable in Case 1 will give the desired result. (Of course, it's possible that I don't understand what your desired result is...)

Posted 1 month ago

"With" implements local constants, i.e. read-only lexical variables. In my case "graphs" is not a constant but calculated within the Block/Module before being displayed. Hence this does not help.

Posted 1 month ago

The Wolfram language supports many (perhaps too many) different programming paradigms and patterns. This often means that there are an infinite number of good solutions to every challenge.

In cases like this, when we do not have to use a global variable, I usually prefer to keep things local by passing them in with something like:

myApp[graphs_List] := Manipulate[
   graphs[[i]],
   {
    {i, 1, "Step"},
    1,
    Length[graphs],
    1,
    Appearance -> "Open"
    },
   LabelStyle -> Directive[Black, Bold, 11],
   ControlPlacement -> Bottom
   ];

myGraphs = {1, 20, 30, 40};
myApp[myGraphs]

I believe this solution:

  • Keeps things simple
  • Makes it clear that visualization never updates the input data
  • Allows the visualization to be reused

Of course, there are other simple solutions. And sometimes, keeping your data global is the best solution. In this case, I got the following to work:

graphs = {1, 20, 30, 40};

Manipulate[
 graphs[[i]],
 {
  {i, 1, "Step"},
  1,
  Length[graphs],
  1,
  Appearance -> "Open"
  },
 LabelStyle -> Directive[Black, Bold, 11],
 ControlPlacement -> Bottom
 ]

Also, I am curious as to why you are printing the Manipulate instead of just returning it?

Posted 1 month ago

Thanks very much, Mike. Your solution, packing the Manipulate into a function (myApp in your example) is exactly what does the job.

Concerning your question why I Print the Manipulate: The Manipulate-output is just part of some more output (a grid actually).

Posted 1 month ago

Werner:

You are welcome. Thanks for the great question. I'm still learning Manipulate. Working through this example taught me a great deal.

And thank you for explaining why you are using Print. And I agree, that Print does provide a solution to multiple outputs.

I try to avoid Print for anything but debugging. When I need multiple outputs, I usually return either an association, list, grid, column, or row. In my opinion, handling multiple outputs this way helps me solve some other challenges:

  • Testing. I believe in Test Driven Development as much as possible. By returning values instead of printing them, I can utilize Wolframs unit test framework.

  • Reuse. By returning an association, list, etc., I can reuse the function in many places.

  • Pipelines. More and more I am getting into the pipeline paradigm. For me, a pipeline is a set of functions that each take an association that serves as a payload. I then use composition to pass the payload through all of the functions. This helps me keep focus when coding by separating presentation from data prep from analytics.

Have a great summer

Posted 1 month ago

This summer is not so great unfortunately. Actually currently I am at a hospital for a few days. It's not Corona, but relatively harmless. Anyway, all those rules for daily life are not so agreeable. Paticularily in hospitals.

Concerning "Print" again: Just like probably you do I try to avoid global variables. Hence each functional piece of code is packed into a block or module. Not only functions but the final "main"-program as well. (Btw: I did not really understand why WL has Block and Module. I prefer Block because it acts similar to ordinary program call-stacks in other languages).

Hence, if you need multiple outputs from within one block you have to use either multiple prints or do what you do: Packing them all together into an overlayed larger structures and returning that from the block.

I can see your points why you do that, but for me most of the time this is just an additional more or less complicated layer which makes the program worse understandable and is functional superfluous. Print is a much more straight forward approach.

And, on top of that, what do you do if you have conditional outputs within that block? The standard WL-way of printing the returned variable at end of block will not work then. You would either need another function just for outputting your output structure. Or you would need a lot of different return-points within your block, which is really ugly and bad for testing.

Posted 1 month ago

I forgot to mention: For debugging I use conditional Echo only, never Print.

BTW: WL/Mathematica is a wonderful language, but I think it's the only one I have used during the last 30 or 40 years which does not have a debugger. This is totally incomprehensible for me and really stone-aged. (I know of course that there is something that calls itself a "debugger" but this is useless and obviously abandoned. And I know that higher-level software developing environments (like Eclipse and some others) can be used, at least with professional WL-licences.)

Posted 1 month ago

I hope things turn around for you this summer.

I agree with you on the debugger. But, I started my career debugging two-wire communication using an oscilloscope on the wires and a logic analyzer around the CPU. So, I am able to remind myself things could be much worse.

To answer your question on conditional outputs, I use a pipeline paradigm. You are correct, it does add a bit of overhead (more functions), but I am usually able to keep the functions smaller and focused on a single task.

One of the strengths of WL is that it supports so many different ways of doing things. Of course, that is also one of its biggest drawbacks. We cannot win them all.

Thanks for the conversation.

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract