22、MATLAB 高级函数使用指南

MATLAB 高级函数使用指南

1. 函数句柄的使用

1.1 匿名函数的保存与加载

在 MATLAB 中,调用函数时,即使不传递参数,也必须使用括号。匿名函数可以保存到 MAT - file 中,需要时再加载。以下是具体操作步骤:
1. 创建匿名函数:

cirarea = @ (radius) pi * radius .^2;
  1. 将匿名函数保存到 MAT - file:
save anonfns cirarea
  1. 清除当前工作区:
clear
  1. 加载保存的 MAT - file:
load anonfns
  1. 查看工作区变量:
who

输出结果会显示 cirarea 变量。还可以将其他匿名函数追加到这个 MAT - file 中,将相关的匿名函数组保存到 MAT - file 中通常很有用。

1.2 为其他函数创建句柄

函数句柄不仅可以为匿名函数创建,还可以为内置函数和用户定义的函数创建。例如,为内置的阶乘函数创建句柄:

facth = @factorial;

使用这个句柄调用函数:

facth(5)

输出结果为 120

1.3 函数函数

使用函数句柄的一个原因是能够将函数作为参数传递给其他函数,这些函数被称为函数函数。例如,有一个函数 fnfnexamp 用于绘制函数曲线:

function fnfnexamp(funh)
% Example of a function function. The handle of a function
% is passed and that function of x is plotted
x = 1:.25:6;
y = funh(x);
plot(x,y,'ko')

直接传递函数名会出错:

fnfnexamp(sin)

需要传递函数的句柄:

fnfnexamp(@sin)

也可以传递 @cos 等其他函数的句柄来绘制不同的曲线。

还可以使用 str2func 函数将字符串转换为函数句柄,使用 func2str 函数将函数句柄转换为字符串。例如:

function fnstrfn2(funstr)
% A function name is passed as an argument to this
% function; it converts this to a function handle and
% then plots the function of x
x = 1:.25:6;
funh = str2func(funstr);
y = funh(x);
plot(x,y,'bo')

调用方式为:

fnstrfn2('sin')

MATLAB 还有一些内置的函数函数,如 fplot 用于在指定范围内绘制函数曲线:

fplot(@sin, [-pi pi])

feval 函数用于计算函数句柄并执行指定参数的函数:

feval(@sin, 3.2)

1.4 练习 9.1

创建自己的匿名函数来计算圆、矩形和其他形状的面积,并将这些匿名函数存储在名为 myareas.mat 的文件中。

2. 可变数量的参数

2.1 可变数量的输入参数

在之前编写的函数中,输入和输出参数的数量通常是固定的,但也可以有可变数量的参数。可以使用内置的单元数组 varargin 来存储可变数量的输入参数,使用 nargin 函数返回传递给函数的输入参数数量。

例如, areafori 函数可以接受 1 个或 2 个输入参数:

function area = areafori(varargin)
% Calculates and returns the area of a circle in feet
% The radius is passed, and potentially the unit of
% inches is also passed, in which case the result will be
% given in inches instead of feet
n = nargin; % number of input arguments
radius = varargin{1}; % Given in feet by default
if n == 2
    unit = varargin{2};
    % if inches is specified, convert the radius
    if unit == 'i'
        radius = radius * 12;
    end
end
area = pi * radius ^ 2;

调用示例:

areafori(3)
areafori(1,'i')

也可以修改函数头,明确指定半径参数,再使用 varargin 处理剩余的可变参数:

function area = areafori2(radius, varargin)
% Calculates and returns the area of a circle in feet
% The radius is passed, and potentially the unit of
% inches is also passed, in which case the result will be
% given in inches instead of feet
n = nargin; % number of input arguments
if n == 2
    unit = varargin{1};
    % if inches is specified, convert the radius
    if unit == 'i'
        radius = radius * 12;
    end
end
area = pi * radius ^ 2;

