最佳注释——Programmers(40)

载于《程序员》杂志2012年第9期。

改编自What is the best comment in source code you have ever encountered?

感谢《IT外刊评论》的翻译 http://www.aqee.net/10-best-code-comments/

这个系列的漫画讲述程序员——这种神秘人类的囧事,故事多来源于我身边的程序员朋友,且以互联网开发背景为主。

如果你有什么可乐的关于程序员的故事、对话、代码,愿意通过漫画的形式分享,请给我发邮件。

高危工种——Programmers(39)

载于《程序员》杂志2012年第8期。

这个系列的漫画讲述程序员——这种神秘人类的囧事,故事多来源于我身边的程序员朋友,且以互联网开发背景为主。

如果你有什么可乐的关于程序员的故事、对话、代码,愿意通过漫画的形式分享,请给我发邮件。

有效流程——Programmers(38)

载于《程序员》杂志2012年第7期。

改编自It Takes 6 Days to Change 1 Line of Code

这个系列的漫画讲述程序员——这种神秘人类的囧事,故事多来源于我身边的程序员朋友,且以互联网开发背景为主。

如果你有什么可乐的关于程序员的故事、对话、代码,愿意通过漫画的形式分享,请给我发邮件。

产品的价值

最近一直在想,我能够做什么,怎样才能发挥自己最大的价值。 最近也一直在对比,开发的价值,产品的价值,团队的价值,我的价值。

一直以来的开发经验,一直让自己看不起产品这份工作。 今天看着一片一片死在自己塔防下面的小妖,突然顿悟了。 如果把开发比做小妖 , 3个开发,就是这样一个冲刺的小团队:

如果我作为一个开发,加入其中,那就是4个小妖。

生命:280 攻击:20-40 移动:中速

这样一个冲刺小队,初级箭塔倒是能闯过去,碰上点高级货,必定全灭了。

那换一个身份呢?假如产品是具备“鼓舞”技能的辅助职业: 如果我作为一个产品,加入这个冲刺团队,鼓舞同伴,给他们加攻击、护甲、速度和生命。

还有谁能挡住我们的冲刺?

相关日志: 超越期望 产品要学会拒绝 Mac下的浏览器们 […]

C++11(及现代C++风格)和快速迭代式开发

过去的一年我在微软亚洲研究院做输入法,我们的产品叫“英库拼音输入法” (下载Beta版),如果你用过“英库词典”(现已更名为必应词典),应该知道“英库”这个名字(实际上我们的核心开发团队也有很大一部分来源于英库团队的老成员)。整个项目是微软亚洲研究院的自然语言处理组、互联网搜索与挖掘组和我们创新工程中心,以及微软中国Office商务软件部(MODC)多组合作的结果。至于我们的输入法有哪些创新的feature,以及这些feature背后的种种有趣故事… 本文暂不讨论。虽然整个过程中我也参与了很多feature的设想和设计,但90%的职责还是开发,所以作为client端的核心开发人员之一,我想跟大家分享这一年来在项目中全面使用C++11以及现代C++风格(Elements of Modern C++ Style)来做开发的种种经验。

我们用的开发环境是VS2010 SP1,该版本已经支持了相当多的C++11的特性:lambda表达式,右值引用,auto类型推导,static_assert,decltype,nullptr,exception_ptr等等。C++曾经饱受“学院派”标签的困扰,不过这个标签着实被贴得挺冤,C++11的新feature没有一个是从学院派角度出发来设计的,以上提到的所有这些feature都在我们的项目中得到了适得其所的运用,并且带来了很大的收益。尤其是lambda表达式。

说起来我跟C++也算是有相当大的缘分,03年还在读本科的时候,第一篇发表在程序员上面的文章就是Boost库的源码剖析,那个时候Boost库在国内还真是相当的阳春白雪,至今已经快十年了,Boost库如今已经是写C++代码不可或缺的库,被誉为“准标准库”,C++的TR1基本就脱胎于Boost的一系列子库,而TR2同样也大量从Boost库中取材。之后有好几年,我在CSDN上的博客几乎纯粹是C++的前沿技术文章,包括从06年就开始写的“C++0x漫谈”系列。(后来写技术文章写得少了,也就把博客从CSDN博客独立了出来,便是现在的mindhacks.cn)。自从独立博客了之后我就没有再写过C++相关的文章(不过仍然一直对C++的发展保持了一定的关注),一方面我喜欢关注前沿的进展,写完了Boost源码剖析系列和C++0x漫谈系列之后我觉得这一波的前沿进展从大方面来说也都写得差不多了,所以不想再费时间。另一方面的原因也是我虽然对C++关注较深,但实践经验却始终绝大多数都是“替代经验”,即从别人那儿看来的,并非自己第一手的。而过去一年来深度参与的英库输入法项目弥补了这个缺憾,所以我就决定重新开始写一点C++11的实践经验。算是对努力一年的项目发布第一版的一个小结。

