Long long age, far far away. There are some powerpoints of bonus on the Moodle. Many braves tried to find it. But they all failed. And then, our story starts from here......

再说正题之前,我先说点其他的。建议每次运行前先在“命令行窗口(Command Window)”打“clear”,把之前在workshop里储存的变量清空,防止搞混或者程序因为无法运算直接用上一次的结果进行运算。之后打“clc”清除command window里面之前的指令,使得整体变得整洁,一目了然。当然,这些你也可以打在“编辑器(Editor)”里面,就不用每次都打一遍

回到正题

之前在moodle上有两个bonus的ppt,他们分别讲了图像的绘制和Debug的操作。但是估计教授们认为这部分太超前了。没过几天,这两个ppt就消失了。个人猜测,教授们更多想要培养我们的逻辑思维,而不是程序的操控能力,所以到了考试也还是笔试

但是个人认为,学习一门编程语言,Debug是每个人都需要掌握的基础能力。老师没有教,着实有点可惜

借用百度百科解释一下Debug这个词。Debug是供程序员使用的程序调试工具,它可以用于逐指令执行某个程序以验证程序运行的正确性,也可以追踪执行过程、比较一个指令执行前后的值

有了Debug,我们就可以更快的找出我们程序中的漏洞(bug),这样一来,我们编写程序的效率就可以得到极大的提升。接下来我将以hw3的method 3为例子,浅谈一下Debug的操作

% --method 3--

%  get input from user

a1 = input('Please enter the array a1 : ');

a2 = input('Please enter the array a2 : ');

% form a matrix and wait a1 & a2 to fill in

r=zeros(size(a1,1)+size(a2,1),size(a1,2));

% find minimum rows of a1 & a2

minimum=min(size(a1,1),size(a2,1));

% fill a1 & a2 into the matrix

r(1:2:2*minimum,:)=a1(1:minimum,:);

r(2:2:2*minimum,:)=a2(1:minimum,:);

r(2*minimum+1:end,:)=[a1(minimum+1:end,:) a2(minimum+1:end,:)];

这个程序,正如我在评论区讲到的那样a1,a2都非空,或者都空集的情况能解决。但是一空一非空的情况会出错(尤其指a1为空集)。因为我在调试的时候,只考虑到了前两种情况,后来经熊逸飞同学的提醒,我才意识到这个问题。(感谢)

因为我最初没有通过Debug的思维去找漏洞,结果思维越发的混乱,程序也变得十分累赘:

% --method 3--

%  get input from user

a1 = input('Please enter the array a1 : ');

a2 = input('Please enter the array a2 : ');

% form a matrix and wait a1 & a2 to fill in

r=zeros(size(a1,1)+size(a2,1),max(size(a1,2),size(a2,2)));

% find minimum rows of a1 & a2

minimum=~isempty(a1)*~isempty(a2)*min(size(a1,1),size(a2,1))+~isempty(a1)*isempty(a2)*size(a1,1)+isempty(a1)*~isempty(a2)*size(a2,1);

% fill a1 & a2 into the matrix

r(1:1+~isempty(a2):(1+~isempty(a2))*minimum*~isempty(a1),:)=a1(1:minimum*~isempty(a1),:);

r(1+~isempty(a1):1+~isempty(a1):(1+~isempty(a1))*minimum*~isempty(a2),:)=a2(1:minimum*~isempty(a2),:);

r(2*minimum+1:end,:)=[a1(minimum*~isempty(a1)+1:end,:) a2(minimum*~isempty(a2)+1:end,:)];

这个方法,可以是可以,就是太复杂了。让我们看回我们最先错误的程序,怎么Debug吧。

废话不多说,点击“运行”(或者F5)运行试试看,因为我已经知道错误在哪里,为了节约篇幅,我只打会出错的情况(a1为空集,a2不是空集)

正如Israel所说的,Matlab是非常gentle的,它不会骂你,但它会告诉你你错在哪里。所以我们也要耐心地对待它,看看它都说了些什么

首先看错误点

Unable to perform assignment because thesize of the left side is 3-by-0 and the size of the right side is 3-by-3.

无法执行赋值,因为左侧的大小为 3×0,右侧的大小为 3×3。

嗯嗯,错误在于,等号左侧array的大小为3x0,等号右侧array的大小为3x3,大小不一样,导致无法赋值

那么大小不一样在哪里呢?嗯,是表示column长度的0和3不一样

分析完错误,我们就要找到bug的来源(俗称“万恶之源”)。Debug就是这样,最重要的是学会“溯源”。因为大多数程序都是不停的引用先前生成的变量进行后续运算,所以找到先前变量的变化点很重要。我们要看变量可以打指令“whos”或者直接看“Workshop(工作区)”

Bug的位置和内容,Matlab已经为我们找出来了

Error in test (line 16)

出错 test (line 16)

r(2*minimum+1:end,:)=[a1(minimum+1:end,:) a2(minimum+1:end,:)];

