我算故我在分享 http://blog.sciencenet.cn/u/metanb

博文

我如何指导研究生

已有 4377 次阅读 2015-5-26 21:39 |个人分类:师生园地|系统分类:教学心得

[按:数学系出身的很多学生编程能力较弱,主要是因为在数学系,学生本科期间的精力主要放在理论方面,对于编程缺乏适量的实践和深入的认识。下文展示的是今天下午与学生的QQ讨论,关于课题中涉及到的编程环节。文中隐去了涉及课题的敏感信息。希望向有关同行和同学展示本人培养学生的方式,权作某种交流。象这样的讨论基本上每周一次,必要时增加一、两次,目的是加快学生的进展。]


学生 15:04:39

可以开始了吗

Yiwei 15:06:04

今天先讨论例1,就是你上次编写的程序中的问题。

学生 15:06:11

Yiwei 15:09:21

以前没有给你指出来,就是变量名称应该使用英语,而不要使用汉语拼音。

学生 15:10:29

嗯,知道了

Yiwei 15:10:51

每个子程序,也就是matlab函数,都有一个主要的功能,应该以这个功能为主为子程序或函数取名字。

Yiwei 15:11:49

解决一个问题,往往需要开发一套程序,其中主程序只有一个,其它的都是子程序。

Yiwei 15:13:22

主程序有点象“老板”,它的内部主要是设定各种参数,以及组织和调用子程序。换句话说,主程序里一般不涉及大量的计算。

Yiwei 15:14:20

更准确地讲,主程序本身很少直接含有涉及计算的语句。

学生 15:14:39

嗯,知道了

Yiwei 15:17:36

现在说说子程序或函数。Matlab里面的子程序全部用函数的形式实现。一般而言,一套程序里可能包含个别子程序,它们的作用就是大量的重复操作,这种子程序可以看作“发动机”,一般而言,其中有一个计算量最大的子程序,可以看作“主发动机”,其它的则可能只是这个主发动机的“零件”。

Yiwei 15:20:46

有些子程序会产生一些数据,其中有些数据是中间结果,而有些数据需要反复使用。需要反复使用的数据,应该采取“一次生成 多次使用”的策略。而不要在每次需要的时候,一次一次地反复生成。

学生 15:22:22

Yiwei 15:23:42

再有,就是要注意程序的通用性。不能说,问题稍微一改变,整个程序都要从头到尾修改,甚至重新编写。这样的话,效率就很低了。

Yiwei 15:24:56

在程序中,有可能变化的量,就不要用具体的数值。

学生 15:25:55

嗯,明白


{博主按:以上主要向学生讲了编写程序的一般原则和注意事项。因为不是讲课,而学生也有一定的编程基础,所以这里讲的是最需要注意的几点。以下则结合学生编写的程序进行具体的讨论。}


Yiwei 15:26:18

现在你打开“chazhi.m”,就是上次讨论的程序。

学生 15:26:28

打开了

Yiwei 15:27:02

看第4行:[R,wucha]=xxx(c,N);

Yiwei 15:27:22

这一行的目的是得到R与wucha

Yiwei 15:27:47

再看第5行:R1=R(1):1e-4:R(25);

Yiwei 15:28:24

我现在问你,第5行有何毛病?

学生 15:29:21

看不出来,就是给R插值去

Yiwei 15:30:40

刚说了通用性,你看看刚才跟你讲的那段话,再看这里有何毛病。

学生 15:30:53

嗯,知道了,还是求有关R的,可以放在一个程序里

学生 15:31:14

不是

学生 15:31:35

还有就是R(1),R(25)

学生 15:31:47

不通用


Yiwei 15:32:05

还有呢?

学生 15:32:23

插值的步长

Yiwei 15:32:43

对了。

Yiwei 15:34:13

再看那个for循环,有何毛病?

学生 15:35:00

800不通用

Yiwei 15:35:17

还有呢?

学生 15:36:29

看不出了

学生 15:37:14

可以当成一个子程序求x

Yiwei 15:40:07

这里的问题就是两件事情,全都放在一起了。而且放在一起是一堆语句,这和上面的程序“不对称”。上面主要是一个调用和几行作为插值准备的语句。

Yiwei 15:40:54

一件事情用一个子程序去实现,这就是模块化思想。