09年入职微软亚洲研究院之后,前两年跟C++基本没沾边,第一个项目倒是用C++的,不过是工作在既有代码基上,时间也相对较短。第二个项目为Bing Image Search用javascript写前端,第三个项目则给Visual Studio 2012写Code Clone Detection,用C#和WPF。直到一年前英库输入法这个项目,是我在研究院的第四个项目了,也是最大的一个,一年来我很开心,因为又回到了C++。

这个项目我们从零开始,,而client端的核心开发人员也很紧凑,只有3个。这个项目有很多特殊之处,对高效的快速迭代开发提出了很大的挑战(研究院所倡导的“以实践为驱动的研究(Deployment-Driven-Research)”要求我们迅速对用户的需求作出响应):

长期时间压力:从零开始到发布,只有一年时间,我们既要在主要feature上能和主流的输入法相较,还需要实现我们自己独特的创新feature,从而能够和其他输入法产品区分开来。 短期时间压力:输入法在中国是一个非常成熟的市场,谁也没法保证闷着头搞一年搞出来的东西就一炮而红,所以我们从第一天起就进入demo驱动的准迭代式开发,整个过程中必须不断有阶段性输出,抬头看路好过闷头走路。但工程师最头疼的二难问题之一恐怕就是短期与长远的矛盾:要持续不断出短期的成果,就必须经常在某些地方赶工,赶工的结果则可能导致在设计和代码质量上面的折衷,这些折衷也被称为Technical Debt(技术债)。没有任何项目没有技术债,只是多少,以及偿还的方式的区别。我们的目的不是消除技术债,而是通过不断持续改进代码质量,阻止技术债的滚雪球式积累。 C++是一门不容易用好的语言:错误的使用方式会给代码基的质量带来很大的损伤。而C++的误用方式又特别多。 输入法是个很特殊的应用程序,在Windows下面,输入法是加载到目标进程空间当中的dll,所以,输入法对质量的要求极高,别的软件出了错误崩溃了大不了重启一下,而输入法如果崩溃就会造成整个目标进程崩溃,如果用户的文档未保存就可能会丢失宝贵的用户数据,所以输入法最容不得崩溃。可是只要是人写的代码怎么可能没有bug呢?所以关键在于如何减少bug及其产生的影响和如何能尽快响应并修复bug。所以我们的做法分为三步:1). 使用现代C++技术减少bug产生的机会。2). 即便bug产生了,也尽量减少对用户产生的影响。3). 完善的bug汇报系统使开发人员能够第一时间拥有足够的信息修复bug。

至于为什么要用C++而不是C呢?对于我们来说理由很现实:时间紧任务重,用C的话需要发明的轮子太多了,C++的抽象层次高,代码量少,bug相对就会更少,现代C++的内存管理完全自动,以至于从头到尾我根本不记得曾遇到过什么内存管理相关的bug,现代C++的错误处理机制也非常适合快速开发的同时不用担心bug乱飞,另外有了C++11的强大支持更是如虎添翼,当然,这一切都必须建立在核心团队必须善用C++的大前提上,而这对于我们这个紧凑的小团队来说这不是问题,因为大家都有较好的C++背景,没有陡峭的学习曲线要爬。(至于C++在大规模团队中各人对C++的掌握良莠不齐的情况下所带来的一些包袱本文也不作讨论,呵呵,语言之争别找我。)

下面就说说我们在这个项目中是如何使用C++11和现代C++风格来开发的,什么是现代C++风格以及它给我们开发带来的好处。

资源管理

说到Native Languages就不得不说资源管理,因为资源管理向来都是Native Languages的一个大问题,其中内存管理又是资源当中的一个大问题,由于堆内存需要手动分配和释放,所以必须确保内存得到释放,对此一般原则是“谁分配谁负责释放”,但即便如此仍然还是经常会导致内存泄漏、野指针等等问题。更不用说这种手动释放给API设计带来的问题(例如Win32 API WideCharToMultiByte就是一个典型的例子,你需要提供一个缓冲区给它来接收编码转换的结果,但是你又不能确保你的缓冲区足够大,所以就出现了一个两次调用的pattern,第一次给个NULL缓冲区,于是API返回的是所需的缓冲区的大小,根据这个大小分配缓冲区之后再第二次调用它,别提多别扭了)。