这个错误在第16行,所以我们就重点看第16行就好。Debug就是这样帮我们“聚焦”的

正如前面分析的那样,column的长度0和3不一样,导致无法赋值。等号右侧的3很好理解,因为这就是我们输入array的column长度,这个是正确的。所以很明显,这里的错误出在了等号左边的column长度。我们这样就更完美地“对焦”了

等号左边是

r(2*minimum+1:end,:)

它表示选取变量r的2*minimum+1:end的row,以及所有的column。所以问题就在于这个所有“:”。既然我们选择了所有column,它还是长度为0。说明要么一开始我们创造r的时候,它的column就是0;要么我们中间把所有column都删了

接下来要做的,就是“溯源”了

我们一直找找找找找。中间并没有删除r中哪一部分的指令,那么出错的地方就只可能是一开始创造r的第10行了,而且是等号右边赋值给等号左边的column长度值

r=zeros(size(a1,1)+size(a2,1),size(a1,2));

看到bug来源再结合我们的输入值“a1”“a2”,我们很快就能明白error的真正原因了

因为一开始审题的时候,看到了a1,a2的column是一样的,就直接选取a1的column作为r的column长度。但是这样的话,就没有考虑到一空一非空的情况

找出bug修改起来就简单多了,只要把第10行修改为

r=zeros(size(a1,1)+size(a2,1),max(size(a1,2),size(a2,2)));

这道题就完美解决了

但是这种方法对编写者的要求还是比较高的,你需要清楚你程序每一部分才能很好的运用这种方法

刚刚举的例子就是很好的例子。Matlab告诉你error的所在处,但它不一定真的就是bug的真正所在处。只不过是前面的bug并不会导致Matlab完全无法运算,而这个bug的影响到了后面error(完全无法运算)处才被真正地显示出来

Matlab还提供了另一种Debug的方法,这种方法或许更加直观,更易找到bug

Another way

这种方法叫做“断点(Breakpoints)”,你可以每写一行指令就按一下F12,你这一行指令前面就会出现一个红点,那个就是断点。运行的时候,Matlab会在每一个断点处停一下,在command window显示这一行的指令,此时的变量还是没有运行此行指令时的值。这样你就可以十分清楚地知道每个时刻发生了什么。之后按F5或者F10继续接下来的指令

当然,还有另一种生成断点的方法,那就是点有效指令前面的“—”,它会变成红点,再点一次,就变回“—”

还是这道题的例子

首先,点上断点

然后运行

运行到第10行的时候,我们就可以很快发现,r创造的时候就是空集,而不是我们想要的全零array。所以bug很快就被找到了

有同学反映我hw3的Question 4的method 1解释的不清楚,我就在这里解释一下吧

% --method 1--

%  get input from user

a = input('Please enter the array : ');

% sort the array

sort_a=sort(a);

% find the position of the elements

position=a==sort_a';

% find the order of the elements

r=find(position==1)'-(0:length(a)-1).*length(a);

我们要找到排序后每个元素原先的编号(index),所以我们自然而然想到sort这个指令

然后我就要解释一下什么叫做1d array了。1d array指的是一个题目里只有1xn或者只有nx1的array。(各式各样的空集也可以算在1d array里)

而hw3要我们使用2d array。所以我们只要打破1d array的规则就可以把1d array的东西变为2d array

意思就是,只要一道题里同时出现1xn和nx1的array,虽然每个单独看起来是1d array,但总体上却变为2d array了

因为a和sort_a都是1xn的矩阵,所以我们就可以把其中一个转置获得nx1的array,从而达成获得2d array的目的

问题是转置哪一个呢?

首先我们要理解1xn和nx1两个array比较将会是通过什么方法比较(比较的结果一般情况下都是获得logical数组)

拿我代码作为例子

% find the position of the elements

position=a==sort_a';

Matlab的比较方式是,“==”左侧的每一行和“==”右侧每一行进行比较。在这道题的例子里就是a的所有元素和sort_a的每一个元素进行比较,从而达到我们的目的

如图所示,因为我们要sort_a和index一一对应,所以要把sort_a进行转置

找到了每一个元素的位置后,我们要把它变为index

我之前也说了,Matlab是以column为基准进行读取的

a=[1 2 3;4 5 6;7 8 9]

回到这道题,我们要matlab输出值为[2 3 1],因为[5 7 2]排序后[2 5 7]的index就是如此。再放一个图,或许好理解一点

我们就自然而然想到可以用mod来获取index

r=mod(position,length(a));

然而这个方法实际上是行不通的,因为mod会把在length(a)位的index变为我们不想要的0

所以,我们只要把matlab眼中的index变成我们想要的index就行。每过一列就减length(a)。注意,是累计着减(看着上面图片,可能更好理解这句话)

第一列减0*n,第二列减1*n......最后一列减(n-1)*n  (n为length(a))

所以找到最后是,第一个index减0*n,第二个index减1*n......最后一个index减(n-1)*n

所以代码可以写为