『MATLAB 学习笔记』导航 & 资源 & 笔记

在 Coursera 上学习 MATLAB 课程的过程中,自己把笔记整理总结了一下,一方面方便自己日后的参考,另一方面也提供给大家一些资源和经验。

课程

MATLAB 程序设计入门

【前言】这一部分偏重的是基于 MATLAB 的程序设计,即编程的方法,当然也包含了很多 MATLAB 的基础操作。个人觉得,之后对 MATLAB 一些工具的使用在有了 MATLAB 的编程基础的前提下回变得相对的轻松。

我是在 15 年下半年(大一)在 Coursera 学习了这一门课 MATLAB 程序设计入门,英文课程,总体难度不大,毕竟是针对初学者的课程。但有一些章节的课后作业还是挺有难度的。 下面是我在学习这一课程过程中的笔记,因为是在课余学习的,只记了关键的一些操作与命令。考虑会在后续补上截图与操作。

  1. MATLAB 介绍 (略)
  2. 矩阵的操作
  3. 函数与脚本
  4. 分支结构
  5. 循环结构
  6. 多态函数
  7. 输入输出和绘图
  8. 数据类型
  9. 文件操作

MATLAB 应用

后续不断补充

Mathworks 官方资源


矩阵的操作

克隆运算符(Colon Operator)

x=1:3:7 生成数组 1 4 7 表示 x 从 1 开始逐加 3,到 x<=7,相当于 for (i=1; i<=7; i+=3) 这样一个循环。 x=1:100,生成 1 2 3...100 序列,即中间参数没有指定时默认逐加 1。

操作矩阵的一部分(Access Parts of a Matrix)

有如下的一些方法: 假设 x= 1 2 3 4 5 6 7 8 9

x(2,3) 矩阵 x 第 2 行,第 3 列的元素 6 x(end,2) 矩阵 x 最后一行,第 2 列的元素 8 x(2,[1 3]) 矩阵 x 第 2 行的第 1 列和第 3 列的元素 4 6 x(2,1:3) 第二行的 1,2,3 列元素 4 5 6 x(:,2) 第 2 列所有元素 [m,n] = size(x) 得到 m 为行数,n 为列数 sum(x) 对逐列求和,输出每列和的行向量 12 15 18

矩阵生成(Matrix Building)

指定元素

zeros(5,6):5×6 的方阵,元素全为 0 ones(4,2):4×2 的方阵,元素全为 1 5*ones(4,2):元素全为 5 zeros(4):4×4 的方阵 diag(7 3 9 2):对角阵,7 3 9 2 位于其主对角线上,其余元素全为 0

随机元素

rand(3,4),rand(5):分别生成 3×4 和 5×5 的矩阵,元素值 0-1 fix(1+rand(5,4)*10):fix 为取整 randi(10,4,5):生成 1-10 的 4×5 矩阵 randi([5 10],4,5):生成 5-10 的 4×5 矩阵 randn(1,1000):n 代表 normal,按正态分布生成随机数

随机数生成器(Random Generator) 每次打开 MATLAB 后 rand 的值便固定了。需要重置随机数。 rng(参数),参数部分可以是数字,可以是字符串。


函数与脚本

函数 - Function

MATLAB 自带了丰富的函数,当然我们也可以自定义函数来实现自己想要的功能。 比如:rand(3,4) 就能生成 3×4 的数表,每个数在 0~1 之间。 输入 edit 编辑新文件

1
2
3
function myRand
    a=1+rand(3,4)*9
end

保存为 myRand.m,这样就定义了一个 myRand 函数,用来生成 1-10 之间的数。其中 a 为局部变量,只在其作用域 (myRand) 中有效。 就这样执行 myRand 结果会被保存在变量 ans 中。

修改一下第一行:function a = myRand,本意是想输出 a,运行之后会发现输出了 a=... ans=... 两个部分。想要只输出 ans,只需在 a=1+rand(3,4)*9 后加上 ; 输入 b=myRand 便可将值保存到变量 b 里。 将函数修改一下

1
2
3
function a=myRand(low, high)
    a=low+rand(3,4)*(high-low);
end

就可以输出在low~high之间的随机数表了。若想输出a中的全部数字的和,则可以改成如下

1
2
3
4
5
function [a,s]=myRand(low, high)
    a=low+rand(3,4)*(high-low);
    v=a(:);
    s=sum(v);
end

然后在命令行中输入 [x ss]=myRand(1,10) 就会分别输出数表 x 及所有数字之和 ss