托管语言们为了解决这个问题引入了GC,其理念是“内存管理太重要了,不能交给程序员来做”。但GC对于Native开发也常常有它自己的问题。而且另一方面Native界也常常诟病GC,说“内存管理太重要了,不能交给机器来做”。

C++也许是第一个提供了完美折衷的语言(不过这个机制直到C++11的出现才真正达到了易用的程度),即:既不是完全交给机器来做,也不是完全交给程序员来做,而是程序员先在代码中指定怎么做,至于什么时候做,如何确保一定会得到执行,则交由编译器来确定。

首先是C++98提供了语言机制:对象在超出作用域的时候其析构函数会被自动调用。接着,Bjarne Stroustrup在TC++PL里面定义了RAII(Resource Acquisition is Initialization)范式(即:对象构造的时候其所需的资源便应该在构造函数中初始化,而对象析构的时候则释放这些资源)。RAII意味着我们应该用类来封装和管理资源,对于内存管理而言,Boost第一个实现了工业强度的智能指针,如今智能指针(shared_ptr和unique_ptr)已经是C++11的一部分,简单来说有了智能指针意味着你的C++代码基中几乎就不应该出现delete了。

不过,RAII范式虽然很好,但还不足够易用,很多时候我们并不想为了一个CloseHandle, ReleaseDC, GlobalUnlock等等而去大张旗鼓地另写一个类出来,所以这些时候我们往往会因为怕麻烦而直接手动去调这些释放函数,手动调的一个坏处是,如果在资源申请和释放之间发生了异常,那么释放将不会发生,此外,手动释放需要在函数的所有出口处都去调释放函数,万一某天有人修改了代码,加了一处return,而在return之前忘了调释放函数,资源就泄露了。理想情况下我们希望语言能够支持这样的范式:

void foo() { HANDLE h = CreateFile(…); ON_SCOPE_EXIT { CloseHandle(h); } […]

“曾经我们想要的是飞行汽车,而不是短短的140个字符。”

-在Peter Thiel创办的风投企业Founders Fund网页上有一篇《宣言》,“曾经我们想要的是飞行汽车,而不是短短的140个字符(指微博)。”他认为,是想象力的丧失导致了美国制造业崩溃、工资停滞不涨到金融业的过度膨胀等诸多问题。他说,“这些改变令人眼花缭乱,但是却不是进步。”

非常欣赏他的观点,这篇文章来自于:http://laoyaoba.com/ss6/html/77/n-336477.html (1192)

为什么我反对纯算法面试题

算法面试可能是微软搞出来的面试方法,现在很多公司都在效仿,而且我们的程序员也乐于解算法题,我个人以为,这是应试教育的毒瘤!我在《再谈“我是怎么招程序员”》中比较保守地说过,“问难的算法题并没有错,错的很多面试官只是在肤浅甚至错误地理解着面试算法题的目的。”,今天,我想加强一下这个观点——我反对纯算法题面试!(注意,我说的是纯算法题)

图片源Wikipedia(点击图片查看词条)

我再次引用我以前的一个观点——

能解算法题并不意味着这个人就有能力就能在工作中解决问题,你可以想想,小学奥数题可能比这些题更难,但并不意味着那些奥数能手就能解决实际问题。

好了,让我们来看一个示例(这个示例是昨天在微博上的一个讨论),这个题是——“找出无序数组中第2大的数”,几乎所有的人都用了O(n)的算法,我相信对于我们这些应试教育出来的人来说,不用排序用O(n)算法是很正常的事,连我都不由自主地认为O(n)算法是这个题的标准答案。我们太习惯于标准答案了,这是我国教育最悲哀的地方。(广义的洗脑就是让你的意识依赖于某个标准答案,然后通过给你标准答案让你不会思考而控制你)

功能性需求分析

试想,如果我们在实际工作中得到这样一个题 我们会怎么做?我一定会分析这个需求,因为我害怕需求未来会改变,今天你叫我找一个第2大的数,明天你找我找一个第4大的数,后天叫我找一个第100大的数,我不搞死了。需求变化是很正常的事。分析完这个需求后,我会很自然地去写找第K大数的算法——难度一下子就增大了。

很多人会以为找第K大的需求是一种“过早扩展”的思路,不是这样的,我相信我们在实际编码中写过太多这样的程序了,你一定不会设计出这样的函数接口—— Find2ndMaxNum(int* array, int len),就好像你不会设计出 DestroyBaghdad(); 这样的接口,而是设计一个DestoryCity( City& ); 的接口,而把Baghdad当成参数传进去!所以,你应该是声明一个叫FindKthMaxNum(int* array, int len, int kth),把2当成参数传进去。这是最基本的编程方法,用数学的话来说,叫代数!最简单的需求分析方法就是把需求翻译成函数名,然后看看是这个接口不是很二?!

(注:不要纠结于FindMaxNum()或FindMinNum(),因为这两个函数名的业务意义很清楚了,不像Find2ndMaxNum()那么二)

非功能性需求分析

性能之类的东西从来都是非功能性需求,对于算法题,我们太喜欢研究算法题的空间和时间复杂度了。我们希望做到空间和时间双丰收,这是算法学术界的风格。所以,习惯于标准答案的我们已经失去思考的能力,只会机械地思考算法之内的性能,而忽略了算法之外的性能。

如果题目是——“从无序数组中找到第K个最大的数”,那么,我们一定会去思考用O(n)的线性算法找出第K个数。事实上,也有线性算法——STL中可以用nth_element求得类似的第n大的数,其利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:1)Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;2) Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n)。