2.2 可变数量的输出参数

可以使用内置的单元数组 varargout 来存储可变数量的输出参数,使用 nargout 函数确定调用函数时期望返回的输出参数数量。

例如, typesize 函数根据输入参数的类型返回不同数量的输出参数:

function [arrtype, varargout] = typesize(inputval)
% Demonstrates a variable number of output arguments
[r c ] = size(inputval);
if r==1 && c==1
    arrtype = 's';
elseif r==1 || c==1
    arrtype = 'v';
    varargout{1} = length(inputval);
else
    arrtype = 'm';
    varargout{1} = r;
    varargout{2} = c;
end

调用示例:

typesize(5)
[arrtype, len] = typesize(4:6)
[arrtype, r, c] = typesize([4:6;3:5])

mysize 函数根据 nargout 的值返回不同数量的输出参数:

function [row col varargout] = mysize(mat)
% Demonstrates the use of nargout
[row col] = size(mat);
if nargout == 3
    varargout{1} = row*col;
end

调用示例:

[r c] = mysize(eye(3))
[r c elem] = mysize(eye(3))

2.3 练习 9.2

编写一个函数,接收一个 x 向量和一个函数句柄,创建一个 y 向量(该向量是 x 的函数),并绘制 x y 向量的数据,同时在标题中显示函数名。

2.4 练习 9.3

编写一个名为 geomser 的函数,接收一个 r 值,计算并返回几何级数的和。如果传递第二个参数,则该参数为 n 的值;否则,函数为 n 生成一个 5 到 30 之间的随机整数。函数头如下:

function sgs = geomser(r, varargin)

3. 嵌套函数

3.1 嵌套函数的基本概念

函数可以像循环一样嵌套,外层函数可以包含内层函数。每个函数都必须有 end 语句。嵌套函数的一般格式如下:

outer function header
    body of outer function
    inner function header
        body of inner function
    end % inner function
    more body of outer function
end % outer function

内层函数可以位于外层函数体的任何部分,外层函数体在内外层函数前后都可以有代码,也可以有多个内层函数。

3.2 变量的作用域

变量的作用域是定义和使用它的最外层函数的工作区。在外层函数中定义的变量可以在内层函数中使用,在内层函数中定义的变量如果在外层函数中未使用,则其作用域仅限于内层函数。

例如, nestedvolume 函数计算立方体的体积,外层函数调用嵌套函数计算立方体底面的面积:

function outvol = nestedvolume(len, wid, ht)
% Demonstrates a nested function
outvol = base * ht;
    function outbase = base
    outbase = len * wid;
    end % base function
end % nestedvolume function

调用示例:

v = nestedvolume(3,5,7)

输出结果为 105

输出参数与变量不同,输出参数的作用域仅限于嵌套函数,不能在外层函数中使用。例如,在 nestedvolume2 函数中, outbase 只能在 base 函数中使用, bvar base 函数的局部变量,而 cvar 由于在外层函数中使用,其作用域为 nestedvolume2 函数的工作区。

function outvol = nestedvolume2(len, wid, ht)
% Demonstrates scope within a nested function
disp('This function calculates a volume')
% Call the base function, and calculate and
% print the volume
outvol = base * ht;
fprintf('outvol is %.1f\n', outvol)
fprintf('cvar is %.1f\n', cvar)
% Call the printstuff function
printstuff
% Not valid because it is an output argument:
% fprintf('outbase is %.1f\n', outbase)
    function outbase = base
     bvar = len * wid;
     cvar = len * wid;
     outbase = bvar;
    end
    function printstuff
     fprintf('outvol is %.1f\n', outvol)
     %Not valid because bvar is not used in nestedvolume2:
     % fprintf('bvar is %.1f\n', bvar)
     fprintf('cvar is %.1f\n', cvar)
    end
end

4. 递归函数

4.1 递归的概念

