4.4 函数

4.4.1 M文件函数

M文件函数是可以定义输入参数或返回输出变量的M文件。M文件名称和函数的名称必须一致。

函数文件的第一行为function所引导的函数声明行,该行列出该函数的所有输入输出的变量名称。函数文件对输入输出变量的数量并没有限制,可以完全没有输入变量,也可以是任意数目的组合。常用的M函数文件参数控制命令如表4.3所示。

表4.3 M文件函数参数控制命令

例4-16,查看M函数文件结构。

在命令行窗口输入:

    type mean.m

输出结果如下:

    function y = mean(x, dim)
    %MEAN    Average or mean value.
    %  ……(省略部分)
    %    $Revision: 5.17.4.5 $  $Date: 2016/09/02 13:35:22 $
    if nargin==1,
      % Determine which dimension SUM will use
      dim = find(size(x)~=1, 1 );
      if isempty(dim), dim = 1; end
      y = sum(x)/size(x, dim);
    else
      y = sum(x, dim)/size(x, dim);
    end

提示

MATLAB提供的M函数文件内容包括声明行、说明内容、主体和注释。

说明:

● 函数定义名应和文件保存名一致,当两者不一致时,MATLAB将忽视文件首行的函数定义名,而以文件保存名为准。

● MATLAB中的函数文件名必须以字母开头,可以是字母、下划线以及数字的任意组合,但不可以超过31个字符。

函数文件与脚本文件不同的是:函数文件的内部运算流程不可见,而是一般只能看到其输入的参数和输出的运算结果,这种函数文件的主要特点如下:

● MATLAB在实现对函数的调用时,允许使用比声明变量数目少的输入输出变量。

● 当函数文件运行时,MATLAB会专门为它打开一个临时的函数工作区,函数运行中产生的所有的中间变量都存放在函数工作区中。

● 当执行完文件最后一条命令或遇到return命令时,就结束该函数文件的运行,同时该临时的函数空间及其保存的所有中间变量将立即被清除。函数只执行自己工作区内的变量。

● 调用一个函数时,输入变量不会被复制到函数的工作区,但是它们的值在函数内可读。但当改变输入变量内的任何值时,输入变量就会被复制到函数的工作区。

● 函数工作区随着M函数文件的被调用而产生,随着调用的结束而删除。相对于基本空间,函数工作区是独立和临时的,在MATLAB的整个运行期间,可以产生任意多个临时函数空间。

● 如果函数文件对脚本文件进行了调用,那么该脚本文件运行产生的所有变量都存放在函数工作区中,而不是存放在基本空间中。

例4-17,函数文件调用示例。

在命令行窗口输入:

    a=magic(3)             %调用magic.m函数文件产生矩阵
    avg1=mean(a)           %调用mean.m函数文件,输入一个参数计算平均值
    avg2=mean(a,2)         %调用mean.m函数文件,输入一个参数计算平均值

输出结果如下:

    a = 8      1      6
        3      5      7
        4      9      2
    avg1 =   5      5      5
    avg2 =   5
             5
             5

4.4.2 匿名函数

匿名函数没有函数名,也不是函数M文件,只有表达式和输入、输出参数。可以在命令行窗口中输入代码,创建匿名函数。匿名函数创建方法为:

    f = @(input1, input2, …) expression

其中,f为创建的函数句柄;input1、input2等为输入变量;expression为函数的主体表达式。

注意

如果匿名函数没有输入参数,则在调用函数时,需要用空格来替换input,否则MATLAB将不执行该程序。

例4-18,匿名函数示例。

在命令行窗口输入:

    sqr = @(x) x.^2 %创建匿名函数句柄
    t= sqr([1.25 2])
    whos

输出结果如下:

    sqr =     @(x)x.^2
    t =     1.5625     4.0000
      Name       Size               Bytes  Class Attributes
      sqr        1x1                   16  function_handle
      t          1x2                   16  double

4.4.3 子函数

子函数也称为局部函数,是指在MATLAB中,多个函数的代码可以同时写到一个M函数文件中时出现的一种函数。

M函数文件出现的第一个函数称为主函数,其他函数称为子函数。保存时所用的函数文件名应当与主函数定义名相同,而且外部程序只能对主函数进行调用。

子函数的书写规范有如下几条:

● 每个子函数的第一行是其函数声明行。

● 在M函数文件中,主函数的位置不能改变,但是多个子函数的排列顺序可以任意改变。

● 子函数只能被处于同一M文件中的主函数或其他子函数调用。

● 在M函数文件中,任何命令通过名称对函数进行调用时,子函数优先级仅次于MATLAB内置函数。

● 同一M文件的主函数、子函数的工作区都是彼此独立的。各个函数间的信息传递,可以通过输入输出变量、全局变量或跨空间命令来实现。

● help, lookfor等帮助命令不能显示M文件中的子函数的任何相关信息。

例4-19,子函数示例。

