概述
盡可能趁有空更一次的 MATLAB 筆記。
這次內(nèi)容講講 MATALB 中的函數(shù)的高級(jí)部分。
- MATLAB 函數(shù)的原理:使用函數(shù)的好處在于簡(jiǎn)化了代碼同時(shí)增加了代碼的可讀性。但是在使用函數(shù)的時(shí)候可能會(huì)有一些有偏差的理解,因此需要從機(jī)器的尺度上取解釋 MATLAB 函數(shù)的原理。
- MATLAB 可選參數(shù):同一個(gè)可能要求不同的使用方法,如果逐一編寫單獨(dú)的函數(shù)實(shí)現(xiàn)不僅浪費(fèi)而且難以使用,因此調(diào)用函數(shù)的使用要能夠自適應(yīng)不同的輸入?yún)?shù)和輸出參數(shù)。
- MATLAB 函數(shù)的其他類型:MATLAB 官方定義 MATLAB 函數(shù)有四種其他類型:匿名函數(shù)、局部函數(shù)、嵌套函數(shù)和私有函數(shù)。(私以為前兩種可能更常用)
MATLAB 函數(shù)的原理
對(duì)于某一個(gè) MATLAB 函數(shù)而言,它就像一個(gè)黑盒子(Black Box),用戶只需要知道函數(shù)的用途而不必知道函數(shù)內(nèi)部是怎么實(shí)現(xiàn)的。在主程序調(diào)用函數(shù)時(shí),計(jì)算機(jī)開(kāi)辟另一塊內(nèi)存空間,進(jìn)入函數(shù)內(nèi)部,運(yùn)行函數(shù)命令,函數(shù)運(yùn)行結(jié)束后,系統(tǒng)自動(dòng)將這塊內(nèi)存空間回收,除了輸出參數(shù)以外的所有函數(shù)內(nèi)容都不再存在。
請(qǐng)務(wù)必記得:在函數(shù)內(nèi)部做的一切不會(huì)對(duì)外部造成任何影響。
以下方這個(gè)函數(shù)為例
function add_1(x)
x = x + 1;
disp(['In function: x = ' num2str(x)]);
end
運(yùn)行如下命令
x = 1;
add_1(x); % 在函數(shù)內(nèi)部,x 的值被修改了
disp(['In main: x = ' num2str(x)]); % 主程序的 x 并沒(méi)有被修改
結(jié)果得到
In function: x = 2
In main: x = 1
這又是什么情況呢?事實(shí)上,兩個(gè) x 是不同的值(函數(shù)并不執(zhí)行“手遞手式”地傳遞參數(shù),而是將原來(lái)的變量拷貝一份放進(jìn)函數(shù)的內(nèi)存空間中),因此修改了函數(shù)內(nèi)部的 x 后,在函數(shù)結(jié)束時(shí)就被回收,而外部的 x 并沒(méi)有受到任何影響。
你可以將主程序的 x 替換成 a ,并在函數(shù)內(nèi)部的“x = x + 1;”添加斷點(diǎn)(只需單擊每一行最左側(cè)行號(hào)后的區(qū)域,即顯示一個(gè)紅色的斷點(diǎn);再次單擊可以取消斷點(diǎn))。運(yùn)行時(shí),會(huì)在函數(shù)內(nèi)部暫停,觀察 Workspace 可以發(fā)現(xiàn)函數(shù)內(nèi)部的 x = 1。點(diǎn)擊工具欄中的 Continue 則可以繼續(xù)運(yùn)行看到主程序仍然是 a = 1。
a = 1;
add_1(a); % 在函數(shù)內(nèi)部,x 的值被修改了
disp(['In main: a = ' num2str(a)]); % 主程序的 a 并沒(méi)有被修改
由此可見(jiàn),函數(shù)內(nèi)部的變量在主程序中是“看不見(jiàn)”,函數(shù)內(nèi)部無(wú)法使用主程序中的變量,同樣地,主程序也無(wú)法使用函數(shù)內(nèi)部的變量。因此,函數(shù)與主程序的“交流”就依賴于輸入?yún)?shù)和輸出參數(shù)。
將函數(shù)需要使用的變量傳入,將函數(shù)計(jì)算所得結(jié)果傳出。
MATLAB 可選參數(shù)
MATLAB 中的許多函數(shù)都有著各種靈活的輸入方式,可以支持不同數(shù)量的輸入?yún)?shù)和輸出參數(shù),比如常用的 zeros 函數(shù)就可以根據(jù)不同數(shù)量的輸入?yún)?shù)創(chuàng)建不同維度的零矩陣。這種靈活性就基于 MATLAB 函數(shù)中的可選參數(shù)。
MATLAB 提供的可選參數(shù)有如下:
- nargin 函數(shù)輸入?yún)?shù)數(shù)目
- nargout 函數(shù)輸出參數(shù)數(shù)目
- varargin 可變長(zhǎng)度輸入?yún)?shù)列表
- varargout 可變長(zhǎng)度的輸出參數(shù)列表
- narginchk 驗(yàn)證輸入?yún)?shù)數(shù)目
- nargoutchk 驗(yàn)證輸出參數(shù)數(shù)目
- validateattributes 檢查數(shù)組的有效性
- validatestring 檢查文本的有效性
- inputParser 函數(shù)的輸入解析器
- inputname 函數(shù)輸入的變量名稱
- mfilename 當(dāng)前正在運(yùn)行的代碼的文件名
樣例一:對(duì)于不同輸入?yún)?shù)執(zhí)行不同操作
有時(shí)候?qū)τ谝粋€(gè)函數(shù)希望有多種的調(diào)用方式時(shí),使用可選參數(shù) nargin。同理對(duì)于不同的輸出參數(shù)采用不同的調(diào)用方式時(shí),使用可選參數(shù) nargout。
function report_name_id(name,id)
switch nargin % 在函數(shù)中 nargin 就是調(diào)用函數(shù)時(shí)輸入?yún)?shù)的數(shù)量
case 2
disp(['name: ' name ' id:' num2str(id)]);
% 兩個(gè)輸入?yún)?shù)時(shí)輸出名字和 id
case 1
disp(['name: ' name ' without id information.']);
% 僅輸入一個(gè)參數(shù)名字(就是第一個(gè)參數(shù))時(shí)輸出名字和 無(wú) id 信息
otherwise
disp('null');
% 不給輸入?yún)?shù)時(shí),輸出 null。
end
end
執(zhí)行以下命令
report_name_id('Tom',153412); % 兩個(gè)輸入?yún)?shù)時(shí)輸出名字和 id
report_name_id('Tom'); % 輸出名字和 無(wú) id 信息
report_name_id(); % 輸出 null
樣例二:對(duì)于未知數(shù)量參數(shù)執(zhí)行不同操作
有時(shí)候調(diào)用函數(shù)甚至不知道有多少個(gè)輸入?yún)?shù)時(shí),又該如何處置呢?這時(shí)就可以使用 varargin 來(lái)接收任意個(gè)輸入?yún)?shù),通過(guò) varargin{index)}來(lái)訪問(wèn)對(duì)應(yīng)的輸入?yún)?shù)。
function information_items(name,varargin)
disp(name);
for index = 1:nargin-1
disp(varargin{index});
end
end
三種調(diào)用方式會(huì)導(dǎo)致不同的結(jié)果,可以看到 varargin 都可以將多余的任意數(shù)量個(gè)參數(shù)都取得,并且可以通過(guò) varargin{index}來(lái)訪問(wèn)。(事實(shí)上,varargin 是一個(gè) cell 型變量)
information_items('Tom','height:180','weight:72kg','age:17');
information_items('Jack','height:169','weight:74kg');
information_items('Bill','height:171','weight:88kg','age:23','shcool:Stanford');
同理,也可以對(duì)應(yīng)地向 varargout{index} 賦值,將任意數(shù)量個(gè)輸出參數(shù)傳出。
樣例三:驗(yàn)證輸入?yún)?shù)數(shù)目
narginchk 是基于給定的輸入?yún)?shù)上下限來(lái)驗(yàn)證輸入?yún)?shù)是否符合要求的命令。
function two_or_three_inputs(varargin)
narginchk(2,3);
disp('There are two or three inputs');
end
同樣使用三種調(diào)用方式
two_or_three_inputs(1); % 報(bào)錯(cuò) error,輸入?yún)?shù)不夠
two_or_three_inputs(1,1);
two_or_three_inputs(1,1,1,1); % 報(bào)錯(cuò) error,輸入?yún)?shù)過(guò)多
同理也可以使用 nargoutchk 對(duì)輸出參數(shù)驗(yàn)證,此外,不妨嘗試自己使用 error 函數(shù)給出合適的調(diào)用函數(shù)提示信息。
樣例四:函數(shù)輸入的變量名稱
你可能會(huì)想函數(shù)輸入的變量名稱肯定是已知的,為什么還要特地設(shè)計(jì)這樣一個(gè)函數(shù)呢?這個(gè)函數(shù)是有一定作用的。
function y = data_to_string(varargin)
y = [];
for index = 1:nargin
y = [y inputname(index) ':' varargin{index} ';'];
end
end
這個(gè)函數(shù)可以將數(shù)據(jù)根據(jù)對(duì)應(yīng)的變量類型串成一個(gè)數(shù)據(jù)包。
name = 'Tom';
age = '18';
height = '180cm';
weight = '70kg';
str = data_to_string(name,age,height,weight);
MATLAB 函數(shù)其他類型
MATLAB 官方確定的其他類型的函數(shù)包括四種:
- 匿名函數(shù)
- 局部函數(shù)
- 嵌套函數(shù)
- 私有函數(shù)
匿名函數(shù)
匿名函數(shù)是僅包含一句 MATLAB 命令的函數(shù)。匿名函數(shù)的優(yōu)點(diǎn)在于無(wú)需另外創(chuàng)建保存一個(gè) m 文件,甚至可以在腳本文件和命令行中隨時(shí)定義隨時(shí)使用。形如下式的命令可以創(chuàng)建一個(gè)匿名函數(shù):
fun = @(x,y)x.^2+y.^2-2*x*y+4;
其中 fun 是函數(shù)句柄,@ 運(yùn)算符則用于創(chuàng)建一個(gè)句柄。
局部函數(shù)
局部函數(shù)也叫做子函數(shù),相當(dāng)于某個(gè)完整函數(shù)的附屬函數(shù)。局部函數(shù)編寫于某個(gè)函數(shù)最后一行之后,是該函數(shù)的附屬函數(shù)。這意味著局部函數(shù)使用范圍有限:僅能被同一個(gè)文件中的其他函數(shù)調(diào)用,對(duì)其他函數(shù)和命令行不可見(jiàn)。
function [avg, med] = mystats(x)
% 主函數(shù) 可以被外部檢索調(diào)用
n = length(x);
avg = mymean(x,n);
med = mymedian(x,n);
end
function a = mymean(v,n)
% 子函數(shù) 1
% 只能被主函數(shù)和其他子函數(shù)調(diào)用
a = sum(v)/n;
end
function m = mymedian(v,n)
% 子函數(shù) 2
% 只能被主函數(shù)和其他子函數(shù)調(diào)用
w = sort(v);
if rem(n,2) == 1
m = w((n + 1)/2);
else
m = (w(n/2) + w(n/2 + 1))/2;
end
end
特別地,如果你習(xí)慣省略函數(shù)體結(jié)尾的 end,在同一個(gè)文件中應(yīng)當(dāng)保證子函數(shù)和主函數(shù)使用同一種格式。
嵌套函數(shù)
嵌套函數(shù)是定義在函數(shù)中的函數(shù),外層函數(shù)可以調(diào)用內(nèi)層函數(shù)并且如果內(nèi)層函數(shù)變量在外層函數(shù)中有定義,那么嵌套函數(shù)可以訪問(wèn)和修改在其父函數(shù)中定義的變量。
function main1
x = 5;
nestfun1
function nestfun1
x = x + 1;
end
end
私有函數(shù)
假設(shè)當(dāng)前工作路徑為 “/xxx”,在文件夾 xxx 創(chuàng)建一個(gè)名為“private”子文件夾,可以指定一個(gè)函數(shù)為私有函數(shù)。這樣一來(lái),只有文件夾 xxx下的函數(shù)可以調(diào)用這個(gè)私有函數(shù)。
小結(jié)
到此函數(shù)的內(nèi)容也告一段落,本文除了 MATLAB 函數(shù)的內(nèi)容外,還有一個(gè)額外的點(diǎn)就是斷點(diǎn)調(diào)試(快速地中斷和保留中斷時(shí)的變量,能夠讓你加快調(diào)試的步驟)。