By Stuart McGarrity, MathWorks and Loren Shure, MathWorks

When you are developing algorithms to solve technical computing problems, it is often useful to create functions on-the-fly so that you can customize them at run-time without having to define them in files beforehand. For example:

- You may want to create a function based on a user's input and then evaluate or plot it over a specific range.
- You may want to define a function and then pass it as an argument to an optimization, numerical integration, or ODE solving routine (also known as a
*function function*in MATLAB). - In curve fitting, you need to create a function that best fits some chosen data and subsequently need to evaluate it at other data points or perform further operations on it, such as integration.
- Sometimes it is necessary to create a function that needs to retain dynamic state and which must be initialized the first time it is called and updated at each subsequent call.

Other uses for dynamically created functions include callbacks in MATLAB Handle Graphics, data acquisition objects and timers, and, generally, any time you need to pass a function as a parameter to another routine.

Earlier versions of MATLAB already provided a number of features for managing functions, but there were limitations, for example:

- The MATLAB language has function handles that let you pass functions as parameters to routines, but they needed to be associated with a function definition in an M-file and evaluated with the
`feval`

command. - You can specify arbitrary functions as a string with the
`inline`

command and pass them to the function function routines, but this command is difficult to use, particularly to separate the many different types of parameters, including the arguments to the inlined function and the function function itself. - The keyword
`persistent`

provides state in functions, but you can have only one instance of a traditional MATLAB function. Also, the function must carry out initialization the first time it is called.

Two new language features in MATLAB 7, anonymous functions and nested functions, address these issues and requirements for creating and managing functions.

Using MathWorks tools, the researchers developed simple data acquisition code that could access a variety of data acquisition cards. “Data Acquisition Toolbox allows us to perform a one-time configuration of the hardware,” explains Vogt. “With less than a page of code, I could perform my data acquisition on four different platforms—each one with different hardware.”

The SSDK uses voltammetry, an advanced chemical analysis technique that translates chemical reactions into voltammetric “signature” outputs, enabling the detection of minute quantities of gaseous chemicals—something that was not previously possible.

The researchers implemented gaseous voltammetry using Data Acquisition Toolbox to output precisely shaped analog waveforms, each consisting of 100 to 1,000 points of data, and to excite chemical reactions on the microsensor. Using Data Acquisition Toolbox, they also sampled the resulting signals with an analog input channel. They then used filters from Signal Processing Toolbox to remove line noise. The resulting signals, or voltammetric signatures, included information that identified the chemicals to which the sensor was exposed.

The researchers used Neural Network Toolbox to perform pattern recognition and identify the chemicals by comparing their signatures to those in a prestored library.

“With Neural Network Toolbox, we can take an unknown sample signature, compare it to a signature library, find the best match, and calculate a confidence factor indicating how certain the match is,” Vogt says.

To generate the sensor signal-processing algorithms, Argonne used the Sensor Algorithm Generation Environment (ChemSAGE), a Department of Defense-funded software tool, written in MATLAB, that helps sensor researchers code and analyze complex signals from experimental sensors.

To complete their application, researchers developed a graphical user interface in MATLAB that enables users to set voltammetry parameters and visualize the voltammetric signatures. “The application runs easily on any notebook computer,” Vogt says.

Using MATLAB Compiler, Argonne recoded the MATLAB algorithms in C for execution on popular single-chip microcontrollers.

Argonne’s sponsors plan to develop commercial instruments based on SSDK, including intelligent fire detectors and air pollutant monitors.

Doctors Michael Vogt, Laura Skubal, Erika Shoemaker, and John Ziegler developed the SSDK. Vogt, Dan MacShane, Christopher Klaus, and Maria Poulos developed the measurement and ChemSAGE software.

*Anonymous functions* let you define a function on-the-fly at the command line or in M code, without an associated file. For example, to create a polynomial function,

*y(x)* = 3*x*^{2} + 2*x* + 1

type

y = @(x) 3*x.^2 + 2*x + 1;

This returns function handle `y`

representing a function of one variable defined by the expression on the right. The variables in parentheses following the @ symbol (in this case, only `x`

) specify variables of which `y`

is a function. The function `y`

can now be evaluated by calling it like any other function (another new feature in MATLAB 7).

y(3)

` ans =`

` 34`

You could evaluate this polynomial over a range of points and plot the results together with the previously evaluated point.

x=-5:.1:5; plot(x,y(x),'-b',4,y(4),'r*'); title('y(x) = 3*x^2 + 2*x + 1'); xlabel('x'); ylabel('y'); text(4,y(4),'y(4)'); grid

We can create handles to functions of multiple variables.