递归是指事物用自身来定义。在编程中,递归函数是调用自身的函数。递归在编程中经常使用,但许多简单的例子(包括本节中的一些例子)实际上效率不高,可以用迭代方法(循环或 MATLAB 中的向量化代码)代替。

4.2 阶乘的递归定义

以阶乘为例,通常整数 n 的阶乘迭代定义为:
[n! = 1 \times 2 \times 3 \times \cdots \times n]
递归定义为:
[n! = n \times (n - 1)!](一般情况)
[1! = 1](基例)

递归定义包含一般情况和基例,基例用于终止递归。例如,计算 3! 的过程如下:
[3! = 3 \times 2!]
[2! = 2 \times 1!]
[1! = 1]
所以 (2! = 2),(3! = 6)。

4.3 递归函数的实现

编写递归函数 fact 来计算阶乘:

function facn = fact(n)
% This function recursively finds n!
if n == 1
    facn = 1;
else
    facn = n * fact(n−1);
end

调用示例:

fact(5)

输出结果与内置的 factorial 函数相同。

4.4 另一个递归函数示例

prtwords 函数用于将句子中的单词按逆序打印:

function prtwords(sent)
% This function recusively prints the words in a string
% in reverse order
[word, rest] = strtok(sent);
if ~isempty(rest)
    prtwords(rest);
end
disp(word)

调用示例:

prtwords('what does this do')

输出结果为:

do
this
does
what

4.5 递归函数的执行流程

以下是 prtwords 函数的执行流程 mermaid 流程图:

graph TD;
    A[接收句子] --> B[分割句子为单词和剩余部分];
    B --> C{剩余部分是否为空};
    C -- 否 --> D[递归调用 prtwords 处理剩余部分];
    D --> E[打印当前单词];
    C -- 是 --> E[打印当前单词];

4.6 总结

递归函数通过不断调用自身,利用基例终止递归,实现特定的功能。在使用递归函数时,要确保有基例且能在某个点达到基例,否则会导致无限递归。虽然递归在某些情况下很有用,但对于一些简单问题,迭代方法可能更高效。

表格总结

函数类型 特点 示例代码
函数句柄 可用于匿名函数、内置函数和用户定义函数,方便函数传递 facth = @factorial; facth(5)
可变参数函数 输入和输出参数数量可变,使用 varargin varargout function area = areafori(varargin)
嵌套函数 函数可以嵌套,变量作用域有特定规则 function outvol = nestedvolume(len, wid, ht)
递归函数 函数调用自身,包含基例和一般情况 function facn = fact(n)

5. 综合应用与实践

5.1 函数句柄与可变参数的结合应用

在实际应用中,函数句柄和可变参数可以结合使用,以实现更灵活的功能。例如,我们可以编写一个函数,它接受一个函数句柄和可变数量的输入参数,然后根据这些参数调用相应的函数。

function result = applyFunction(funh, varargin)
    % 应用函数句柄到可变数量的输入参数
    result = feval(funh, varargin{:});
end

调用示例:

% 调用 applyFunction 函数,传入 sin 函数句柄和参数 3.2
result = applyFunction(@sin, 3.2)

5.2 嵌套函数与递归函数的结合

嵌套函数和递归函数也可以结合使用,以实现复杂的功能。例如,我们可以编写一个嵌套的递归函数来计算斐波那契数列。

function fib = fibonacci(n)
    % 计算斐波那契数列的第 n 项
    fib = innerFib(n);

    function f = innerFib(k)
        % 内部递归函数
        if k == 0 || k == 1
            f = k;
        else
            f = innerFib(k - 1) + innerFib(k - 2);
        end
    end
end

调用示例:

% 计算斐波那契数列的第 6 项
fib = fibonacci(6)

5.3 实践项目:数据处理与可视化

我们可以结合上述知识,完成一个数据处理与可视化的实践项目。假设我们有一组数据,需要对其进行不同的处理,并将处理结果可视化。

