# Define Function with Many Variants

I need to define many variants of a function which take special values when any of the arguments are zero (or the calculation can be significantly simplified). I can imagine this can be done programmatically, but so far I have not found how.

For example, consider the following example:

f[0, 0] = 0;
f[x_, 0] = Integrate[foo[xx, 0], {xx, 0, x}];
f[0, y_] = Integrate[foo[0, yy], {yy, 0, y}];
f[x_, y_] = Integrate[foo[xx, yy], {xx, 0, x}, {yy, 0, y}];


This is already a little annoying to do with 2 arguments, but I need to do something similar with a 6-function arguments...

Any hint as to how this can be done?

Even better, I'm defining these functions within another function and thus I know which argument(s) will be zero at run time. Thus I have currently something like:

process[f[n_, m_, a_, b_]] := Block[{disc},
disc[0, 0] = 0;
disc[x_, 0] = Sum[auxFunction[f[n, m, x, 0], v], {v, {x}}];
disc[0, y_] = Sum[auxFunction[f[n, m, 0, y], v], {v, {y}}];
disc[x_, y_] = Sum[auxFunction[f[n, m, x, y], v], {v, {x, y}}];

disc[a, b] / (2 I)
];


It is clear that as the Block is being evaluated, I will know which of a and/or b will be 0 thus only one of the 4 variants needs to be computed.

• How many times will you run the code, vs. how many times will you write the code? :p – Marius Ladegård Meyer May 28 '20 at 11:38

You may use Sequence and Nothing to build the Integrate variable list with Formal Symbols to prevent naming conflicts with variables outside of your function.

With

foo[x_, y_, z_] := x + 2 y + 3 z


then

f[x_, y_, z_] :=
Integrate[
foo[\[FormalX], \[FormalY], \[FormalZ]],
Sequence @@ {
If[x != 0, {\[FormalX], 0, x}, Nothing],
If[y != 0, {\[FormalY], 0, y}, Nothing],
If[z != 0, {\[FormalZ], 0, z}, Nothing]
}
];


Which gives the desired results

{#, f @@ #} & /@
Rest@Tuples[{0, 1}, 3] //
Prepend[{"{x,y,z}", "f[x,y,z]"}] //
Grid


Hope this helps.

• Close, but your example doesn't quite give the right result. There shouldn't be any dependence on the formal symbols in the output. The other thing is that I want to use Set and not SetDelayed as the 0-cases can be precomputed more easily. – JP-Ellis May 29 '20 at 2:33
• @JP-Ellis The formal symbols are easily replaced: f[0, 1, 0] /. Thread[{\[FormalX], \[FormalY], \[FormalZ]} -> {x, y, z}] . They are needed in the function as "safe" variables to use for Integrate. – Edmund May 29 '20 at 3:16
• And what about using Set vs SetDelayed? – JP-Ellis May 30 '20 at 4:56

I have found a way to do exactly what I want with some help from @Edmund's answer.

The full answer, using Integrate as the example is:

Off[RuleDelayed::rhs];
Do[
With[{
head = f @@ (vars /. x : (a | b | c) :> x_),
opts = vars /. {
x : (a | b | c) :> {x, 0, x},
0 -> Nothing
},
replacement = Thread[Complement[{a, b, c}, vars] -> 0]
},
If[Length[opts] == 0,
,
head = Integrate[2 (a + b + c), Sequence @@ opts] /. replacement;
]
]
,
{vars, Tuples[{{0, a}, {0, b}, {0, c}}]}
]


Checking the definitions of f once this has been evaluated gives:

f[0,  0,  0 ] = 0
f[0,  0,  c_] = c^2
f[0,  b_, 0 ] = b^2
f[0,  b_, c_] = b c (b+c)
f[a_, 0,  0 ] = a^2
f[a_, 0,  c_] = a c (a+c)
f[a_, b_, 0 ] = a b (a+b)
f[a_, b_, c_] = a b c (a+b+c)


as desired, with the right hand side begin evaluated already.

A breakdown of how this all works:

1. The Do loop goes through every combinations of the arguments being possibly non-zero as generated by Tuples[{{0, a}, {0, b}, {0, c}}].
2. The With function generates the left-handed side of Set. That is, head will be f[a_, 0, b_] for example. This must be done by using With as Set holds the left hand side remains unevaluated which prevents placing f @@ (...) directly.
1. vars /. x : (a | b | c) :> x_ converts the variables with the corresponding pattern. Note that since we have a pattern on the right hand side, Mathematica will highlight this as being wrong and will produce error messages (which are disabled by Off[RuleDelayed::rhs] but this is one of the few cases where we do want patterns on the right hand side of the replacement.
2. The opts variable stores the integration variables and bounds which will be used by Integrate, replacement 0 with nothing.
3. The replacement variable is used to replacement unintegrated variables by 0 (as they would in fact be integrated from 0 to 0.
3. In the body of With we can now perform the definition of f with
head = Integrate[2 (a + b + c), Sequence @@ opts] /. replacement;

which is evaluated as if the right hand side was f[a_, 0, b_] (and similar).