Dynamic Function Creation with Anonymous and Nested Functions

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.

Anonymous and Nested Functions in MATLAB 7

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) = 3x2 + 2x + 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 

df_graph1_w.gif

Click on image to see enlarged view.

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'); 

df_graph2_w.jpg

Click on image to see enlarged view.

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.

Customizing Functions

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 = ax2 + bx + c

and customize a, b, or c at runtime, we write

a=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 

df_graph3_w.gif

Click on image to see enlarged view.

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.

Working with Function Functions

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 

df_graph4_w.gif

Click on image to see enlarged view.

Handling Dynamic Function State

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.

Simple Function State

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

Complex Function State

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.

Other Uses of Nested and Anonymous Functions

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

Function Composition

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

Memoization

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'
ype:'nested'
file:[1x76 char]
workspace:{[1x1 struct]}


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]

Data Structures

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

Receive the latest MATLAB and Simulink technical articles.

Related Resources

Latest Blogs