搞学术的nuts们到了这一步一定会欢呼胜利!但是他们哪里能想得到性能的需求分析也是来源自业务的!

我们一说性能,基本上是个人都会问,请求量有多大?如果我们的FindKthMaxNum()的请求量是m次,那么你的这个每次都要O(n)复杂度的算法得到的效果就是O(n*m),这一点,是书呆子式的学院派人永远想不到的。因为应试教育让我们不会从实际思考了。

工程式的解法

根据上面的需求分析,有软件工程经验的人的解法通常会这样:

1)把数组排序,从大到小。

2)于是你要第k大的数,就直接访问 array[k]。

排序只需要一次,O(n*log(n)),然后,接下来的m次对FindKthMaxNum()的调用全是O(1)的,整体复杂度反而成了线性的。

其实,上述的还不是工程式的最好的解法,因为,在业务中,那数组中的数据可能会是会变化的,所以,如果是用数组排序的话,有数据的改动会让我重新排序,这个太耗性能了,如果实际情况中会有很多的插入或删除操作,那么可以考虑使用B+树。

工程式的解法有以下特点:

1)很方便扩展,因为数据排好序了,你还可以方便地支持各种需求,如从第k1大到k2大的数据(那些学院派写出来的代码在拿到这个需求时又开始挠头苦想了)

2)规整的数据会简化整体的算法复杂度,从而整体性能会更好。(公欲善其事,必先利其器)

3)代码变得清晰,易懂,易维护!(学院派的和STL一样的近似O(n)复杂度的算法没人敢动)

争论

你可能会和我有以下争论,

如果程序员做这个算法题用排序的方式,他一定不会像你想那么多。是的,你说得对。但是我想说,很多时候,我们直觉地思考,恰恰是正确的路。因为“排序”这个思路符合人类大脑处理问题的方式,而使用学院派的方式是反大脑直觉的。反大脑直觉的,通常意味着晦涩难懂,维护成本上升。 就是一道面试题,我就是想测试一下你的算法技能,这也扯太多了。没问题,不过,我们要清楚我们是在招什么人?是一个只会写算法的人,还是一个会做软件的人?这个只有你自己最清楚。 这个算法题太容易诱导到学院派的思路了。是的这道“找出第K大的数”,其实可以变换为更为业务一点的题目——“我要和别的商户竞价,我想排在所有竞争对手报价的第K名,请写一个程序,我输入K,和一个商品名,系统告诉我应该订多少价?(商家的所有商品的报价在一数组中)”——业务分析,整体性能,算法,数据结构,增加需求让应聘者重构,这一个问题就全考了。 你是不是在说算法不重要,不用学?千万别这样理解我,搞得好像如果面试不面,我就可以不学。算法很重要,算法题能锻炼我们的思维,而且也有很多实际用处。我这篇文章不是让大家不要去学算法,这是完全错误的,我是让大家带着业务问题去使用算法。问你业务问题,一样会问到算法题上来。 小结

