相见恨晚的Matlab编程小技巧(4)-程序运行太慢了咋解决——合理使用循环语句(2)

news2025/1/20 18:28:28

        上一篇博客介绍了通过向量化运算、预分配空间两种常用的方式提高代码的运行效率。实际上,matlab还有几种内置函数来避免循环语句的使用,分别为arrayfun、cellfun以及structfun函数。由于这几种函数需要用到匿名函数以及函数句柄的概念,很多人可能不太清楚,所以我才单独写一篇博客进行介绍。

1.匿名函数与函数句柄

        匿名函数是 Matlab 中的一种函数类型,它可以方便地定义和使用轻量级的函数,通常被用于一些简单的计算和数据分析任务中。匿名函数的语法简单,能够大大提高代码的可读性和可维护性,也更加符合函数式编程的思想。

1.1匿名函数的定义

        在 Matlab 中,定义匿名函数的语法如下:

f = @(arguments) expression

        其中,f 表示匿名函数名称(也称为函数句柄),arguments 为函数中的变量,expression 为匿名函数的表达式。参数列表可以是零个或多个参数,多个参数使用逗号分隔。表达式返回值将作为函数的输出。例如,可以定义一个匿名函数,功能是将输入的变量 x 先平方再加1:

f = @(x) x.^2 + 1;

        这个匿名函数的表达式是 x.^2 + 1,意思是对输入参数 x 逐元素平方并加1,并输出一个新的数组。另外,和普通的子函数一样,也可以定义没有参数的匿名函数,例如:

g = @() disp('Hello, world!');

        这个匿名函数不需要输入参数,它的表达式是 disp(‘Hello, world!’),也就是在命令行输出“Hello, world! ”字符串。

1.2匿名函数的使用

        定义完匿名函数之后,我们可以通过函数句柄来调用这个匿名函数。调用匿名函数的语法与调用普通函数一样,例如:

f = @(x) x.^2 + 1;
x=1:10;
y=f(x)

        输出结果为:

1.3函数句柄的定义

        刚才也提到了,定义匿名函数,匿名函数的名称也叫做函数句柄。其实,在 Matlab 中,通过在函数名前面加上 “@” 符号,就可以定义一个函数句柄。而函数句柄的使用方式类似于函数名的使用方式。如果有C或者C++的基础,可以发现所谓的函数句柄其实和指针的概念非常相似。函数句柄即可以指向普通的子函数,也可以指向匿名函数,例如:

f1=@fun1;           % 定义函数句柄f1指向函数fun1
f2=@(x) x.^2;       % 定义函数句柄f2指向匿名函数
a=[1 2 3];
b1=f1(a)
b2=f2(a)
function y = fun1(x)
    y = x.^2;
end

        输出结果完全一样

1.4函数句柄的使用

        使用函数句柄,我们还能够实现动态函数调用。例如,在 Matlab 中,如果我们想要调用内置函数或者自定义的子函数,可以使用这些函数的函数句柄来实现动态调用,而不需要显式地调用这些函数。这么说可能比较难理解,举个简单的例子:

f1=@fun1;           % 定义函数句柄f1指向函数fun1
f2=@fun2;           % 定义函数句柄f2指向函数fun2
f3=@fun3;           % 定义函数句柄f3指向函数fun3
f4=@fun4;           % 定义函数句柄f4指向函数fun4
a=[1 2 3];
b1=fun(f1,a)+fun(f2,a)+fun(f3,a)+fun(f4,a)
b2=f1(a)+f2(a)+f3(a)+f4(a)
function y = fun1(x)
    y = x;
end
function y = fun2(x)
    y = 2*x.^2;
end
function y = fun3(x)
    y = 3*x.^3;
end
function y = fun4(x)
    y = 4*x.^4;
end
function y = fun(f,x)
    y = f(x);
end

        这个例子里,我们首先定义了子函数fun1-fun4,用于计算k*x^k。还定义了子函数fun,这个函数的作用是传入一个函数句柄f和一个变量x,输出使用传入的函数f对变量x处理后的结果y。b1和b2的结果是完全一样的:

        通过这个例子,大家可能体会到了函数句柄进行动态函数调用的用法,但是并没有感觉使用函数句柄进行动态函数调用有哪些优势,我将用另外一个例子进行说明。

     假设我们需要一个数据处理的程序,用于对输入的数据进行多个不同的数据转换操作,例如对数据进行过滤、自动缩放、归一化等操作。为了方便,我们希望将这些不同的数据转换操作都封装成具体的函数,并使用一个统一的函数来调用这些函数。使用函数句柄,我们能够实现这一点。例如:

clc
clear

