PSYCHTOOLBOX工具箱及MATLAB编程实例
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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个。