When does anonymous function capture variables in scope? Is it always capture by value?

8 visualizzazioni (ultimi 30 giorni)
When does anonymous function capture variables in scope?
For example, if a file helperFunction1.m has
function A=helperFunction1(a)
A={1,2};
A{end+1}=a;
end
and in Matlab's main workspace, we make
a={1,2};
func=@()helperFunction(a);
We know that a is passed by value. So the local copy of helperFunction retains a copy of a. And even if we set a{1}=3. Outputs of func() doesn't change.
What if we change helperFunction1.m? Such as by modifying the definition of its local A after the creation of func. Earlier func received a local copy of helperFunction.m right?
Do anonymous functions always capture everything in-scope at the time of their creation?
If the whole sequence of commands were in a function and helperFunction1 had been a nested function, does the anonymous function func capture all shared variables by reference?
Also if anonymous functions do always capture everything in-scope at the time of their creation, do I need to worry about their computational efficiency? Do Matlab tell apart what variables are referenced in the definition of the anonymous function being created and only capture those?

Risposta accettata

Stephen23
Stephen23 il 15 Ago 2025
Modificato: Stephen23 il 15 Ago 2025
1) What does an anonymous function “capture”?
  • Only the variables it actually references in its definition, not the whole workspace.
  • Those variables are captured at the time the handle is created.
  • For value types (e.g. numeric arrays, cell arrays, structs) this means the value is copied into the handle’s workspace at creation. Changing the original variable later does not affect the handle.
  • For handle objects (e.g. graphics handles, containers.Map, user-defined handle classes), the handle reference is captured—so later changes/mutations of the object are visible through the function handle.
Example (value type):
a = {1,2};
f = @() a; % captures the cell array value now!
a{1} = 3;
f() % still returns {1,2}
ans = 1×2 cell array
{[1]} {[2]}
Example (handle object):
m = containers.Map("k",1);
f = @() m("k"); % captures the *handle* to m
m("k") = 99;
f() % now returns 99
ans = 99
2) What if you edit the called M-file later?
Your anonymous function f=@()helperFunction1(a) captures a as discussed above.
The code it calls (i.e. helperFunction1) is not frozen inside f. A function handle (directly or inside an anonymous function) points to the function by path/name, not a snapshot of its code. If you edit helperFunction1.m after creating f, subsequent calls to f will use the updated code in that same file/path. (If you move/rename the file or create a higher-priority shadow on the path, you can change which code is called; otherwise, edits take effect automatically).
3) What about nested functions (closures)?
Lets try it right now:
fin = outer();
fin()
ans = 42
function g = outer()
x = 1;
g = @inner;
x = 42;
function y = inner()
y = x;
end
end
  • Nested functions share the parent workspace. The returned handle keeps that workspace alive.
  • Changes to shared variables are seen by all nested-function handles, even if the change happens after the handle was created (as long as it’s the same shared variable).
  • This is by-reference to the shared workspace, not a one-time copy (standard copy-on-write semantics still apply to arrays when you reassign them, but the identity of the shared variable is the same).
So, for nested functions: think “closure by reference to the shared workspace,” unlike anonymous captures of value types from the base or caller workspace.
4) Do anonymous functions always capture “everything in scope”?
No. They only capture the variables that appear in the anonymous expression (the free variables). Unused variables are not captured.
5) Should you worry about performance/memory?
Usually, no—but a few tips:
  • Only referenced variables are captured. MATLAB already excludes everything else.
For large value-type variables, capture implies keeping a separate stored value with the handle. If you create many handles that each capture a large array, that’s memory you’ll retain. In those cases, consider:
  • Passing the large data as an input argument instead of capturing it, or
  • Capturing a handle object that references the data (if appropriate), or
  • Storing large data somewhere accessible and only capturing a small key/handle.
  • Call overhead of a handle/anonymous function is small and typically negligible compared to real work inside.
  6 Commenti
Paul
Paul il 15 Ago 2025
As to the first portion of the second part of your comment, consider:
aaa = 1;
f = @() aaa;
g = @(x) x + f();
sg = functions(g)
sg = struct with fields:
function: '@(x)x+f()' type: 'anonymous' file: '/tmp/Editor_zpunc/LiveEditorEvaluationHelperEeditorId.m' within_file_path: '' workspace: {[1×1 struct]}
sg.workspace{1}
ans = struct with fields:
f: @()aaa
sg.workspace{1}.f
ans = function_handle with value:
@()aaa
sgf = functions(sg.workspace{1}.f)
sgf = struct with fields:
function: '@()aaa' type: 'anonymous' file: '/tmp/Editor_zpunc/LiveEditorEvaluationHelperEeditorId.m' within_file_path: '' workspace: {[1×1 struct]}
sgf.workspace{1}
ans = struct with fields:
aaa: 1
The local workspace of g includes the definition of f, which in turn has its own local workspace that includes the value of aaa. Hence any changes to f or aaa in the base workspace have no bearing on the evaluation of g(). If that's what you mean by "capture by value."
As to the second portion "and @f directly would be by reference", I don't know what @f means in this context.
DZ
DZ il 16 Ago 2025
Modificato: DZ il 16 Ago 2025
Thank you so much
A function having its local workspace doesn't really predict whether its local variables are by reference or by copy right?
From earlier comments and examples, I know I need to generally think of function_handle to a .m file as a reference.
It would be nice if there are a set of rules where it is known exactly when function_handle is passed by refernece and when it is pass by copy in the sense that a version of the original function is copied. So that when I use existing function_handles to create new ones, it is clear to see their meanings.

Accedi per commentare.

Più risposte (0)

Tag

Prodotti


Release

R2020b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by