Strongly recommend Coursera

2012年5月4日 没有评论

Today I’ve received an epilogue letter from Tim Roughgarden, a professor at Stanford, who has completed an Algorithm course on Coursera, which I strongly recommend that to you.

You may have already watched some open course videos online. You may have visited Sina Open Course or Netease Open Course, or you may have downloaded open course videos from YYEts. But trust me, Coursera is a better choice than just watching those videos. Attending a Coursera-class, you watch short videos which are well-filmed by professor, you complete homework and programming questions before deadline, you discuss with other students in course forum, and you take a final exam as we do in college.

Taking the Algorithm class for example. I registered the class on Jan 8th and the class kicked off on March 12th. As the class kicked off, Tim, the professor, uploaded the videos, prepared problem set and programming exercises for the first week, and emailed each student. I watched those videos and did those homework. The deadline of the homework was two weeks later. I could attempt several times before I got the final score, e.g. I attempted 2 times on problem set — week 3 and got 7.2 out of 8. As one week passed by, the process iterated. The class lasted five weeks, during which I warmed up Quick-Sort, Dijkstra Algorithm, and learned several useful and smart algorithms such as Strassen’s Algorithm and Karger’s Algorithm. On April 25th I took the final exam at home, which required a three-hour long period without interrupts. The total score of the course is 30(Problem Sets)+40(Programming exercises)+30(Final Exam).

So how about the difficulty of the course? I think the most significant problem for us Chinese students is listening. Tim speaks very fast. Good news is, Tim provides subtitles as well, in English. If you have no difficulty in displaying the videos online, you can load subtitles when watching videos. But you may be blocked from displaying videos directly( You know, we are in China), then you need to download both videos and subtitles to your local machine. You may ask whether the course itself is hard, the answer is no. Tim made it simple enough, so never mind if you have already forgotten whatever you have learned in computer science classroom, you will be warmed up quickly and enjoy it. My final score is 98.2, it’s easy for Chinese students to get high scores.

S0 what can you benefit from the course? For me, I’m warmed up, not only knowledge, but also my life as a college student. I clarified confusions of some particular algorithms before. And I managed to complete the class within deadlines, despite that I was busy with working, wedding…at the same time.

You may not interested in Algorithm, you may not interested in computer science at all. On Coursera there are classes in other fields, you can check them out there. As I know, Udacity provides such classes as well. There must be some classes fit you if you are desired to study something.

BTW, in Tim’s epilogue letter he mentions that he will re-run the class in a month or so. So if anyone interested, you can register on Coursera or directly visit the course page.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 技术 标签: ,

开发杂记——小数点

2012年3月7日 没有评论

项目背景:基于.NET Framework 2.0的Rich Client程序。

一日,风和日丽,办公室清静,程序员老李正坐在自己位子上,气定神闲地喝着咖啡。

“大事不妙!”小周忽然来报。

“何事如此慌张?”

“数字校验方法有异!”

“此法千锤百炼,自.NET Framework升级以来,已历三载,未有显著变动,何以有异?莫非幻觉?”

“非也,今有QA,误输入逗号于数字中即提交,本想数字校验时当报其格式错误,谁知校验方法竟信以为真,放它通过了校验。以至于数据库接收到非预期输入,抛异常矣!”

老李按QA步骤重现,果然如此,遂忆起该方法的实现来。这段程序本是在.NET 1.1 Framework下写成,当时只能使用decimal.Parse(string s)方法,若字符串不合法,该方法会抛出异常,若逢逗号,也是当作不合法,抛出异常了事。可这抛出异常的方式却有局限,try-catch若逢异常,额外性能开销可观。我们只关心数字是否合法,不关心异常种类,所以当年老李将.NET Framework升级到2.0之后,改弦更张,使用Framework为了避免抛出异常而提供的decimal.TryParse(string s,out decimal result)方法。这个方法尝试将一个字符串转换成decimal类型的数字,返回值表示转换是否成功,转换得到的数字保存在result参数中。如果验证不通过,不会提示遇到了哪种错误。

老李又忆起小学数学,中学数学和大学数学,大学数学已端的难以忆起,以老李的知识,若只传入一个仅包含阿拉伯数字和逗号的字符串,除非按三个数字放在一组,否则转换应当不成功。可事实并非如此。在Windows XP .NET 2.0 Framework,en-US语言环境下,老李传入“123,45”,TryParse返回的结果为true,怪哉!