x = -5:0.1:5;                           % 定义原始输入数据
y1 = processData(x, 'filterData');      % 过滤数据
y2 = processData(x, 'scaleData');       % 缩放数据
y3 = processData(x, 'normalizeData');   % 数据归一化

% 绘制计算结果
subplot(2,2,1); plot(x,x);
subplot(2,2,2); plot(x,y1);
subplot(2,2,3); plot(x,y2);
subplot(2,2,4); plot(x,y3);

% 定义具体的数据转换函数
function y1 = filterData(x)
    y1(x>0) = x(x>0);
    y1(x<=0) = 0;
end

function y2 = scaleData(x)
    y2 = x./max(abs(x));
end

function y3 = normalizeData(x)
    y3 = (x-mean(x))./std(x);
end

% 定义数据转换调用函数
function y = processData(x, funcName)
    func = str2func(funcName);  % 将函数名转换为函数句柄
    y = func(x);  % 根据函数句柄调用具体的数据转换函数
end

        运行结果为:

         如果经常使用Matlab应该会发现,Matlab的内置函数也有很多这样的函数,通过类似下面语句完成函数处理,可以大大提升效率,这里我不再过多介绍。

函数名(变量,'函数关键字')

2.通过内置函数arrayfun、cellfun、structfun避免使用循环

        在Matlab中,arrayfun、cellfun、structfun是三个用于处理数组、单元数组和结构体的函数,它们可以帮助用户快速进行数据处理。

2.1.arrayfun函数

        arrayfun函数是用于对数组进行批量处理的函数,它的基本语法如下:

B = arrayfun(fun, A)

        其中,第一个参数 fun 是一个函数句柄,表示对 A 中每个元素进行的操作,返回的结果保存在一个新的数组 B 中。arrayfun函数将逐个处理 A 中的每个元素,并将结果传递给 fun 函数进行处理。

        举个例子,假设我们要对一个向量 x 中的每个元素进行平方处理,可以使用 arrayfun 来实现:

x = [1 2 3 4 5];
y = arrayfun(@(a) a^2, x);
disp(y);

        在这个例子中,我们定义了一个匿名函数 @(a) a^2,并将它作为 arrayfun 的第一个参数传入。arrayfun 将逐个处理 x 中的每个元素,并将结果传递给匿名函数进行平方处理。最终,arrayfun 将这些结果存储在另一个向量 y 中,并输出。

2.2 cellfun函数

        cellfun函数是一个用于对单元数组进行批量处理的函数,它的基本语法如下:

B = cellfun(fun, C)

        其中,第一个参数 fun 是一个函数句柄,表示对 C 中每个元素进行的操作,返回的结果保存在一个新的单元数组 B 中。cellfun函数将逐个处理 C 中的每个元素,并将结果传递给 fun 函数进行处理。

        举个例子,假设我们要将一个单元数组 C 中每个元素的大小写转换,可以使用 cellfun 来实现:

C = {'Hello', 'World'};
D = cellfun(@(a) upper(a), C, 'UniformOutput', false);
disp(D);

        在这个例子中,我们定义了一个匿名函数 @(a) upper(a),表示将一个字符串转换成大写。我们将匿名函数作为 cellfun 的第一个参数进行调用,cellfun 将逐个处理 C 中的每个元素,并将结果传递给匿名函数进行大小写转换。最终,cellfun 将这些结果存储在另一个单元数组 D 中,并输出。需要注意的是,在使用 cellfun 时,需要指定参数 'UniformOutput' 为 false,否则函数的返回结果不会被封装为单元数组,因为不同大小的返回值无法合并为单个单元数组。

2.3structfun函数

        structfun函数是用于对结构体进行批量处理的函数,它的基本语法如下:

B = structfun(fun, S)

        其中,第一个参数 fun 是一个函数句柄,表示对 S 中每个字段进行的操作,返回的结果保存在一个新的结构体 B 中。structfun将逐个处理 S 中的每个字段,并将结果传递给 fun 函数进行处理。

        举个例子,假设我们要将一个结构体 S 中的数值型字段乘以2,可以使用 structfun 来实现:

S.x = 1;
S.y = 2.5;
S.z = 3.7;
T = structfun(@(a) 2*a, S, 'UniformOutput', false);
disp(T);

        在这个例子中,我们定义了一个匿名函数 @(a) 2*a,表示将一个字段的值乘以2。我们将匿名函数作为 structfun 的第一个参数进行调用,structfun 将逐个处理 S 中的每个字段,并将结果传递给匿名函数进行处理。

2.4 避免循环语句的使用

        使用 cellfun 可以以简洁的方式避免使用循环语句,并提高代码的可读性和可维护性。下面我们通过一个实例来说明。

例1:单元数组中字符串拼接

        假设我们有一个单元数组 C,其中包含了字符串,我们想要将其中的字符串进行拼接,并返回一个新的字符串。我们先使用循环语句来实现,代码如下:

% 定义数组
A = {{'H','e','l','l','o',' '}, {'W','o','r','l','d'}, {'——','M','a','t','l','a','b'}};
B = cell(1,10000);
for k=1:10000
    B{k}=A;
end
% 循环计算
result = '';
tic
for k = 1:numel(B)
    for kk=1:numel(B{k})
        for kkk=1:numel(B{k}{kk})
            result = [result, B{k}{kk}{kkk}];
        end
    end
end
toc

        运行时间为:

        这段代码使用了循环语句来计算单元数组中字符串的拼接,使用一个 for 循环来遍历单元数组中的元素,然后每次循环将当前元素添加到最终结果中,如果使用 cellfun 函数来实现相同的功能,代码如下:

可以使用如下`cellfun`函数来避免循环:```
 

A = {{'H','e','l','l','o',' '}, {'W','o','r','l','d'}, {'——','M','a','t','l','a','b'}};
B = cell(1,10000);
for k=1:10000
    B{k}=A;
end
tic
C = cellfun(@(x) cellfun(@(y) [y{:}], x, 'UniformOutput', false), B, 'UniformOutput', false);
D = [C{:}];
result = [D{:}];
toc

        上述代码中,使用了两个嵌套的`cellfun`函数,将多维cell数组中的字符元素连接成一个字符串。其中第一个`cellfun`函数会对`B`中每个元素应用第二个`cellfun`函数,将其分别处理成一个字符串序列并封装到一个cell中。最后使用`{:}`操作符将所有结果取出来,连接成一个字符串。这样,就可以避免使用for循环,提高了代码运行的效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/539643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

厚积薄发|迭代为什么叫冲刺?

上士闻道&#xff0c;勤而行之&#xff1b;中士闻道&#xff0c;若存若亡&#xff1b;下士闻道&#xff0c;大笑之。不笑不足以为道。–《道德经》 软件工程从原始的作坊式工作方式&#xff0c;经过了哪些思考、哪些方案的试探&#xff0c;才在不断地尝试与改善后&#xff0c;走…

E. Border(裴蜀定理)

Problem - E - Codeforces 太空人纳塔莎抵达了火星。她知道火星人非常贫穷。为了保障火星公民更好的生活&#xff0c;他们的皇帝决定向每个游客征收税费。纳塔莎是地球的居民&#xff0c;因此她必须支付进入火星领土所需的税费。 火星上有n种纸币面额&#xff1a;第i种纸币的面…

TTL、 RS-232,RS-485的区别

TTL、 RS-232&#xff0c;RS-485的区别 参考:【gt】TTL&#xff0c; RS-232&#xff0c;RS-485的区别 前言&#xff1a;串口、UART口、COM口、USB口是指的物理接口形式(硬件)。而TTL、RS-232、RS-485是指的电平标准(电信号)。 TTL &#xff1a;全双工 &#xff0c;逻辑0对应…

深度学习实战30-AIGC项目:自动生成思维导图文件,解放双手

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下深度学习实战30-AIGC项目&#xff1a;自动生成思维导图文件&#xff0c;解放双手&#xff0c;思维导图是一种常见的工具&#xff0c;用于将复杂的信息和概念以图形化方式展示出来。AIGC项目旨在将这种思维导图的创建…

GNU C 语言的常用扩展

本文摘自《奔跑吧 Linux 内核&#xff08;第二版&#xff09;》2.2 Linux 内核中常用的 C 语言技巧 读到这本书的这一小节时&#xff0c;感觉其中不少技巧在项目中有遇到过&#xff0c;有必要做个笔记&#xff0c;方便以后查阅。 文章目录 前言1.语句表达式2.变长数组3. case 的…

Linux内核(十五)sysrq 详解 II —— 代码框架详解

文章目录 sysrq模块初始化流程图sysrq模块代码详解sysrq产生kernel crash事件流程sysrq 整体框架图sysrq 框架中添加自定义实例 Linux 版本&#xff1a;Linux version 3.18.24 sysrq模块初始化流程图 sysrq模块源码路径&#xff1a;linux-3.18.24.x/drivers/tty/sysrq.c 编译…

Unity3D :Animator Controller

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 Animator Controller Animator Controller 允许您为角色或对象安排和维护一组动画剪辑以及关联的动画过渡。在大多数情况下&#xff0c;拥有多个动画并在满足某些游戏条件时在这些…

就业内推 | 国企专场,HCIE、CCIE认证优先,最高20k*15薪

01 银河互联网电视有限公司 &#x1f537;招聘岗位&#xff1a;网络运维工程师 &#x1f537;职责描述&#xff1a; 1、负责IDC网络方向的运维服务&#xff1b; 2、负责日常紧急变更&#xff0c;撰写上线变更方案等文档工作&#xff1b; 3、负责应急问题处理、控制变更风险&am…

CAN周期波动优化方法

摘要&#xff1a; 如果遇到报文周期偏大的问题该从何下手&#xff0c;或者说有哪些解决办法呢&#xff1f; 对于车载控制器来说&#xff0c;CAN周期的波动通常是有严格的标准&#xff0c;国标要求如下&#xff0c;基于国标&#xff0c;各个主机厂在这一块稍微有些差异&#xf…

遇到系统无法访问此文件问题怎么办?

昨天我整理了一下自己喜欢的照片&#xff0c;然后将其分门别类地归纳起来&#xff0c;然后将其复制到了另一个硬盘做了一下简单的备份。后面又想到了之前在某篇文章中貌似提到过什么异地备份的说法&#xff0c;一想到自己的OneDrive中还有点空间&#xff0c;所以就想把它们存到…

记一次docker迁移目录后oracle11g启动报错问题

迁移docker目录后启动oracle报错&#xff1a; 容器启动失败&#xff0c;无法查看日志&#xff0c;分析是文件目录权限问题&#xff0c;迁移后的目录是/home/docker&#xff0c;因此执行chmod -R 777 /home/docker ,给迁移后目录全局权限。 然后启动容器&#xff0c;发现可以启…

基层管理岗和技术岗如何抉择?

基层管理岗和技术岗如何抉择&#xff1f; 体制内&#xff0c;现为技术研发岗&#xff0c;33岁&#xff0c;本想沿着现在的技术序列走&#xff0c;到时间再申请专家&#xff0c;但近期发现专家没有调配资源的能力&#xff0c;没有办法组建团队。现纠结是否争取管理路线。 技术…

【AIGC】ChatGPT联动脑机接口实验,用脑电波回邮件

国外某公司发现&#xff0c;ChatGPT已经可以用于脑机接口实验了。不过也有眼尖的网友点出&#xff1a;这个过程是不是提前预设好回复邮件的prompt&#xff0c;非常重要。 ChatGPT&#xff0c;已经可以用于脑机接口实验了&#xff01; 近日&#xff0c;Araya公司的一个团队&am…

代码随想录-链表

基础知识 链表是一种通过指针串联在一起的线性结构。在内存中不是连续分布的&#xff0c;分配机制取决于操作系统内存管理。 类型 三种类型&#xff1a;单链表、双链表、循环链表 单链表&#xff1a;每个节点由两部分组成&#xff0c;数据域和指针域&#xff08;存放指向下…

注解与反射

1、注解&#xff08;Annotation&#xff09; 1.1、何为注解&#xff1f; 注解&#xff08;Annotation&#xff09;是从JDK5.0开始引入的技术&#xff0c;以注解名在代码中存在&#xff0c;例如&#xff1a; Override&#xff1a;限定重写父类方法&#xff0c;该注解只能用于方…

Word控件Spire.Doc 【文本框】教程(5): 插入、读取和删除表格

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

提问超时~Zabbix Meetup成都站棒在哪里?

图片 2023年5月13日&#xff0c;迎着初夏灿烂的阳光&#xff0c;Zabbix Meetup首次在成都举办。本次活动吸引了来自Zabbix社区用户、合作伙伴等约50余位伙伴热情参与&#xff0c;倾听来自原厂培训师、社区资深用户们关于Zabbix的精彩内容分享。还有定制周边礼品和晚宴深入交流…

防火墙之iptables

防火墙之iptables 一.iptables概述 Linux 系统的防火墙 &#xff1a;IP信息包过滤系统&#xff0c;它实际上由两个组件netfilter 和 iptables组成。 主要工作在网络层&#xff0c;针对IP数据包。体现在对包内的IP地址、端口、协议等信息的处理上。二.netfilter/iptables 关系…

《终身成长》笔记二——失败挫折的意义

目录 经典摘录 思维模式改变了失败的意义/如何看待失败与成功 思维模式与抑郁症 努力后还是失败的事实会让自己找不到借口&#xff0c;是固定型思维模式者们最大的恐惧 问与答 经典摘录 思维模式改变了失败的意义/如何看待失败与成功 《纽约时报》上的一篇文章指出的&…

学生档案管理系统(SpringBoot,SSM,Maven,H-UI,Mysql)

【项目功能介绍】 本系统包含学生,后台管理员二种角色的用户 学生的功能包含: 登录,退出,修改密码,用户信息查看与修改,首页信息展示,轮播图,栏目及栏目文章列表,我的课程,我的课表,我的成绩,意见反馈,我的意见反馈。 后台管理员的功能包含: 登录,退出,修改密码,管理员管理,…