看过这上面的分析,我相信你明白我为什么反对纯算法面试题了。原因就是纯算法的面试题根本不能反应一个程序的综合素质!

那么,在面试中,我们应该要考量程序员的那些综合素质呢?我以为有下面这些东西:

会不会做需求分析?怎么理解问题的? 解决问题的思路是什么?想法如何? 会不会对基础的算法和数据结构灵活运用? […]

Egg Box,你的鸡蛋保护“专家”

鸡蛋在从菜市场或超市到达自家冰箱这段旅程中,主人总是倍加小心,生怕鸡蛋被撞到“亲吻”地面。还在 Moholy-Nagy 大学(匈牙利莫霍利·纳吉艺术与设计大学)读硕士学位的匈牙利设计师 Otília Andrea Erdélyi 看样子也遇到了类似的麻烦,于是她给出了自己的鸡蛋保护方案——Egg box。

结构简单的 Egg box 由微波纸板做成,只需一款纸板并将其剪成几个小椭圆模型即可。这样,一来使用尽可能少的材料来保护环境,二来鸡蛋在椭圆状内也能给鸡蛋充足的保护。

责任编辑:36

您可能也喜欢: Eggs Everywhere,随时随地热鸡蛋! 怎么表白更有趣?鸡蛋上面用心思 圆润之美:EGG MOUSE蛋形鼠标 切肤之痛,德国保护动物权利海报赏 没有最贵只有更贵,iPad金典款保护壳出现 无觅 […]

网站防止CC攻击的方法

  CC攻击(Challenge Collapsar)是DDOS(分布式拒绝服务)的一种,也是一种常见的网站攻击方法,攻击者通过代理服务器或者肉鸡向向受害主机不停地发大量数据包,造成对方服务器资源耗尽,一直到宕机崩溃。

  CC攻击的攻击技术含量低,利用工具和一些IP代理,一个初、中级的电脑水平的用户就能够实施攻击。不过,如果了解了CC攻击的原理,那就不难针对CC攻击实施一些有效的防范措施。

  通常防止CC攻击的方法有几种,一个是通过防火墙,另外一些网络公司也提供了一些防火墙服务,例如XX网站卫士和XX宝,还有一种方法是自己写程序预防,昨天网站遇到CC攻击,这也让我尝试了一下各种防止CC攻击方法的有效性。

  一开始我想使用某某网站卫士来预防攻击,从界面上看,似乎是防止了大量的CC攻击,但登录网站后发现,流量依旧异常,攻击还是依旧,看起来这个网站卫士的效果并没有达到。

  从原理上看,基本上所有的防火墙都会检测并发的TCP/IP连接数目,超过一定数目一定频率就会被认为是Connection-Flood。但如果IP的数量足够大,使得单个IP的连接数较少,那么防火墙未必能阻止CC攻击。

  不仅如此,我还发现,启用了某某网站卫士之后,反而更容易被CC攻击,因为这个网站卫士并不能过滤掉CC攻击,攻击的IP经过其加速后,更换成为这个网站卫士的IP,在网站服务器端显示的IP都是相同的,导致服务器端无法过滤这些IP。

  实际上,不使用网站卫士类的服务,直接通过分析网站日志,还是很容易分辨出哪个IP是CC攻击的,因为CC攻击毕竟是通过程序来抓取网页,与普通浏览者的特性区别还是很大的,例如普通浏览者访问一个网页,必定会连续抓取网页的HTML文件、CSS文件、JS文件和图片等一系列相关文件,而CC攻击者仅仅只会抓取一个URL地址的文件,不会抓取其他类型的文件,其User Agent也大部分和普通浏览者不同,这就可以在服务器上很容易分辨出哪些访问者是CC攻击了,既然可以判断出攻击者的IP,那么预防措施就很简单,只需要批量将这些IP屏蔽,即可达到防范CC攻击的目的。

  最终,我花了半个小时写了一段小程序,运行之后自动屏蔽了数百个IP,网站才算正常,从而证明,防火墙对于CC攻击的防御并不有效,最有效的方法还是在服务器端通过程序自动屏蔽来预防。

  看来CC攻击的门槛还真低啊,搞个几百个代理或者肉鸡就能攻击别人了,其成本非常低,但效果比较明显,如果攻击者流量巨大的话,通过耗费带宽资源的方式都可以进行攻击。但是,CC攻击也有明显的技术缺陷,就是攻击者的IP并不是海量的,通常就是几百数千的级别,并且是真实访问了网站页面,这就使得网站可以通过程序过滤的方式,轻松获取到这些攻击者IP,批量进行屏蔽,那么这种CC攻击就会得到预防。

