The idea
If you adhere to the convention that the size
argument is always first in all your functions, then what you ask for can be achieved with some metaprogramming / dynamic environments. A dynamic environment is a function that takes your code, and generally modifies some definitions locally, only for the code that runs inside it.
Preparation
As a simple example, consider these 3 functions:
diag[size_, diagElement_] := DiagonalMatrix[ConstantArray[diagElement, size]]
identity[size_] := IdentityMatrix[size];
norms[size_, genF_, dotF_] := Outer[dotF, #, #] &[genF /@ Range[size]]
They take the matrix size and sometimes other parameters, and generate various matrices. For example:
diag[2, 10]
(* {{10, 0}, {0, 10}} *)
identity[2]
(* {{1, 0}, {0, 1}} *)
norms[
2,
Function[x, HermiteH[#, x]] &,
Function[{l, r}, Integrate[l[y]*r[y]* Exp[-y^2], {y, -Infinity, Infinity}]]
]
(* {{2 Sqrt[\[Pi]], 0}, {0, 8 Sqrt[\[Pi]]}} *)
where the last matrix is made of dot products of Hermite polynomials.
Automating redefinitions
What I suggest to do is to create a dynamic environment in which all these functions will be redefined to automatically assume the first (in this case) argument size
to some fixed value.
Here is the code:
redefine[syms:{___Symbol}, f_]:=
Module[{inSym},
Scan[
Function[sym,
With[{protected = Unprotect[sym]},
sym[args___] /; !TrueQ[inSym[sym]] :=
Internal`InheritedBlock[{inSym},
inSym[sym] = True;
f[sym, {args}]
];
Protect[protected]
]
],
syms
]
]
withCurried[fixedParams__][affectedFunctions___Symbol] := Function[
code
,
Internal`InheritedBlock[{affectedFunctions},
redefine[
{affectedFunctions},
Function[{s, a}, s[fixedParams, Sequence @@ a]]
];
code
]
,
HoldAll
]
The first function takes a number of symbols and a function, that takes the symbol, the argument passed to that symbol, and then uses that to do arbitrary computation with those.
The second function takes a number of fixed argument values in the first group of arguments, and a number of symbols in the second, and creates a dynamic environment where all these symbols are redefined such that these fixed arguments are automatically prepended to the list of passed arguments when these functions are called.
Illustration
Let's assume we want to fix the size
argument value to be 3
, and we want all 3 functions from the example above, to be affected. We create a dynamic environment, and save it in a variable:
dynenv = withCurried[3][diag, identity, norms]
We are now ready to use it:
dynenv[{
diag[10],
identity[],
norms[
Function[x, HermiteH[#, x]] &,
Function[{l, r}, Integrate[l[y]*r[y]* Exp[-y^2], {y, -Infinity, Infinity}]
]
}]
(*
{
{{10, 0, 0}, {0, 10, 0}, {0, 0, 10}},
{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}},
{{2 Sqrt[\[Pi]], 0, 0}, {0, 8 Sqrt[\[Pi]], 0}, {0, 0, 48 Sqrt[\[Pi]]}}
}
*)
You can see that now we have omitted the first parameter (size
) in the calls to all these functions, yet they got the value 3
automatically passed to them.
You can create as many dynamic environments as you want, with different values of parameters (size
in this case) embedded into them. The enviroments only affect the code that runs inside them. The global definitions of functions diag, identity, norms
did not change.
This seems to be a more economical solution that creating new symbols in some new contexts. The only problematic case I can see for this approach is when you need to use these functions with different settings of size
within the same piece of code.