4.5 函数参数的处理方式
对比4.2 节和4.3 节中的示例,你会发现函数的灵活性,因为在运行程序时,只要给出不同参数就可以得到想要的结果,这要比更改程序的代码方便得多。如果某函数的使用频率较高且参数较多,每次输入参数值就比较麻烦,而且当参数输入有误时,程序就会无法正确运行,如 4.3 节中的示例而言,如果输入以下内容
>> playnoisef(8890,0.05,500, 5000)↙
??? Input argument "loops" is undefined. Error in ==> playnoisef at 13 for i=1:loops %循环50次(播放50次)
上面显示了错误信息,因为少输入一个表示循环次数(播放次数)的参数,就无法找到变量loops,错误出现在程序playnoisef.m中的第13行,并且给出了13行的内容“for i=1:loops %循环50次(播放50次)”,这是大家不希望看到的情况。下面来看处理这种情况的方法。
4.5.1 默认处理
采用默认参数值,即如果没有输入某个参数值,则使用默认值。
利用函数nargin可以获取某函数输入参数的个数,根据输入参数的个数来设置默认值,示例如下:
文件名:playnoisef1.m(文件位于Psyfeng\Little_Examples目录下)
function playnoisef1(sf,duration,lowfreqs,highfreqs,loops) if nargin<1 %如果没有任何参数 sf=44100; duration=0.1; lowfreqs=500; highfreqs=1000; loops=10; elseif nargin<2 %如果只有一个参数 duration=0.1; lowfreqs=500; highfreqs=1000; loops=10; elseif nargin<3 %如果输入2个参数 lowfreqs=500; highfreqs=1000; loops=10; elseif nargin<4 %如果输入了3个参数 highfreqs=1000; loops=10; elseif nargin<5 %如果输入了4个参数 loops=10; end for i=1:loops %循环播放 r=randi([lowfreqs highfreqs]); %从范围中随机频率 s1=MakeBeep(r,duration,sf); %生成噪音 sound(s1,sf); %播放 end end
对于上面的示例程序(playnoisef1.m),当利用mcc生成可执行文件后,通过开始→运行…(WinXP系统下)或开始→搜索程序或文件(Win7系统下),以Win7为例,在其中输入cmd,然后选择cmd (或者通用方法:开始→(所有)程序→附件→命令提示符),打开命令窗口,假设编译生成的可执行文件在D盘根目录下,切换至D盘,然后输入playnoisef1↙,就可以执行程序,但如果使用参数,如playnoisef1 22500↙,就会出现错误,在MATLAB命令窗口中这样运行也同样出错,原因是此时22500不再是数值型,而是字符型,显然提供的参数类型与所要求的不符。
图4-2 搜索cmd命令
图4-3 DOS命令窗口
处理上面错误的一种方法就是使用转换函数str2double,将字符型转换成数值型。
文件名:playnoisef2.m(文件位于Psyfeng\Little_Examples目录下)
function playnoisef2(sf,duration,lowfreqs,highfreqs,loops) if nargin<1 %如果没有任何参数 sf=44100; duration=0.1; lowfreqs=500; highfreqs=1000; loops=10; elseif nargin<2 %如果只有一个参数 duration=0.1; lowfreqs=500; highfreqs=1000; loops=10; elseif nargin<3 %如果输入2个参数 lowfreqs=500; highfreqs=1000; loops=10; elseif nargin<4 %如果输入3个参数 highfreqs=1000; loops=10; elseif nargin<5 %如果输入4个参数 loops=10; end if ischar(sf) %如果是字符型,则将其转换为数值型 sf=str2double(sf); end if ischar(duration) duration=str2double(duration); end if ischar(lowfreqs) lowfreqs=str2double(lowfreqs); end if ischar(highfreqs) highfreqs=str2double(highfreqs); end if ischar(loops) loops=str2double(loops); end for i=1:loops %循环播放 r=randi([lowfreqs highfreqs]); %从范围中随机频率 s1=MakeBeep(r,duration,sf); %生成噪音 sound(s1,sf); %播放 end end
改动之后,就可以在MATLAB命令窗口或DOS窗口中,直接使用如下形式来运行程序
playnoisef2 3800 0.05 5000 10000 10↙
4.5.2 命令行输入
通过提示使用者输入来设置参数,对程序playnoisef1.m做如下改动(本例中可以将function语句去掉)。
文件名:playnoisef 3.m(文件位于Psyfeng\Little_Examples目录下)
function playnoisef3 sf=input('输入采样频率(回车默认值为44100):'); if isempty(sf) sf=44100; end duration=input('输入声音长度(回车默认值为0.5秒):'); if isempty(duration) duration=0.5; end lowfreqs=input('输入声音频率下限(回车默认值500Hz):'); if isempty(lowfreqs) lowfreqs=500; end highfreqs=input('输入声音频率上限(回车默认值1000Hz):'); if isempty(highfreqs) highfreqs=1000; end loops=input('输入循环次数(回车默认值10次):'); if isempty(loops) loops=10; end for i=1:loops %循环50次(播放50次) r=randi([lowfreqs highfreqs]); %从范围中随机频率 s1=MakeBeep(r,duration,sf); %生成噪音 sound(s1,sf); %播放 end end
4.5.3 对话框设置参数
利用MATLAB提供的输入对话框inputdlg函数(参见8.8节)可以方便快捷地设置参数输入界面,注意下面示例文件中与function对应的end关键字。
文件名:playnoisef4.m(文件位于Psyfeng\Little_Examples目录下)
function playnoisef4 paras=getmyParameter; %获取参数 if isempty(paras) return; else sf=str2double(paras{1}); duration=str2double(paras{2}); freqs=sscanf(paras{3},'%d'); loops=str2double(paras{4}); end for i=1:loops %循环播放 r=randi(freqs); %从范围中随机频率 s1=MakeBeep(r,duration,sf); %生成噪音 sound(s1,sf); %播放 end end function paras=getmyParameter %获取参数的函数 paras=inputdlg({'采样频率','声音长度','声音频率范围','循环次数'},'参数设置',1,{'44100','0.05','500 1000','10'}); %调用inputdlg函数,将返回结果存储于paras end
运行程序playnoisef4,首先出现如图4-4所示的对话框来设置相应的参数。参数设置完毕后,点击OK按钮,就会播放一定频率范围内的纯音。
图4-4 参数设置窗口
尽管对话框式的参数设置比较方便,也较为直观,但如果实验过程中发现设置的默认值不理想,这样每次运行程序就都需要更改某些内容,而且如果实验开始前忘记更改,对实验结果的影响可想而知,因此需要程序能够记录下上次所设置的参数值,下次执行程序时能够自动将前一次使用的参数填充到图4-4所示的对话框。
4.5.4 参数值的记忆与存取
可以利用MATLAB提供的文件读写函数fgetl和fprintf将设置的参数值写入某个文件中,当需要时再从文件中读取来使用。
文件名:playnoisef5.m(文件位于Psyfeng\Little_Examples目录下)
function playnoisef5 mainfilename=mfilename; %利用mfilename函数获取文件名信息 paras=getmyParameter(mainfilename); %将文件名作为参数传给getmyParameter if isempty(paras) %如果在参数设置窗口中选择Cancel,则返回值为空,退出程序 return; else sf=str2double(paras{1}); %将各个参数转换为数值 duration=str2double(paras{2}); freqs=sscanf(paras{3},'%d'); loops=str2double(paras{4}); end for i=1:loops %循环播放 r=randi(freqs); %从范围中随机频率 s1=MakeBeep(r,duration,sf); %生成噪音 sound(s1,sf); %播放 end end function paras=getmyParameter(configfilename) prompt={'采样频率','声音长度','声音频率范围','循环次数'}; dlg_title='参数设置'; default_answer=[]; if exist([configfilename '.ini'],'file') %如果有配置文件,尝试使用之 fid=fopen([configfilename '.ini'],'r'); %打开配置文件 if fid~=-1 %如果文件打开成功 while true linestr=fgetl(fid); %逐行读取文件中内容 if ~ischar(linestr), break, end %如果读至文件尾,则退出while循环 default_answer=[default_answer {linestr}]; %将读取内容拼接 end fclose(fid); %关闭文件 else error(['无法打开配置文件:' configfilename '.ini']); end else %如果没有配置文件就使用默认值 default_answer={'44100','0.1','500 1000','10'}; end paras=inputdlg(prompt,dlg_title,1,default_answer); if ~isempty(paras) %如果paras为非空 fid=fopen([configfilename '.ini'],'w'); %打开文件,写入参数值 if fid~=-1 for i=1:length(paras) fprintf(fid,'%s\r\n',paras{i}); %循环写入字符信息 end fclose(fid); else error(['无法打开配置文件:' configfilename '.ini']); end end end
4.5.5 函数的返回参数
正如4.5.4小节中函数getmyParameter返回输入的信息参数paras一样,在设计函数时,可以通过函数的返回值获取函数执行结束后的某些重要信息。从函数的定义形式 function [out1, out2, ...] =myfun(in1, in2, ...)知道,out1、out2代表返回或输出参数,看下面的示例,计算输入矩阵列方向X的总和、均值和标准差,并返回这些值。
文件名:myStat.m(文件位于Psyfeng\Little_Examples目录下)
function [sumx avgx stdx]=myStat(X) %三个输出参数sumx avgx stdx放在[]内 X n=ones(1,size(X,2))*size(X,1); %利用size和ones函数构建每列元素个数数组 sumx=sum(X); %计算总和,sumx作为返回参数 avgx=avg(sumx,n); %利用自定义函数avg计算均值,avgx作为返回参数 stdx=stdev(X,avgx,n);%利用自定义函数stdev计算标准差,stdx作为返回参数 end function avgx=avg(sumx,n) %自定义函数avgx计算均值 avgx=sumx./n; end function stdx=stdev(X,avgx,n) %自定义函数stdev计算标准差 avgarray=repmat(avgx,size(X,1),1); %构造均值矩阵,使每列具有相同均值 stdx=sqrt(sum((X-avgarray).^2)./n); %计算标准差 end
运行函数myStat,结果如下:
>> myStat([10 10 10 10;5 9 10 12])↙
X = 10 10 10 10 5 9 10 12 ans = 15 19 20 22
如直接调用,仅返回第一个输出参数的内容,要想获取多个输出参数内容,则
>> [sums avg stds]=myStat([10 10 10 10;5 9 10 12])↙
X = 10 10 10 10 5 9 10 12 sums = 15 19 20 22 avg = 7.5000 9.5000 10.0000 11.0000 stds = 2.5000 0.5000 0 1.0000
>> [sums avg stds]=myStat([10 10 10 10;5 9 10 12]')↙
X = 10 5 10 9 10 10 10 12 sums = 40 36 avg = 10 9 stds = 0 2.5495
4.5.6 可变数目的输入/输出参数
当输入参数个数不确定时,可以使用函数varargin,与输出参数对应的函数是varargout。MATLAB将可变的输入/输出参数视作单元数组(因其存储的数据类型可变),如当绘制顶点个数不同的多边形时,事先可能不清楚顶点的个数,使用varargin 作为函数的参数,就可以方便地达到目的,并且具有很强的灵活性。
文件名:varplot.m(文件位于Psyfeng\Little_Examples目录下)
function varplot(varargin) x=zeros(1,length(varargin)); %根据输入参数的个数为变量预分配内存 y=zeros(1,length(varargin)); for i=1:length(varargin) %循环生成顶点坐标值 x(i)=varargin{i}(1); %由于输入参数为单元数组,使用{}存储元素 y(i)=varargin{i}(2); end minx=min(0,min(x)); %取包含0在内的最小值,供坐标轴使用 miny=min(0,min(y)); plot(x,y); %利用plot函数绘制多边形 axis([minx max(x)+1 miny max(y)+1]); %设置坐标轴范围 end
在命令窗口中输入以下内容,进行测试。
>> varplot([5 3],[4 7],[9 6])↙
绘图结果如图4-5所示。
图4-5 绘图结果1
>> varplot([5 3],[4 7],[9 6],[5 3])↙
绘图结果如图4-6所示。
图4-6 绘图结果2
下面的示例文件演示了可变输出参数varargout函数的应用。
文件名:varout.m(文件位于Psyfeng\Little_Examples目录下)
function varargout=varout(argin) if iscell(argin) %如果是单元数组,使用{}索引 %首先将第1个输出参数设置为输入参数的元素个数 varargout{1}=['输入参数元素个数共' num2str(numel(argin)) '个,分别是:']; for i=1:numel(argin) %根据输入参数元素个数多少,变更输出参数数目 varargout{i+1}=argin{i}; end else %否则使用()索引 varargout{1}=['输入参数元素个数共' num2str(numel(argin)) '个,分别是:']; for i=1:numel(argin) varargout{i+1}=argin(i); end end for i=1:numel(varargout) disp(varargout{i}); %遍历各个输出参数的内容 end disp(['输出参数个数为:' num2str(i) '个']); %显示汇总信息,使用类型转换函数 end
运行示例如下
>> varout([4 5 4 ;9 9 10]);↙
输入参数元素个数共6个,分别是:
4 9 5 9 4 10
输出参数个数为7个。