M文件

如果需要重复调用命令行,或者命令行的行数比较多,则可以将它们写成M文件的形式并进行保存。[大谦MAT

如果需要重复调用命令行,或者命令行的行数比较多,则可以将它们写成M文件的形式并进行保存。[大谦MATLAB,dqmatlab点com]

脚本式M文件和函数式M文件

【例3-9】

假设想绘制一个单位球面,并且要让球面看起来比较平滑,可以在命令窗口中输入下面三行命令,其中,第一行命令是一个sphere命令函数,表示绘制一个单位球面,第二行命令对球面进行插值着色,第三行命令设置坐标系,使各坐标方向上的度量单位相同,如果不同,则会使球面看上去像椭球面一样。

code.matlab
sphere
shading interp
axis equal

生成如图3-3所示的球面。

【例3-10】

假设除需要生成球面外,还想生成柱面,则可以考虑编制一个绘图函数,这个函数有一个surface参数。在调用这个函数时,如果将该参数设置为“sphere”,则生成一个单位球面;如果设置为“cylinder”,则生成一个单位柱面。

创建和编辑程序的工作在M文件编辑器中完成,在主界面工具栏中单击“新建脚本”按钮,打开新的M文件编辑窗口。

在M文件编辑器中输入下面的代码:

code.matlab
function drawsur(surface)
    switch surface
      case 'sphere'
        sphere
      case 'cylinder'
        cylinder
    end
    shading interp
    axis equal

然后将它保存到MATLAB安装目录下的bin目录下,命名为drawsur.m。现在,可以在命令窗口中调用drawsur函数了。

注意:如果将程序保存在其他目录下,则需要在当前目录窗口中将该目录设置为当前目录。

注意:如果将程序保存在其他目录下,则需要在当前目录窗口中将该目录设置为当前目录。

在命令窗口中输入下面的命令行:

code.matlab
drawsur('sphere')

生成与图3-3中相同的球面。输入

code.matlab
drawsur('cylinder')

生成的单位柱面如图3-4所示。

M文件有两种,一种是脚本式M文件,另一种是函数式M文件。上面使用的是函数式形式。为了进行比较,我们继续使用前面的例子。新建一个M文件,在编辑器中输入下列代码:

code.matlab
sphere
shading interp
axis equal

将其保存为spher.m。在命令窗口中输入

code.matlab
spher

生成单位球面。

很明显,这里使用的方式与drawsur文件完全不同。它没有function关键字,没有输入参数。这就是脚本式M文件的使用方式,而drawsur文件中使用的是函数式M文件方式。

一般而言,脚本式M文件和函数式M文件的区别如表3-6所示。

表3-6 脚本式M文件和函数式M文件的区别

脚本式M文件 函数式M文件
不接受输入参数,没有返回值 可以接受输入参数,可以有返回值
基于工作空间中的数据进行操作 默认时,文件中参数的作用范围只限于函数内部
在自动完成需要花费很多时间的多步操作时使用 在扩展MATLAB语言功能时使用

M文件的基本结构

下面是函数式M文件比较标准的格式,其中黑体加粗的文字表示M文件的基本组成部分:

code.matlab
function [x, y] = myfun(a, b, c)        函数定义行
% H1行——用一行文字来综述函数的功能
% 帮助文本——用一行或多行文本解释如何使用函数
% 在命令行中输入"help <functionname>"时可以使用它
% 函数体——一般从第一个空白行后开始。
% 注释 – 描述函数的行为,输入输出的类型等。
% 在命令行中输入"help <functionname>"时不会显示这些文本。
x = prod(a, b); %:开始编写函数代码

所以,一个完整的函数式M文件应该包括函数定义行、H1行、帮助文本、函数体、注释和函数代码等方面的内容,其中函数定义行和函数代码是必需的。

创建帮助文本

在程序的开始部分写一些文本,提供关于如何使用函数的帮助信息,是一种好的编程习惯。如果格式正确,则在命令行中输入help <functionname>命令时会显示帮助文本。

MATLAB把紧接函数定义行的以%符号开头的第一组连续行作为函数的帮助文本。

【例3-11】

设有如下的小程序:

code.matlab
function y=tentimes(x)
% 本程序求输入值与10的乘积
% 用于演示MATLAB中函数帮助文本的实现方法
y=x*10;

把它保存到MATLAB的work目录中,将M文件命名为tentimes.m。在命令窗口中输入

code.matlab
help tentimes

显示

code.matlab
  本程序求输入值与10的乘积
  用于演示MATLAB中函数帮助信息的实现方法

输入

code.matlab
y=tentimes(10)

得到返回值

code.matlab
y =
   100

函数的参数

在调用函数时,调用者通过一个参数列表传递数据,并获取返回值。

1.检查输入参数的个数

利用nargin和nargout函数可以确定函数的输入参数和输出参数的个数。然后可以根据参数个数用条件语句完成不同的任务。

【例3-12】

对下面的函数testarg

code.matlab
function c = testarg1(a, b)
  if(nargin == 1)
      c = a .^ 2;
  elseif(nargin == 2)
      c = a + b;
  end

当给定一个输入参数时,函数计算输入值的平方;当给定两个输入参数时,求它们的和。

【例3-13】下面是一个更高级的示例此示例为寻找字符串中的第一个连续字符集。当给定一个输入时,函数假设默认的间隔符为空格;当给定两个输入时,函数会让你指定另外一个分隔符。下面定义的函数有一到两个输出参数:

code.matlab
function [token, remainder] = strtok(string, delimiters)
  % 至少需要一个输入参数的函数
  if nargin < 1
     error('请输入更多输入参数。');
  end
  token = []; remainder = [];
  len = length(string);
  if len == 0
     return
  end

  % 如果有一个输入参数,则使用空格作为分隔符
  if(nargin == 1)
     delimiters = [9:13 32]; % 空格字符
  end
  i = 1;

  % 确定非分隔符字符开始的地方
  while(any(string(i) == delimiters))
     i = i + 1;
     if(i > len), return, end
  end

  % 查找连续字符结束的地方
  start = i;
  while(~any(string(i) == delimiters))
     i = i + 1;
     if(i > len), break, end
  end
  finish = i - 1;
  token = string(start:finish);

  % 当有两个输出参数时,计算第一个分隔符后面的字符数
  if(nargout == 2)
     remainder = string(finish+1:end);
  end

2.传递参数

使用varargin和varargout函数可以传递任意个数的输入参数或者返回任意个数的输出参数给函数。

MATLAB把所有的输入参数指定到一个单元数组中。每个单元可以包含任意大小或类型的数据。对于输出参数,函数代码必须把它们打包到一个单元数组中,这样,MATLAB就可以把参数返回给调用函数。

【例3-14】

例中的函数接受任意个数的二元素矢量,并用直线段连接它们:

code.matlab
function testvar(varargin)
  for k = 1:length(varargin)
     x(k) = varargin{k}(1); % 单元数组索引
     y(k) = varargin{k}(2);
  end
  xmin = min(0,min(x));
  ymin = min(0,min(y));
  axis([xmin fix(max(x))+3 ymin fix(max(y))+3])
  plot(x,y)

按照这种方法编码,在使用testvar函数时可以有不同的输入列表,如:

code.matlab
testvar([2 3],[1 5],[4 8],[6 5],[4 2],[2 3])
testvar([-1 0],[3 -5],[4 2],[1 1])

3.解包varargin中的内容

因为varargin将所有输入参数都包含在一个单元数组中,所以有必要使用单元数组索引来提取数据。例如

code.matlab
y(n) = varargin{n}(2);

其中,索引表达式{n}获取varargin的第n个单元。表达式(2)表示单元内容的第二个元素。

4.打包varargout

当允许有任意多个输出参数时,必须将所有输出打包到varargout单元数组中。使用nargout可以确定输出参数的个数。

【例3-15】

接受两列输入数组的代码如下,其中第一列表示一系列x坐标,第二列表示y坐标。它将数组分成单独的[xi,yi]矢量。可以将该矢量传递给testvar函数。

code.matlab
function [varargout] = testvar2(arrayin)
  for k = 1:nargout
     varargout{k} = arrayin(k,:);   % 单元数组赋值
  end

for循环中的赋值语句使用了单元数组的赋值语法。语句左侧的单元数组通过大括号进行索引。在调用testvar2函数时,输入:

code.matlab
a = [1 2; 3 4; 5 6; 7 8; 9 0];
[p1, p2, p3, p4, p5] = testvar2(a)
p1 =
     1     2
p2 =
     3     4
p3 =
     5     6
p4 =
     7     8
p5 =
    9     0

5.参数列表中的varargin和varargout

varargin或varargout必须出现在参数列表的最后,即在调用函数时必须首先指定必要的参数。下面的函数声明行显示了varargin和varargout的正确位置:

code.matlab
function [out1,out2] = example1(a,b,varargin)
function [i,j,varargout] = example2(x1,y1,x2,y2,flag)

6.返回输出参数

放在函数定义行中等号左侧的任何参数都是调用函数的返回值。如果是传递任何函数都可以修改的输入参数,则需要把相同的参数作为输出参数,这样,调用函数可以获得更新后的值。例如

code.matlab
[text, offset] = readText(filestart, offset)

函数句柄

1.创建一个函数句柄

在MATLAB中,在函数名前加@符号可创建一个函数句柄。

【例3-16】为humps函数创建一个函数句柄,并将它指定给变量fhandle。

code.matlab
fhandle = @humps;

可以像传递其他变量一样将句柄传递给另一个函数。本例将创建的函数句柄传递给fminbnd函数,然后在区间[0.3 1]上进行最小化:

code.matlab
x = fminbnd(fhandle, 0.3, 1)
x =
    0.6370

fminbnd函数用feval函数处理@humps函数句柄,下面是fminbnd M文件的一小部分,第一行中,funfcn输入参数接收传入的函数句柄@humps的第113行中的feval函数处理句柄。

code.matlab
1    function [xf,fval,exitflag,output] = ...
         fminbnd(funfcn,ax,bx,options,varargin)
            …     .
113  fx = feval(funfcn,x,varargin{:});

注意:在创建函数句柄时,只在@符号后面使用函数名,其中不能包括任何路径信息。例如下面的语法是错误的:

code.matlab
fhandle = @\home\user4\humps.

用于句柄的函数名最多只能有N个字符,其中N是函数namelengthmax返回的数字。如果函数名大于该长度,则MATLAB会对名称后面多出的部分进行截断。

code.matlab
N = namelengthmax
N =
    63

2.使用句柄

用MATLAB中的feval命令可以运行函数句柄的目标函数。在本命令中使用函数句柄的语法格式为:

code.matlab
feval(fhandle, arg1, arg2,…, argn)

这与直接调用句柄所代表的函数效果差不多。主要差别在于:函数句柄能在参数传递的目标函数中进行处理。

【例3-17】定义一个名为plot_fhandle的函数,它接收一个函数句柄和数据,然后基于data

【例3-17】定义一个名为plot_fhandle的函数,它接收一个函数句柄和数据,然后基于data数据进行计算和绘图。

code.matlab
function x = plot_fhandle(fhandle, data)
plot(data, feval(fhandle, data))

进行如下调用时,生成如图3-5所示的图形。

code.matlab
plot_fhandle(@sin, -pi:0.01:pi)

3.函数句柄操作

MATLAB提供了两个函数,使用它们可以在函数句柄和函数名字符串之间转换。Matlab还提供了一些测试函数,看变量是否包含函数句柄,以及比较函数句柄。

(1)把函数句柄转换为函数名

如果需要完成字符串操作,例如基于函数句柄进行字符串比较和显示,则可以使用func2str函数以字符串格式获取函数名。

【例3-18】将sin函数句柄转换为字符串的语句行如下:

code.matlab
fhandle = @sin;
func2str(fhandle)
ans =
    sin

注意:func2str函数不操作非标量函数句柄。如果给func2str传递一个非标量函数句柄,则将生成一个错误。

(2)在出错信息中显示函数名

【例3-19】如下的catcherr函数接受函数句柄和数据参数,并试图用它的句柄来运行函数。如果函数运行失败,则catcherr函数使用sprintf函数显示出错信息,该出错信息会给出失败函数的信息。函数名必须是sprintf函数要显示的字符串:

code.matlab
function catcherr(func, data)
try
    ans = feval(func, data);
    disp('答案是:');
    ans
catch
    sprintf('执行函数时出错:''%s''\n', func2str(func))
end

下面调用catcherr函数,传递句柄和一个合法的数据参数给round函数。第二个调用传递相同的函数句柄和一个错误数据类型的数据,这次round函数运行失败,catcherr函数显示一则出错信息,其中显示了失败函数的名称。

code.matlab
catcherr(@round, 5.432)
ans =
答案是:5
xstruct.value = 5.432;
catcherr(@round, xstruct)
执行函数时出错:"round"

(3)把函数名转换为函数句柄

使用str2func函数,可以根据字符串创建一个函数句柄,该字符串包含一个MATLAB函数名。

【例3-20】

要把字符串“sin”转换为该函数的句柄,可使用下面的语句:

code.matlab
fh = str2func('sin')
fh =
    @sin

【例3-21】如果传递参数中的函数名字符串,接收该变量的函数能将函数名转换为使用str2func的函数句柄,下面先传递一个参数funcname给函数makeHandle,然后创建一个函数句柄。

code.matlab
function fh = makeHandle(funcname)
fh = str2func(funcname);
makeHandle('sin')
ans =
    @sin

【例3-22】

还可以基于函数名字符串单元数组运行str2fuc函数,此时,str2func函数返回一个函数句柄数组。

code.matlab
fh_array = str2func({'sin' 'cos' 'tan'})
fh_array =
    @sin    @cos    @tan

4.测试数据类型

使用functiom_handle标记,可以通过isa函数查看是否包含函数句柄。

【例3-23】测试输入参数的函数如下所示,在调用函数处理它以前会检查它是否为函数句柄。

code.matlab
function evaluate_handle(arg1, arg2)
  if isa(arg1, 'function_handle')
      feval(arg1, arg2);
  else
      disp '需要传递一个函数句柄。';
  end

5.保存和装载函数句柄

可以使用MATLAB的save和load函数保存MAT文件的函数句柄,然后把它们载入MATLAB工作空间中。

【例3-24】下面保存函数句柄数组savefile,然后将它载入工作空间中。

code.matlab
fh_array = {@sin @cos @tan};
save savefile fh_array;
clear
load savefile
whos
  Name             Size         Bytes  Class    Attributes
  fh_array       1x3             432  	cell