老李去google了一下这个现象,发现果然有不少人因为这个问题困扰过,只不过有的人困扰的原因不一样:他们需要把句点转成逗号作为小数点!这颠覆了他的知识——他一直以为,小数点就应该是用句点表示的。

谬矣!

在英文维基百科上,有个条目叫Decimal mark,这维基条目命名得严谨,用了mark,而非point,其中必有文章:原来使用逗号作为小数点的国家还真不少,著名的有俄罗斯,德国,法国。事实上,整个欧洲除了英国,基本都用逗号做小数点。再仔细看,使用逗号的国家比使用句点的多多了,只不过使用句点的人口可能更多些,因为使用句点的国家包括中国,印度,美国,英国。

那么,为什么那些国家会使用逗号作为小数点呢?不得不提到西方表示数字的一种传统:每3位数字并为一组,要么用逗号,要么用句号分隔。西方印刷术刚刚发明时,在法国,德国等欧洲大陆国家,为了罗马数字的可读性,使用句点作为数字分组符,逗号分隔整数部分和小数部分;而英国则反其道而行之,使用逗号分隔数字,句点分隔整数和小数。

由于双方各行其是,又各自在势力范围内推广,小数点难以统一了。这时有一标准化组织出马,召集多方代表会谈,终于制定了标准:句点和逗号都是合法的小数点,但是数组分隔符不能再用句点或逗号,只能用空格表示。欲知决议详情,且看这个网页。

这样看来,.NET Framework的TryParse方法认为带逗号的数字合法,就不足为怪了。不过,TryParse方法并没有采用符合上述标准的定义来解析数字,而是将逗号作为数字分组符!这样的话,“123,45”会被解析为12345,而且即使原始字符串包含多个逗号,也会被认为是合法的。

另外,表示数字的方式还有很多种,除了我们比较熟悉的阿拉伯数字和罗马数字,常用的还有“阿拉伯文数字”。此外老李还注意到一种叫做苏州码子的数字表现形式,这种形式在大陆和台湾已经绝迹了,但香港还在使用,Unicode也收录了,但把字符集错误命名为杭州了。

原因已明,问题待修。系统用户多为华人与老美,但也有和法德一样习惯的荷兰用户,不可厚此薄彼,有所偏废。老李眉头一皱,计上心来,按照客户端所在电脑的区域和语言设置,获取当地的合法小数点,这样逗号和句点就不能在同一个数字里相容了。编码,写单元测试,运行,问题解决。

“老李,你怎么还是一直用‘小数点’这个词啊?”

“积习难改⋯⋯”

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 兴趣, 技术 标签: ,

A Hello-World post from Mac air

2012年2月16日 没有评论

I have bought a Mac air last November and is trying to migrate from Windows family OS to Unix/Linux family OS. Now it’s the first blog post from my Mac. Up to now, I have not yet found a substitute for Microsoft Live Writer for me to write blogs, so it’s somehow inconvenient for me to write blogs on Mac.

Besides, from this post, I choose a creative commons license to protect my copyright.

And I will try to blog in English. It’s difficulty indeed, however, it’s worth trying.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 生活 标签:

输入验证码也能拯救世界——谈谈reCAPTCHA

2011年12月23日 1 条评论

大家可能已经注意到,我给我的博客加上了经reCAPTCHA验证后才能发表评论的限制(有的朋友居然以为这是广告)。其验证码看起来有点让人望而生畏:

但是,一些炙手可热的国外网站,例如facebook,偏偏喜欢用这种验证方式。每次见到这种验证方式,要辨认,输入一长串难认的单词,我都烦不胜烦。

直到有一天,我偶然从网上发现了reCAPTCHA的来由。于是,我从原来的抵触,转变为如今的支持。下面就说说原因。

一组典型的reCAPTCHA验证码包括两个单词,按照大多数使用reCAPTCHA进行验证的网站的说法,用户需要将两个单词都正确识别并输入,验证才能通过。其实这个说法是不对的。这两个单词分为两种情况:一个单词是计算机已知答案的,叫做对照词(Control word);另一个是未知词(Unknown word),reCAPTCHA系统在终端用户(以下称为“挑战者”,为人类)正确输入对照词的情况下,会推断挑战者输入的未知词也是正确的,以备后用。所以有时候,我们正确输入了验证码中的一个单词,输错了另外一个,也是可以验证通过的。

