匿名函数和函数句柄
基本
匿名函数是 MATLAB 语言的强大工具。它们是本地存在的函数,即:在当前工作空间中。但是,它们不像常规函数那样存在于 MATLAB 路径中,例如在 m 文件中。这就是为什么它们被称为匿名,尽管它们可以在工作区中具有类似变量的名称。
@
运算符
使用 @
运算符创建匿名函数和函数句柄。例如,要创建 sin
函数的句柄(正弦)并将其用作 f
:
>> f = @sin
f =
@sin
现在 f
是 sin
函数的一个句柄。就像(在现实生活中)门把手是一种使用门的方式一样,功能手柄是一种使用功能的方式。要使用 f
,参数将传递给它,就像它是 sin
函数一样:
>> f(pi/2)
ans =
1
f
接受 sin
函数接受的任何输入参数。如果 sin
是一个接受零输入参数的函数(它没有,但是其他参数,例如 peaks
函数),f()
将用于在没有输入参数的情况下调用它。
自定义匿名功能
一个变量的匿名函数
创建现有函数的句柄显然没有用,例如上面示例中的 sin
。在这个例子中它是多余的。但是,创建匿名函数非常有用,这些函数可以执行自定义操作,否则需要多次重复或创建单独的函数。作为自定义匿名函数的一个示例,它接受一个变量作为其输入,求和信号的正弦和余弦平方:
>> f = @(x) sin(x)+cos(x).^2
f =
@(x)sin(x)+cos(x).^2
现在 f
接受一个名为 x
的输入参数。这是在 @
运算符之后直接使用括号 (...)
指定的。f
现在是 x
:f(x)
的匿名函数。通过将 x
的值传递给 f
来使用它:
>> f(pi)
ans =
1.0000
值矢量或变量也可以传递给 f
,只要它们在 f
中以有效的方式使用:
>> f(1:3) % pass a vector to f
ans =
1.1334 1.0825 1.1212
>> n = 5:7;
>> f(n) % pass n to f
ans =
-0.8785 0.6425 1.2254
多个变量的匿名函数
以同样的方式,可以创建匿名函数以接受多个变量。一个接受三个变量的匿名函数示例:
>> f = @(x,y,z) x.^2 + y.^2 - z.^2
f =
@(x,y,z)x.^2+y.^2-z.^2
>> f(2,3,4)
ans =
-3
参数化匿名函数
工作空间中的变量可以在匿名函数的定义中使用。这称为参数化。例如,要在匿名函数中使用常量 c = 2
:
>> c = 2;
>> f = @(x) c*x
f =
@(x)c*x
>> f(3)
ans =
6
f(3)
使用变量 c
作为参数与所提供的 x
相乘。请注意,如果此时 c
的值设置为不同的值,则调用 f(3)
,结果不会有所不同。c
的值是创建匿名函数时的值:
>> c = 2;
>> f = @(x) c*x;
>> f(3)
ans =
6
>> c = 3;
>> f(3)
ans =
6
匿名函数的输入参数不引用工作空间变量
请注意,使用工作空间中的变量名称作为匿名函数的输入参数之一(即使用 @(...)
)将不会使用这些变量的值。相反,它们被视为匿名函数范围内的不同变量,即:匿名函数具有其私有工作空间,其中输入变量从不引用主工作空间中的变量。主工作区和匿名函数的工作区不了解彼此的内容。举例说明:
>> x = 3 % x in main workspace
x =
3
>> f = @(x) x+1; % here x refers to a private x variable
>> f(5)
ans =
6
>> x
x =
3
f
中未使用来自主工作空间的 x
的值。此外,在主要工作区中,x
保持不变。在 f
的范围内,@
运算符后括号内的变量名与主工作空间变量无关。
匿名函数存储在变量中
匿名函数(或者更确切地说,指向匿名函数的函数句柄)与当前工作空间中的任何其他值一样存储:在变量中(如上所述),在单元格数组({@(x)x.^2,@(x)x+1}
)中,甚至在属性(如用于交互式图形的 h.ButtonDownFcn
)。这意味着匿名函数可以像任何其他值一样对待。将其存储在变量中时,它在当前工作空间中具有名称,并且可以像保存数字的变量一样进行更改和清除。
换句话说:函数句柄(无论是在 @sin
形式还是用于匿名函数)只是一个可以存储在变量中的值,就像数值矩阵一样。
高级用途
将函数句柄传递给其他函数
由于函数句柄被视为变量,因此它们可以传递给接受函数句柄作为输入参数的函数。
示例:在 m 文件中创建一个函数,该文件接受函数句柄和标量数。然后通过将 3
传递给它来调用函数句柄,然后将标量数添加到结果中。结果返回。
funHandleDemo.m
的内容:
function y = funHandleDemo(fun,x)
y = fun(3);
y = y + x;
将它保存在路径上的某个位置,例如在 MATLAB 的当前文件夹中。现在 funHandleDemo
可以如下使用,例如:
>> f = @(x) x^2; % an anonymous function
>> y = funHandleDemo(f,10) % pass f and a scalar to funHandleDemo
y =
19
另一个现有函数的句柄可以传递给 funHandleDemo
:
>> y = funHandleDemo(@sin,-5)
y =
-4.8589
请注意 @sin
如何快速访问 sin
函数,而无需先使用 f = @sin
将其存储在变量中。
使用 bsxfun
,cellfun
和具有匿名功能的类似功能
MATLAB 有一些内置函数可以接受匿名函数作为输入。这是一种用最少的代码行执行许多计算的方法。例如,bsxfun
执行逐元素二元运算,即:它以逐元素方式在两个向量或矩阵上应用函数。通常,这需要使用 for
-loops,这通常需要预先分配速度。使用 bsxfun
加快了这个过程。以下示例使用 tic
和 toc
来说明这一点,这两个函数可用于计算代码所需的时间。它计算矩阵列均值中每个矩阵元素的差异。
A = rand(50); % 50-by-50 matrix of random values between 0 and 1
% method 1: slow and lots of lines of code
tic
meanA = mean(A); % mean of every matrix column: a row vector
% pre-allocate result for speed, remove this for even worse performance
result = zeros(size(A));
for j = 1:size(A,1)
result(j,:) = A(j,:) - meanA;
end
toc
clear result % make sure method 2 creates its own result
% method 2: fast and only one line of code
tic
result = bsxfun(@minus,A,mean(A));
toc
运行上面的示例会产生两个输出:
Elapsed time is 0.015153 seconds.
Elapsed time is 0.007884 seconds.
这些行来自 toc
函数,它们打印自上次调用 tic
函数以来经过的时间。
bsxfun
调用将第一个输入参数中的函数应用于其他两个输入参数。@minus
是与减号相同的操作的长名称。可以指定与任何其他函数不同的匿名函数或句柄(@
),只要它接受 A
和 mean(A)
作为输入以生成有意义的结果。
特别是对于大型矩阵中的大量数据,bsxfun
可以大大加快速度。它还使代码看起来更干净,尽管对于不了解 MATLAB 或 bsxfun
的人来说可能更难解释。 (请注意,在 MATLAB R2016a 及更高版本中,以前使用 bsxfun
的许多操作不再需要它们; A-mean(A)
直接工作,在某些情况下可能更快。)