function 的定义

1
2
    funtion [out-arg1, out-arg2, ...]
=function-name(in-arg1, in-arg2, ...)

不确定函数名是否已定义,可以用 help exist

subfunction - 定义多个 (子) 函数

举个栗子

1
2
3
4
5
6
function [a, s] = myRand(low, high)
            a = low+rand(3,4)*(high-low)
            s = sumAllElements(a);
function summa = sumAllElements(M)
            v = M(:);
            summa = sum(v);

全局变量

1
2
global v;
v=(...);

一定要分开写 新手尽量少使用,可能会导致难检测的错误。

function 的优点

  1. 将一个大问题分解
  2. 功能的分解
  3. 代码复用
  4. 普遍性

Script - 脚本

是一个 .m 文件,用来执行一系列命令、赋值等计算 执行脚本时只需输名字

1
2
3
fprint('This is matlab\n');
pause(5);
quit;

分支结构

if语句

function guess_my_number(x)
    if x == 2
    fprintf('Congrats! You guessed my number!\n');
end

注意每个 if 语句都要以 end 结尾

if-else语句

function guess_my_number(x)
if x == 2
    fprintf('Congrats! You guessed my number!\n');
else
    fprintf('Not right, but a good guess.\n');
end

if-elseif-else 语句

1
2
3
4
5
6
7
8
function guess_my_number(x)
if x == 2
    fprintf('Congrats! You guessed my number!\n');
elseif x < 42
    fprintf('Too small. Try again\n');
else
    fprintf('Not right, but a good guess.\n');
end

最后一个 else 可以不要,就是一个 if-elseif 结构。

关系运算符(Relational operators)

运算符含义
==判断是否等于
~=是否不等于
>是否大于
<是否小于
>=是否大于等于
<=是否小于等于

进行判断后返回值为 1 (真) 或 0 (假)

可以进行一组数的比较

1
2
[4 -1 7 5 3] > [5 -9 6 5 -3]
ans = 0 1 1 0 1

当然也可以这样,逐个与 4 进行比较:

1
[4 -1 7 5 3] <= 4

还有很多玩法

1
sum([14 9 3 14 8 3] == 14)

逻辑运算符(Logical operators)

运算符含义
&&逻辑与
||逻辑或
~逻辑非
输入&&||
falsefalse00
falsetrue01
truefalse01
truetrue11

非零为真,零为假

1
2
~[1 pi 0 -2]
ans = 0 0 1 0

但对两个矩阵使用 &&||,应变为 &|

1
2
[1 -3 0 9 0] & [pi 0 0 2 3]
ans = 1 1 0 1 1

help precedence 获取关于运算符优先级的帮助

循环嵌套(Nested if-statements)

1
2
3
4
5
6
7
8
9
if arg_1
    if arg_2
        statement_1;
    else
        statement_2;
    end
else
    statement_3;
end

多态函数

多态函数(Polymorphic functions)

什么是多态

是指函数可以根据输入输出的表达式 个数类型 作出不同的反应。 举个栗子:zeros(5,6) 生成 5×6 矩阵,zeros(4) 生成 4×4 的矩阵。 MATLAB 中许多内置的函数是多态的(sqrt, max, size, plot, etc.)

如何使自己的函数具有多态性?

nargin: 返回实际的输入参数的个数 nargout: 返回实际要求的输出参数个数

下面这个函数 multable(n,m) 生成 n×m 的数阵,我们来使其具有多态性

1
2
3
4
5
6
7
8
function [table summa] = multable(n,m)
if nargin < 2
    m = n;
end
table = (1:n)' * (1:m);
if nargout == 2
    summa = sum(table(:));
end

[table s] = multable(3,4),会输出 3×4 的数表和其所有元素和 table = multable(5),只会输出 5×5 数表

健壮性(Robustness)

一个健壮的函数,会对各种可能出现错误的情况进行处理。上面的函数可以进行如下的优化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function [table summa] = multable(n,m)

% MULTABLE muliplication table.
% T = MULTABLE(N) return an N-by-N matrix
% containing the multiplication table for
% the integers 1 through N.
% MULTABLE(N,M) returns an N-by-M matrix.
% Both input arguments must be positive integers.
% [T SM] = MULTABEL(...) returns the matrix
% containing the multiplication table in T
% and the sum of all its elements in SM

if nargin < 1    % 没有输入
    error('must have at least one input argument');
end
if nargin < 2
    m = n;
