>
> How can I make a function dynamically define a conditional function?
>
> Given a list of arguments {{a,A}, {b,B}, ...} I want to write a function
> that will take these arguments, and generate a new function, f say,
> which is defined as (for example):
> f[x_] := x+a /; x==A
> f[x_] := x+b /; x==B
> etc.
>
> So, the obvious solution is to define a function as follows:
>
> In[1] := TestFn[data_List] := Module[{args},
> ClearAll[f];
> Do[
> args = data[[i]];
> f[x_] = x + args[[1]] /; x==args[[2]],
> {i, Length[data]}
> ]]
>
> and call it using something like TestFn[{{1,2},{3,4},{5,6}}].
>
> But this doesn't work (see attached notebook) as it appears that
> Mathematica does not evaluate any part of the condition at the time of
> definition, so args[[2]] remains unevaluated. As a consequence, the
> resulting function definition is not properly defined.
>
> So, the obvious solution to this is to wrap Evaluate[] around the
> condition (i.e. define the function as f[x_] = x + args[[1]] /;
> Evaluate[x == args[[2]]]. And this appears to work. If you do ?f, then
> you see a function comprising a number of conditional definitions.
> However, if you come to use the function, then it appears that
> Mathematica does not perform the condition test that appears in the
> definition! It simply uses the first definition it finds.
>
> What is going on?! How can I make this work?
>
> I attach a notebook with example code. [Contact Paul to
> get this notebook - Moderator]
>
> Many thanks for any help.
>
> Paul
>
What happens is this: SetDelayed (or := as it is usually called) has
an attribute HoldAll su that neither the left hand side (TestFn[data_List])
nor the right-hand side (Module[{args}, ... ]) are evaluated at the time.
When you call the function with TestFn[{{1,2},{3,4},{5,6}}] what happens is
this: The loop gets evaluated Lenght[data_List] times (in this case 3);
you assign the i-th element of data_List to the temporary element args (which
is unnecessary since you can use data[[i,1]] and data[[i,2]] instead) and then
attempt to use Set (=) and Condition (/;) to define you functions. This
doesn't work because Condition has an attribute HoldAll which prevents
x==args[[2]] from being evaluated. Now, the thing to ude with HoldAll
attribute is Evaluate, but if you define
In[1]:= TestFn[data_List] := Module[{args},
ClearAll[f];
Do[
args = data[[i]];
f[x_] = x + args[[1]] /; Evaluate[x==args[[2]]],
{i, Length[data]}
]]
it will appear to work:
In[2]:= TestFn[{{1,2},{3,4},{5,6}}];
In[3]:= ?f
Out[3]= Global`f
f[x$_] = 1 + x$ /; x$ == 2
f[x$_] = 3 + x$ /; x$ == 4
f[x$_] = 5 + x$ /; x$ == 6
but when you try to use it it won't work after all:
In[4]:= {f[1],f[2],f[3],f[4],f[5],f[6]}
Out[4]= {2 /; 1==2,3 /; 2==2, 4 /; 3==2, 5 /; 4==2, 6 /; 5==2,7 /; 6==2}
What happens here is this: the args part in x==args[[2]] gets evaluated,
since it doesn't give you either True or False, it's left unevaluated, and the
whole right-hand side gets assigned to f[x$_] like this:
f[x_] = ( x + args[[1]] /; Evaluate[x==args[[2]]] ),
so that the f[x_] is defined to be Condition[x + args[[1]], x==args[[2]]]]:
f[x$_] = Condition[1 + x$ , x$ == 2]
f[x$_] = Condition[3 + x$ , x$ == 4]
f[x$_] = Condition[5 + x$ , x$ == 6]
which againd has the attribute HoldAll so that the arguments don't get
evaluated. Putting any additional Evaluate commands won't change anything
since they will be evaluated at the time Set is evaluated and when you latter
call the function f[4] (for example), 4 will mathch x$_ (everything will) and
you'll get the result back 5/; 4==2. The thing to do is:
In[5]:= TestFn[data_List] :=(ClearAll[f];Do[
f[x_] := Evaluate[ x + data[[i,1]] /; Evaluate[ x==data[[i,2]]]],
{i, Length[data]}])
Since SetDelayed has attributes HoldAll, the whole right rand side of the
equation is wrapped in Evaluate, so that Condition is evaluated, and then the
other Evaluate after /; is needed to evaluate the data[[i,2]] part. What
you're left with looks the same as the last cas
In[6]:= TestFn[{{1,2},{3,4},{5,6}}];
In[7]:= ?f
Out[7]= Global`f
f[x$_] := 1 + x$ /; x$ == 2
f[x$_] := 3 + x$ /; x$ == 4
f[x$_] := 5 + x$ /; x$ == 6
but now it gets interpreted like you want it,
SetDelayed[f[x$_] , Condition[1 + x$, x$ == 2]]
so each time you type something like f[x] Mathematica tries to match x
to the second argument of the Condition command and then use the appropriate
definition:
In[8]:= {f[1],f[2],f[3],f[4],f[5],f[6]}
Out[8]= {f[1],3,f[3],7,f[5],11}
To finish this email, since you aren't using any patterns in the right hand
side of Condition (you have pure numbers like 2,4,6) it's a much better idea
to use something like
TestFn[data_List] :=(ClearAll[f];Do[
f[data[[i,2]]] =data[[i,2]] + data[[i,1]] ,{i, Length[data]}])
It will work in most cases and it's much faster.
Bye, Bojan
-------------------------------------------------------------
Bojan Bistrovic, bojanb at physics.odu.edu
Old Dominion University, Physics Department, Norfolk, VA
-------------------------------------------------------------