I like to use With[]
with constants that I need to use in 2 different places.
Instead of typing the same long list of constants in 2 places, I have been trying to figure how to use a variable for this list, and then use this variable in the few places I want to use the list.
The problem is that I need to Hold
the list and then ReleaseHold
it later when time to use it, but I can't get this part right. (tried many things, nothing working for me)
Here is an example:
With[{$age = 2, $salary = 3},
x = $age
]
With[{$age = 2, $salary = 3},
y = $age
]
I do not want to type the same constants around (in my case, these are very long), and I was trying to do something like this:
c = HoldForm[{$age = 2, $salary = 3}]
With[Release[c],
x = $age
]
With[Release[c],
y = $age
]
I tried many other combinations of the above. So many version of Hold* and Release*, I find them all very confusing.
question is: Any one knows how to do the above, so I can reused the list in more than one place without actually copy it?
To put this in context, here is an example where this would be needed:
I can't do surround everything with With
in Manipulate: (and I can't put a With
outside Manipulate for what I am doing, everything must be inside Manipulate
)
Manipulate[
Evaluate@With[{$age = 2, $salary = 3},
x;
$age,
{{x, $age, "age="}, 0, 10, 1}
]
]
Not valid syntax. (due to the "," needed to separate the Manipulate
expression from the controls). (it now thinks With has 3 arguments)
I could ofcourse do
Manipulate[
With[{$age = 2, $salary = 3},
x;
$age
],
Evaluate@With[{$age = 2, $salary = 3},
{{x, $age, "age="}, 0, 10, 1}
]
]
But as you see, I had to copy the list of constants around.
If I can figure how to define the list once, I can put it in the Initialization
section on Manipulate
and use it, like this:
Manipulate[
With[ReleaseHold[myList],
x;
$age
],
Evaluate@With[ReleaseHold[myList],
{{x, $age, "age="}, 0, 10, 1}
],
Initialization :>
(
myList = HoldAll[{$age = 2, $salary = 3}];
)
]
I think what I want to do is possible, I just can't figure the correct commands to use for the Hold/Release hold part.
edit (1)
I thought I put an example of why I want to use constants in With
.
I came up with this new method :) to allow me to simulate a record or a struct.
The constant values will be the named field of the record (which is just a list really).
For field name, I give it a sequential number, (I start from 1) and I use $field=number
, and then in the code, I write struct[[$field]]=...
to access the field.
I need to share the values of the named fields to the struct between the Manipulate expression and the control area, since both need to use the same struct.
Here is a simple example below of a Manipulate that reads the age and current salary from the UI and the expression and assigns new salary based on current salary and ago.
The record is used to communicate the data between the control area and the expression and lower level functions.
In my current demo, I have hundreds of such parameters, (I actually have now few demos, all in one demo, and I flip between different UI layouts (controls) based on which choice is selected on the UI) and using records will simplify life for me, since now I can make function calls, and just pass few parameters, records that contain the UI parameters, and not 100's of the individual parameters, which is what I have to do now. As I said many times before, Mathematica needs a real record/struct as a basic data struct, in addition to List, and Array and such, which is integrated into M.
(the UI parameters have to be send, all, to lower level functions, and there is no other choice than doing this. I do not want to use global variables. Bad design).
I also can now pass this record by reference If I want to to allow updates to happen into it inside other much lower level functions. I am still evaluating this method to see if I can actually use it in my main code.
(*verison 1.1*)
Manipulate[
With[{$age = 1, $salary = 2, $newSalary = 3},
updateRecord[record_] := Module[{},
(*update/process UI input*)
record[[$newSalary]] = record[[$salary]] + record[[$age]]*10;
(*return result*)
record
];
(*call lower level function to process UI parameters*)
myRecord = updateRecord[Unevaluated@myRecord];
(*display the result *)
Grid[{
{"age=", myRecord[[$age]]},
{"current salary=", myRecord[[$salary]]},
{"new salary=", myRecord[[$newSalary]]}
}]
],
(* build the UI controls *)
Evaluate@With[{$age = 1, $salary = 2, $newSalary = 3},
Grid[{
{"age=",
Manipulator[Dynamic[age, {age = #; myRecord[[$age]] = age} &],
{10, 100, 1}, ImageSize -> Tiny], Dynamic[age]},
{"salary=",
Manipulator[
Dynamic[salary, {salary = #; myRecord[[$salary]] = salary} &],
{10, 10000, 10}, ImageSize -> Tiny], Dynamic[salary]}
}
]
],
{{myRecord, {10, 100, 100}}, None},
{{age, 10}, None},
{{salary, 1000}, None},
TrackedSymbols -> {age, salary}
]
edit(2)
On trying to use Leonid below, I can use it in the Manipulate expression, but can't figure how to use in the control area.
Here is the original example using With
in 2 places:
Manipulate[
With[{$age = 2, $salary = 3},
x + $age
],
Evaluate@With[
{$age = 2, $salary = 3},
{{x, $age, "age="}, 0, 10, 1}
],
{x, None}
]
now using the new Leonid method below, this is what I have so far:
Manipulate[
env[
x + $age
],
Evaluate@With[
{$age = 2, $salary = 3},
{{x, $age, "age="}, 0, 10, 1}
],
{x, None},
Initialization :>
(
ClearAll[makeCustomEnvironment];
SetAttributes[makeCustomEnvironment, HoldAll];
makeCustomEnvironment[values : (_Symbol = _) ..] :=
Function[code, With @@ Hold[{values}, code], HoldAll];
env = makeCustomEnvironment[$age = 2, $salary = 3];
)
]
But is it possible to use it for the control also? I can't just do this:
Manipulate[
env[
x + $age
],
env[
{$age = 2, $salary = 3},
{{x, $age, "age="}, 0, 10, 1}
],
{x, None},
Initialization :>
(
ClearAll[makeCustomEnvironment];
SetAttributes[makeCustomEnvironment, HoldAll];
makeCustomEnvironment[values : (_Symbol = _) ..] :=
Function[code, With @@ Hold[{values}, code], HoldAll];
env = makeCustomEnvironment[$age = 2, $salary = 3];
)
]
as the above gives many errors.
edit(3)
deleted as was incorrect
edit (4)
deleted content of edit(3) above as it included a user error on my side reporting an issue.
Here is the WRI support response to why I was getting the error
Manipulate::vsform: Manipulate argument env[{{age,100,age},10,200,1}] does
not have the correct form for a variable specification. >>
When I was writing the following code:
Manipulate[
env[
record[[$age]] = age;
record[[$salary]] = 60*age;
{record[[$age]], record[[$salary]]}
],
env[
{{age, 100, "age"}, 10, 200, 1}
],
{{record, {40, 5}}, None},
{{salary, 40000}, None},
TrackedSymbols :> {age},
Initialization :>
(
makeCustomEnvironmentAlt =
Function[Null, Function[code, With @@ Hold[{##}, code], HoldAll],
HoldAll];
env = makeCustomEnvironmentAlt[$age = 1, $salary = 2];
)
]
This is the support explanation of why this error came showed up:
The issue is specifically with the section:
Evaluate@env[{{age, 100, "age"}, 10, 200, 1}]
Manipulate doesn't really evaluate until it gets to the Initialization
option, but it will check its input for correct form. Mathematica reads the
main body of the Manipulate before running the Initialization option. This
is can be verified by using a Print statement:
Initialization -> (Print["Test"];
makeCustomEnvironmentAlt =
Function[Null, Function[code, With @@ Hold[{##}, code], HoldAll],
HoldAll];
env = makeCustomEnvironmentAlt[$age = 1, $salary = 2];
Print["Test"])
Test does not print.
Getting around this will be probably not be clean.
....
Having the code for the controller for age depend on evaluation of
some function which must be initialized does not appear to be possible
with simply Manipulate.
I hope this information helps. And thanks for everyone's help and also for WRI support and explanation.