Yiwei 15:41:42

子程序的功能越单一,它的可重用性和通用性就越强。

Yiwei 15:42:30

明白了吗?

学生 15:42:48

明白意思了

学生 15:43:13

但是还不是很会整理

Yiwei 15:44:21

其实你首先要能从中看出一件事情,就是从???中找出???的、???的数据对儿。

学生 15:45:02

Yiwei 15:45:25

你现在就编写一个子程序,专门用于这一件事。

学生 15:46:33

Yiwei 15:46:59

做完后发到我的邮箱。

学生 15:55:27

m

学生给您发送了一个窗口抖动。

Yiwei 15:57:12

看到了。等。

Yiwei 16:00:56

这个程序还是存在问题,就是有多余的东西。

Yiwei 16:02:18

提示:去掉num和R1. (输入及子程序内部都去掉这两个变量)

Yiwei 16:02:59

修改完成后发到我的邮箱(以后这句话用“r”表示)

学生 16:04:12

不太明白,子程序中的怎么可以去掉呢,要用到的啊

Yiwei 16:04:45

只用到???就能完成任务。

学生 16:05:21

学生 16:06:48

那要输出什么了输出使误差反号的R,就得有R1啊

Yiwei 16:07:21

输出索引不就行了?

学生 16:08:14

不太会,你教我改下,我学下

Yiwei 16:09:08

好吧。

Yiwei 16:09:33

在输入列表中去除num和R1

学生 16:09:57

Yiwei 16:10:44

删除第2行。在那里写上语句:???=???(:);

Yiwei 16:11:42

接着在下一行写上:[num,ntemp]=size(???);

Yiwei 16:12:22

第4行:num改为num-1

Yiwei 16:12:54

做完了吗?

学生 16:12:59

完了

学生 16:13:58

6,7行的R1哪来的

Yiwei 16:14:19

不需要。

Yiwei 16:14:39

在第4行写上:idx{[]};

Yiwei 16:15:38

这是一个空的cell变量,用于存储符号要求的索引,就是数组的序号。

学生 16:16:47

学生 16:16:52

好高级

Yiwei 16:16:57

其中idx是index的缩写记号。在编程时,变量名用英文单词的全拼,或者是“声母”字头的组合。

Yiwei 16:17:52

接着上面的语句,在下一行写上j=0.

Yiwei 16:18:36

现在删除含有R2和j+1的句子。

学生 16:19:09

Yiwei 16:20:05

然后在if 和 end 之间,写上:

j=j+1;

idx(j)=i;

Yiwei 16:20:39

再回到idx={[]},改为:idx=[]。

Yiwei 16:22:20

换句话说,我刚刚改了主意,就是只保存一对儿索引中的前一个。这就够了。这样的话就不用cell了,所以用idx=[]表示一个空的数组就可以了。

Yiwei 16:22:45

现在输出的地方用idx替换R2.

Yiwei 16:23:22

总共11行程序,完成了吗?

学生 16:23:40

完成了

Yiwei 16:24:15

最后的end和前面的for对齐。

学生 16:24:54

嗯,明白了,好高级,idx都没见过

Yiwei 16:25:49

刚才的做法背后有一个原理:最少输入原理。

Yiwei 16:26:47

就是任何子程序,在编写时一定要排除多余的输入变量,做到最少输入。

学生 16:27:21

嗯,,明白了


{博主按:上文中的讨论,主要是针对学生程序中的“典型情况”进行诘问式启发,尽可能让学生自己发现问题,待学生确实无计可施时,再予以援手;必要时予以示范。我注意到一些导师宁愿化n个小时讲“空话”,也不愿意花1个小时去交给学生实在的技术和经验,这对于欧洲导师而言是不可想象的。上面讲的“典型情况”,就是程序缺乏层次,不懂得尽可能地用子程序实现单一功能的模块,而是一大堆语句放在一起。学生不能从这一堆语句中分辨其中相对独立的过程,从而不能从一堆语句中提取抽象的任务概念,并编制相应的通用子程序。}


Yiwei 16:28:40

好了,刚才完成了主要的事项。现在程序还有做进一步修改。

学生 16:28:52

Yiwei 16:28:59

将所有的???用???替换。

学生 16:29:36

就刚刚那个子程序吗