% 生成示例数据
x = 1:0.1:10;
y = sin(x);

% 定义不同的处理函数
function1 = @(data) data.^2;
function2 = @(data) log(data);

% 定义一个函数来处理数据并绘制图形
function processAndPlot(data, funh, titleStr)
    % 处理数据
    processedData = feval(funh, data);

    % 绘制图形
    plot(x, processedData);
    title(titleStr);
    xlabel('x');
    ylabel('y');
end

% 处理并绘制数据
processAndPlot(y, function1, 'Data Squared');
figure;
processAndPlot(y, function2, 'Log of Data');

5.4 实践项目流程 mermaid 流程图

graph TD;
    A[生成示例数据] --> B[定义处理函数];
    B --> C[定义处理并绘图函数];
    C --> D[处理并绘制数据 1];
    C --> E[处理并绘制数据 2];

6. 性能优化与注意事项

6.1 递归函数的性能问题

递归函数虽然简洁,但在处理大规模数据时可能会导致性能问题,因为递归调用会消耗大量的内存和时间。例如,在计算斐波那契数列时,递归方法会有大量的重复计算。可以使用迭代方法来优化性能。

function fib = fibonacciIterative(n)
    % 迭代方法计算斐波那契数列
    if n == 0 || n == 1
        fib = n;
    else
        a = 0;
        b = 1;
        for i = 2:n
            temp = a + b;
            a = b;
            b = temp;
        end
        fib = b;
    end
end

6.2 可变参数函数的使用注意事项

在使用可变参数函数时,要注意参数的类型和数量。特别是在处理 varargin varargout 时,要确保对不同类型的参数进行正确的处理。例如,在 areafori 函数中,要确保第二个参数是正确的单位标识。

6.3 函数句柄的内存管理

函数句柄本身占用的内存较小,但在使用大量函数句柄时,也要注意内存管理。避免不必要的函数句柄创建和存储。

7. 总结与展望

7.1 知识总结

通过本文的学习,我们了解了 MATLAB 中函数句柄、可变数量的参数、嵌套函数和递归函数的使用方法。以下是对这些知识的总结表格:

知识类型 关键要点
函数句柄 可用于匿名函数、内置函数和用户定义函数,方便函数传递和调用,使用 @ 运算符获取函数句柄
可变数量的参数 输入参数使用 varargin 存储,输出参数使用 varargout 存储, nargin nargout 用于获取参数数量
嵌套函数 函数可以嵌套,变量作用域与函数嵌套层次有关,输出参数作用域仅限于嵌套函数
递归函数 函数调用自身,包含基例和一般情况,基例用于终止递归,使用时要注意性能问题

7.2 未来展望

这些高级函数的使用可以帮助我们编写更灵活、更高效的 MATLAB 代码。在未来的应用中,我们可以将这些知识应用到更复杂的领域,如机器学习、数据分析等。同时,不断探索和优化这些函数的使用方法,以提高代码的性能和可读性。

7.3 学习建议

为了更好地掌握这些知识,建议多进行实践,编写不同类型的函数,并尝试将它们组合使用。同时,阅读优秀的 MATLAB 代码,学习他人的编程思路和技巧。

参考代码列表

  • 函数句柄相关代码:
facth = @factorial;
facth(5)
  • 可变参数函数相关代码:
function area = areafori(varargin)
    n = nargin;
    radius = varargin{1};
    if n == 2
        unit = varargin{2};
        if unit == 'i'
            radius = radius * 12;
        end
    end
    area = pi * radius ^ 2;
end
  • 嵌套函数相关代码:
function outvol = nestedvolume(len, wid, ht)
    outvol = base * ht;
    function outbase = base
        outbase = len * wid;
    end
end
  • 递归函数相关代码:
function facn = fact(n)
    if n == 1
        facn = 1;
    else
        facn = n * fact(n - 1);
    end
end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值