我们知道,验证码做的好不好,关键在于是不是难以被计算机识别出来。如今最可靠最常用的计算机文字识别技术是OCR(Optical Character Recognition),例如有道词典这样的桌面词典工具就是用这种技术实现某些环境下的屏幕取词。但是OCR技术还远不完美,由于人类的印刷文字具有各种字体,各种字号,各种特殊情况,而且年代久远的图书,书本泛黄、污渍的情况也会影响到OCR识别的准确率,对于Google BooksInternet Archive这样雄心勃勃地要数字化海量图书,特别是旧书的项目而言,OCR技术是不可靠的。以下图片是OCR识别一段扫描自旧书或者旧报纸的例子:

我们人类识别这段文字毫无压力,但OCR的扫描结果错误百出。

由于OCR的不完美,至少是现阶段的不完美,2002年,CMU的几位学者发明了CAPTCHA,也就是我们所说的验证码。而reCAPTCHA也是同一批人在CAPTCHA的基础上提出的。在reCAPTCHA中,系统使用两种不同的OCR算法来识别单词,如果两种算法对一个单词的识别结果不一致,那么这个单词将被认为是“可疑的”,需要加入reCAPTCHA可疑词库,连同一个已知的对照词,随机分配给不同的挑战者来辨认。如果满足如下条件之一,那么一个可疑词将被认为是得到了辨认:

条件甲,前三个挑战者的识别结果完全一致。

条件乙,所有可能的识别结果进行投票PK:每个相同的挑战者识别结果贡献一票,每个相同的OCR识别结果贡献半票,取最先达到2.5票的结果为辨认结果。

选择对照词的基本原则,是不能被上面提到的两种不同的OCR算法同时识别出。由于上一步辨认未知词保证了每个被辨认的结果都来自OCR不能达到一致的可疑词,那么一旦这些词得到辨认,它们自然可以作为合适的对照词。这样,随着越来越多的未知词被世界各地的挑战者解决掉,对照词库也更加丰富,这个系统自然也越来越健壮。

我们现在来看一下第一张图片里的两个单词,reffeFr这个词是控制词,因为系统已经知道了这个词的意思,所以它可以尽情地扭曲这个词来加大OCR识别这个单词的难度;recall是未知词,系统当然也可以对这个词做一些扭曲变形,不过,既然reCAPTCHA肩负拯救世界的使命,它完全可以对人类降低识别难度,同时也就降低了普通人拯救世界的门槛。

那么,reCAPTCHA肩负了什么样的使命呢?那就是我前面提到的,海量图书和典籍的数字化。我们常说,互联网的发明给我嘛带来了海量信息,在这个信息爆炸的时代,我们获取信息变得极为便利,以至于我们目不暇接。其实让时光倒流几个世纪,印刷机的发明,同样引燃了中世纪的信息爆炸。甚至我们可以看到更久远,纸张的发明……仅仅印刷术和出版业数百年的发展就为我们留下了浩如烟海的图书,但随着时光流逝和天灾人祸,也不断有图书亡佚和典籍散失。亚历山大图书馆的浩劫以及萧绎焚书的悲剧,让读书人每每为之扼腕。因此,不论Google Books,Internet Archive还是古藤堡计划,都是在拯救人类文明和智慧的成果。而从现在reCAPTCHA的实践来看,这个项目确实大大加速了图书数字化的过程:如果仅仅扫描图书,这样的扫描结果基本难以利用,想想自己看扫描版PDF的痛苦就知道了(卡通漫画除外);如果纯人工录入,速度慢也易出错;借助OCR,错误率高,不可靠。而reCAPTCHA可以快速,准确,自动地完成这一繁重工作。下面让我们用数字说明问题。

以下数据摘录于2008年9月《科学》杂志的文献

每天人类解决上百万个reCAPTCHA谜题。正确率超过99%。

在识别结果中,67.87%的可疑词只经过两个挑战者的辨认就被解决。

该系统运行一年后,人类解决了12亿reCAPTCHA谜题,4.4亿可疑词被成功辨认。

可以想象,2008年以来随着reCAPTCHA被越来越多的网站使用,我们在不知不觉中又完成了许许多多书籍的数字化工作。这也是互联网时代来自大众的力量。

目前reCAPTCHA这个项目已被Google收购,主页在此。现在该项目主要专注于Google Books项目以及New York Times的数字化,支持的语言有多种,但目前还不支持中文。另外,对于《死海古卷》这种上古文字,《银雀山汉墓竹简》这种考古学家都视为畏途的,也确实难以借助广大普通人的力量。如果中文图书的数字化有机会用这样的思路来做,对于中华古文化的传承会有坚强的助力作用。