Yiwei 16:30:00

是的。在???里做。

学生 16:31:07


Yiwei 16:31:49

很好。为了方便,输出中增加j,就是数据对儿的个数。

学生 16:32:21


Yiwei 16:32:24

j放在idx的后面,两个输出用逗号隔开,放到方括号中。

Yiwei 16:32:43

很好。

学生 16:32:44


Yiwei 16:33:05

好了,现在你可以提问了。

学生 16:33:47

为什么要把???变为???

学生 16:34:56

还有第二行有什么作用,不要不行吗

Yiwei 16:34:57

这只是名称的变化,程序的功能不变。但是这里反映了一种追求,就是变量名称的通用性。

学生 16:35:12

学生 16:35:24

还有第二行有什么作用,不要不行吗

Yiwei 16:35:48

不行。第二行是为第三行做准备的。

Yiwei 16:36:49

第二行,是把向量转换为列向量。无论???原来是行向量还是列向量,出来的结果都是列向量。

学生 16:37:32

学生 16:38:28

明白了

Yiwei 16:38:40

size是给出???的尺寸,它的输出总是假定第一个输出表示行数,第二个输出表示列数。我们只需要行数,但列数也必须用一个临时变量占住位置。

学生 16:39:27

嗯,明白

学生 16:40:03

没了

Yiwei 16:40:27

换句话说,size的输出约定,决定了我们必须保证???为列向量。但子程序里的操作,只在子程序内部起作用,子程序执行完毕后,???还是原来的样子。

Yiwei 16:40:53

好,你没问题了,我问你一个问题。

学生 16:40:59

Yiwei 16:41:12

idx=[]; 这句有何用处?

学生 16:42:29

idx=[]表示一个空的数组,为9行的存放准备

Yiwei 16:42:59

去掉它会出现何种问题?

学生 16:43:46

第九行会出现没有变量idx的错误

Yiwei 16:44:34

好吧,我们看看是不是这样。在idx=[];前面增加注释符号%

Yiwei 16:44:51

然后保存。


{博主按:以上是子程序完成后的一些细节讨论,这些细节不会影响程序的功能,而是体现编程的风格和程序员素养方面的东西。讨论中通过问答的形式加深学生对这些事宜的印象,顺带地埋了一个伏笔。}


Yiwei 16:45:54

现在我们进入测试环节,就是针对这一个子程序进行测试,顺便检查刚才提出的问题及你的回答。

学生 16:46:08

学生 16:47:06

Too many output arguments.

Yiwei 16:47:09

>> t=0:1/100:2*pi;

>> y=sin(t);

Yiwei 16:47:26

你不要自己做,跟着我来。

学生 16:47:29

Yiwei 16:47:48

上面的两句是生成一个测试用的数据。

Yiwei 16:49:05

注意,测试子程序不需要用???这种特定的数据。可以另外生成独立的测试数据,用于独立地测试所考虑的子程序。

学生 16:49:31

Yiwei 16:51:09

这样做的好处是,你不必专门生成???. 而是独立地进行测试。这一点看起来没什么,但重要的是,你可以不必考虑该子程序的上下文。这样就可以减少很多[子程序]与“外界”的纠缠。

学生 16:51:46

Yiwei 16:51:53

好了,现在我们画图看一下数据y。

Yiwei 16:52:16

>> figure;plot(y)

Yiwei 16:53:07

刚才的画图语句不要了。

Yiwei 16:53:10

>> figure;plot(y,'r.')

Yiwei 16:53:15

用这个。

学生 16:53:55


Yiwei 16:54:43

很好。现在用放大镜找出???的、???的数据的索引。

Yiwei 16:55:01

就是用手工观察的办法找出答案。

Yiwei 16:56:39

学生 16:57:34

300左右

Yiwei 16:57:53

要准确答案。

Yiwei 16:58:31

这也是检查一下你对放大镜的掌握情况。

学生 16:59:32

315与316反号

Yiwei 17:00:07

好的。

Yiwei 17:00:23

现在用刚才的子程序去算。


学生 17:01:32

 

学生 17:02:33

这就是刚刚在idx=[];前面增加注释符号%造成的吧

Yiwei 17:02:52

你把注释符号去掉看看。

学生 17:03:10

也不对

Yiwei 17:04:16