```
g=@(x,y) (x.^2 + y.^2);
ezsurf(g);
shading flat;
title('f(x,y)=x^2+y^2');
```

Using MathWorks tools, the researchers developed simple data acquisition code that could access a variety of data acquisition cards. “Data Acquisition Toolbox allows us to perform a one-time configuration of the hardware,” explains Vogt. “With less than a page of code, I could perform my data acquisition on four different platforms—each one with different hardware.”

The SSDK uses voltammetry, an advanced chemical analysis technique that translates chemical reactions into voltammetric “signature” outputs, enabling the detection of minute quantities of gaseous chemicals—something that was not previously possible.

The researchers implemented gaseous voltammetry using Data Acquisition Toolbox to output precisely shaped analog waveforms, each consisting of 100 to 1,000 points of data, and to excite chemical reactions on the microsensor. Using Data Acquisition Toolbox, they also sampled the resulting signals with an analog input channel. They then used filters from Signal Processing Toolbox to remove line noise. The resulting signals, or voltammetric signatures, included information that identified the chemicals to which the sensor was exposed.

The researchers used Neural Network Toolbox to perform pattern recognition and identify the chemicals by comparing their signatures to those in a prestored library.

“With Neural Network Toolbox, we can take an unknown sample signature, compare it to a signature library, find the best match, and calculate a confidence factor indicating how certain the match is,” Vogt says.

To generate the sensor signal-processing algorithms, Argonne used the Sensor Algorithm Generation Environment (ChemSAGE), a Department of Defense-funded software tool, written in MATLAB, that helps sensor researchers code and analyze complex signals from experimental sensors.

To complete their application, researchers developed a graphical user interface in MATLAB that enables users to set voltammetry parameters and visualize the voltammetric signatures. “The application runs easily on any notebook computer,” Vogt says.

Using MATLAB Compiler, Argonne recoded the MATLAB algorithms in C for execution on popular single-chip microcontrollers.

Argonne’s sponsors plan to develop commercial instruments based on SSDK, including intelligent fire detectors and air pollutant monitors.

Doctors Michael Vogt, Laura Skubal, Erika Shoemaker, and John Ziegler developed the SSDK. Vogt, Dan MacShane, Christopher Klaus, and Maria Poulos developed the measurement and ChemSAGE software.

*Nested functions* are functions in M-files that are nested inside other functions and which can see the parent function’s workspace. The following function `taxDemo.m`

contains a nested function.

function y = taxDemo(income) % Calculate the tax on income. AdjustedIncome = income - 6000; % Calculate adjusted income % Call 'computeTax' without passing 'AdjustedIncome' as a parameter. y = computeTax; function y = computeTax % This function can see the variable 'AdjustedIncome' % in the calling function's workspace y = 0.28 * AdjustedIncome; end end

The nested function `computeTax`

can see the variables in the parent function's workspace, in this case `AdjustedIncome`

and `income`

. This makes sharing of data between multiple nested functions easy. We can call the function in the usual way.

```
% What is the tax on income of 80,000?
tax=taxDemo(80e3)
```

` tax =`

` 2.0720e+004`

The ability of nested functions to see into their parent's workspace enables you to control the scope of your variables, letting them be accessed by multiple functions, while avoiding the side effects of us ing a global variable. At the same time, you can reduce your memory requirements by sharing large data sets in a controlled way.

An `end`

statement is required at the end of all nested functions, making them different from traditional local subfunctions. However, all functions in a file containing nested functions, including traditional subfunctions, require termination with an `end`

statement. Functions can be nested to any level.

You can create different variants or customized versions of the same function with anonymous functions or nested functions. For example, if we want to create a function like

*y = ax ^{2} + bx + c*

and customize

`a`

, `b`

, or `c`

at runtime, we writea=3; b=2; c=-10; y1=@(x) a*x.^2 + b*x + c;

Any variables in the function that are not listed in parentheses are taken from the calling workspace at the time of definition.

Now let's change `a`

, `b`

, or `c`

and generate other customized functions. For example,

a=-5; b=1; c=20; y2=@(x) a*x.^2 + b*x + c;

Evaluate or plot these as before.

x=-5:.1:5; plot(x, y1(x), x, y2(x)); legend('y_1(x)=3x^2+2x-10','y_2(x)=-5x^2+x+20'); xlabel('x'); ylabel('y_1 and y_2'); title('y(x) = a*x^2 + b*x + c'); grid

Using MathWorks tools, the researchers developed simple data acquisition code that could access a variety of data acquisition cards. “Data Acquisition Toolbox allows us to perform a one-time configuration of the hardware,” explains Vogt. “With less than a page of code, I could perform my data acquisition on four different platforms—each one with different hardware.”

