I am not sure how to initialize a struct in Mathematica for use with Manipulate to help me better organize the many control variables I have.
I'll give few examples, which helps show the issue.
Here is the most basic Manipulate:
method 1
Clear["Global`*"]
Manipulate[process[a, b],
{{a, 1, "a"}, -10, 10, 1},
{{b, 2, "b"}, -10, 10, 1},
Initialization :>
{
process[a_, b_] := Module[{}, Row[{"a=", a, " b=", b}]]
}
]
In a demo I am writing, I have many more controls. Some functions (such as the function called process[] in the above) end up having to be called with 15 and more arguments, which makes it hard to maintain for me. (I have no choice about this, can't really factor it out).
So, I am trying to use a struct to collect these variable in. Related question is this
Setting up a "struct" in Mathematica safely
So, next I tried this below: (I know this choice of making a struct in Mathematica is not the best, but I wanted first to see if it will work)
method 2
Clear["Global`*"]
Manipulate[process[myData],
{{myData["a"], 1, "a"}, -10, 10, 1},
{{myData["b"], 1, "b"}, -10, 10, 1},
Initialization :>
{
process[myData_] :=
Module[{}, Row[{"a=", myData["a"], " b=", myData["b"]}]]
}
]
But the above does not work. I get
Manipulate::vsform does not have the correct form for a variable specification
So, I figured, ok, let me fill in the struct myself, before calling the process[] function, so I did this:
method 3
Clear["Global`*"]
Manipulate[
(
myData["a"] = a;
myData["b"] = b;
process[myData]
),
{{a, 1, "a"}, -10, 10, 1},
{{b, 1, "b"}, -10, 10, 1},
Initialization :>
{
process[myData_] :=
Module[{}, Row[{"a=", myData["a"], " b=", myData["b"]}]]
}
]
And this works. OK, but now, since I am actually using the Leonid macro method to set up my controls, explained here
How to define part of a Manipulate control variable definition to reduce code duplication
Now I tried method (2) above, using the macro method, and it works now! but the problem that remains, is how to initialize the struct fields. Here is method (2) above, using macro
method (2) using Leonid macro method to set up controls
Clear["Global`*"]
Manipulate[process[myData],
(*-- define 2 control variables, using the macro method --*)
Evaluate@With[{s1 = Function[{struct},
Grid[{
{Manipulator[Dynamic[struct["a"]], {-20, 20, 0.1}]},
{Manipulator[Dynamic[struct["b"]], {-2, 2, .5}]}
}],
HoldAll]
},
s1[myData]
],
Initialization :>
{
process[struct_] := Module[{},
Text[Grid[{
{"a=", struct["a"]},
{" b=", struct["b"]}
}]]
]
}
]
The above works, but myData is not initialized. And this is the question I have. I need to initialize it so that initial display on the screen will have some correct values.
I can use the Initialization section to do that, but now myData will become global, and this can cause a problem when I make snapshots of the Manipulate later on, as global data will be shared between snap shots:
method (2) above, initialized using global section
Clear["Global`*"]
Manipulate[process[myData],
(*-- define 2 control variables, using the macro method --*)
Evaluate@With[{s1 = Function[{struct},
Grid[{
{Manipulator[Dynamic[struct["a"]], {-20, 20, 0.1}]},
{Manipulator[Dynamic[struct["b"]], {-2, 2, .5}]}
}],
HoldAll]
},
s1[myData]
],
Initialization :>
{
myData["a"] = 1; (* now OK, but mydata is global *)
myData["b"] = 1;
process[struct_] := Module[{},
Text[Grid[{
{"a=", struct["a"]},
{" b=", struct["b"]}
}]]
]
}
]
When I try to initialize myData inside the Manipulate, it does not work, may be there is a trick to do this?
method (2) above, try to initialized inside Manipulate using None control
Clear["Global`*"]
Manipulate[process[myData],
(*-- define 2 control variables, using the macro method --*)
Evaluate@With[{s1 = Function[{struct},
Grid[{
{Manipulator[Dynamic[struct["a"]], {-20, 20, 0.1}]},
{Manipulator[Dynamic[struct["b"]], {-2, 2, .5}]}
}],
HoldAll]
},
s1[myData]
],
{{myData["a"], 1}, None}, (*invalid syntax*)
{{myData["b"], 1}, None},
Initialization :>
{
process[struct_] := Module[{},
Text[Grid[{
{"a=", struct["a"]},
{" b=", struct["b"]}
}]]
]
}
]
So, my question is: How can I initialize myData in method (2) above, but without making it global?
I just wanted to add, that this is for a demo, so the rule is that it must start with Manipulate[..]. No Module outside. Everything must be inside Manipulate[...]
thanks
update:
I settled down to the pattern in (3) above, where I fill in the struct myself at the start.
It seems to work well, and made the calls to the many functions I have easier now, since the number of arguments is much reduced (I had functions being called with 20 arguments!, now it is reduced to 5 arguments, since I 'packaged' related control variables into one struct, now it is easier to work with). Not a perfect solution, but at least it helped make the code easier to work with for me.
This is how the 'pattern' looks now:
Manipulate[
Module[{myData},
(*fill in the struct *)
myData["a"] = a;
myData["b"] = b;
myData["c"] = c;
process[myData]
],
(*-- define control macro --*)
Evaluate@With[{s1 = Function[{var},
Manipulator[Dynamic[var], {-20, 20, 0.1}], HoldAll]
},
Column[{s1[a], s1[b], s1[c]}]
],
(*initialize fields of the struct, or the control variables*)
{{a, 1}, None},
{{b, 2}, None},
{{c, 3}, None},
Initialization :>
{
process[myData_] := Module[{},
Text[Grid[{
{"a=", myData["a"]},
{" b=", myData["b"]},
{" c=", myData["c"]}
}]]
]
}
]
I think Mathematica needs a real struct/record to help one organize variables in larger programs.