前言
大家好,我是Lau师傅,今天继续和大家分享知识。
本文是学习郭老师的MATLAB教程第三节过程中所做的笔记,内容涉及撰写脚本,结构化编程和用户自定义函数三个重要板块。
郭彦甫老师上课使用的版本:MATLAB R2014a
我学习时使用的版本:MATLAB R2022b
一、Script Writing-撰写脚本程式
1.MATLAB Script-MATLAB脚本程式
-
MATLAB脚本程式是一个包含一系列MATLAB命令的文档。
-
更像是一个C/C++程序。
-
脚本不用经过编译就可以执行(逐行读取执行),但是必须保存为.m格式的文件才能执行。
2.Start A Script(.m)File-新建脚本文档(.m)
点击左上角“新建脚本”,创建了一个脚本编辑器-untitled,然后拖动untitled窗口可以使之单独显示。
3.Script Editor-脚本编辑器
复制如下代码粘贴到脚本编辑器中:
for i = 1:10
x = linspace(0,10,101);
plot(x,sin(x+i));
print(gcf,'-deps',strcat('plot',num2str(i),'.ps'));
end
脚本执行方法有两种:
-
(1)单击编辑器选项卡中的“运行”:
-
(2)按下键盘上的“F5”键。
选择上述两种方法之一后,软件强制弹出如下脚本储存窗口,需要完成路径选择和命名工作,这里使用默认名称untitled。
若弹出如下窗口,选择“更改文件夹”即可。
脚本执行结果如下图所示。
思考:代码中的每个函数的分别是什么意思?
4.常用功能介绍
-
首先是查找function的功能:因为郭老师使用的是2014a版本,该功能键在界面上方,而我使用的是2022b版本,该功能键已经改变了位置,放到了命令行窗口的“fx”中,如下图所示。
以查找“cos”为例,点击“fx”,选择“数学——初等数学——三角学——cos”,如下图所示,显示cos函数的使用简介,然后双击cos即可输入cos。
-
注释:如果有某些行的代码不需要执行,那么可以将它们注释掉,使用百分比符号%进行注释。方法:
(1)选中代码后右键选择注释;
(2)选中代码后按下快捷键Ctrl+R;
(3)选中代码后鼠标单击编辑器-代码的注释按钮。
取消注释也都有对应的方法:
(1)选中注释后右键选择取消注释;
(2)选中注释后按下快捷键Ctrl+Shift+R;
(3)选中注释后鼠标单击编辑器-代码的取消注释按钮。
注意编辑器-代码:
-
分节:将代码分为几个部分称为节,在调试(debug)时非常有用。使用两个百分比符号+空格进行分节。方法:
(1)在需要分节的地方单击右键,选择分节符;
(2)在需要分节的地方按下快捷键Ctrl+Alt+Enter;
(3)在需要分节的地方鼠标单击编辑器-节的分节符。
注意编辑器-节:
-
调试(debug):对程序进行错误排查、修复、解决故障。
断点(breakpoint):可以在调试过程中使用的方法,设置断点后,程序运行到断点行就暂停执行。
在2022b版本当中,单击代码前的数字就可以设置断点。如下图所示,在第4行设置断点。
运行后效果如下图所示。
再单击代码前的数字将断点取消,点击继续,则debug取消,全部运行。
-
智能缩进、增加缩进、减少缩进
脚本可以保存执行的程序指令,而命令行指令不能,所以建议以后都使用脚本进行程序编写,可以储存为文档随时查看使用。
5.Script Flow-脚本流程
通常来说,脚本从第一行运行到最后一行。
使用结构化编程(包括子进程、循环、条件等)能够让程序看起来更加简洁。
二、Structured Programming-结构化编程
1.Flow Control-流程控制
(1)Review
下表是MATLAB中使用的一些语法结构的review。
Structure | Meaning |
---|---|
if,elseif,else | 如果条件为true,则执行语句 |
for | 按指定的次数执行语句 |
switch,case,otherwise | 执行几组语句中的一组 |
try,catch | 执行语句并捕获生成的错误 |
while | 在条件为true时,重复执行语句 |
Terminal | Meaning |
---|---|
break | 终止 for 或 while 循环的执行 |
continue | 将控制权传递给 for 或 while 循环的下一个迭代 |
end | 终止代码块,或指示最后一个数组索引 |
pause | 暂时停止执行 |
return | 将控制权返回给调用函数 |
(2)Relational(Logical) Operators-关系(逻辑)运算符
我们把对逻辑关系进行判断的运算如a>10,a==10,a<10,a>=10,a<=10称为relational operation,即关系运算或者逻辑运算,其中用作逻辑判断的运算符号>,<,==等称为relational(logical) operator,即关系运算符或逻辑运算符。这些运算属于布尔逻辑体系,运算结果有2种,分别用1和0代指true(是)和false(非)。
如下表所示,是MATLAB中的常用的8种逻辑运算符。
Operator-运算符 | Meaning-含义 |
---|---|
< | 小于 |
<= | 小于或等于 |
> | 大于 |
>= | 大于或等于 |
== | 等于 |
~= | 不等于 |
&& | 与 |
l l | 或(两根竖线) |
注意:
(1)a=10,这里单独的等号是assignment赋值运算。
(2)MATLAB的不等于是~=,而C语言的不等于是!=,二者写法不一样。
2.常用的结构化编程语法
所有结构化编程的语法都必须配有相应的结束指令terminal,如if用end(end of some structured programming command)结束。
注意:一般将if,switch称为分支结构,将for,while称为循环结构。
(1)if,elseif,else
如果条件1为true,那么执行语句1;如果条件1不为true,那么检查条件2,条件2为true则执行语句2;如果所有条件都不符合即条件1和2都不为true,则执行语句3;最后执行end结束指令。
“elseif”和“else”都是可选择的。
请练习下列代码:
a = 3; % a赋值实数3
if rem(a,2) == 0 % rem取余数,判断a/2的余数是否为0,为0则true,为1则false
disp('a is even') % a/2的余数为0则显示a is even
else % a/2的余数不为0即为1时
disp('a is odd') % a/2的余数不为0时显示a is odd
end % 结束指令
执行结果在命令行窗口显示:
可以发现,结构化编程能够跳过某行指令,直接执行后面的指令。
(2)switch
switch与if else的不同在于它不是条件判断true/false,而是判断数值,依据数值选择执行几组语句中的某一组。
判断expression的数值,如果值为value1,则执行语句1,如果值为value2,则执行语句2,…,如果以上值都不满足,则执行最后的语句,然后执行end结束指令。
请练习如下代码:
input_num = 1; % input_num赋值为实数1
switch input_num % 判断input_num数值
case -1 % 如果input_num数值为1
disp('negative -1'); % 则执行,显示negative -1
case 0 % 如果input_num数值为0
disp('zero'); % 则执行,显示zero
case 1 % 如果input_num数值为1
disp('positive 1'); % 则执行,显示positive 1
otherwise % 如果input_num数值不为1,0,-1
disp('other value'); % 则执行,显示other value
end % 结束指令
(3)while
当条件表达式expression为true时,执行语句,当条件不为true时,执行end结束指令。
请练习下列代码:
n = 1; % n赋值为实数1
while prod(1:n) < 1e100 % prod返回数组元素的乘积,判断数组元素的乘积是否小于1e100,即prod(1:n)=prod([1 2 3 ... n])=1x2x3x...xn=n!
n = n + 1; % n!小于1e100时,n递增1
end % n!不小于1e100时跳出循环,结束指令,n有最大值
结果如下图,可知,最大69!<1e100
Exercise-练习
-
使用while循环计算1+2+3+…+999的和
% 我练习写的代码 n=1; sum=0; while n<1000 sum=sum+n; n=n+1; end disp(sum);
(4)for
在变量从start到end以increment为间隔变化时,执行commands语句,最后执行end结束指令。
请练习下列代码,思考若n取1、3、5、7、9时应如何修改:
for n =1:10 % 变量n从1到10变化,步长为1。见“冒号运算符”
a(n) = 2^n; % n每次变化,求a(n) = 2^n
end % 结束指令
disp(a); % 显示a
当n取1、3、5、7、9时,若修改如下:
for n =1:2:10 % n取1、3、5、7、9
a(n) = 2^n; % a(1)、a(3)、a(5)、a(7)、a(9)
end
disp(a);
显然不是我们所想要的结果,a(2)、a(4)、a(6)、a(8)自动补零了,思考如何修改?
我的修改如下:
clear;
for n = 1:2:10
a((n+1)/2) = 2^n;
end
disp(a);
或者
clear;
for n = 1:5
a(n) = 2^(2*n-1);
end
disp(a);
或者
clear;
i = 1;
for n =1:2:10
a(i) = 2^n;
i=i+1;
end
disp(a);
Exercise-练习
如下图示,使用结构化编程完成:
-
将矩阵A的元素复制给矩阵B;
-
如果矩阵A中的相应元素为负数,则更改矩阵B中的值。
提示:使用for循环就可以完成。
下面是我练习写的代码:
clear;
A = [0 -1 4;9 -14 25;-34 49 64]; % 创建矩阵A
B = A % 把A复制给B
for n = 1:9 % 遍历A的每一个元素
if A(n)<0 % A某元素为负时
B(n)=-B(n); % 将B对应的元素变为正
end
end
disp(B); % 显示更改后的B
运行结果:
(5)终止符
1)break
break可以终止执行for或while循环,不执行循环中在break语句之后显示的语句,可以用来避免电脑宕机、陷入死循环等情况。
如上图所示,当条件error>error_threshold成立时,执行while循环内的语句。通常我们还会在循环内加入其它的判断,当k>100成立时,执行break直接跳到end终止while循环而不执行循环中break之后显示的语句。
3.Pre-allocating Space to Variables-给变量预分配空间
在之前的例子中,我们没有预先分配空间给a,而是让MATLAB在每次迭代时调整其大小,那么哪种方法更快?
下图中,A方法无预分配,B有预分配,那么哪种方法更快?
如何得知MATLAB的程序执行花费的时间?这里使用了tic(开始计时)和toc(结束计时)来计算执行用时。
clear;
tic; % 开始计时
for ii = 1:2000
for jj = 1:2000
A(ii,jj) = ii + jj;
end
end
toc; % 结束计时
clear;
tic; % 开始计时
A = zeros(2000,2000); % 创建2000x2000的全零数组
for ii = 1:size(A,1) % size(A,1)返回A的第1个维度的长度即2000
for jj = 1:size(A,2) % size(A,2)返回A的第2个维度的长度即2000
A(ii,jj) = ii + jj;
end
end
toc; % 结束计时
结果如下图所示:
可以看到,分别运行两节时的时间相差还是很大的,显然预分配节省了很多时间,建议自己练习感受一下。
因此,建议在能够得知变量大小的情况下尽量进行预分配空间的操作。
4.Tips for Script Writing-对于撰写脚本的建议
-
(1)在脚本开头,使用指令
-
clear all来清除之前的变量
-
close all来关闭所有图形窗口
-
-
(2)在命令末尾使用分号 ; 来禁止不需要的输出
-
(3)当指令过长时,可以使用省略号 … (起到换行的作用)来使脚本更具可读性
-
(4)如果出现宕机,按快捷键 Ctrl+C 在结束前终止脚本
-
(5)使用clc来清除命令行窗口历史
三、User-defined Function-用户自定义函数
脚本和函数都是包含MATLAB指令的.m文件,有时我们经常使用某些指令,把这些指令写成函数就能使工作方便很多。
Scripts | Functions |
---|---|
无输入参数 | 有输入参数 |
无输出参数 | 有输出参数 |
在全局工作区中操作数据 | 在本地工作区中操作数据 |
1.Concent of MATLAB Built-in Functions-MATLAB内置函数的内容
我们求平均用到的mean就是一个函数function,这是MATLAB内置的函数。
在命令行窗口输入edit(which('mean.m')
可以打开mean函数的.m文件,查看mean函数的内容。
在第一行看到function y = mean(x,dim,flag,flag2)
,蓝色的function
是关键字,括号内的x,dim,flag,flag2
为输入参数,y
为输出参数,mean
是函数名称。
所以,函数格式如上图所示,keyword output = function_name(input)
即关键字 输出参数 = 函数名(输入参数)
,其中function_name函数名和file_name文件名相同,然后下面是注释和代码。
呼叫mean函数时,首先执行下面的代码,然后将数值给到y
,y
再给到命令行窗口或者其他呼叫mean函数的脚本中。
Some Observation-一些观察
-
关键字:
function
-
函数名称和文件名称相匹配
-
目录:MATLAB需要找到这个函数
-
输入和输出变量可选
-
本地变量:
dim
和flag
不能被访问
2.User Define Functions-用户定义函数
(1)
写一个计算自由落体运动的位移x的函数,给定初始位移 x0、初始速度 v0 和下落持续时间 t ,那么初始位移 x0
、初始速度 v0
和下落持续时间 t
是输入参数,待计算的位移x
是输出参数,函数名称是freebody
。
function x = freebody(x0,v0,t)
% calculation of free falling
% x0: initial displacement in m
% v0: initial velocity in m/sec
% t: the elapsed time in sec
% x: the depth of falling in m
x = x0 + v0.*t + 1/2*9.8*t.*t;
在新建脚本中输入这些代码,然后保存,默认会保存文件名和函数名相同。
此时,我们注意到代码中使用了点乘.*
(见MATLAB教学02),为什么要用点乘?
我们知道计算不同角度(1,2,3,4,5)的cos值可以使用cos([1,2,3,4,5])
,那么计算不同x0
、v0
和t
也可以同理,即使用freebody([0 1],[0 1],[10 20])
同时计算了x0=0
、v0=0
、t=10
和x0=1
、v0=1
、t=20
两种情况的结果,这是使用点乘的情况。但是如果不使用点乘,只使用乘法即x = x0 + v0.*t + 1/2*9.8*t*t
则运行freebody([0 1],[0 1],[10 20])
会出错,t*t
计算不出来,如下图所示。
(2)Functions with Multiple Inputs and Outputs-具有多个输入和输出的函数
粒子的加速度和作用在它身上的力如下图所示。要求写一个计算粒子的加速度a和作用在它身上的力F的函数,输入参数为末速度v2
、初速度v1
、末时间t2
、初时间t1
、质量m
,输出参数为加速度a
和力F
,函数名称是acc
。
输出参数有2个,如何读取到呢?用中括号[]
括起来。注意,这里我们使用了点除./
和点乘.*
。
function [a F] = acc(v2,v1,t2,t1,m)
a = (v2-v1)./(t2-t1);
F = m.*a;
使用时要写2个输出如[Acc Force] = acc(20,10,5,4,2)
。
Exercise-练习
写一个函数,要求输入温度(以华氏温度°F为单位),然后计算对应的摄氏温度(C=(F-32)*5/9),最后显示换算的摄氏温度是多少。这个函数应当保持执行直到没有可供转换的数。你可能会使用到这些函数:input
,isempty
,break
,disp
,num2str
。效果如下图所示。这个练习有一定难度,请一定要经过自己的思考。
下面是我练习写的代码:
function [] = F2C(~)
% F2C无输入、无输出
% tem_F表示华氏温度,tem_C表示摄氏温度
while 1 % 表示无限循环,重复执行
prompt = "Temperature in F is "; % prompt是显示给用户的提示文本
tem_F = input(prompt); % 要求用户输入,输入记为tem_F
if isempty(tem_F) % isempty判断数组是否为空,即判断tem_F是否为空
break % tem_F为空时,跳出while循环
end % 终止if
tem_C = (tem_F-32)*5/9; % 华氏度到摄氏度的转换公式
Tem_C = ['==>Temperature in C is ',num2str(tem_C)]; % num2str将数字tem_C转换成字符串放在Tem_C中,再用在disp中
disp(Tem_C); % 显示Tem_C的内容
end % 终止while
end % 终止function
执行结果:
3.Function Default Variables-函数默认变量
Variable | Meaning |
---|---|
inputname | 函数输入的变量名 |
mfilename | 当前执行函数的文件名 |
nargin | 函数输入参数的数量 |
nargout | 函数输出参数的数量 |
varargin | 变量长度输入参数列表 |
varargout | 变量长度输出参数列表 |
如上图所示,函数pillar
,当nargin=2
输入参数2个时,默认height=1
。
4.Function Handles-函数句柄
函数句柄是一种表示函数的 MATLAB 数据类型,一种创建匿名函数的方法,即不必在m文档中定义的一行表达式函数。函数句柄还是一种指针pointer。
命令行窗口执行后在工作区查看变量f,类型为function_handle。将f
指向exp(-2*x)
,f
的输入为x
。即输入的x
为向量,输出的f(x)
是x
的数学函数也是向量,可以plot出来。
总结
以上就是本节课程的全部内容,如有错误望指出。之后陆续上传后面章节的笔记和PDF版本,欢迎大家关注。
我最常用的写作开源软件Marktext的介绍、下载、安装与设置
MATLAB教学__01学习导览
MATLAB教学__02MATLAB as A Calculator and Array Operation-计算器与矩阵操作
MATLAB教学__03Script Writing,Structured Programming and User-defined Function-撰写脚本,结构化编程和用户自定义函数