The SSDK uses voltammetry, an advanced chemical analysis technique that translates chemical reactions into voltammetric “signature” outputs, enabling the detection of minute quantities of gaseous chemicals—something that was not previously possible.

The researchers implemented gaseous voltammetry using Data Acquisition Toolbox to output precisely shaped analog waveforms, each consisting of 100 to 1,000 points of data, and to excite chemical reactions on the microsensor. Using Data Acquisition Toolbox, they also sampled the resulting signals with an analog input channel. They then used filters from Signal Processing Toolbox to remove line noise. The resulting signals, or voltammetric signatures, included information that identified the chemicals to which the sensor was exposed.

The researchers used Neural Network Toolbox to perform pattern recognition and identify the chemicals by comparing their signatures to those in a prestored library.

“With Neural Network Toolbox, we can take an unknown sample signature, compare it to a signature library, find the best match, and calculate a confidence factor indicating how certain the match is,” Vogt says.

To generate the sensor signal-processing algorithms, Argonne used the Sensor Algorithm Generation Environment (ChemSAGE), a Department of Defense-funded software tool, written in MATLAB, that helps sensor researchers code and analyze complex signals from experimental sensors.

To complete their application, researchers developed a graphical user interface in MATLAB that enables users to set voltammetry parameters and visualize the voltammetric signatures. “The application runs easily on any notebook computer,” Vogt says.

Using MATLAB Compiler, Argonne recoded the MATLAB algorithms in C for execution on popular single-chip microcontrollers.

Argonne’s sponsors plan to develop commercial instruments based on SSDK, including intelligent fire detectors and air pollutant monitors.

Doctors Michael Vogt, Laura Skubal, Erika Shoemaker, and John Ziegler developed the SSDK. Vogt, Dan MacShane, Christopher Klaus, and Maria Poulos developed the measurement and ChemSAGE software.

Since the definitions of `y1`

and `y2`

included the values of `a`

, `b`

, and `c`

at the time the function handles are created, they are robust to variations in the workspace such as changed or deleted values of `a`

, `b`

, or `c`

.

a=100; y1(3)

` ans =`

` 23`

a=5000; y1(3)

` ans =`

` 23`

Let's see how we can carry out a similar task with nested functions by looking at `makefcn.m`

, which contains a nested function.

function fcn = makefcn(a,b,c) % This function returns a handle to a customized version of 'parabola'. % a,b,c specifies the coefficients of the function. fcn = @parabola; % Return handle to nested function function y = parabola(x) % This nested function can see the variables 'a','b', and 'c' y = a*x.^2 + b.*x + c; end end

When you call `makefcn`

, it returns a function handle to the internal nested function, which has been customized according to the parameters passed to the parent function. For example,

f1 = makefcn(3,2,10); f2 = makefcn(0,5,25);

We can evaluate these two different functions as before.

f1(2)

` ans =`

` 26`

f2(2)

` ans =`

` 35`

In general, anonymous functions are better for quick, on-the-fly simple function creation and customization. Nested functions are more suited to more complex function customization and management.

You can pass nested and anonymous functions to optimization or integration routines, known as function functions in MATLAB. For example, to find the area under the curve `f1`

between 0 and 4, use

areaunder=quad(f1,0,4)

` areaunder =`

` 120.0000`

By having the integrand `f1`

defined beforehand, you keep its arguments (such as the polynomial coefficients) separate from those of the integrator quad (such as the limits of integration). This can eliminate potential confusion.

We will plot `f1`

again together with a plot of the area just calculated.

x=-5:.1:5; plot(x,f1(x)); hold on x=0:.1:4; area(x,f1(x),'Facecolor','g'); % You can evaluate f without feval hold off title('Area under f_1(x), between 0 and 4'); grid

If you need to create a function that retains dynamic state, you can use a nested function and store its state in the parent's workspace. Functions with dynamic state can represent algorithms that range from simple counters to components of a large system.

Let's create a counter function that returns a number which increments each time it is called. Let's look at the function `makecounter.m`

.

function countfcn = makecounter(initvalue) % This function returns a handle to a customized nested function 'getCounter'. % initvalue specifies the initial value of the counter whose handle is % returned. currentCount = initvalue; % Initial value countfcn = @getCounter; % Return handle to getCounter function count = getCounter % This function increments the variable 'currentCount', when it % gets called (using its function handle). currentCount = currentCount + 1; count = currentCount; end end

When you call `makecounter`

, it returns a handle to its nested function `getCounter`

. `getCounter`

is customized by the value of `initvalue`

, a variable it can see via nesting within the workspace of `makecounter`

. Let's make a couple of counter functions.

counter1 = makecounter(0); % Define counter initialized to 0 counter2 = makecounter(10); % Define counter initialized to 10