总之,reCAPTCHA技术一举多得,如果你读了这篇小小的介绍有所感触,那么请不吝留下评论,顺便解决一两个谜题,为拯救世界出一份力。

最后顺便说一句,如果一组reCAPTCHA验证码特别难认,挑战者可以放弃这次挑战,换一组验证码。不过,如果累计有六位挑战者表示放弃,而这时这个谜题仍未解决,那么系统会遗憾地将这个谜题设置为不可解,不再作为挑战题目。如果这样的话,世界上某一本书的某个角落,某段文字或某个单词失去了被自动数字化的机会,要解决这个难题可能只能关门,放专家了。

为了避免这种悲剧的发生,咱们还是再试一次吧,咱们强过OCR的,放心吧。

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 技术 标签: ,

12月4日,浦口

2011年12月21日 5 条评论

半月前,携妻探访阔别六年的南大浦口校区。校园依旧在,只是已经归了金陵学院,本科生都搬到仙林了。仙林虽美,与我无干。浦口承载了我的三年青春岁月,虽已物是人非,仍激动不已。

玉辉楼,冷冷清清。

教学楼和大平台。入校注册就在这里。

这么多年了,这些自动售货机居然还在。

南大星。

我离开浦口时还没有这么好的图书馆。

在新图书馆看老图书馆。是的,就这俩铁皮房子。

左涤江运动馆。

秋风萧瑟。运动场上只有一个跑者。

原来的八食堂+清真+金乐乐。现在居然开起了KTV。

六舍。现在要刷卡进入。

通往4,5,6舍的的便道封起来了,必须从大门刷卡,以保证安全。

七食堂还在,只是编号换了。

原来水房的位置,开满了小商店。

浦苑餐厅也没有了。以前很喜欢那里的铁板牛肉饭。

六食堂也变了,不过教超还在。

上课,自修的必经之路。考六级差点睡过头,也是从这里奔的。

八角楼的教室。我抢不到中间的位子,都是坐边上。

南大百年校庆时,徐州校友会送的。现在狮子很寂寞。

这个食堂,以及周围的一些宿舍,是我大三左右才盖好的,我基本没有去过这边。

无情最是台城柳,依旧烟笼十里堤。

PS,今天收到短信,我12月4号当天在南京长江大桥违章,被罚款100元……

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 生活 标签: ,

租车自驾经验谈

2011年6月1日 没有评论

自从2010年初拿到驾照以来,我在一嗨、神州两家租车公司分别租了若干次车,此外,有一回同事在安飞士租车我也陪同了。下面总结下我租车的经验教训,并对这些租车公司做一些对比。

租车手续

从我拿到驾照以来,租车行业的发展和进步很快,回想我刚刚拿到驾照时,在网上查询如何租车,答案通常是:一年以上驾龄,本地户籍,无本地户籍需要担保,大笔押金。一嗨租车的服务方式还是新鲜事物,用户也不多,现在,这种方式已经成为行内标准。可以描述属下:

1. 在租车公司网站注册。

2. 在租车公司网站自助选择时间和车型,下单,在预定的时间到门店取车。如果是第一次租车,租车公司往往都有首日首次半价优惠。当然,初次见面用户需要提供身份证和驾驶证。

3. 刷预授权。现在租车公司通过两证一卡(身份证,驾驶证,信用卡)来保证用户不会一去不返,所以用户需要准备一张额度足够的信用卡。

4. 当场验车。门店工作人员会带着用户检查车况,这个步骤双方都要细心一些,把车子的伤逐一记录下来。一般门店的工作人员都会主动指出车子所有的问题,但自己也要仔细看,以免遗漏造成换车时出问题。此外,确保所有灯光没问题,后备箱里工具齐全(备胎,千斤顶和工具包),有行驶证。外观检查完后就是记录油量和公里数,内饰是不看的。

5. 在验车单上签好字就可以开始享受驾驶乐趣了。注意安全,别出事故,别违章。

6. 按期还车。还车时门店工作人员首先验一下车况,这次验车一般是比提车时要松的,然后核对油量是否和取车时一致,公里数是否超标。验车完毕后,撤销取车时的预授权,另刷一笔1000元的违章预授权。消费有的在取车时收取,有的在还车时收取,也有在网上预先收取的,这个无所谓。

异常处理