把你的程序文本贴到屏幕上给我看。

学生 17:04:49

(略)

Yiwei 17:06:03

把运行情况的给我看看。

学生 17:06:28

(略)

Yiwei 17:06:50

你修改程序后要保存,然后才起作用。

学生 17:06:59

等会。我知道哪错了

学生 17:07:42

(略)

Yiwei 17:07:57

刚才是什么问题?

学生 17:08:33

弄错了,在另一个命名的程序里改的

Yiwei 17:08:59

改的什么?

学生 17:09:54

我把这个程序写在???里了

学生 17:10:35

???里就没改

学生 17:11:09

还是我原来自己弄得那个程序

Yiwei 17:11:16

说明你没有严格跟着我走。以后注意不要自作主张。

学生 17:11:26

Yiwei 17:12:20

你原来的程序邮箱里有,不需要另外备份。每次让你将程序发到邮箱就是这个道理。

学生 17:12:39

嗯,加%运行的结果一样

Yiwei 17:12:49

好的。

Yiwei 17:13:11

现在保持那一行的%。

Yiwei 17:13:34

我们考虑另一个测试。

Yiwei 17:13:51

>> clear;

学生 17:14:56

Yiwei 17:15:20

>> t=0:0.01:1;

>> y=exp(t);

Yiwei 17:17:25

>> figure;plot(y,'r.')

Yiwei 17:17:35

把你的图贴出来。

学生 17:18:01

(略)

Yiwei 17:18:14

>> [???,???]=???(y);

Yiwei 17:18:23

贴出你看到的。

学生 17:19:15

(略)

Yiwei 17:19:50

知道为什么出现错误吗?

学生 17:20:34

???没定义

Yiwei 17:21:19

也可以说没有赋值,而被要求输出。

Yiwei 17:21:58

再问你一个问题:这里???为什么没定义?

学生 17:23:41

???没有起到索引的作用?

Yiwei 17:23:58

回答错误。

学生 17:24:20

刚这两个例子有什么区别了

Yiwei 17:24:41

想想看有何区别?

学生 17:25:30

哦,知道了,下一个图就没有???点

学生 17:26:58

就因为没有,所以找不出来???就附不了值

Yiwei 17:28:05

对了。所以,???; 是为这种情况而准备的。

学生 17:28:27

如果去了%,就是空的,等着重新赋值

学生 17:28:35

嗯,明白了

Yiwei 17:29:02

去掉那个%, 然后再运行。贴出你看到的结果。

学生 17:29:21

(略)

Yiwei 17:29:49

去掉末尾的分号运行。

学生 17:30:08

(略)

学生 17:30:22

哦,彻底理解了

Yiwei 17:30:29

很好。

Yiwei 17:31:19

这里的j=0也很有用。你可以通过j的值判断是否有解,而不用去读取空的数组。

学生 17:32:03

嗯,明白

Yiwei 17:33:41

今天就讨论了这么一个小的子程序,但背后的道理都在里面了。最主要的,就是:最少输入原理,变量名通用性,还有独立测试。这三点是最重要的。

学生 17:34:28

嗯,大彻大悟

Yiwei 17:34:59

现在休息吧。吃完晚饭接着讨论后面的内容。

学生 17:35:24

嗯,好的,几点大概

Yiwei 17:35:34

老时间。

学生 17:35:42

嗯,好的


{博主按:上面的讨论主要是围绕测试子程序进行的,其中有个插曲,就是有时学生做了一些不必要的事情,但讨论时并未告知我,后来学生自己也忘记了,从而在某个阶段就产生了问题,我就无法知道是什么问题。学生想起出现问题的原因后,我就一定要问清楚究竟是什么情况(尽管可能是无谓的问题)。等弄清楚原因以后,嘱咐学生如何避免出现此类问题。在这个阶段,也通过引导的方式让学生领悟了前一个阶段所埋的伏笔问题。当然,伏笔和测试例子都是作为引导者即兴的设计,目的是加深学生的印象。}





https://blog.sciencenet.cn/blog-315774-893324.html

上一篇:时光
下一篇:讲话
收藏 IP: 110.178.25.*| 热度|

8 李学宽 王安良 余波 张忆文 李炳新 张旸 许天来 姚伟

评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-3-29 08:53

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部