Anonymous Functions and Function Handles
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Written against MATLAB R2026b. Function handles and anonymous functions are old features of the language and nothing on this page needs a recent release. The snippets marked "Octave 11.3.0 output" were run in GNU Octave 11.3.0, which accepts the same syntax, and Octave’s printed layout and some message texts differ from MATLAB’s, as noted where it matters. Snippets labelled "not run here" are checked line by line against the R2026b documentation and show no invented output.
A function handle is a MATLAB data type (class function_handle) that stores an association to a function, so that the function can be passed around, stored and called later. It is the way MATLAB passes behaviour as data: the "function functions" such as fzero, integral, ode45 and fminsearch take handles, callbacks of graphical interfaces are handles, and arrayfun and cellfun apply a handle to every element. An anonymous function is a handle to a one-line expression that lives in a variable and needs no file. This page is the foundation for Integration, ODEs and Optimization, Local, Nested and Private Functions and the callbacks that appear in the graphical-interface pages. The files, signatures and workspaces that the handles point to are on Functions.
Function handles
To create a handle to a function, precede its name with @. The function can be a built-in, a function in a program file, or a local function of the current file. A handle is called with the same syntax as the function itself:
h = @sin;
h(pi/2)
class(h)
isa(h, 'function_handle')
A handle to a function that takes no inputs is called with empty parentheses; without them the statement just copies the handle and calls nothing. This is the same trap as a bare function name in a script: d = h copies, d = h() calls.
f = @() disp('hello');
f()
g = f; % copies the handle, nothing is called
Function handles are always scalars: a handle cannot be an element of a numeric array. To keep several of them together, use a cell array or a structure; the documentation shows both, C = {@sin, @cos, @tan}; C{2}(pi) and S.a = @sin; S.a(pi/2). Chained indexing such as C{2}(pi) selects the handle first and then calls it.
A handle to a named function remembers which function it means at creation time. The function has to be in scope then (on the path, in the current folder, or a local or nested function of the current file), and MATLAB resolves the name with the same precedence rules as a normal call (the rules are on Function Precedence and Name Resolution). The documentation also says that a handle to a file-based function stores the absolute path, so it still works after the current folder changes, but a handle saved in a MAT-file becomes invalid if the file is moved or renamed: invoking an invalid handle is an error.
Anonymous functions
An anonymous function is created with h = @(arglist) expression. It has these properties, all from the documentation:
-
it consists of one executable expression; there are no statements, no assignments and no
if,fororendblocks; -
it can take any number of inputs, separated by commas, and returns one output, the value of the expression;
-
it can take no inputs (
@() expression), and must then be called with(); -
it is stored in a variable of class
function_handle, needs no file, and can also be written directly as an argument, as inintegral(@(x) x.^2, 0, 1).
sq = @(x) x.^2;
sq(3)
g = @(x,y) x + y;
g(2,3)
nargin(g)
Octave 11.3.0 output (the matrix layout is Octave’s):
ans = 9
ans = 5
ans = 2
One output only. If the expression calls a function that can return several outputs, the anonymous function still returns what the caller asks for: the documentation shows mygrid = @(x,y) ndgrid(-x:x/c:x, -y:y/c:y) called as [x,y] = mygrid(pi,2*pi), and the deal function can spread one value over several outputs. What an anonymous function cannot do is run several statements or assign to a variable. When the body needs more than one line, write a local function (see Handles to local functions).
Operators work elementwise only if you write them so. @(x) x^2 squares a matrix; @(x) x.^2 squares each element. Function functions such as integral call the handle with vectors, so the elementwise operators (.^, .*, ./) are normally what you want; fzero and fminsearch call it with scalars. The same rule applies to the array-based arrayfun. See Operators and Precedence.
Handles can contain handles. The documentation builds a parameterised integral by nesting: g = @(c) integral(@(x) x.^2 + c*x + 1, 0, 1) is a function of c that integrates a function of x; g(2) is 2.3333.
Captured variables: snapshot semantics
The most important property of an anonymous function is what it does with the variables in its body that are not its own inputs. When the handle is created, MATLAB looks at the current workspace, takes the values of those variables, and stores them in the handle. The handle does not see the workspace later: the documentation says the values persist in the handle even if you clear the variables, and that to supply different values you must create a new handle.
a = 2;
p = @(x) a*x;
a = 10;
p(5) % uses the a that existed when p was created
func2str(p)
clear a
p(5) % still works: the value is stored in the handle
Octave 11.3.0 output (first call; the text of func2str differs in spacing between Octave and MATLAB, see below):
ans = 10
ans = @(x) a * x
ans = 10
The value of a that the handle uses is the value at creation time, 2, so p(5) is 10 and not 50; clearing a afterwards changes nothing. The consequences are the pitfalls of this page:
-
Changing the parameter later has no effect. To change a captured value, rebuild the handle:
p = @(x) a*x;again. This is the same rule as the documentation’s note forfzero: "to change the values of the parameters, you must create a new anonymous function". -
Only explicit variables are captured. The body is scanned for names, so a variable that is created by
eval,evalin,assigninorloadand is not written in the body is not found, and invoking the handle then raises an error. The documentation tells you to avoid these functions inside anonymous functions. In the same waystr2funcof the text of an anonymous function (see below) captures nothing. -
Captured arrays travel with the handle. A handle stores the values it captured, so capturing a large matrix only to look up a few values keeps all of it alive; pass the matrix as an argument, or capture only what is needed.
-
Names that are undefined at creation time are not an error until the handle is called. A typo in a function name inside the body shows up on the first call (Octave 11.3.0 reports
'undefined_thing' undefined; MATLAB raisesMATLAB:UndefinedFunction, not run here). -
The handle is saved with its captured values when it is stored with
save, and restored byload; the documentation showssave myfile.mat parabola.
The introspection function functions shows what is captured (see below), and is the debugging tool when a handle returns a surprising number.
Converting and inspecting handles: func2str, str2func, functions, feval
-
c = func2str(fh)returns the function name for a handle to a named function ('cos'), and the text of an anonymous function otherwise. MATLAB’s reference page shows the anonymous text in the compact form written without a space after the argument list; Octave prints its own spacing (@(x) a * x), so do not compare these strings across the two systems. -
fh = str2func(str)builds a handle from a function name or from the text of an anonymous function. The documentation warns that handles made withstr2funcdo not have access to variables outside their own workspace, nor to nested functions; and that if you give the text of an anonymous function, the handle has no access to private or local functions. Use it for names read from a configuration file, not to capture local variables. -
s = functions(fh)returns a structure with the function name, type and file, and for an anonymous function aworkspacefield with the captured values (s.workspace{1}). The page says to usefunctionsfor querying and debugging only, not programmatically, because its behaviour could change in later releases. -
feval(fun, x1, ..., xM)calls a function given by name or by handle. With a handle it is equivalent to calling the handle directly, sofevalis only needed whenfunis a name (a character vector or string) that you have in a variable.
q = str2func('@(x) x.^3');
q(2)
feval(@plus, 1, 2)
feval('max', [4 9 2])
f = functions(@sin)
Octave 11.3.0 output (functions prints a structure; MATLAB’s fields and display differ):
ans = 8
ans = 3
ans = 9
f =
scalar structure containing the fields:
function = sin
type = simple
file =
Parameterising function functions
The function functions expect a handle with exactly the number of inputs they supply: fzero and integral call a function of one variable. To solve a problem that has extra parameters, close over them. The documentation shows two ways: an anonymous function that uses the parameters from the workspace, and a nested function that shares its parent’s workspace. A third way, a wrapper @(x) cubicpoly(x,b,c) around a general function, is common when the function lives in a file.
The heatflow scenario has a decaying temperature mode on a plate of length L with diffusivity alpha. The amplitude of the slowest mode is exp(-alpha*(pi/L)^2*t). How long until it halves, and how does the profile look at one minute?
alpha = 1.1e-4; % diffusivity in m^2/s
L = 0.5; % plate length in m
amp = @(t) exp(-alpha*(pi/L)^2*t); % amplitude of the slowest mode
tHalf = fzero(@(t) amp(t) - 0.5, [0 1000]);
fprintf('half-amplitude time: %.2f s\n', tHalf)
alpha = 5e-4; % changing alpha later does not change amp
fprintf('amp(100) is still %.4f\n', amp(100))
profile = @(x,t) sin(pi*x/L) .* amp(t);
x = linspace(0, L, 5);
fprintf('%.4f ', profile(x, 60)); fprintf('\n')
rate = @(t,T) -alpha*(pi/L)^2 * T;
[t, T] = ode45(rate, [0 600], 1);
fprintf('ode45 final %.3e, exact %.3e\n', T(end), exp(-alpha*(pi/L)^2*600))
Octave 11.3.0 output:
half-amplitude time: 159.61 s
amp(100) is still 0.6477
0.0000 0.5449 0.7706 0.5449 0.0000
ode45 final 7.241e-06, exact 7.185e-06
Three things are visible. The root 159.61 is found by fzero on an anonymous function that itself calls another handle (amp), which has captured alpha and L. The reassignment alpha = 5e-4 did not change amp, because its alpha was copied when it was created (the second line shows 0.6477 is unchanged), but it did change rate, which was created afterwards. And ode45 receives a handle with two inputs, the time and the state, exactly as it requires; the ODE solution at 600 s is close to the exact value (the difference is the solver’s default tolerance, ode45 is not exact). The solvers and their options are on Integration, ODEs and Optimization; MATLAB’s integral, fzero, fminsearch and ode45 are in base MATLAB and need no toolbox.
Nested functions as the alternative. A nested function shares the workspace of its parent, so a handle to it sees the current value of the parent’s variables, not a snapshot. The documentation example is a file findzero.m whose nested function poly(x) uses the parent’s b and c. It is not run here (it is a function file); the semantics differ from an anonymous function and this is why it matters:
function y = findzero(b, c, x0)
y = fzero(@poly, x0);
function y = poly(x)
y = x^3 + b*x + c;
end
end
Not run here, from the MATLAB documentation page. findzero(2, 3.5, 0) returns -1.0945 there. If the parent changes b after creating @poly and before the call, the nested function sees the new value; an anonymous function does not. See Local, Nested and Private Functions.
Applying handles to arrays and cells: arrayfun and cellfun
arrayfun(fun, A) calls fun once for each element of an array, and cellfun(fun, C) once for each cell of a cell array, so that a handle replaces a loop. By default the results must be scalars of one class (UniformOutput is true) and are collected in an array. Use 'UniformOutput', false to get a cell array of results of any size, and 'ErrorHandler' to supply a handle called with the error information (and the inputs) when fun fails, so that one bad element does not stop the whole call.
v = arrayfun(@(n) n^2, 1:4)
c = cellfun(@numel, {'ab','cde',''})
d = cellfun(@(s) [s '!'], {'a','bc'}, 'UniformOutput', false)
Octave 11.3.0 output:
v =
1 4 9 16
c =
2 3 0
d =
{
[1,1] = a!
[1,2] = bc!
}
A cell array of handles can be applied in the same way: with c = {@sin, @cos, @(x) x.^2}, cellfun(@(f) f(0.5), c) evaluates every function at 0.5 (Octave: 0.4794 0.8776 0.2500). This is a pattern for a table of methods, such as the three heatflow boundary conditions as handles indexed by name in a structure.
arrayfun and cellfun are a matter of clarity, not speed: a handle is called once per element, and a vectorised expression on the whole array is normally the better choice (measure with timeit) (see Vectorization and Preallocation). The error message from a non-scalar result with UniformOutput true is a classic: the fix is to add 'UniformOutput', false (its identifier and wording vary by release; not run here).
Handles to local functions
A handle to a local function (a function defined after the main function in a file, or at the end of a script) can be created inside the file and returned or passed out, which is the way to call a local function from outside, as the documentation points out. The handle works wherever it goes, even though the local function cannot be called by name from other files. The documentation’s functionsExample returns a handle to a local and a nested function; functions reports their type and file. In a function file:
function h = stepper(scheme)
% STEPPER Return a handle to a time-stepping rule chosen by name.
switch scheme
case "explicit"
h = @explicitStep;
case "damped"
h = @(T, dt) dampedStep(T, dt, 0.9);
end
end
function T = explicitStep(T, dt)
T = T + dt;
end
function T = dampedStep(T, dt, w)
T = T + w*dt;
end
Not run here (a function file with local functions; the MATLAB file layout is the point). h = stepper("explicit"); h(1, 0.1) gives 1.1000; the handle to the local explicitStep is valid outside stepper.m, and the anonymous function keeps 0.9 as a literal. Scripts can define local functions too, since R2016b: before R2024a they had to be at the end of the script file (see Scripts and Code Sections).
Precedence. A handle to a name is resolved at creation by the normal precedence rules: a variable named sin does not become a handle to the function, and a local function with the same name as a path function wins inside the file. See Function Precedence and Name Resolution.
Overloading. When a handle is called with an object as the dominant argument, MATLAB invokes the object’s class method of the same name instead, if there is one (the documentation says so in the section on creating handles). This is how a handle such as @size called with an object of a class that defines its own size method ends up in that method.
Comparing handles
Handles to the same named function compare as equal with isequal, and a handle copied from another is equal to it. Two anonymous functions that were typed separately with identical text are different handles with their own captured data. Octave 11.3.0 gives these results (isequal of two handles that are not copies is not specified on the pages consulted for MATLAB, so verify it on your release before relying on it, not run here):
h = @twice; h2 = @twice;
isequal(h, h2) % same named function
s1 = @(x) x+1; s2 = @(x) x+1;
isequal(s1, s2) % separately typed anonymous functions
s3 = s1;
isequal(s1, s3) % a copy
Octave 11.3.0 output: ans = 1, ans = 0 and ans = 1. A comparison of two anonymous functions is therefore not a way to test whether they compute the same thing; compare func2str text if that is what you mean, or call both on a test input.
Source: MATLAB documentation: isequal
Pitfalls
| Pitfall | What happens | What to do |
|---|---|---|
A captured variable is changed later |
The handle keeps the value from creation time |
Rebuild the handle, or pass the value as an argument |
|
The assignment copies the handle; nothing is called |
Write |
|
|
Write |
Body needs several statements |
An anonymous function holds one expression |
Use a local function and a handle to it |
|
The new handle has no access to the variable |
Build the handle with |
|
The variables are not captured; calling the handle fails |
Name every variable in the body |
|
The documentation reserves it for debugging; its behaviour may change |
Store the data you need explicitly |
Handle saved in a MAT-file for a file function that moves |
The handle becomes invalid; a call is an error |
Save the data and rebuild the handle on load |
Large array captured in a handle |
The data is stored in the handle and copied with it |
Pass it as an argument |
Where older books differ
Older books pass functions to solvers as names in character vectors, call them with feval, or use inline objects; today a handle is passed and called directly (feval is only needed when you hold a name), and anonymous functions replace inline. Older texts also pass extra parameters to fzero or ode45 as additional arguments after the options structure; the current documentation parameterises with an anonymous or nested function, as above, and the solvers expect a handle with the arguments they supply.