评论《网站防止CC攻击的方法》的内容…

相关文章: Windows网站服务器安全设置 Jeff向自己的网站StackOverflow说再见 IIS的没落 Windows IIS日志文件分析程序 网站备案被注销引发网站生存危机

微博:新浪微博 – 腾讯微博 – 论坛 月光博客投稿信箱:williamlong.info(at)gmail.comCreated by William Long www.williamlong.info

[…]

关于ICFP 2012编程赛的那点事情

ICFP Programming Contest 2012的结果出来了。不出所料,第1轮惨遭淘汰。

不过总算是混到了整整1200分,居然还不是最垫底,哥真是太感动了

221支参赛队提交了最终程序,拿到正分值的是194支,真的有人敢拿零分和负分的说(汗)

(・U・)

今年比赛的题目,简单地概括就是:操纵机器人在地底环境采矿。输入一个完全已知的由ASCII字符组成的地图(可能是标准的m×n矩形也可能是不规则形状什么的),地图上有许多叫做“lambda”的矿物(当然位置是已知的),机器人的初始位置也是给定在地图上的,你的任务就是要给出一串机器人行动的指令序列,使机器人依照这个指令序列所走的路径能够采完地图上所有矿点、并且最终抵达地图上的“升降井”位置以顺利完成整个采矿过程(在最理想的情况下)。机器人每采集一个矿点加25分,中途主动放弃的话每个采集到的矿点可以再额外增加25分,最终完成全部开采(抵达升降井)则每个矿点可以额外增加50分,当然作为代价,每执行一步指令要相应地扣除1分。

当然啦,问题不可能设得这么没难度,才不会拿简简单单的动态规划来作比赛题呢。所以地图上的每个格子会有不同的地形,比如墙壁,比如泥土,比如石头,机器人的行动会造成各种后效性。泥土是可以被机器人钻开的,石头是可以被机器人推动的,石头下面如果没有别的物体支撑会以固定速率向下坠落,石头落到别的石头上因为无法支撑会滚向旁边,然后,如果机器人刚好在石头落下的时候处于下面格子的位置,就会被砸坏掉,采矿被迫终止并且得不到额外的加分。

当然啦,即使这样问题还是太简单了,命题委员会(今年是University of St Andrews)于是充分发挥了一把苏格兰人的幽默感(黑色幽默?):由于最近在苏格兰肆虐的洪水,在比赛开始后不久,我们的地下矿藏被水淹了!水位将以某给定的速率从地图下方开始上升,机器人在水下连续活动的时间由一个防水参数给定,超过这个时间仍然处于水下的机器人将会报废。

当然啦,命题组还是觉得问题太简单了,于是在水灾的第二天,他们在地图里追加了一种叫做“蹦床”的元素。与其说是蹦床,不如说成是传送门更合适,当机器人走到一个蹦床的位置,它会被瞬间转移到地图上的另一个目标点。(宏观物质传输这不科学啊喂……)

当然啦,命题组还是觉得……问题太简单了。于是接下来,地下矿藏里长出了一种奇葩的生物,叫做“Wadler的胡须”。这种生活在地底的诡异植物会以某个固定的速率疯长,每过一段时间就会向四周蔓延一格,这些被胡须占据的格子阻碍了机器人的行动,因此机器人必须使用有限的“Hutton的剃刀”来进行除草。一把剃刀只能使用一次,不过好在地图上也是分布有一些剃刀可以去拿的。(是不是有点像魔塔之类的游戏)

一点小插曲,关于胡须和剃刀这两个名字:Philip Wadler,Univ of Edinburgh的教授,是Haskell语言和XQuery的主要设计者之一;Graham Hutton,Univ of Nottingham的教授,ICFP(The International Conference on Functional Programming,国际函数式编程会议)组委会的主席,那本《Programming in Haskell》的作者。

Hutton的剃刀(Hutton's Razor)的概念(和名称)仿照自奥卡姆剃刀原理(Occam's Razor):“用较少的东西,同样可以做好的事情”。假定存在这么一种极小化的编程语言,它由两种元素构成:

常量,包括从0开始的全部自然数;

一个“+”运算符,从而我们可以写出(37 + 5) + 15 + (42 + 0)这样的语句。

data Exp = Const Int | Add Exp Exp eval :: Exp […]

Category

Archives