1. 事故处理。如果遇上事故,建议第一时间拨打110报案,并致电自己的租车公司,报案请交警处理是为了进保,交警当面明确责任后,双方一起去指定地点,由保险公司定损。如果要私了的话,最好也和自己的租车公司,或者门店工作人员沟通清楚。我在第二次租车时遇到过一次事故,被追尾,对方全责,就走的上面报案定损的程序,由对方保险公司赔付,我不需要为此额外付钱。

2. 违章。到目前为止,我一共违章一次,是2011年5月20日在杭州江城路违章停车被城管贴条,罚款150元。贴条这种情况租车人可以当场得知自己违规了,可以第一时间去把罚款处理掉。但对于电子眼拍到的违章,就得等一段时间了,如果有违章,租车公司会打电话来骚扰的。这种电话自然谁都不乐意接到,我现在好在没接到过,如果接到,而且自己没空去办,或者太远了不可能特意跑一趟的话(比如我住在上海,我去广州旅游并租趟车,回来后有一天晴天霹雳来一张罚单,你不可能为一张罚单跑一趟广州吧),就要花点钱请租车公司代办了。

3. 油量差异。如果提车时租车公司把油箱加满发车,那是最方便的,还车时也把油箱加满就行了,但租车公司常常在油箱不满的情况下交车(我租车的订单中,有一半订单是不满箱交车的,这是个普遍现象),所以常常会造成油箱油量有偏差的情况。如果用户加油加多了,租车公司会按照多加的油量乘以租车公司当地的油价,在订单完成后返还给用户,下次租车时可以使用;如果加油加少了,把差价补给门店工作人员,让工作人员把油量记录为和取车时相同即可。

4. 异地还车。这个我也尝试过一回,义乌租车杭州还,这会产生异地还车费,费用=两地之间公里数*每公里费用,每公里费用通常是1元,不同车型可能不同。异地还车要提前告知租车公司,并保留好取车时的预授权单,验车单和租车合同,届时还车门店的验车会趋于严格。

5. 超公里。这事我也干过,每个租车公司有每日公里数限制,超过的话要按超出的公里数乘以每公里价格,通常是1元,在还车时结算。

租车公司对比

去年上半年租车行业还是一嗨一家独大,自从神州拉到了联想控股的巨额投资,这个市场就展开了激烈的竞争。这对消费者当然是好事,因为这意味着价格更低廉,服务更完善,不过,每次下单租车之前,还是要考虑一下每家租车公司的特长和短处,选择适合自己情况的一家。

价格:神州租车经常大搞价格战,华东地区低端车型常常有半价优惠活动,大约是日租金300元以内的,租期两天及以上,且非重要节日的,每日租金打对折。不过,神州热火朝天地搞半价,打折,大面积地铺广告,一嗨没什么反应。从这一点来看,神州的价格应该是最低。

公里数:神州普卡公里数为230/天,且金卡标准比较难,金卡公里数为350/天;一嗨普卡公里数为300/天,且金卡标准相对宽松,金卡公里数也是350/天。这一点一嗨占优。

网点:神州和一嗨都有大量门店和服务点,但神州门店20点下班,一嗨门店22点下班,有一回我火车晚点造成没有赶上神州门店的下班时间,马上去一嗨租了一辆。

至于安飞士,网点少,价格高,一般不考虑。

综合考虑上面几点,我建议朋友们在租车前review一下自己的需求,如果短途开开并且多于一天的话,必然选神州;如果需要跑远一些,估摸着公里数不够的,选一嗨;取车时间比较晚,选一嗨;清明端午这样的节日,选哪家都行,但一定要抢到车,不要车子都被别人租掉了干瞪眼。

TIPS

1. 清明,五一,端午这样的节日,车子紧俏,租金也坚挺,可以结合自己的行程,到租金便宜的城市租车,例如4月30日我在义乌租到一辆乐风,每天259,租用2天,如果我在杭州租,只有400以上的,而且根据我的行程需要租3天,于是我花费了较少租金,少租了一天,代价是异地还车费用一百多点,依旧划算。

2. 提前做功课,打探好便宜加油站的位置,选好加油地点,比如能在浙江加油就不在上海加油,在上海能在民营加油站加油就不在中石化加油。

3. 了解租车当地的地方性法规政策,例如上海工作日高峰时段禁止外牌车辆上高架,外牌车进上海要交30块,杭州本地牌照车辆随车有一张高速公路统缴卡,可以折抵杭州绕城高速等路段的通行费用。

