匿名函式和函式控制代碼

基本

匿名函式是 MATLAB 語言的強大工具。它們是本地存在的函式,即:在當前工作空間中。但是,它們不像常規函式那樣存在於 MATLAB 路徑中,例如在 m 檔案中。這就是為什麼它們被稱為匿名,儘管它們可以在工作區中具有類似變數的名稱。

@ 運算子

使用 @ 運算子建立匿名函式和函式控制代碼。例如,要建立 sin 函式的控制代碼(正弦)並將其用作 f

>> f = @sin
f = 
    @sin

現在 fsin 函式的一個控制代碼。就像(在現實生活中)門把手是一種使用門的方式一樣,功能手柄是一種使用功能的方式。要使用 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 現在是 xf(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 將其儲存在變數中。

使用 bsxfuncellfun 和具有匿名功能的類似功能

MATLAB 有一些內建函式可以接受匿名函式作為輸入。這是一種用最少的程式碼行執行許多計算的方法。例如,bsxfun 執行逐元素二元運算,即:它以逐元素方式在兩個向量或矩陣上應用函式。通常,這需要使用 for-loops,這通常需要預先分配速度。使用 bsxfun 加快了這個過程。以下示例使用 tictoc 來說明這一點,這兩個函式可用於計算程式碼所需的時間。它計算矩陣列均值中每個矩陣元素的差異。

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 是與減號相同的操作的長名稱。可以指定與任何其他函式不同的匿名函式或控制代碼(@),只要它接受 Amean(A) 作為輸入以生成有意義的結果。

特別是對於大型矩陣中的大量資料,bsxfun 可以大大加快速度。它還使程式碼看起來更乾淨,儘管對於不瞭解 MATLAB 或 bsxfun 的人來說可能更難解釋。 (請注意,在 MATLAB R2016a 及更高版本中,以前使用 bsxfun 的許多操作不再需要它們; A-mean(A) 直接工作,在某些情況下可能更快。)