36
$\begingroup$

I would like to be able to define the gain function of a system from its parameters. Specifically, I'd like to define a function that accepts two inputs, call them $b$ and $w$, and returns a function that accepts a single parameter, $v$ and returns $1/\sqrt{(w^2 - v^2)^2 + b^2 v^2}$ .

What's the right way to do this? Presumably I'd like to be able to write

gain[b,w][v]

to evaluate the function, with parameters b and w, at v. But I'd also like to be able to write

f[v_] = gain[b,w]

and have f[v] be the same as gain[b,w][v].

$\endgroup$
1
  • 2
    $\begingroup$ Version 10 introduced "operator forms" that make this syntax native to a number of built-in functions. $\endgroup$
    – Mr.Wizard
    May 21, 2015 at 16:48

2 Answers 2

20
$\begingroup$

The only thing you have to change is the definition of the last line:

f = gain[b, v]

Then calling f[v] will be the same as gain[b,w][v]

In more explicit terms:

gain[b_, w_][v_] := 1/Sqrt[(w^2 - v^2)^2 + b^2 v^2]

gain[1.1, 1][.3]

(* ==> 1.03307 *)

f = gain[1.1, 1]

(* ==> gain[1.1, 1] *)

f[.3]

(* ==> 1.03307 *)
$\endgroup$
2
  • $\begingroup$ It really is that simple. Thanks. Syntactically, what is gain[b_,w_][v_]? (I'm talking about syntax trees here). Is gain[b_,w_] itself a function? I'm just trying to figure out how to conceptualize this - I've always been somewhat confused about the difference between functions and symbols in this language. $\endgroup$
    – rogerl
    Jul 6, 2012 at 22:35
  • 1
    $\begingroup$ Well, gain[b_,w_][v_] and gain[b_,w_] are patterns that have definitions associated with them. As I started typing this, Mr. Wizard has posted a more in-depth discussion of where the actual function is stored. It's interesting to compare the output of SubValues applied either to gain or to a gain2 defined with the single gain2[b_, w_, v_] :=. The latter has its definition stored in DownValues[gain2]. $\endgroup$
    – Jens
    Jul 6, 2012 at 22:52
34
$\begingroup$

You are wise to be interested in this syntax. It can significantly streamline code.
In my opinion it should be used more often.

There are two primary ways to build this kind of function: a SubValues definition, and Function.

Either form will let you define and use f as f = gain[1, 2].

SubValues

SubValues are created when you make a definition like this:

gain[b_, w_][v_] := 1/Sqrt[(w^2 - v^2)^2 + b^2 v^2]

SubValues[gain]
 {HoldPattern[gain[b_, w_][v_]] :> 1/Sqrt[(w^2 - v^2)^2 + b^2*v^2]}

With this form you are not actually creating a function when you call gain[1, 2].
It simply sits unevaluated until the third argument is given.

Unlike the Function method you retain full pattern capabilities with the additional arguments. You can for instance define this, which would require If or Switch in Function:

f[a_, b_][n_?EvenQ] := a^b + n
f[a_, b_][n_?OddQ]  := b^a - n

Function

The other common method is to actually build a Function:

ClearAll[gain]

gain[b_, w_] := Function[v, 1/Sqrt[(w^2 - v^2)^2 + b^2 v^2]]

This definition is stored in DownValues:

DownValues[gain]
{HoldPattern[gain[b_, w_]] :> 
     Function[v, 1/Sqrt[(w^2 - v^2)^2 + b^2*v^2]]}

With this form when you call gain[1, 2] you get this:

Function[v$, 1/Sqrt[(2^2 - v$^2)^2 + 1^2*v$^2]]

One important use for this form is when you need the additional arguments (in your example v) to be held unevaluated with HoldAll, etc. The SubValues form cannot do this*, but the third argument of Function can:

stylePrint[style_] := 
 Function[expr, Print@Style[HoldForm@expr, style], HoldAll]

printRed = stylePrint[{18, Red, Bold}];

printRed[2 + 3 + 1^2]

Mathematica graphics

This form is also superior if your function can be partially evaluated because the result of the evaluation can be incorporated into the Function, often using With.

Additional notes

Be mindful of the fact that you can accidentally blend these forms in a potentially confusing way. If you define a SubValue first (or later, if it is a form that does not evaluate on a pattern) and later a DownValue with the same pattern you may get unexpected results.

ClearAll[gain]

gain[b_, w_][v_] := 1/Sqrt[(w^2 - v^2)^2 + b^2 v^2]

gain[x_, y_] := Sin[x y] + Cos[y/x]

gain[1, 2][3]
(Cos[2] + Sin[2])[3]

This could be particularly confusing (or powerful) if you use a constrained pattern like gain[x_Real, y_] := . . . as you may have one pattern match in one case and the other in another.


While the two forms I show above are the most common and arguably best it is worth understanding that they are not exclusive. Because heads evaluate first, including x[y] in x[y][z], any definition for x[y] that returns a function can act upon z. Here is stylePrint again, using neither SubVales nor Function:

stylePrint2[style_] :=
  Module[{fn},
    SetAttributes[fn, HoldAll];
    fn[expr_] := Print@Style[HoldForm@expr, style];
    fn
  ]

"Pure functions" with machine-size arguments can often be auto-compiled in a way that pattern-based definitions (*Values) cannot. This is attempted by various functions such as Map, Table, and Fold as controlled by the values in SystemOptions["CompileOptions"].

Let us compare the performance of SubValues and Function using machine-size Real numbers:

g1[b_, w_][v_] := 1/Sqrt[(w^2 - v^2)^2 + b^2 v^2]
g2[b_, w_] := Function[v, 1/Sqrt[(w^2 - v^2)^2 + b^2 v^2]]

Table[
  f /@ RandomReal[14, 500000] // Timing // First,
  {f, {g1[0.3, 0.7], g2[0.3, 0.7]}}
]
{1.466, 0.078}

* I said that SubValues definitions cannot handle Hold attributes for the additional arguments. Strictly this is true, but there are some clever workarounds, with limitations. Please see these Q&A's for examples and further discussion:

$\endgroup$

Your Answer

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

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