5
$\begingroup$

I would like to build a block that outputs a pure function. Something like:

createfunction[data_]:= Block[{parameter,function},
                   parameter = data^2;
                   function = Function[x,x + parameter];
                   Return[function];
                   ];

I would like to use this module in another part of my notebook as

g = createfunction[data];

The problem is that if I do so, then I would get

In[1]:= g[x];
Out[1]:= x + a

I guess that the problem is something like: a is a local variable for block, Function[x, x + a ], does not actually substitute for a the value computed in the block (data^2), but wait till its called to do so, and since it is called after the block ran it will not work.

How to solve the problem?

_______________________________________________________________-

Update

A couple of answers were given that work for the current example, but not for the full problem I am working on, so I will post here a more dressed up example.

My problem is the following: given a set of points P_i in the xy-plane, compute the x mean $\mu_x$ and variance $\sigma_x$ , the y mean $\mu_y$ and variance $\sigma_y$, and output the multi-variate normal distribution p({x,y},$\mu_x$,$\sigma_x$,$\mu_y$,$\sigma_y$). Later I will want to work with function p in many ways.

I decided to solve this problem creating a function "train" that takes the list {P_1,...,P_m} and outputs p as a pure function. What train does is to compute the parameters:

train[data_] := 
  Block[{m = data // Length, i, j, density, \[Mu], \[Sigma]},
   \[Mu] = 
    1/m Table[Sum[data[[i, j]], {j, 1, data // Length}], {i, 1, 2}];
   \[Sigma] = 
    1/m Table[
      Sum[(data[[i, j]] - \[Mu][[i]])^2, {j, 1, m}], {i, 1, 2}];
   density = 
    Function[x, 
     Product[1/(Sqrt[
           2 \[Pi]] \[Sigma][[i]]) Exp[-(x[[i]] - \[Mu][[i]])^2/(2 (\
\[Sigma][[i]])^2)], {i, 1, 2}]];
   Return[density];
   ];

Now, it is difficult for me to see how to address this problem using With. I am not an expert in scoping with mathematica, but With seems to me as something you use when you want to use some constants, I would say something already known or quickly computed (as the lenght of data in my prievous example), whilst here mu and sigma are obtained in a "complicate" way, which involves using tables as well.

In general, if you have a situation where you want to build a pure function whose "form" you already know, but that depends on certain parameters which you obtain doing some complicate at will operations on certain input data, how would you set up the scoping?

$\endgroup$
6
  • $\begingroup$ Sorry, I misswrote the code, now is correct. $\endgroup$ Commented Sep 8, 2017 at 11:45
  • $\begingroup$ Why do you keep using Return and extra semicolons when they are not necessary (even even hurt performance)? Use Block[{}, ...; result], not Block[{}, ...; x = result; Return[x];]. $\endgroup$
    – Szabolcs
    Commented Sep 8, 2017 at 12:43
  • $\begingroup$ Re your update: Once you've calculated $\mu$ and $\sigma$ and stored them into mu0 and sigma0, use With to inject them. With[{mu = mu0, sigma = sigma0}, Function[..., ... mu ... sigma ...]]. In fact you can even just use mu and sigma throughout (not mu0) and do With[{mu=mu, sigma=sigma}, ...]. If this is confusing to you, stick to separate names (mu and mu0). $\endgroup$
    – Szabolcs
    Commented Sep 8, 2017 at 12:47
  • $\begingroup$ Actually you should not be using Block here because the results will be messed up if data contains \[Mu] (or even i). Generally, stick to Module unless you know that you need Block. If you use Module, you will need separate mu and mu0 due to Module's symbol renaming. $\endgroup$
    – Szabolcs
    Commented Sep 8, 2017 at 12:52
  • 1
    $\begingroup$ Could it be that there's an issue with the ordering of the indexes for data? Judging from m=data//Length, data seems to be a $n\times 2$ array, but later you're accessing it with i as first index, with $i\in\{1,2\}$ $\endgroup$
    – Lukas Lang
    Commented Sep 8, 2017 at 13:45

3 Answers 3

9
$\begingroup$

Use With here:

createfunction[data_] :=
 With[{a = data^2},
  Function[x, x + a]
 ]

Recommended reading:

Also, do not use semicolons where they are not needed.


To understand why your original approach did not work, try this

a = 2;
Function[x, a + x]
(* Function[x, a + x] *)

As you can see, the body of the Function does not evaluate until the function is given an argument. By that time, the value temporarily set for a in Block is long gone. With does not temporarily set a value for a variable, like Block does. Instead, it replaces every explicit occurrence of a symbol by a value.


Re your update, I would write train like this:

train[data_]:=
    Module[{m=Length[data], i, j, μ0, σ0},
        μ0=1/m Table[Sum[data[[i,j]], {j,1,m}], {i,1,2}];
        σ0=1/m Table[Sum[(data[[i,j]]-μ0[[i]])^2, {j,1,m}], {i,1,2}];
        With[{μ=μ0,σ=σ0},
            Function[x,Times@@(Exp[-((x-μ)^2/(2 σ^2))]/(Sqrt[2 π] σ))]
        ]
    ]
$\endgroup$
4
  • $\begingroup$ Sorry I misswrote the code before, have a look at it now. a is nowhere. $\endgroup$ Commented Sep 8, 2017 at 11:48
  • $\begingroup$ @giuliobullsaver does not matter, does it? $\endgroup$
    – Kuba
    Commented Sep 8, 2017 at 11:48
  • $\begingroup$ May be related soon: mathematica.stackexchange.com/q/20766/5478 $\endgroup$
    – Kuba
    Commented Sep 8, 2017 at 11:49
  • $\begingroup$ it may work, I will try in my actual set up and see if it works. I am afraid that it would not because rather than parameter = data^2 I have a more complicate operation to do, which involves Table and Part, which I am afraid will give problems. If they do I will post my actual code. $\endgroup$ Commented Sep 8, 2017 at 11:54
5
$\begingroup$

Use With to inject values for variables into any expression:

createfunction[data_] := Block[
   {parameter, function},
   function = With[{a = data^2}, Function[x, x + a]];
   Return[function];
   ];
g = createfunction[{a, b}];
g[c]
(* {a^2 + c, b^2 + c} *)

Update

Note: For a more direct solution of your issue, see Szabolcs' answer.

Here is a way to solve your problem utilizing Mathematica's built-in functions (assuming that data is a $2\times n$ array, see my comment):

train[data_] := PDF@MultinormalDistribution[
                  Mean /@ data,
                  StandardDeviation /@ data // DiagonalMatrix
                ]
$\endgroup$
4
$\begingroup$

Alternatives to With include:

createfn1[data_] :=
  Block[{parameter, function, x},
    parameter = data^2;
    Function @@ {x, x + parameter}
  ]

createfn2[data_] :=
  Module[{parameter},
    parameter = data^2;
    Evaluate[# + parameter] &
  ]

createfn3[data_] :=
  data^2 /. parameter_ :> (# + parameter &)

Note that there is a difference between the first two, and the third which acts more like With. The first two evaluate the entire body, rather than only parameter itself; this can sometimes be advantageous.

createfn1[{1, 2, 3}]
createfn2[{1, 2, 3}]
createfn3[{1, 2, 3}]
Function[x, {1 + x, 4 + x, 9 + x}]

{1 + #1, 4 + #1, 9 + #1} &

#1 + {1, 4, 9} &
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.