elseif ~isscalar(m) || m < 1 || m ~= fix(m)  % 参数不为正整数
    error('m needs to be a positive integer');
end
if ~isscalar(n) || n < 1 || n ~= fix(n)
    error('n needs to be a positive integer');
end

table = (1:n)' * (1:m);
if nargout == 2
    summa = sum(table(:));
end

% 后为注释,在命令行中输入 help multable,可以得到最上面一大段注释作为 help 的内容

持续变量(Persistent variables)

和全局变量类似,但又有所区别。

1
2
3
4
5
6
7
8
function total = accumulate(n)
persitent summa;
if isempty(summa)
    summa = n;
else
    summa = summa + n;
end
total = summa;

也就是说,再次调用这个函数的时候,summa 的值是接着上次的值的。 重置:clear accumulate


输入输出和绘图

输入 & 输出(Input&Output)

1
2
3
function a = one_more
x = input('Gimme a number, buddy:');
a = x+1;

上面实现了输入一个 x,并将 x+1 赋值给 a,input 括号中的为控制台输出的提示信息。

1
fprintf('This is an output example\\n')

输出一行信息

1
2
3
function check_out(n,price)
total = n*price;
fprintf('%d items at %.2f each\\nTotal = $%5.2f \\n',n,price,total);

fprintf 中的 f 代表 format,即格式化。

%d,整数 %.2f,f 代表 fixed point,小数位为 2 位 %5.2,共 5 位(包括小数点),小数点后 2 位。

1
fprintf('%4.1f\\n',[1 2 3])

会输出

1
2
3
1.0
2.0
3.0

绘图(Plot)

1
2
a =(1:10).^2
plot(a)

绘图一般是先生成数列,再绘图

plot(t,b),以 t 为 x 轴,b 为 y 轴 figure(2),新建第 2 个空白的图像,如果有就切换到这个图像

1
2
3
x1 = 0:0.1:2*pi; y1 = sin(x1);
x2 = pi/2:0.1:3*pi; y2 = cos(x2);
plot(x1,y1,x2,y2)

自定义图像样式 plot(t,b,'m--o'),最后一个参数中,m 表示 magenta (品红色),-- 表示虚线,o 表示小圆圈标出每个点。可以在 help plot 中找到更多相关参数

hold on 在当前 figure 上保留画出来的 图像,可以再做图像。与之相对的是 hold off plot(x2,y2,'r:'),红色连续点线,plot(x1,y1,'g*'),绿色,点用 * 表示 grid,打开网格 title('xxxx'),加上标题 xlabel('xxxx'),x 轴加上标签 ylabel('xxxx'),y 轴加上标签 legend('sine','consine'),图例说明 axis([-2 12 -1.5 1.5]),指定 x 轴从 - 2 到 12,y 轴从 - 1.5 到 1.5 close(1),关闭 figure1 close all,关闭所有图像


数据类型

数据类型(Datatypes)

同其他的编程语言一样,MATLAB 也有许多的数据类型。下面我们来看看我们用过哪些数据类型。

class(arg) 可以返回 arg 的类型。如:

1
2
>> class(0)
ans = double

whos 命令可以列出当前工作区域变量的详细信息。

数字类型(Numerical Types)

双精度实数(Double)

  • MATLAB 中默认的数据类型
  • 浮点表示形式,例如:12.34 = 1234 * 10-2
  • 内存占用:64 bits(8 bytes)

单精度实数(Single)

  • 与双精度类似。
  • 内存占用:32 bits(4 bytes)