创建ex4_19func.m文件,并写入:

    function [avg, med] = ex4_18func (x)
    n = length(x);
    avg = mymean(x, n);
    med = mymedian(x, n);
    end
    function a = mymean(v, n)
    % 求平均子函数
    a = sum(v)/n;
    end
    function m = mymedian(v, n)
    % 求中位数子函数
    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=[1 3 5 9 11];
    [mean, median]= ex4_18func(x)

输出结果如下:

    mean =     5.8000
    median =      5

4.4.4 私有函数

私有函数是指位于私有目录private上的M函数文件,它的主要性质如下:

● 私有函数的构造与普通M函数完全相同。

● 关于私有函数的调用:私有函数只能被private直接父目录内的M文件所调用,而不能被其他目录上的任何M文件或MATLAB命令窗中的命令所调用。

● M文件中任何命令调用函数时,私有函数优先级仅次于MATLAB内置函数和子函数。

● help, lookfor等帮助命令都不能显示私有函数文件的任何相关信息。

4.4.5 重载函数

重载函数经常用于处理功能类似但变量属性不同的函数。

MATLAB内置函数中就有许多重载函数,分别放置在不同的文件路径下。这些文件所在的文件夹通常命名为@+代表MATLAB数据类型的字符,例如@int16路径下的重载函数的输入变量应为16位整形变量,而@double路径下的重载函数的输入变量应为双精度浮点类型。

4.4.6 内联函数

内联函数的属性与编写方式和普通函数文件不同,但相对来说,内联函数的创建要简单得多。其调用形式如下:

    inline(expr)
    inline(expr, arg1, arg2, ...)
    inline(expr, n)

inline命令的功能为字符串表达式expr转化为输入变量自动生成的内联函数;arg1, arg2, ...为指定的输入变量,n为指定输入变量个数。

MATLAB中关于内联函数的属性的相关命令如表4.4所示。

表4.4 内联函数属性命令集

例4-20,内联函数示例。

在命令行窗口输入:

    f = inline('3 *x.^2')
    ans1=argnames(f)
    ans2=formula(f)
    ans3=char(f)
    ans4=class(f)
    ans5=f([1 2 3 4 5])

输出结果如下:

    f =    Inline function:
        f(x) = 3 *x.^2
    ans1 =      'x'
    ans2 =3 *x.^2
    ans3 =3 *x.^2
    ans4 =inline
    ans5 =      3     12     27     48     75

4.4.7 eval、feval函数

eval、feval函数可用于将文本与函数联系起来。

1.eval函数

eval函数用于翻译并执行文本字符串并返回结果,调用形式如下:

    eval(expression)
    [output1, ..., outputN] = eval(expression)

例4-21, eval函数示例。

在命令行窗口输入:

    x=3
    str='magic(x)'
    magic3=eval(str)

输出结果如下:

    x =      3
    str =magic(x)
    magic3 =8      1      6
                3      5      7
                4      9      2

2.feval函数

feval函数可以将函数句柄文本与函数连续,提供函数计算输入并返回求解结果。其调用形式如下:

    [y1, y2, ...] = feval(fhandle, x1, ..., xn)
    [y1, y2, ...] = feval(fname, x1, ..., xn)

例4-22, feval函数示例。

在命令行窗口输入:

    x=3
    str=@magic
    magic3=feval(str, 3)

输出结果如下:

    x =      3
    str =      @magic
    magic3 =8      1      6
            3      5      7
            4      9      2

4.4.8 函数的函数

函数的函数是指以函数名为自变量的函数。这类函数的用户包括:求零点、最优化、求积分和常微分方程等。

例4-23,函数的函数示例。

在命令行窗口输入:

    f = @(x, c) x(1).^2+c.*x(2).^2;         % 匿名函数
    c = 1.5;                           %参数
    X = fminsearch(@(x) f(x, c), [0.3;1])   %使用函数的函数求最小值

输出结果如下:

    X =    1.0e-04 *
      -0.2447
        0.3159

4.4.9 内嵌函数

内嵌函数是指完全包含在父函数中的函数。任何函数都可以包含内嵌函数,例如下面的代码中函数parent包含了内嵌函数nestedfx:

    function parent
    disp('This is the parent function')
    nestedfx
      function nestedfx
          disp('This is the nested function')
      end
    end

4.4.10 函数编写建议

在编写大型程序时,有必要养成一些好的编程习惯,包括:

(1)为函数语句添加必要的注释。

(2)尽可能地将循环转化为向量,例如下面语句可以进行向量化。

    x = 0.01;
    for k = 1:1001
      y(k) = log(x);
      x = x + 0.01;
    end

向量化可得到:

    x =0 .01:0.01:10;
    y = log(x);

(3)如果代码不能向量化,可以通过预分配任何输出结果所在的向量或数组以加快循环。例如:

    r = zeros(32,1);                %创建矩阵为结果预留空间
    for n = 1:32
        r(n) = rank(magic(n));
    end

(4)在编程时应在程序前以注释的形式编写帮助文件,以方便使用和维护。