4. GPS导航很有用。自己手机支持最好,租一部GPS导航仪也成,但有的时候租不到。

最重要的,还是注意安全!平安上路平安到家。

 

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 生活 标签: ,

郑州游玩笔记

2011年5月30日 1 条评论

前面是偏技术一些的郑州出差总结,其实出差一趟不能纯粹把时间奉献给工作,作为一个热爱旅行的人,作为一个没有来过郑州的人(以前只有路过),如果只能机场,仓库,酒店三点一线,就太亏了。

于是我抓住一切机会去了解郑州,下面请跟着我的镜头,来看看我短短几天出差过程中对郑州的了解。

这是刚刚从郑州新郑机场出港时拍的,神马实业。郑州机场不是第一次来,但以前是转机,这次是第一次走出郑州机场。郑州机场周围都是树,机场高速两边也都是树,对北方城市而言,“绿城”这一称谓还是可以接受的。

我们公司的仓库对面就是郑州富士康,中午,富士康的大批员工走出厂区,到食堂吃饭。去年深圳富士康连环跳楼事件后,郭台铭迅速在内地布点,而郑州政府也一路绿灯,郑州富士康的厂房、办公楼、食堂在几个月时间内从一片空地上长了出来,工人也如雨骈集。

到了晚上,富士康门口的路上密密麻麻停满了该厂的班车。富士康的员工宿舍和厂区不在同一个地方,我们无缘得见,从班车的路线来看,他们的宿舍也分布在好几处的,例如一个叫金庄的地方。

中原福塔,郑州刚刚建起来的一座铁塔,怎么看怎么是形象工程。

按照当地同事的推荐我们来景祥烩面吃了一顿地道的当地风味,这里的烩面确实是我在郑州吃到的最好的烩面,汤鲜肉嫩,面薄如纸,再按照当地人的习惯,要几个凉菜,烤几串羊肉再喝点儿啤酒,实在是太合我的口味了!

我喜欢有梧桐树的城市!这条路可能叫纬五路,名字土了点,推测是1949年后城市扩建扩出来的吧,稍后再追寻真相。

其实很想乘一次公交,但时间不允许,同事也不允许。

郑州早高峰一样堵,城市病在中国到处泛滥。这是金水路。

富士康在郑东的又一处厂区,占地面积又很大,据说,郑州为了保证富士康的用地,把原本给某大型民营企业留的地转给富士康了,这个大型民企只能等待来年。

郑东新区一些仿欧式风格的小楼。

郑东新区的人工水道,背景是郑东CBD.

河南博物院镇院之宝之一——莲鹤方壶

河南博物院镇院之宝之二——武则天除罪金简。必须坦陈,来河南博物院之前,功课做得不够,时间又太少,赶了又赶,走马观花地看完全部展厅。

在河南博物院附近偶遇一家小而有爱的书店“书是生活”,小小一方书店里满是纯文艺图书,音乐舒缓,沙发松软,这样的小店只应在鼓浪屿有,开在小巷子终点的榕树下,不期然在郑州遇见,门外大道车马喧嚣,打开书店门却是爱书人的世界。

来自鼓浪屿的明信片,天下文艺青年是一家啊。

在这里买了一本书,作为到此一游的纪念。

总之,我对郑州印象美好,遇到的郑州人都热情好客,乐于助人,让我很感动。

顺便说说这次出差的诸多不顺:

牙磕掉一块,后来拔掉了;由于牙齿生毛病吃饭困难;打车遇到有些司机不给发票;住在市区的时候下雨,没能去二七广场看看;手机丢了。

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 旅游 标签:

郑州出差技术笔记

2011年5月30日 没有评论

4月17日到22日,为了试验我们的语音拣货项目,我去郑州仓库出差5天。从结果来看,这次出差虽然没有预期顺利,但最终还是完成了预期要达到的目标:得到了当地同事的支持;找到固定人选长期使用语音方式拣货;收集和及时处理问题。回想这次出差经历,把一些心得记录下来。

出差决策:我通过争取进入了这次出差的名单,事实证明我这一行动非常正确。我们的语音拣货项目由业务逻辑和语音引擎组成,在办公室编程、实验期间,语音引擎的表现一直相当优秀,因此大家都很乐观,老大也只打算安排业务逻辑的需求分析人员与开发人员两个人过去。但作为语音引擎开发的负责任,我认为我不能不去,现场和办公室噪音背景不一样,工人口音和对语音引擎接受程度不一样,于是我就去争取。后来到了现场,语音引擎的表现真的比在办公室时差很多。

