概述
盡可能趁有空更一次的 MATLAB 筆記丸边。
這次內(nèi)容講講 MATALB 中的函數(shù)的高級部分塘辅。
- MATLAB 函數(shù)的原理:使用函數(shù)的好處在于簡化了代碼同時增加了代碼的可讀性嚣艇。但是在使用函數(shù)的時候可能會有一些有偏差的理解讽坏,因此需要從機(jī)器的尺度上取解釋 MATLAB 函數(shù)的原理目木。
- MATLAB 可選參數(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ù)的原理
對于某一個 MATLAB 函數(shù)而言,它就像一個黑盒子(Black Box)风范,用戶只需要知道函數(shù)的用途而不必知道函數(shù)內(nèi)部是怎么實現(xiàn)的咨跌。在主程序調(diào)用函數(shù)時,計算機(jī)開辟另一塊內(nèi)存空間硼婿,進(jìn)入函數(shù)內(nèi)部锌半,運(yùn)行函數(shù)命令,函數(shù)運(yùn)行結(jié)束后寇漫,系統(tǒng)自動將這塊內(nèi)存空間回收刊殉,除了輸出參數(shù)以外的所有函數(shù)內(nèi)容都不再存在。
請務(wù)必記得:在函數(shù)內(nèi)部做的一切不會對外部造成任何影響猪腕。
以下方這個函數(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 并沒有被修改
結(jié)果得到
In function: x = 2
In main: x = 1
這又是什么情況呢?事實上陋葡,兩個 x 是不同的值(函數(shù)并不執(zhí)行“手遞手式”地傳遞參數(shù)亚亲,而是將原來的變量拷貝一份放進(jìn)函數(shù)的內(nèi)存空間中),因此修改了函數(shù)內(nèi)部的 x 后腐缤,在函數(shù)結(jié)束時就被回收捌归,而外部的 x 并沒有受到任何影響。
你可以將主程序的 x 替換成 a 岭粤,并在函數(shù)內(nèi)部的“x = x + 1;”添加斷點(只需單擊每一行最左側(cè)行號后的區(qū)域惜索,即顯示一個紅色的斷點;再次單擊可以取消斷點)剃浇。運(yùn)行時巾兆,會在函數(shù)內(nèi)部暫停,觀察 Workspace 可以發(fā)現(xiàn)函數(shù)內(nèi)部的 x = 1虎囚。點擊工具欄中的 Continue 則可以繼續(xù)運(yùn)行看到主程序仍然是 a = 1角塑。
a = 1;
add_1(a); % 在函數(shù)內(nèi)部,x 的值被修改了
disp(['In main: a = ' num2str(a)]); % 主程序的 a 并沒有被修改
由此可見淘讥,函數(shù)內(nèi)部的變量在主程序中是“看不見”圃伶,函數(shù)內(nèi)部無法使用主程序中的變量,同樣地蒲列,主程序也無法使用函數(shù)內(nèi)部的變量窒朋。因此,函數(shù)與主程序的“交流”就依賴于輸入?yún)?shù)和輸出參數(shù)蝗岖。
將函數(shù)需要使用的變量傳入侥猩,將函數(shù)計算所得結(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 可變長度輸入?yún)?shù)列表
- varargout 可變長度的輸出參數(shù)列表
- narginchk 驗證輸入?yún)?shù)數(shù)目
- nargoutchk 驗證輸出參數(shù)數(shù)目
- validateattributes 檢查數(shù)組的有效性
- validatestring 檢查文本的有效性
- inputParser 函數(shù)的輸入解析器
- inputname 函數(shù)輸入的變量名稱
- mfilename 當(dāng)前正在運(yùn)行的代碼的文件名
樣例一:對于不同輸入?yún)?shù)執(zhí)行不同操作
有時候?qū)τ谝粋€函數(shù)希望有多種的調(diào)用方式時杰标,使用可選參數(shù) nargin。同理對于不同的輸出參數(shù)采用不同的調(diào)用方式時彩匕,使用可選參數(shù) nargout腔剂。
function report_name_id(name,id)
switch nargin % 在函數(shù)中 nargin 就是調(diào)用函數(shù)時輸入?yún)?shù)的數(shù)量
case 2
disp(['name: ' name ' id:' num2str(id)]);
% 兩個輸入?yún)?shù)時輸出名字和 id
case 1
disp(['name: ' name ' without id information.']);
% 僅輸入一個參數(shù)名字(就是第一個參數(shù))時輸出名字和 無 id 信息
otherwise
disp('null');
% 不給輸入?yún)?shù)時,輸出 null驼仪。
end
end
執(zhí)行以下命令
report_name_id('Tom',153412); % 兩個輸入?yún)?shù)時輸出名字和 id
report_name_id('Tom'); % 輸出名字和 無 id 信息
report_name_id(); % 輸出 null
樣例二:對于未知數(shù)量參數(shù)執(zhí)行不同操作
有時候調(diào)用函數(shù)甚至不知道有多少個輸入?yún)?shù)時掸犬,又該如何處置呢?這時就可以使用 varargin 來接收任意個輸入?yún)?shù)绪爸,通過 varargin{index)}來訪問對應(yīng)的輸入?yún)?shù)湾碎。
function information_items(name,varargin)
disp(name);
for index = 1:nargin-1
disp(varargin{index});
end
end
三種調(diào)用方式會導(dǎo)致不同的結(jié)果,可以看到 varargin 都可以將多余的任意數(shù)量個參數(shù)都取得奠货,并且可以通過 varargin{index}來訪問介褥。(事實上,varargin 是一個 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');
同理递惋,也可以對應(yīng)地向 varargout{index} 賦值柔滔,將任意數(shù)量個輸出參數(shù)傳出。
樣例三:驗證輸入?yún)?shù)數(shù)目
narginchk 是基于給定的輸入?yún)?shù)上下限來驗證輸入?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); % 報錯 error睛廊,輸入?yún)?shù)不夠
two_or_three_inputs(1,1);
two_or_three_inputs(1,1,1,1); % 報錯 error,輸入?yún)?shù)過多
同理也可以使用 nargoutchk 對輸出參數(shù)驗證杉编,此外超全,不妨嘗試自己使用 error 函數(shù)給出合適的調(diào)用函數(shù)提示信息。
樣例四:函數(shù)輸入的變量名稱
你可能會想函數(shù)輸入的變量名稱肯定是已知的邓馒,為什么還要特地設(shè)計這樣一個函數(shù)呢嘶朱?這個函數(shù)是有一定作用的。
function y = data_to_string(varargin)
y = [];
for index = 1:nargin
y = [y inputname(index) ':' varargin{index} ';'];
end
end
這個函數(shù)可以將數(shù)據(jù)根據(jù)對應(yīng)的變量類型串成一個數(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)點在于無需另外創(chuàng)建保存一個 m 文件,甚至可以在腳本文件和命令行中隨時定義隨時使用挂疆。形如下式的命令可以創(chuàng)建一個匿名函數(shù):
fun = @(x,y)x.^2+y.^2-2*x*y+4;
其中 fun 是函數(shù)句柄改览,@ 運(yùn)算符則用于創(chuàng)建一個句柄。
局部函數(shù)
局部函數(shù)也叫做子函數(shù)缤言,相當(dāng)于某個完整函數(shù)的附屬函數(shù)宝当。局部函數(shù)編寫于某個函數(shù)最后一行之后,是該函數(shù)的附屬函數(shù)胆萧。這意味著局部函數(shù)使用范圍有限:僅能被同一個文件中的其他函數(shù)調(diào)用庆揩,對其他函數(shù)和命令行不可見俐东。
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订晌,在同一個文件中應(yīng)當(dāng)保證子函數(shù)和主函數(shù)使用同一種格式虏辫。
嵌套函數(shù)
嵌套函數(shù)是定義在函數(shù)中的函數(shù),外層函數(shù)可以調(diào)用內(nèi)層函數(shù)并且如果內(nèi)層函數(shù)變量在外層函數(shù)中有定義锈拨,那么嵌套函數(shù)可以訪問和修改在其父函數(shù)中定義的變量砌庄。
function main1
x = 5;
nestfun1
function nestfun1
x = x + 1;
end
end
私有函數(shù)
假設(shè)當(dāng)前工作路徑為 “/xxx”,在文件夾 xxx 創(chuàng)建一個名為“private”子文件夾奕枢,可以指定一個函數(shù)為私有函數(shù)娄昆。這樣一來,只有文件夾 xxx下的函數(shù)可以調(diào)用這個私有函數(shù)缝彬。
小結(jié)
到此函數(shù)的內(nèi)容也告一段落萌焰,本文除了 MATLAB 函數(shù)的內(nèi)容外,還有一個額外的點就是斷點調(diào)試(快速地中斷和保留中斷時的變量谷浅,能夠讓你加快調(diào)試的步驟)扒俯。