整数类型(Integer types

  • 分为有符号(Signed)、无符号(Unsigned)两类
  • 内存占用:8, 16, 32, 64 bit long
数据类型范围
int8-27~27-1
int16-215~215-1
int32-231~231-1
int64-263~263-1
uint80~28-1
uint160~216-1
uint320~232-1
uint640~264-1
single-3.4×1038~3.4×1038
double-1.79×10308~1.79×10308

一些实用的函数

类型检测

class 返回类型

isa 返回是否是这个类型,例如

1
>> isa(x,'double')

范围检测

intmax intmin

realmax realmin

1
>> intmax('uint32')

类型转换

1
2
3
int8(x)
uint32(x)
double(x)

需要说明的是,当一个变量重新赋值时,会转换为所赋值的类型。

字符串(Strings)

字符串,顾名思义,是由一串字符组成的。

在 MATLAB 中,字符串其实是一个向量,其中的元素自然是字符(char),字符又是由 ASCII 码值,可以得到一个打印所有可见字符的例子:

1
2
3
4
5
function char_codes
for ii = 33:126
    fprintf('%s',char(ii));
end
fprintf('\n');

MATLAB 中,有一些常用的操作字符串的函数:

函数作用
findstr找到字符串中的子串
num2str将数字转换为字符串
str2num将字符串转换为数字
strcmp比较两个字符串
lower将字符串转换为小写
upper将字符串转换为大写
sprintf将结果写入字符串中

具体使用方法可以参考 MATLAB 自带的 WIKI

注:strcmp 在比较时严格区分大小写,若忽略大小写,可以用 strcmpi

sprintf 的用法和 fprintf 用法基本一致,但区别在于 sprintf 返回一个字符串

结构体类型(Structs)

众所周知,在 MATLAB 中,一个数列只能包含类型相同的一组数据,那我们需要存储类型不同的数据怎么办呢?结构体就能做到。

结构体和数组的区别:

  • 结构体的基本单位是字段 (fields) 而非元素 (elements)
  • 我们访问结构体时用的是字段名称 (field name) 而非索引 (indices)
  • 字段可以有不同的数据类型 (甚至是另一个结构体)

灵活性:字段可以包含子结构体;结构体可以包含数组,同时数组元素也可以是结构体

下面来实践一下:

1
2
3
4
5
6
7
8
>> r.ssn = 12345678
% 以上定义了一个结构体 r

>> r.name = 'Homer Simpson'
% 继续添加其它类型的字段

>> r.address.street = "742 Evergreen Terrace"
% 这里的 r.address 就是一个子结构体

一些常用的操作结构体的函数:

函数作用
isfield判断结构体中是否有字段
setfield动态添加字段
rmfield删除结构体中的一个字段
struct快速构造一个结构体

结构体数组:在已经定义了一个结构体的前提下,可以快速构造结构体数组。已知 account 为一个结构体

1
2
3
4
5
6
>>account(2).number = 7654321;
account =
1×2 struct array with fields:
number
balance
owner

值得注意的是,在 account(2) 中,除了赋过值的 number 字段外,其它字段初始为空

在一个结构体数组中,如果一个结构体的字段是一个子结构体,那么这两个子结构体不一定要相同。

注意:在使用 rmfield 时,应将返回值赋给要删除字段的结构体。

元胞数组(Cells)

这种类型也是 MATLAB 独有的,类似于 C 语言中的指针 (Pointer),但一个元胞 (Cell) 并不直接存内存的地址,而是指向某变量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
%% The Ultimate Legend of Big John  相当于段落文本
page{1} = 'You could find him on the field almost any day.';
page{2} = 'Tall, dark hair, and eyes of steel gray.';
page{3} = 'They say he pulled a Frisbee ''bout half a mile,';
page{4} = 'And when he''d stick in the corner, you could almost catch a smile';
page{5} = 'On Big John.';
%% 打印输出
fprintf('\n');
for ii = 1:length(page)
    fprintf('%s\n',page{ii});
end
fprintf('\n');

可以看出元胞数组的定义是用了一对花括号 {},而里面的数字类似于数组的索引。此外,你可以这样构建元胞数组:

1
2
p = cell{2,3}
p{1,2} = 'Awesome'

类似于 2×3 的数组。但里面的每个元素都是元胞 (Cell)

注意:MATLAB 不允许两个元胞指向同一个对象,也就是说,当你令 c2 = c1 时,虽然表面上 c2 的数据和 c1 是一样的,但 MATLAB 悄悄地将 c1 的数据拷贝到了新的内存地址中,也就是 c2 指向的数据地址和 c1 指向的数据地址是不一样的。

还有许多实用的函数可以操作元胞数组,有兴趣可以看一下官方文档。

文件操作

文件操作(File IO)

使用文件有诸多优点: 首先它是被一直存储在硬盘中的,它里面储存着信息,文件可以由操作系统轻松的管理,文件同时也可以被复制和移动,可以被不同软件访问。 对于 MATLAB,它不仅可以处理其专有文件格式 (.mat .m),还可以处理 txt 文本文档、二进制文件,甚至 Excel 文件。

pwd 命令可以显示当前所在的目录 (Print Working Directory) ls 命令可以列出当前目录下所有文件 cd 命令可以改变目录 (Change Directory)

1
2
3
4
5
cd('Lesson 08')    % 进入当前文件夹下的 Lesson 08 文件夹
cd('..')           % 返回上级目录
cd('../..')        % 返回上上级目录
mkdir('folder')    % 创建新目录
rmdir('folder')    % 删除空目录

save 命令可以保存当前工作区 load 命令可以加载当前工作区

1
2
save my_data_file data s a    % 将变量 a 和 s 保存至 my_data_file.mat
load my_data_file             % 从 my_data_file.mat 中加载变量

Excel 文件操作

MATLAB 支持读取和写入 Microsoft Excel 文件。需要用到 xlsreadxlswrite 两个函数。

1
>> [num,txt,raw] = xlsread('Nashville_climate.xlsx')

num 元素包含了表中所有的数字,txt 则是包含了文字,raw 储存了所有

1
2
3
4
5
6
7
>> temps = xlsread('Nashville_climate.xlsx')  % temps 包含有数字
>> [temps txt] = xlsread('Nashville_climate.xlsx')
>> [~, text] = xlsread('Nashville_climate.xlsx')  % 只输出 text
>> [~, ~, everything] = xlsread('Nashville_climate.xlsx')  
>> num = xlsread('Nashville_climate.xlsx', 1, 'D15:E17')
% 数字 1 表示工作簿,D15 指定了 D15 这个单元格
>> num = xlsread('Nashville_climate.xlsx', 1, 'D15:E17')

至于 xlswrite,可以写入 CSV 文件。不详细讨论了。

txt 文件操作

1
2
fid = fopen(filename, permission);
fclose(fid);

其中 permission 这个参数可以有很多,如:rt wt at r+t w+t a+t

下面用一个示例来进行 txt 文件读取操作的演示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function view_text_file(filename)
fid = fopen(filename,'rt');
if fid < 0
    error('error opening file %s\n', filename);
end

% Read file as a set of strings, one string per line:
oneline = fgets(fid);
while ischar(oneline)
    fprintf('%s',oneline) % display one line
    oneline = fgets(fid);
end
fprintf('\n');
fclose(fid);

再来看看如何写入 txt 文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function write_temp_precip_txt(filename)
Title_1 = 'Climate Data for Nashville, TN';
Title_2 = '(Average highs(F), lows(F), and precip(in)';
Label_1 = 'High';
Label_2 = 'Low';
Label_3 = 'Precip';
Mo_1 = {'Jan','Feb','March','April','May','June'};
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
    Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Mo_2 = {'July','Aug','Sep','Oct','Nov','Dec'};  
Data_1 = [
    46	28	3.98
    51	31	3.7
    61	39	4.88
    70	47	3.94
    78	57	5.08
    85	65	4.09];
Data_2 = [
    89	69	3.78
    88	68	3.27
    82	61	3.58
    71	49	2.87
    59	40	4.45
    49	31	4.53];
fid = fopen(filename,'w+t');
if fid < 0
    fprintf('error opening file\n');
    return;
end
fprintf(fid,'%s\n',Title_1);
fprintf(fid,'%s\n',Title_2);
fprintf(fid,'\n');
fprintf(fid,'       %s%s%s\n',Label_1,Label_2,Label_3);
for ii = 1:6
    fprintf(fid,'%5s: ',Mo_1 {ii});
    fprintf(fid,'%5.2f,%5.2f,%5.2f\n',Data_1(ii,:));  
end
fprintf(fid,'\n');
fprintf(fid,'       %s%s%s\n',Label_1,Label_2,Label_3);
for ii = 1:6
    fprintf(fid,'%5s: ',Mo_2 {ii});
    fprintf(fid,'%5.2f,%5.2f,%5.2f\n',Data_2(ii,:));  
end
fclose(fid);

二进制文件操作

在更多的时候,使用二进制 (binary) 文件进行文件存储和读取具有更高的效率。

下面就给出操作二进制的例子来看看 MATLAB 是如何存取二进制文件的。

1
2
3
4
5
6
7
8
9
function A = read_bin_file(filename,data_type)
fid = fopen(filename,'r');
if fid < 0
    error('error opening file %s\n',filename);
end

A = fread(fid,inf,data_type);

fclose(fid);

下面是写入二进制文件

1
2
3
4
5
6
7
8
9
function write_array_bin(A,filename)
fid = fopen(filename,'w+');
if fid < 0
    error('error opening file %s\n', filename);
end

fwrite(fid,A,'double');

fclose(fid);

小结: MATLAB 提供了对常用文件类型操作的支持,使用文件,我们可以更方便地读入大量数据并进行操作,同时输出易于阅读的数据。

加载评论