现场调试:抵达仓库的第一天,我们的需求分析人员就给分公司领导和员工培训和讲解我们的项目,但尴尬的是,语音引擎不能识别我们说的话,于是我们从多个方面查错和改进:增加灵活的用户配置方式,方便我们修改语音引擎的各项参数;调低辨认门槛;调节语音合成语速;优化流程。拿改进配置方式这一点来说,我们在上海时,出于对语音引擎的信心,按照我们实验得出的经验值将一些参数写死了,例如,一个识别结果如果评分超过60分,我们认为是可信的,然而,在现场看到的大多数评分都是四、五十分,于是我们需要更改这个参数,但我们如果重新在代码里把“60”换成另外一个固定值,如果实验结果仍然不好,又得重新发布、部署一次,于是,我们在主程序页面上增加了这个参数的配置接口,以便随时在运行时修改这一参数。经过几天努力,工人总算是可以使用语音拣货功能了。

版本控制:上述改动由于都是在现场临时改代码,我们只能采用人肉版本控制的方式:尽量只由我来做语音引擎,另外一位同事做业务逻辑,但有时我也需要改业务逻辑层,只能先用U盘把他的代码拷过来,我改好了再拷给他,用这种最原始的方式来维持版本的正确性。另外,上海office的同事也会帮我们debug一些疑难的问题,好了之后把代码mail给我们。可以说,这种原始的方式限制了我们的开发效率,也极容易出错,我们在现场碰到过几次版本出错的情况。如果在现场也能像在办公室一样使用ClearCase/Git就方便多了。也许可以通过在github上建立Private repository的方式来实现这种跨地区的版本控制,不过不是免费的。

代码修改:虽然囿于人肉版本控制,但为了保证代码质量,我们两个开发人员尽量做到:少做改动;用unit test覆盖原有功能;互相review变动的代码。在现场时,我们多数时间一起行动,要么一起编程,要么一起验证,使得每一次改动两个人都心里有数。另外,代码经过unit test覆盖之后,改起来确实底气很充分,我只要保证unit test覆盖了所有可能情况就能够放心改代码了。出差的人往往为了赶时间而直接写代码改代码,但我认为完备的unit test是值得的,可以大大减少以至消除因为赶工造成的错误,磨刀不误砍柴工。

现场协调:虽然我们是技术人员,但在现场我们当然要和方方面面的人进行协调。幸运的是,郑州的同事,特别是仓库经理和操作主管都是对新知识新事物抱有极大热情的人,他们对我们的试验提供了全力的支持,操作主管本人更是连续三天作为最终用户直接试用我们的项目,认真地帮我们做测试并提出真知灼见,他的意见无不是切中要害的,对我们的持续改进很有帮助。

最后附送一张在现场偶得的照片~

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

计划执行情况(2011/4)

2011年5月9日 没有评论

1.读书:本月只读了一本《修改代码的艺术》。执行进度严重被拖后腿。《约翰·克利斯朵夫》仍然在读,过半了。

2.个人博客:是关于《修改代码的艺术》的读书笔记,虽然只读了这一本书,但很有收获。

3.锻炼身体:罚款500元……

4.工作:这个月去郑州出差一周,实施我们做的语音拣选产品,这是计划之外的。前后不太顺利。如果有空写篇博客记录一下。其余执行情况受此影响,有所滞后。

5.旅行:搁置中。

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 生活 标签: , ,

读书笔记:《修改代码的艺术》

2011年4月16日 没有评论

刚刚读完《修改代码的艺术》,我觉得译名没有原名贴切:Work effectively with legacy code。如题目所云,这本书系统而全面地讲述了work with legacy code的思想和诸多具体方法,对于我们每个人维护、重构,修改遗留代码或者基于遗留代码新增、修改功能很有帮助。

在我读来,work with legacy code只有两种方法:写单元测试,解依赖。写单元测试可以验证代码修改的正确性,保护新增或更改的代码;解依赖可以去除遗留代码的不合理依赖,使单元测试的编写成为可能——归根结底还是写单元测试。这样一来本书的主旨浓缩到了三个英文字母:TDD。至于用到的种种方法手段,无不是为了达到让单元测试覆盖所有改动这一目的。

