阿于同学说过,合肥同学不懂什么是"好使",所以,先解释一下。"好使"可能是
东北方言,大意是 可用,能工作,趁手。
今天讲的是在用脚本生成脚本中,可以对付到什么程度。
当年写用vba自动判卷的时候,某张同学说"这个要是再这么一下,那就更好了",
小韩老师说"唉呀大姐 (我记得是这样说的,小张比小韩要小) ,你是要开发个
工具,不是产品。"
韩老师说的话大抵容易懂,而深刻,对我颇有教益,这一句就是。代码可以重用
到什么程度,给用户提供多大程度上的方便 (友好之类的),在工程中,不仅要
看未来和追求,也要视为一个急功近利的短期行为。
如果我们在VBA判卷上做得那么完美的话,当年我跟偶像多半个下午判完全部试
卷的传奇故事就不会发生了。我们可能今天还在追求某个完美的细节。我记得当
年李老师质疑过我的判法,"你这么判,要是那么答,不就错了也得分么?"我
答,"是啊,不过换成人判,比如你,就能那么认真了?"
写程序,我们要的只是比没程序更好,不是最好。"最"这种事,只存在于当今的
媒体、人民的幻想及文革前后的文革之中。对不起,错了,文革语言不仅是"最
",而且是"最最"。需要补课的同学请自行阅读参考书,这里不展开了。
有时候,追求完美的重用--对未来的无比重视--会让我们失去今天。当然,失去
今天也就没有未来了。
比如,年轻男女经常讨论的问题,类似于"以后到底谁刷碗"啊这样的。重用这和
这个类似。事实上,以我们的年轻和技术之浅薄,大多时候,因为没有足够的经
验,我们根本无法预测未来会如何发展。重用是什么?重用是在*未来*被自己或
别人*再次*使用。咱们这点破技术,知道未来如何再次使用这些代码么?
有同学说,"书里说了"。呵呵。
书归正传。用脚本生成脚本,一看这题目,不少同学就会想到编译原理啥的,还
有同学想到了lisp里的宏。我说的不是这些高级题目,而是极其朴素的刚刚能对
付得过去的手段。
1. 所谓朴素
所谓朴素,就是尽可能不使用和依赖高级的技术。比如,我到C++中的 flow
control的时候,讲到循环执行,给过这样的例子:
请输出从1到5的数字。
同学们给出的答案都是:
for(int i=0;i<5;++i)
{
printf ("%d\n", ++1);
}
当然,漂亮。
我的一个答案是这样的:
printf ("1\n");
printf ("2\n");
printf ("3\n");
printf ("4\n");
printf ("5\n");
我看到了,得有超过半个班级的同学对我的写法嗤之以鼻。这代码太烂了,马上
就会有人指出,"要是输出到100呢,要是输出从8到20呢。"
我的代码的缺陷,缺乏重用性,数量大了不好改。一,谁说我准备重用了呢?我
们一看到别人的问题,往往就要预想提问者的动机,有时甚至解读过度。中层对
上层的解读,往往如此,下层就受了苦。二,数量大的时候,其实也简单,我可
以在excel按ctrl拉鼠标,得到数字序列,然后再把这一列粘到C代码里。
之所以能这么对付,是因为,我也可以假设这段代码只用一次,而且只被我一人
使用。
2. 例1,跟牛同学的讨论
前几天跟刘同学一起破解了个软件,我的解法暴力而有效,刘的解法更好,极其
优雅。可惜现在似乎还不宜显摆具体内容。不过,后续与牛同学的一些交流可以
在此讨论一下。
我为牛同学分配的任务是,把这好几千个文件的名字改成符合某个算法要求的文
件名。文件名的对应关系,一部分在数据库里,一部分是C#代码。
你打算怎么实现?牛同学准备写C#程序读数据库。
我建议的方案是这样的,一个标准的对付流程。
(1) 写C#程序,就是那段算法;
(2) 把数据库里的作为算法输入的那一列复制到C#代码里;
(3) 让段C#程序,输出很多行这样的文字:
mv "源文件名" "目标文件名"
以上,用C#生成了shell程序或批处理,然后跑一遍就改了所有文件名。C#本身
并不操作数据库 (人手拷过来的),也不操作文件系统 (由shell程序完成)。
如果算法变了呢?如果源文件列表变了呢?如果……所有这些问题的答案都是:重
新跑一遍上面的流程。用人,而不是用程序的可重用性。
当鳄鱼冲上来咬你的时候,有一种情况,你绝对不要用刀。那就是,你手头没有
刀的时候。另一种情况,不建议用刀。那就是,你用刀非常不熟练的时候。你可
以抱住鳄鱼的嘴,它的咬合力惊人,但是张嘴的肌肉非常软弱。这个时候,并不
是你训练自己用刀的时候。
同样的,当你不熟悉数据库操作,不熟悉在C#里文件系统操作的时候,你应该做
的是 不用它们。项目是用来解决问题的,其原始动机不是给咱们训练技术的--
技术可以在自己练习的时候做。项目唯一能训练的,是工程能力。就像鳄鱼问题
能训练的是随机应变和灵活寻找解决问题的手段。
3. 例2
我有个top250电影目录,下载的时候不知怎么整的,文件名非常长,而且中文部
分是乱码。一直准备把中文部分都删了,长期拖延,昨天晚上整了。
原始的文件名类似于:
Top093.罪æ¶åŸå¸‚.Sin.City.UNRATED.ReCut.EXT.2005.BDRip.MiniSD-TLF.mkv
准备改为
Top093.Sin.City.UNRATED.ReCut.EXT.2005.BDRip.MiniSD-TLF.mkv
这样的文件有250个。
这么整的。下面是一个整行
$ ls -1 Top* | awk -F. '{ first=$1;
min=length($1)+length($2)+3;max=length($0);print ("mv \"" $0 "\" "
first "." substr($0,min,max))}'> ren.sh; chmod +x ren.sh
然后,执行这个脚本,文件名就都改完了。文件中有 ' 或 ( 的,可能会出错。
ren.sh这个脚本,是重定向输出的前半段产生的。这是unix系统管理中常用的一
个手段,用程序A生成一个脚本,用生成的脚本,而不是直接用程序A解决问题。
脚本的内容是很多个这样的行,下面也是一个整行:
mv
Top093.罪æ¶åŸå¸‚.Sin.City.UNRATED.ReCut.EXT.2005.BDRip.MiniSD-TLF.mkv
Top093.Sin.City.UNRATED.ReCut.EXT.2005.BDRip.MiniSD-TLF.mkv
4. 例4
做等级考试的系统时,要批量ping机器以确定每台机器网络正常。我也使用了类
似的方法,用C生成ping.bat。bat里面是很多ping目标地址,及只ping一次的参
数,似乎还有一个超时设置为半秒的限制;C负责循环IP地址,好象是这样
printf ("ping -n 1 192.168.0.%d", i++);。
IP地址的循环,我也用excel实现过。那套方案里,连C也不用。excel的几例中,有
一例是文字"ping -n 1 ",其余几例是IP地址的4个部分,其中最后一部分用
ctrl实现数字的递增。
更早,也更朴素的方法,我用记事本复制第一行ping,然后粘贴几次,每次复制
已粘过的,行数翻倍增长;然后再手动修改IP地址最后一段。
这些方法都很朴素,但是有效。
5. 总结
核心思想是,用代码生成代码;在这个过程是,保持注意目标是"小工具",抛开
重用的幻想。
关同学前两天提到,我们在既往的开发中,代码最终都没有重用。我想,代码不
必重用,或者说,我们远没有达到那个高级阶段。能够解决问题,并重用解决问
题的手段--设计,就非常不错了。而且,毕竟设计很值钱,而代码够便宜。
--------------------
博客会手工同步到以下地址:
[http://giftdotyoung.blogspot.com]
[http://blog.csdn.net/younggift]
[http://www.renren.com/268966623/profile#pblog]
No comments:
Post a Comment