Here we have created two customized counters: one that starts at 0 and one that starts at 10. Each handle is a separate instance of the nested function and its calling workspace. Note `counter1`

does not take any parameters. We need to use the parentheses to invoke the function instead of looking at the function handle variable itself.

counter1Value=counter1()

` counter1Value =`

` 1`

We can call the two functions independently as there are two separate workspaces kept for the parent functions. They remain in memory while the handles to their nested functions exist. Each time the counter functions are called, the associated variable `currentCount`

is incremented.

counter1Value=counter1()

` counter1Value =`

` 2`

counter2Value=counter2()

` counter2Value =`

` 11`

You can use nested functions to store a more complex dynamic state such as that of a filter. Let's look at the function `makeFilter.m`

.

function filterhandle = makeFilter(b,a) % Initialize State state=zeros(max(length(a),length(b))-1,1); % Return handle to filter function filterhandle=@callFilter; function output = callFilter(input) % Calculate output and update state [output,state] = filter(b,a,input,state); end end

The function `makeFilter`

returns a handle to a nested function (`callFilter`

) that performs a filtering operation and maintains its state in the parent's workspace. The filter state is initialized when the function handle is created, then gets updated each time the nested function is called. The nested function also has to calculate the output of the filtering operation and return it as its output argument. You can call this function in a for-loop many times, as shown in `simplesystem.m`

.

%% System Parameters frameSize = 1000; sampleTime = 1e-3; %% Initialize System Components and Component Parameters filter1 = makeFilter([0.02,0,-0.23,0,0.49,0,-0.23,0,0.02],1); filter2 = makeFilter([-0.05 0.13 0.83 0.13 -0.05],1); randSource = makeRandSource(frameSize); scope = makeScope(sampleTime,[-2 2],'My Scope'); %% Simulation Loop for k = 1:100 signal1 = randSource(); signal2 = filter1(signal1); signal3 = filter2(signal2); scope(signal3) end

The main for-loop in the simulation now becomes very clean and simple where the only variables passed between the functions (`signal1`

, `signal2`

, and `signal3`

) are pure data, without being mixed up with other function parameters (such as filter coefficients) and state.

The `makeFilter`

routine could be expanded to return more than one function, such as one to return the filter state, or even one to reset the filter. These function handles could be returned as multiple output arguments or as a structure, where each field is a function handle. The functions can then be called in a manner similar to evaluating the methods of an object.

The function handling capabilities of nested and anonymous functions have many other uses.

You can create handles to functions of functions, allowing a very natural mathematical notation for function composition, such as

```
f=@(x) x.^2;
g=@(x) 3*x;
h=@(x) g(f(x)); % Equivalent to 3*(x^2)
h(3)
```

` ans =`

` 27`

We can store the previously computed return values of a function in a "table" for later use instead of recalculating values. This is helpful if the function is computationally intensive. Let's look at a function that stores computed values and makes them available for reuse.

function f = memoize(F) % One argument F, inputs testable with == % Scaler input to F x = []; y = []; f = @inner; function out = inner(in) ind = find(in == x); if isempty(ind) out = F(in); x(end+1) = in; y(end+1) = out; else out = y(ind); end end end

Here's how to use this function. First you create a "memoized" version of the function for which you want to remember the return values, for example, `sin`

.

f = memoize(@sin)

` f =`

` @memoize/inner`

Let's call the function a few times.

f(pi/2)

` ans =`

` 1`

f(pi/4)

` ans =`

` 0.7071`

f(pi/8)

` ans =`

` 0.3827`

The returned values, in this case `sin(pi/2)`

, `sin(pi/4)`

, and `sin(pi/8)`

are stored. To see how this is working, let's use the `functions`

command to inspect the state of the function handle `f`

.

functionInfo = functions(f)

` functionInfo = `

* function: *'memoize/inner'*
type: *'nested'

file:

workspace:

functionInfo.workspace{1}

` ans = `

` ``f: @memoize/inner`

` F: @sin`

` x: [1.5708 0.7854 0.3927]`

` y: [1 0.7071 0.3827]`

Now if you request a previously computed result, such as for `sin(pi/4)`

, the value is taken from the table without a new value being added. Here you can see the stored workspace doesn't change.

f(pi/4)

` ans = `

` 0.7071`

functionInfo = functions(f); functionInfo.workspace{1}

` ans = `

` ``f: @memoize/inner`

` F: @sin`

` x: [1.5708 0.7854 0.3927]`

` y: [1 0.7071 0.3827]`

Nested functions can be used to create data structures such as lists and trees. Find Sturla Molden's example in the comp.soft-sys.matlab Usenet newsgroup.

Published 2005