我的日常工作其实很大程度是working with legacy code,上古时代,宇宙洪荒,第一代程序员们为了从无到有做出一个系统,筚路蓝缕披星戴月,终于完成了,收获了英雄般的待遇。但是,斗转星移,隔代的程序员们接手了代码,听了新需求,却发现加功能、改特性需要小心翼翼,如履薄冰:文档没有,注释没有或者看不懂,代码纷繁复杂,难以理解而不敢下手——当然终究还是要下手的,好吧,just edit then pray,希望上帝保佑,没有bug——但bug还是大摇大摆地来访。于是修修补补,终于没什么问题了,交工了,松了一口气。转眼又是一代程序员,以上轮回继续……

这样的状况作为开发人员当然是不喜欢的,于是重构成了改善遗留代码结构的必然选择。下面讲讲我第一次对遗留代码动大手术的故事。

我进入公司并接手一个业务模块时,该模块有一个讨厌的操作页面:这个页面的功能是显示一张单据,并对这张单据做一些编辑操作,单据本身有一个header体和二级的detail体,由于不同用户希望看到的展现不同,单据明细的展现有两种方式:a.二级grid;b.上下分离的两个grid,对应系统里的两个文件。由于只是展现不同,很多功能以一式两份又略有不同的方式写在两个文件中。这样,每次需求变更,在文件一里做的改动需要copy到文件二的相应方法中去,这就造成了不少重复代码,况且由于我总是记不得要到另一个文件里修改,我被QA报过很多bug。此外,由于允许用户在这两种模式之间切换,我还要考虑新做的功能如何支持切换,这同样容易产生bug。

在忍受了一段时间这样的工作之后,我下决心改变这一切,首先开刀的是那些高度重复的代码:其实很多代码只是循环遍历所有明细的方法不同,同样的数据,一个遍历第二级grid的所有行,一个遍历下面一个grid的所有行,那么我只要对“循环遍历”方法做接口提取,对循环体内完全一致的操作代码做方法提取,重复代码就被合并成一份了。这一改动立竿见影,重复代码被删除了两千行,维护代价和QA的测试代价也折半了。其次是那些在两种展现下都会用到,但确实实现没有重合部分的操作,我对这些操作也做接口提取,这样的改动对旧功能没有影响,但改善了代码可读性,并且和上一条改动一起,保证了今后新特性的增加都在公共代码部分:如果操作一致最好,在公共部分写实现代码;如果在两种展现下操作不同,在公共部分写接口,各自实现。在这两步重构中我也感受到——设计是自己浮现出来的。最后,通过和BA讨论用户实际使用习惯,讨价还价,成功地砍掉了“在两种模式之间切换”的功能。自从这次重构到现在,在这个页面添加或修改相关功能都没有什么大问题。

下面用简化的UML说明一下这次重构,原来的大致结构:

新的结构:

读完《修改代码的艺术》,反思我当时做的这次重构,还是不够科学,有改进余地的。最大的问题,是我做的是草稿式重构:没有单元测试的保护,直接上去改,改过之后也没有单元测试的验证。当时不写测试自然是有一些借口的:遗留代码没有写单元测试的先例;客户端代码只有一些静态基础类写有单元测试;页面操作严重依赖界面的输入和展现,难以解依赖。但这种重构难免带来一些bug,于是也承受了来自QA不小的压力。现在看来,如果当时用TDD的思想,先写单元测试再做重构,后人改动起来会更得心应手,单元测试本身也是不错的文档(现在这个模块已经移交别人了),而这就需要解除大量对界面操作的依赖,写一些伪对象来替换依赖部分。另外一点:对单一职责原则应用得还不够,现在看来,有些方法虽然公用,但仍然是大段的项目列表式方法,可以进一步细分。

最近有一位同事刚刚做完一个Refactor,这个重构的效果很显著:把某一份月度报表的产生时间从9个小时左右减少到了15分钟以内。他给我们分享了他的重构过程:写单元测试验证遗留代码、重构、用单元测试验证重构后代码、修改bug、完成并交付QA测试。这次重构减少了存储过程的代码量,更加结构化,消除了大量难以在编译期发现问题的拼SQL代码,将主流程置于单元测试保护之下,新写了许多注释,并发现了旧版本的两个bug。

Work with legacy code确实不是件容易的事,重构也不是一件轻松的事,为了把重构做得更可靠,千方百计解依赖、TDD是值得的,这会帮助我们改善既有设计,让更好的设计通过重构,自己浮现出来。当然,如果抱有的是”Après moi, le déluge”的想法,那么,洪水早晚会来。

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 Unported许可协议进行许可。

分类: 读书 标签: , , ,