The issues in this question seem to arise frequently, and so I thought it might be worth analyzing them in some detail.
Avoid Gratuitous Modules
Here's the anti-pattern:
testModule[{func0_, a0_}] :=
Module[{func = func0, a = a0}, Integrate[func[x, a], {x, -1, 1}]]
Module
buys you nothing in this situation. Ironically, the only symbol that might need localization is x
, but we'll return to that later. If you look at the DownValues
for testModule
, you'll see the use of HoldPattern
and RuleDelayed
, and so the arguments func0
and a0
are sufficiently protected. In the vast majority of situations, you can safely do just:
testModule[{func0_, a0_}] := Integrate[func0[x, a0], {x, -1, 1}]
Understand SetDelayed's HoldAll attribute
Setting aside the issues with trying to use SetDelayed
in With
, let's look at:
func0[x_, a_] := vExpressions[[i]][[1]]
No matter what definitions exist for vExpressions
, the resulting DownValues
for func0
will look like this:
{HoldPattern[func0[x_, a_]] :> vExpressions[[1]][[1]]}
(I've replace i
with 1
because the original intent was to use i
as a Table
iterator.)
When we try to evaluate something like func0[5, 5]
, the evaluator will try to substitute 5
into the right-hand side wherever it finds x
(and a
). Since x
(and a
) don't exist in the right-hand side, that will be a no-op. Evaluation will continue and we'll end up with a^2 + x^2
.
You can force the right-hand side to evaluate to the expression you want like this:
func0[x_, a_] := Evaluate[vExpressions[[1]][[1]]]
Consider ReplaceAll when starting with expressions
Let's define an alternate for testModule
:
option1Test[expression_, assignment_] := Integrate[expression /. assignment, {x, -1, 1}]
We expect expression
to be some sort of algebraic expression written in terms of a
and x
, and we expect assignment
to be replacement rules. Given that, we need to alter your expressions a bit:
option1Expressions =
Thread[{{a^2 + x^2, a - x, a + x, Exp[a*x]}, Thread[Rule[a, {1, 2, 1, a}]]}]
{{a^2 + x^2, a -> 1}, {a - x, a -> 2}, {a + x, a -> 1}, {E^(a*x), a -> a}}
We can now feed this into our function:
option1Test @@@ option1Expressions
{8/3, 4, 2, (2*Sinh[a])/a}
Consider using formal symbols
The problem with the above is that our x
and a
symbols are exposed in the Global context. Any assignments we make to them will corrupt our results. You can play games with Hold
/Unevaluated
/Inactive
and the like, as well as setting attributes for your function, but this becomes obnoxious quickly. So, just choose variables that cannot be (trivially) given definitions. You can do this yourself with your preferred symbols, or you can just use the built-in \[Formal*]
system symbols, which are protected by default.
option2Expressions =
Thread[{{\[FormalA]^2 + \[FormalX]^2, \[FormalA] - \[FormalX], \[FormalA] + \[FormalX], Exp[\[FormalA]*\[FormalX]]}, Thread[Rule[\[FormalA], {1, 2, 1, a}]]}];
option2Test[expression_, assignment_] := Integrate[expression /. assignment, {\[FormalX], -1, 1}]
Avoid implicit coupling
The problem with the above is that our option2Test
function assumes that \[FormalX]
will be in the expression. We could make that coupling explicit by introducing another argument to the function that tells us what symbol we're integrating on. This might be nice if we were doing some processing upstream of this part of the workflow, but the way the situation is described, this introduces unnecessary effort. All of our expressions are in x
, and so it feels verbose to repeat the x
. It's not difficult, just a bit awkward:
option3Expressions =
Thread[{{\[FormalA]^2 + \[FormalX]^2, \[FormalA] - \[FormalX], \[FormalA] + \[FormalX], Exp[\[FormalA]*\[FormalX]]}, Thread[Rule[\[FormalA], {1, 2, 1, a}]], \[FormalX]}];
option3Test[expression_, assignment_, symbol_Symbol] :=
Integrate[expression /. assignment, {symbol, -1, 1}]
Consider using Functions
Since you wanted Function
s anyway, why not just start with them? It's not quite as readable, but it's not that bad:
option4Expressions =
Thread[{{#2^2 + #1^2 &, #2 - #1 &, #2 + #1 &, Exp[#2*#1] &}, {1, 2, 1, a}}];
option4Test[function_, parameter_] :=
Integrate[function[\[FormalX], parameter], {\[FormalX], -1, 1}]
This is pretty good, and is probably where I'd stop for most situations, however...
Use Module to localize symbols
We still cannot guarantee that \[FormalX]
hasn't been redefined. This is an appropriate use of Module
:
option5Test[function_, parameter_] :=
Module[{x}, Integrate[function[x, parameter], {x, -1, 1}]]
Use Contexts and attributes to further protect your private symbols
This is probably going too far for this question...
x
, does not have a value previously assigned to it before you evaluate all of this? $\endgroup$Function
and theInactive
-framework. E.g.Activate[Inactive[Function][{x, a}, expr]]
for any expression involvingx
anda
. You may want to check that neitherx
nora
have values assigned, usingValueQ[x]
etc. $\endgroup$