﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-巢穴-随笔分类-个人随笔</title><link>http://www.cppblog.com/ccl0326/category/11335.html</link><description>about:blank</description><language>zh-cn</language><lastBuildDate>Fri, 15 Apr 2011 12:29:31 GMT</lastBuildDate><pubDate>Fri, 15 Apr 2011 12:29:31 GMT</pubDate><ttl>60</ttl><item><title>作为一个Programer，你究竟需要什么样的环境?</title><link>http://www.cppblog.com/ccl0326/archive/2011/04/14/144219.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Thu, 14 Apr 2011 07:53:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2011/04/14/144219.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/144219.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2011/04/14/144219.html#Feedback</comments><slash:comments>15</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/144219.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/144219.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 离职2天了,今天上午参加了一家公司的一面,是hr面的。她问我为什么在上家公司只呆了两个月。另外她认为我曾经带过的两家公司都还算不错,所以她很奇怪我在一年的时间里换了两家公司。其实，事实上是3家，有一家我只呆了11天，还没来得及说。<br>&nbsp;&nbsp;&nbsp; 所以我其实是一个一年离职3次男。<br>&nbsp;&nbsp;&nbsp; 下午收到了这家公司的拒信。好家伙，这是我第二次连技术面都没混到。上一次是我大三那年第一次出去面试。这一下，对我真是当头一棒。要知道，这家公司是我的保底公司。结果我的保底没了。我明白，其实是我飘了。所以其实蛮感谢这家公司的。在我还没有飘的很高之前把我拍了下来，让我摔的还不至于太惨，让我可以冷静下来去思考一些问题。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 下面，我想列出一些我心中所想与大家分享，也希望得到大家的分享。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 第一个问题是关于忠诚<br>&nbsp;&nbsp;&nbsp;&nbsp; 上文已经说了，我是一个一年离职3次男。对于公司，我十分抱歉。但凭良心来说，对于每一次离职我都把自己所有的工作妥善并且负责任的做了交接。我也不想对自己所做的一些事情做什么解释。我也深知一年离职3次这种工作状态并不是一个工作者应该有的。所幸的是我本来就计划在毕业后结束这种不正常的状态，因此接下来要找的工作我将会长期的做下午。不知道有没有朋友曾经有过跟我一样的状态或经历，可否说说你是如何度过那段时期的呢？<br>&nbsp;&nbsp;&nbsp;&nbsp; 然后就引出了第二个问题，试用期<br>&nbsp;&nbsp;&nbsp;&nbsp; 一直以来，在我的理解中，试用期应该是员工与公司双方的一个试用。因为在进入一家公司之前，你可以通过外界的消息以及公司内部员工的消息来了解公司，但我觉得真正能够影响到你的还是你的亲身经历。所以在我看来试用期就是提供给双方这么一个机会。面试的时候，hr也问了一个这个相关的问题，她问我是否能够确认这家公司就是我想要的那种环境。我的回答是不能。朋友们，你们又会做出如何的回答？<br>&nbsp;&nbsp;&nbsp;&nbsp; 再接下来就是环境的问题了，其实这可能也是我被拒的最主要原因<br>&nbsp;&nbsp;&nbsp;&nbsp; hr问,你的这几次入职，发现不合适，然后离职之后，你有没有想过自身的原因？你最终所想要的环境究竟是什么？其实这个问题我真想过，但是遗憾的是我并没有一个清晰的概括，我一直强调的一个词是宽松，这个词太宽泛了，而且对于不同的公司不同的人的理解也是不同。我前几天曾经恶作剧般的把自己的简历名称改为了，我是一个梦想10点上班男，我是一个梦想可以随时打游戏男，如果你能够理解，随时联系我。承认，我也知道我的这种梦想幼稚的可笑，我也从未想过哪家公司能够真的去满足这样宽松的条件。但这依然是我的梦想，我仍然渴望能够有一家公司去努力的提供这种宽松，让我从心底感受到自由。那么，朋友，你又如何看待一个Programer所需要的环境呢？</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 当我没了保底,才突然发现这个世界上没有什么事情是可以保证的,倘若有的话，那就只有你信念。我将吸取这次的经验和教训，踏踏实实的继续去寻找我的梦。</p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/144219.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2011-04-14 15:53 <a href="http://www.cppblog.com/ccl0326/archive/2011/04/14/144219.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lisp的本质(The Nature of Lisp)(转)</title><link>http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 15 Feb 2011 01:29:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/140071.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/140071.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/140071.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Lisp的本质(The Nature of Lisp)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 作者 Slava Akhmechet<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 译者 Alec Jang<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 出处: http://www.defmacro.org/ramblings/lisp.html<br><br><br>简介<br><br>最初在web的某些角落偶然看到有人赞美Lisp时, 我那时已经是一个颇有经验的程序员。<br>在我的履历上, 掌握的语言范围相当广泛, 象C++, Java, C#主流语言等等都不在话下, <br>我觉得我差不多知道所有的有关编程语言的事情。对待编程语言的问题上, 我觉得自己不<br>太会遇到什么大问题。其实我大错特错了。<br><br>我试着学了一下Lisp, 结果马上就撞了墙。我被那些范例代码吓坏了。我想很多初次接触<br>Lisp语言的人, 一定也有过类似的感受。Lisp的语法太次了。一个语言的发明人, 居然不<br>肯用心弄出一套漂亮的语法, 那谁还会愿意学它。反正, 我是确确实实被那些难看的无数<br>的括号搞蒙了。<br><br>回过神来之后, 我和Lisp社区的那伙人交谈, 诉说我的沮丧心情。结果, 立马就有一大套<br>理论砸过来, 这套理论在Lisp社区处处可见, 几成惯例。比如说: Lisp的括号只是表面现<br>象; Lisp的代码和数据的表达方式没有差别, 而且比XML语法高明许多, 所以有无穷的好<br>处; Lisp有强大无比的元语言能力, 程序员可以写出自我维护的代码; Lisp可以创造出针<br>对特定应用的语言子集; Lisp的运行时和编译时没有明确的分界; 等等, 等等, 等等。这<br>么长的赞美词虽然看起来相当动人, 不过对我毫无意义。没人能给我演示这些东西是如何<br>应用的, 因为这些东西一般来说只有在大型系统才会用到。我争辩说, 这些东西传统语言<br>一样办得到。在和别人争论了数个小时之后, 我最终还是放弃了学Lisp的念头。为什么要<br>花费几个月的时间学习语法这么难看的语言呢? 这种语言的概念这么晦涩, 又没什么好懂<br>的例子。也许这语言不是该我这样的人学的。<br><br>几个月来, 我承受着这些Lisp辩护士对我心灵的重压。我一度陷入了困惑。我认识一些绝<br>顶聪明的人, 我对他们相当尊敬, 我看到他们对Lisp的赞美达到了宗教般的高度。这就是<br>说, Lisp中一定有某种神秘的东西存在, 我不能忍受自己对此的无知, 好奇心和求知欲最<br>终不可遏制。我于是咬紧牙关埋头学习Lisp, 经过几个月的时间费劲心力的练习, 终于,<br>我看到了那无穷无尽的泉水的源头。在经过脱胎换骨的磨练之后, 在经过七重地狱的煎熬<br>之后, 终于, 我明白了。<br><br>顿悟在突然之间来临。曾经许多次, 我听到别人引用雷蒙德(译者注: 论文&lt;&lt;大教堂和市<br>集&gt;&gt;的作者, 著名的黑客社区理论家)的话: "Lisp语言值得学习。当你学会Lisp之后, 你<br>会拥有深刻的体验。就算你平常并不用Lisp编程, 它也会使你成为更加优秀的程序员"。<br>过去, 我根本不懂这些话的含义, 我也不相信这是真的。可是现在我懂得了。这些话蕴含<br>的真理远远超过我过去的想像。我内心体会到一种神圣的情感, 一瞬间的顿悟, 几乎使我<br>对电脑科学的观念发生了根本的改变。<br><br>顿悟的那一刻, 我成了Lisp的崇拜者。我体验到了宗教大师的感受: 一定要把我的知识传<br>布开来, 至少要让10个迷失的灵魂得到拯救。按照通常的办法, 我把这些道理(就是刚开<br>始别人砸过来的那一套, 不过现在我明白了真实的含义)告诉旁人。结果太令人失望了, <br>只有少数几个人在我坚持之下, 发生了一点兴趣, 但是仅仅看了几眼Lisp代码, 他们就退<br>却了。照这样的办法, 也许费数年功夫能造就了几个Lisp迷, 但我觉得这样的结果太差强<br>人意了, 我得想一套有更好的办法。<br><br>我深入地思考了这个问题。是不是Lisp有什么很艰深的东西, 令得那么多老练的程序员都<br>不能领会? 不是, 没有任何绝对艰深的东西。因为我能弄懂, 我相信其他人也一定能。那<br>么问题出在那里? 后来我终于找到了答案。我的结论就是, 凡是教人学高级概念, 一定要<br>从他已经懂得的东西开始。如果学习过程很有趣, 学习的内容表达得很恰当, 新概念就会<br>变得相当直观。这就是我的答案。所谓元编程, 所谓数据和代码形式合一, 所谓自修改代<br>码, 所谓特定应用的子语言, 所有这些概念根本就是同族概念, 彼此互为解释, 肯定越讲<br>越不明白。还是从实际的例子出发最有用。<br><br>我把我的想法说给Lisp程序员听, 遭到了他们的反对。"这些东西本身当然不可能用熟悉<br>的知识来解释, 这些概念完全与众不同, 你不可能在别人已有的经验里找到类似的东西",<br>可是我认为这些都是遁词。他们又反问我, "你自己为啥不试一下?" 好吧, 我来试一下。<br>这篇文章就是我尝试的结果。我要用熟悉的直观的方法来解释Lisp, 我希望有勇气的人读<br>完它, 拿杯饮料, 深呼吸一下, 准备被搞得晕头转向。来吧, 愿你获得大能。<br><br>重新审视XML<br><br>千里之行始于足下。让我们的第一步从XML开始。可是XML已经说得更多的了, 还能有什么<br>新意思可说呢? 有的。XML自身虽然谈谈不上有趣, 但是XML和Lisp的关系却相当有趣。<br>XML和Lisp的概念有着惊人的相似之处。XML是我们通向理解Lisp的桥梁。好吧, 我们且把<br>XML当作活马医。让我们拿好手杖, 对XML的无人涉及的荒原地带作一番探险。我们要从一<br>个全新的视角来考察这个题目。<br><br>表面上看, XML是一种标准化语法, 它以适合人阅读的格式来表达任意的层次化数据<br>(hirearchical data)。象任务表(to-do list), 网页, 病历, 汽车保险单, 配置文件等<br>等, 都是XML用武的地方。比如我们拿任务表做例子:<br><br>&lt;todo name="housework"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;item priority="high"&gt;Clean the house.&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp; &lt;item priority="medium"&gt;Wash the dishes.&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp; &lt;item priority="medium"&gt;Buy more soap.&lt;/item&gt;<br>&lt;/todo&gt;<br><br>解析这段数据时会发生什么情况? 解析之后的数据在内存中怎样表示? 显然, 用树来表示<br>这种层次化数据是很恰当的。说到底, XML这种比较容易阅读的数据格式, 就是树型结构<br>数据经过序列化之后的结果。任何可以用树来表示的数据, 同样可以用XML来表示, 反之<br>亦然。希望你能懂得这一点, 这对下面的内容极其重要。<br><br>再进一步。还有什么类型的数据也常用树来表示? 无疑列表(list)也是一种。上过编译课<br>吧? 还模模糊糊记得一点吧? 源代码在解析之后也是用树结构来存放的, 任何编译程序都<br>会把源代码解析成一棵抽象语法树, 这样的表示法很恰当, 因为源代码就是层次结构的: <br>函数包含参数和代码块, 代码快包含表达式和语句, 语句包含变量和运算符等等。<br><br>我们已经知道, 任何树结构都可以轻而易举的写成XML, 而任何代码都会解析成树, 因此,<br>任何代码都可以转换成XML, 对不对? 我举个例子, 请看下面的函数:<br><br>int add(int arg1, int arg2)<br>{<br>&nbsp;&nbsp;&nbsp; return arg1+arg2;<br>}<br><br>能把这个函数变成对等的XML格式吗? 当然可以。我们可以用很多种方式做到, 下面是其<br>中的一种, 十分简单:<br><br>&lt;define-function return-type="int" name="add"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;arguments&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;argument type="int"&gt;arg1&lt;/argument&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;argument type="int"&gt;arg2&lt;/argument&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/arguments&gt;<br>&nbsp;&nbsp;&nbsp; &lt;body&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;return&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;add value1="arg1" value2="arg2" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/return&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/body&gt;<br>&lt;/define&gt;<br><br>这个例子非常简单, 用哪种语言来做都不会有太大问题。我们可以把任何程序码转成XML,<br>也可以把XML转回到原来的程序码。我们可以写一个转换器, 把Java代码转成XML, 另一个<br>转换器把XML转回到Java。一样的道理, 这种手段也可以用来对付C++(这样做跟发疯差不<br>多么。可是的确有人在做, 看看GCC-XML(http://www.gccxml.org)就知道了)。进一步说,<br>凡是有相同语言特性而语法不同的语言, 都可以把XML当作中介来互相转换代码。实际上<br>几乎所有的主流语言都在一定程度上满足这个条件。我们可以把XML作为一种中间表示法,<br>在两种语言之间互相译码。比方说, 我们可以用Java2XML把Java代码转换成XML, 然后用<br>XML2CPP再把XML转换成C++代码, 运气好的话, 就是说, 如果我们小心避免使用那些C++不<br>具备的Java特性的话, 我们可以得到完好的C++程序。这办法怎么样, 漂亮吧?<br><br>这一切充分说明, 我们可以把XML作为源代码的通用存储方式, 其实我们能够产生一整套<br>使用统一语法的程序语言, 也能写出转换器, 把已有代码转换成XML格式。如果真的采纳<br>这种办法, 各种语言的编译器就用不着自己写语法解析了, 它们可以直接用XML的语法解<br>析来直接生成抽象语法树。<br><br>说到这里你该问了, 我们研究了这半天XML, 这和Lisp有什么关系呢? 毕竟XML出来之时,<br>Lisp早已经问世三十年了。这里我可以保证, 你马上就会明白。不过在继续解释之前, 我<br>们先做一个小小的思维练习。看一下上面这个XML版本的add函数例子, 你怎样给它分类, <br>是代码还是数据? 不用太多考虑都能明白, 把它分到哪一类都讲得通。它是XML, 它是标<br>准格式的数据。我们也知道, 它可以通过内存中的树结构来生成(GCC-XML做的就是这个事<br>情)。它保存在不可执行的文件中。我们可以把它解析成树节点, 然后做任意的转换。显<br>而易见, 它是数据。不过且慢, 虽然它语法有点陌生, 可它又确确实实是一个add函数, <br>对吧?&nbsp; 一旦经过解析, 它就可以拿给编译器编译执行。我们可以轻而易举写出这个XML <br>代码解释器, 并且直接运行它。或者我们也可以把它译成Java或C++代码, 然后再编译运<br>行。所以说, 它也是代码。<br><br>我们说到那里了? 不错, 我们已经发现了一个有趣的关键之点。过去被认为很难解的概念<br>已经非常直观非常简单的显现出来。代码也是数据, 并且从来都是如此。这听起来疯疯癫<br>癫的, 实际上却是必然之事。我许诺过会以一种全新的方式来解释Lisp, 我要重申我的许<br>诺。但是我们此刻还没有到预定的地方, 所以还是先继续上边的讨论。<br><br>刚才我说过, 我们可以非常简单地实现XML版的add函数解释器, 这听起来好像不过是说说<br>而已。谁真的会动手做一下呢? 未必有多少人会认真对待这件事。随便说说, 并不打算真<br>的去做, 这样的事情你在生活中恐怕也遇到吧。你明白我这样说的意思吧, 我说的有没有<br>打动你? 有哇, 那好, 我们继续。<br><br>重新审视Ant<br><br>我们现在已经来到了月亮背光的那一面, 先别忙着离开。再探索一下, 看看我们还能发现<br>什么东西。闭上眼睛, 想一想2000年冬天的那个雨夜, 一个名叫James Duncan Davidson <br>的杰出的程序员正在研究Tomcat的servlet容器。那时, 他正小心地保存好刚修改过的文<br>件, 然后执行make。结果冒出了一大堆错误, 显然有什么东西搞错了。经过仔细检查, 他<br>想, 难道是因为tab前面加了个空格而导致命令不能执行吗? 确实如此。老是这样, 他真<br>的受够了。乌云背后的月亮给了他启示, 他创建了一个新的Java项目, 然后写了一个简单<br>但是十分有用的工具, 这个工具巧妙地利用了Java属性文件中的信息来构造工程, 现在<br>James可以写makefile的替代品, 它能起到相同的作用, 而形式更加优美, 也不用担心有<br>makefile那样可恨的空格问题。这个工具能够自动解释属性文件, 然后采取正确的动作来<br>编译工程。真是简单而优美。<br><br>(作者注: 我不认识James, James也不认识我, 这个故事是根据网上关于Ant历史的帖子<br>虚构的)<br><br>使用Ant构造Tomcat之后几个月, 他越来越感到Java的属性文件不足以表达复杂的构造指<br>令。文件需要检出, 拷贝, 编译, 发到另外一台机器, 进行单元测试。要是出错, 就发邮<br>件给相关人员, 要是成功, 就继续在尽可能高层的卷(volumn)上执行构造。追踪到最后, <br>卷要回复到最初的水平上。确实, Java的属性文件不够用了, James需要更有弹性的解决<br>方案。他不想自己写解析器(因为他更希望有一个具有工业标准的方案)。XML看起来是个<br>不错的选择。他花了几天工夫把Ant移植到XML，于是，一件伟大的工具诞生了。<br><br>Ant是怎样工作的？原理非常简单。Ant把包含有构造命令的XML文件(算代码还是算数据, <br>你自己想吧)，交给一个Java程序来解析每一个元素，实际情况比我说的还要简单得多。<br>一个简单的XML指令会导致具有相同名字的Java类装入，并执行其代码。<br><br>&nbsp;&nbsp;&nbsp; &lt;copy todir="../new/dir"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="src_dir" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br><br>这段文字的含义是把源目录复制到目标目录，Ant会找到一个"copy"任务(实际上就是一个<br>Java类), 通过调用Java的方法来设置适当参数(todir和fileset)，然后执行这个任务。<br>Ant带有一组核心类, 可以由用户任意扩展, 只要遵守若干约定就可以。Ant找到这些类, <br>每当遇到XML元素有同样的名字, 就执行相应的代码。过程非常简单。Ant做到了我们前面<br>所说的东西: 它是一个语言解释器, 以XML作为语法, 把XML元素转译为适当的Java指令。<br>我们可以写一个"add"任务, 然后, 当发现XML中有add描述的时候, 就执行这个add任务。<br>由于Ant是非常流行的项目, 前面展示的策略就显得更为明智。毕竟, 这个工具每天差不<br>多有几千家公司在使用。<br><br>到目前为之, 我还没有说Ant在解析XML时所遇到困难。你也不用麻烦去它的网站上去找答<br>案了, 不会找到有价值的东西。至少对我们这个论题来说是如此。我们还是继续下一步讨<br>论吧。我们答案就在那里。<br><br>为什么是XML<br><br>有时候正确的决策并非完全出于深思熟虑。我不知道James选择XML是否出于深思熟虑。也<br>许仅仅是个下意识的决定。至少从James在Ant网站上发表的文章看起来, 他所说的理由完<br>全是似是而非。他的主要理由是移植性和扩展性, 在Ant案例上, 我看不出这两条有什么<br>帮助。使用XML而不是Java代码, 到底有什么好处? 为什么不写一组Java类, 提供api来满<br>足基本任务(拷贝目录, 编译等等), 然后在Java里直接调用这些代码? 这样做仍然可以保<br>证移植性, 扩展性也是毫无疑问的。而且语法也更为熟悉, 看着顺眼。那为什么要用 XML<br>呢? 有什么更好的理由吗?<br><br>有的。虽然我不确定James是否确实意识到了。在语义的可构造性方面, XML的弹性是Java<br>望尘莫及的。我不想用高深莫测的名词来吓唬你, 其中的道理相当简单, 解释起来并不费<br>很多功夫。好, 做好预备动作, 我们马上就要朝向顿悟的时刻做奋力一跃。<br><br>上面的那个copy的例子, 用Java代码怎样实现呢? 我们可以这样做:<br><br>&nbsp;&nbsp;&nbsp; CopyTask copy = new CopyTask();<br>&nbsp;&nbsp;&nbsp; Fileset fileset = new Fileset();<br><br>&nbsp;&nbsp;&nbsp; fileset.setDir("src_dir");<br>&nbsp;&nbsp;&nbsp; copy.setToDir("../new/dir");<br>&nbsp;&nbsp;&nbsp; copy.setFileset(fileset);<br><br>&nbsp;&nbsp;&nbsp; copy.excute();<br><br>这个代码看起来和XML的那个很相似, 只是稍微长一点。差别在那里? 差别在于XML构造了<br>一个特殊的copy动词, 如果我们硬要用Java来写的话, 应该是这个样子:<br><br>&nbsp;&nbsp;&nbsp; copy("../new/dir");<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fileset("src_dir");<br>&nbsp;&nbsp;&nbsp; }<br><br>看到差别了吗? 以上代码(如果可以在Java中用的化), 是一个特殊的copy算符, 有点像<br>for循环或者Java5中的foreach循环。如果我们有一个转换器, 可以把XML转换到Java, 大<br>概就会得到上面这段事实上不可以执行的代码。因为Java的技术规范是定死的, 我们没有<br>办法在程序里改变它。我们可以增加包, 增加类, 增加方法, 但是我们没办法增加算符, <br>而对于XML, 我们显然可以任由自己增加这样的东西。对于XML的语法树来说, 只要原意, <br>我们可以任意增加任何元素, 因此等于我们可以任意增加算符。如果你还不太明白的话, <br>看下面这个例子, 加入我们要给Java引入一个unless算符:<br><br>&nbsp;&nbsp;&nbsp; unless(someObject.canFly())<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; someObject.transportByGround():<br>&nbsp;&nbsp;&nbsp; }<br><br>在上面的两个例子中, 我们打算给Java语法扩展两个算符, 成组拷贝文件算符和条件算符<br>unless, 我们要想做到这一点, 就必须修改Java编译器能够接受的抽象语法树, 显然我们<br>无法用Java标准的功能来实现它。但是在XML中我们可以轻而易举地做到。我们的解析器<br>根据 XML元素, 生成抽象语法树, 由此生成算符, 所以, 我们可以任意引入任何算符。<br><br>对于复杂的算符来说, 这样做的好处显而易见。比如, 用特定的算符来做检出源码, 编译<br>文件, 单元测试, 发送邮件等任务, 想想看有多么美妙。对于特定的题目, 比如说构造软<br>件项目, 这些算符的使用可以大幅减低少代码的数量。增加代码的清晰程度和可重用性。<br>解释性的XML可以很容易的达到这个目标。XML是存储层次化数据的简单数据文件, 而在<br>Java中, 由于层次结构是定死的(你很快就会看到, Lisp的情况与此截然不同), 我们就没<br>法达到上述目标。也许这正是Ant的成功之处呢。<br><br>你可以注意一下最近Java和C#的变化(尤其是C#3.0的技术规范), C#把常用的功能抽象出<br>来, 作为算符增加到C#中。C#新增加的query算符就是一个例子。它用的还是传统的作法:<br>C#的设计者修改抽象语法树, 然后增加对应的实现。如果程序员自己也能修改抽象语法树<br>该有多好! 那样我们就可以构造用于特定问题的子语言(比如说就像Ant这种用于构造项目<br>的语言), 你能想到别的例子吗? 再思考一下这个概念。不过也不必思考太甚, 我们待会<br>还会回到这个题目。那时候就会更加清晰。<br><br>离Lisp越来越近<br><br>我们先把算符的事情放一放, 考虑一下Ant设计局限之外的东西。我早先说过, Ant可以通<br>过写Java类来扩展。Ant解析器会根据名字来匹配XML元素和Java类, 一旦找到匹配, 就执<br>行相应任务。为什么不用Ant自己来扩展Ant呢? 毕竟核心任务要包含很多传统语言的结构<br>(例如"if"), 如果Ant自身就能提供构造任务的能力(而不是依赖java类), 我们就可以得<br>到更高的移植性。我们将会依赖一组核心任务(如果你原意, 也不妨把它称作标准库), 而<br>不用管有没有Java 环境了。这组核心任务可以用任何方式来实现, 而其他任务建筑在这<br>组核心任务之上, 那样的话, Ant就会成为通用的, 可扩展的, 基于XML的编程语言。考虑<br>下面这种代码的可能性:<br><br>&nbsp;&nbsp;&nbsp; &lt;task name="Test"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;echo message="Hello World" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/task&gt;<br>&nbsp;&nbsp;&nbsp; &lt;Test /&gt;<br><br>如果XML支持"task"的创建, 上面这段代码就会输出"Hello World!". 实际上, 我们可以<br>用Java写个"task"任务, 然后用Ant-XML来扩展它。Ant可以在简单原语的基础上写出更复<br>杂的原语, 就像其他编程语言常用的作法一样。这也就是我们一开始提到的基于XML的编<br>程语言。这样做用处不大(你知道为甚么吗?), 但是真的很酷。<br><br>再看一回我们刚才说的Task任务。祝贺你呀, 你在看Lisp代码!!! 我说什么? 一点都不像<br>Lisp吗? 没关系, 我们再给它收拾一下。<br><br>比XML更好<br><br>前面一节说过, Ant自我扩展没什么大用, 原因在于XML很烦琐。对于数据来说, 这个问题<br>还不太大, 但如果代码很烦琐的话, 光是打字上的麻烦就足以抵消它的好处。你写过Ant <br>的脚本吗? 我写过, 当脚本达到一定复杂度的时候, XML非常让人厌烦。想想看吧, 为了<br>写结束标签, 每个词都得打两遍, 不发疯算好的!<br><br>为了解决这个问题, 我们应当简化写法。须知, XML仅仅是一种表达层次化数据的方式。<br>我们并不是一定要使用尖括号才能得到树的序列化结果。我们完全可以采用其他的格式。<br>其中的一种(刚好就是Lisp所采用的)格式, 叫做s表达式。s表达式要做的和XML一样, 但<br>它的好处是写法更简单, 简单的写法更适合代码输入。后面我会详细讲s表达式。这之前<br>我要清理一下XML的东西。考虑一下关于拷贝文件的例子:<br><br>&nbsp;&nbsp;&nbsp; &lt;copy toDir="../new/dir"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="src_dir"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br><br>想想看在内存里面, 这段代码的解析树在内存会是什么样子? 会有一个"copy"节点, 其下<br>有一个 "fileset"节点, 但是属性在哪里呢? 它怎样表达呢? 如果你以前用过XML, 并且<br>弄不清楚该用元素还是该用属性, 你不用感到孤单, 别人一样糊涂着呢。没人真的搞得清<br>楚。这个选择与其说是基于技术的理由, 还不如说是闭着眼瞎摸。从概念上来讲, 属性也<br>是一种元素, 任何属性能做的, 元素一样做得到。XML引入属性的理由, 其实就是为了让<br>XML写法不那么冗长。比如我们看个例子:<br><br>&nbsp;&nbsp;&nbsp; &lt;copy&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;toDir&gt;../new/dir&lt;/toDir&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;dir&gt;src_dir&lt;/dir&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/fileset&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/copy&gt;<br><br>两下比较, 内容的信息量完全一样, 用属性可以减少打字数量。如果XML没有属性的话, <br>光是打字就够把人搞疯掉。<br><br>说完了属性的问题, 我们再来看一看s表达式。之所以绕这么个弯, 是因为s表达式没有属<br>性的概念。因为s表达式非常简练, 根本没有必要引入属性。我们在把XML转换成s表达式<br>的时候, 心里应该记住这一点。看个例子, 上面的代码译成s表达式是这样的:<br><br>&nbsp;&nbsp;&nbsp; (copy <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (todir "../new/dir")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (fileset (dir "src_dir")))<br><br>仔细看看这个例子, 差别在哪里? 尖括号改成了圆括号, 每个元素原来是有一对括号标记<br>包围的, 现在取消了后一个(就是带斜杠的那个)括号标记。表示元素的结束只需要一个")"<br>就可以了。不错, 差别就是这些。这两种表达方式的转换, 非常自然, 也非常简单。s表<br>达式打起字来, 也省事得多。第一次看s表达式(Lisp)时, 括号很烦人是吧? 现在我们明<br>白了背后的道理, 一下子就变得容易多了。至少, 比XML要好的多。用s表达式写代码, 不<br>单是实用, 而且也很让人愉快。s表达式具有XML的一切好处, 这些好处是我们刚刚探讨过<br>的。现在我们看看更加Lisp风格的task例子:<br><br>&nbsp;&nbsp;&nbsp; (task (name "Test")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (echo (message "Hellow World!")))<br>&nbsp;&nbsp;&nbsp; (Test)<br><br>用Lisp的行话来讲, s表达式称为表(list)。对于上面的例子, 如果我们写的时候不加换<br>行, 用逗号来代替空格, 那么这个表达式看起来就非常像一个元素列表, 其中又嵌套着其<br>他标记。<br><br>&nbsp;&nbsp;&nbsp; (task, (name, "test"), (echo, (message, "Hello World!")))<br><br>XML自然也可以用这样的风格来写。当然上面这句并不是一般意义上的元素表。它实际上<br>是一个树。这和XML的作用是一样的。称它为列表, 希望你不会感到迷惑, 因为嵌套表和<br>树实际上是一码事。Lisp的字面意思就是表处理(list processing), 其实也可以称为树<br>处理, 这和处理XML节点没有什么不同。<br><br>经受这一番折磨以后, 现在我们终于相当接近Lisp了, Lisp的括号的神秘本质(就像许多<br>Lisp狂热分子认为的)逐渐显现出来。现在我们继续研究其他内容。<br><br>重新审视C语言的宏<br><br>到了这里, 对XML的讨论你大概都听累了, 我都讲累了。我们先停一停, 把树, s表达式,<br>Ant这些东西先放一放, 我们来说说C的预处理器。一定有人问了, 我们的话题和C有什么<br>关系? 我们已经知道了很多关于元编程的事情, 也探讨过专门写代码的代码。理解这问题<br>有一定难度, 因为相关讨论文章所使用的编程语言, 都是你们不熟悉的。但是如果只论概<br>念的话, 就相对要简单一些。我相信, 如果以C语言做例子来讨论元编程, 理解起来一定<br>会容易得多。好, 我们接着看。<br><br>一个问题是, 为什么要用代码来写代码呢? 在实际的编程中, 怎样做到这一点呢? 到底元<br>编程是什么意思? 你大概已经听说过这些问题的答案, 但是并不懂得其中缘由。为了揭示<br>背后的真理, 我们来看一下一个简单的数据库查询问题。这种题目我们都做过。比方说, <br>直接在程序码里到处写SQL语句来修改表(table)里的数据, 写多了就非常烦人。即便用<br>C#3.0的LINQ, 仍然不减其痛苦。写一个完整的SQL查询(尽管语法很优美)来修改某人的地<br>址, 或者查找某人的名字, 绝对是件令程序员倍感乏味的事情, 那么我们该怎样来解决这<br>个问题? 答案就是: 使用数据访问层。 <br><br>概念挺简单, 其要点是把数据访问的内容(至少是那些比较琐碎的部分)抽象出来, 用类来<br>映射数据库的表, 然后用访问对象属性访问器(accessor)的办法来间接实现查询。这样就<br>极大地简化了开发工作量。我们用访问对象的方法(或者属性赋值, 这要视你选用的语言<br>而定)来代替写SQL查询语句。凡是用过这种方法的人, 都知道这很节省时间。当然, 如果<br>你要亲自写这样一个抽象层, 那可是要花非常多的时间的--你要写一组类来映射表, 把属<br>性访问转换为SQL查询, 这个活相当耗费精力。用手工来做显然是很不明智的。但是一旦<br>你有了方案和模板, 实际上就没有多少东西需要思考的。你只需要按照同样的模板一次又<br>一次重复编写相似代码就可以了。事实上很多人已经发现了更好的方法, 有一些工具可以<br>帮助你连接数据库, 抓取数据库结构定义(schema), 按照预定义的或者用户定制的模板来<br>自动编写代码。<br><br>如果你用过这种工具, 你肯定会对它的神奇效果深为折服。往往只需要鼠标点击数次, 就<br>可以连接到数据库, 产生数据访问源码, 然后把文件加入到你的工程里面, 十几分钟的工<br>作, 按照往常手工方式来作的话, 也许需要数百个小时人工(man-hours)才能完成。可是,<br>如果你的数据库结构定义后来改变了怎么办? 那样的话, 你只需把这个过程重复一遍就可<br>以了。甚至有一些工具能自动完成这项变动工作。你只要把它作为工程构造的一部分, 每<br>次编译工程的时候, 数据库部分也会自动地重新构造。这真的太棒了。你要做的事情基本<br>上减到了0。如果数据库结构定义发生了改变, 并在编译时自动更新了数据访问层的代码,<br>那么程序中任何使用过时的旧代码的地方, 都会引发编译错误。<br><br>数据访问层是个很好的例子, 这样的例子还有好多。从GUI样板代码, WEB代码, COM和<br>CORBA存根, 以及MFC和ATL等等。在这些地方, 都是有好多相似代码多次重复。既然这些<br>代码有可能自动编写, 而程序员时间又远远比CPU时间昂贵, 当然就产生了好多工具来自<br>动生成样板代码。这些工具的本质是什么呢? 它们实际上就是制造程序的程序。它们有一<br>个神秘的名字, 叫做元编程。所谓元编程的本义, 就是如此。<br><br>元编程本来可以用到无数多的地方, 但实际上使用的次数却没有那么多。归根结底, 我们<br>心里还是在盘算, 假设重复代码用拷贝粘贴的话, 大概要重复6,7次, 对于这样的工作量,<br>值得专门建立一套生成工具吗? 当然不值得。数据访问层和COM存根往往需要重用数百次,<br>甚至上千次, 所以用工具生成是最好的办法。而那些仅仅是重复几次十几次的代码, 是没<br>有必要专门做工具的。不必要的时候也去开发代码生成工具, 那就显然过度估计了代码生<br>成的好处。当然, 如果创建这类工具足够简单的话, 还是应当尽量多用, 因为这样做必然<br>会节省时间。现在来看一下有没有合理的办法来达到这个目的。<br><br>现在, C预处理器要派上用场了。我们都用过C/C++的预处理器, 我们用它执行简单的编译<br>指令, 来产生简单的代码变换(比方说, 设置调试代码开关), 看一个例子:<br><br>&nbsp;&nbsp;&nbsp; #define triple(X) X+X+X<br><br>这一行的作用是什么? 这是一个简单的预编译指令, 它把程序中的triple(X)替换称为<br>X+X+X。例如, 把所有的triple(5)都换成5+5+5, 然后再交给编译器编译。这就是一个简<br>单的代码生成的例子。要是C的预处理器再强大一点, 要是能够允许连接数据库, 要是能<br>多一些其他简单的机制, 我们就可以在我们程序的内部开发自己的数据访问层。下面这个<br>例子, 是一个假想的对C宏的扩展:<br><br>&nbsp;&nbsp;&nbsp; #get-db-schema("127.0.0.1")<br>&nbsp;&nbsp;&nbsp; #iterate-through-tables<br>&nbsp;&nbsp;&nbsp; #for-each-table<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class #table-name<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; #end-for-each<br><br>我们连接数据库结构定义, 遍历数据表, 然后对每个表创建一个类, 只消几行代码就完成<br>了这个工作。这样每次编译工程的时候, 这些类都会根据数据库的定义同步更新。显而易<br>见, 我们不费吹灰之力就在程序内部建立了一个完整的数据访问层, 根本用不着任何外部<br>工具。当然这种作法有一个缺点, 那就是我们得学习一套新的"编译时语言", 另一个缺点<br>就是根本不存在这么一个高级版的C预处理器。需要做复杂代码生成的时候, 这个语言(译<br>者注: 这里指预处理指令, 即作者所说的"编译时语言")本身也一定会变得相当复杂。它<br>必须支持足够多的库和语言结构。比如说我们想要生成的代码要依赖某些ftp服务器上的<br>文件, 预处理器就得支持ftp访问, 仅仅因为这个任务而不得不创造和学习一门新的语言,<br>真是有点让人恶心(事实上已经存在着有此能力的语言, 这样做就更显荒谬)。我们不妨再<br>灵活一点, 为什么不直接用 C/C++自己作为自己的预处理语言呢?&nbsp; 这样子的话, 我们可<br>以发挥语言的强大能力, 要学的新东西也只不过是几个简单的指示字 , 这些指示字用来<br>区别编译时代码和运行时代码。<br><br>&nbsp;&nbsp;&nbsp; &lt;%<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"Enter a number: ";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cin&gt;&gt;n;<br>&nbsp;&nbsp;&nbsp; %&gt;<br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt; &lt;% n %&gt;;i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"hello"&lt;&lt;endl;<br>&nbsp;&nbsp;&nbsp; }<br><br>你明白了吗? 在&lt;%和%&gt;标记之间的代码是在编译时运行的, 标记之外的其他代码都是普通<br>代码。编译程序时, 系统会提示你输入一个数, 这个数在后面的循环中会用到。而for循<br>环的代码会被编译。假定你在编译时输入5, for循环的代码将会是:<br><br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;5; i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;"hello"&lt;&lt;endl;<br>&nbsp;&nbsp;&nbsp; }<br><br>又简单又有效率, 也不需要另外的预处理语言。我们可以在编译时就充分发挥宿主语言( <br>此处是C/C++)的强大能力, 我们可以很容易地在编译时连接数据库, 建立数据访问层, 就<br>像JSP或者ASP创建网页那样。我们也用不着专门的窗口工具来另外建立工程。我们可以在<br>代码中立即加入必要的工具。我们也用不着顾虑建立这种工具是不是值得, 因为这太容易<br>了, 太简单了。这样子不知可以节省多少时间啊。<br><br>你好, Lisp<br><br>到此刻为止, 我们所知的关于Lisp的指示可以总结为一句话: Lisp是一个可执行的语法更<br>优美的XML, 但我们还没有说Lisp是怎样做到这一点的, 现在开始补上这个话题。 <br><br>Lisp有丰富的内置数据类型, 其中的整数和字符串和其他语言没什么分别。像71或者<br>"hello"这样的值, 含义也和C++或者Java这样的语言大体相同。真正有意思的三种类型是<br>符号(symbol), 表和函数。这一章的剩余部分, 我都会用来介绍这几种类型, 还要介绍<br>Lisp环境是怎样编译和运行源码的。这个过程用Lisp的术语来说通常叫做求值。通读这一<br>节内容, 对于透彻理解元编程的真正潜力, 以及代码和数据的同一性, 和面向领域语言的<br>观念, 都极其重要。万勿等闲视之。我会尽量讲得生动有趣一些, 也希望你能获得一些<br>启发。那好, 我们先讲符号。<br><br>大体上, 符号相当于C++或Java语言中的标志符, 它的名字可以用来访问变量值(例如<br>currentTime, arrayCount, n, 等等), 差别在于, Lisp中的符号更加基本。在C++或<br>Java里面, 变量名只能用字母和下划线的组合, 而Lisp的符号则非常有包容性, 比如, 加<br>号(+)就是一个合法的符号, 其他的像-, =, hello-world, *等等都可以是符号名。符号<br>名的命名规则可以在网上查到。你可以给这些符号任意赋值, 我们这里先用伪码来说明这<br>一点。假定函数set是给变量赋值(就像等号=在C++和Java里的作用), 下面是我们的例子:<br><br>&nbsp;&nbsp;&nbsp; set(test, 5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号test的值为5<br>&nbsp;&nbsp;&nbsp; set(=, 5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号=的值为5<br>&nbsp;&nbsp;&nbsp; set(test, "hello")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号test的值为字符串"hello"<br>&nbsp;&nbsp;&nbsp; set(test, =)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 此时符号=的值为5, 所以test的也为5<br>&nbsp;&nbsp;&nbsp; set(*, "hello")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 符号*的值为"hello"<br><br>好像有什么不对的地方? 假定我们对*赋给整数或者字符串值, 那做乘法时怎么办? 不管<br>怎么说, *总是乘法呀? 答案简单极了。Lisp中函数的角色十分特殊, 函数也是一种数据<br>类型, 就像整数和字符串一样, 因此可以把它赋值给符号。乘法函数Lisp的内置函数, 默<br>认赋给*, 你可以把其他函数赋值给*, 那样*就不代表乘法了。你也可以把这函数的值存<br>到另外的变量里。我们再用伪码来说明一下:<br><br>&nbsp;&nbsp;&nbsp; *(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 3乘4, 结果是12<br>&nbsp;&nbsp;&nbsp; set(temp, *)&nbsp;&nbsp;&nbsp; // 把*的值, 也就是乘法函数, 赋值给temp<br>&nbsp;&nbsp;&nbsp; set(*, 3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 把3赋予*<br>&nbsp;&nbsp;&nbsp; *(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 错误的表达式, *不再是乘法, 而是数值3<br>&nbsp;&nbsp;&nbsp; temp(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // temp是乘法函数, 所以此表达式的值为3乘4等于12<br>&nbsp;&nbsp;&nbsp; set(*, temp)&nbsp;&nbsp;&nbsp; // 再次把乘法函数赋予*<br>&nbsp;&nbsp;&nbsp; *(3,4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 3乘4等于12<br><br>再古怪一点, 把减号的值赋给加号:<br><br>&nbsp;&nbsp;&nbsp; set(+, -)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 减号(-)是内置的减法函数<br>&nbsp;&nbsp;&nbsp; +(5, 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 加号(+)现在是代表减法函数, 结果是5减4等于1<br><br>这只是举例子, 我还没有详细讲函数。Lisp中的函数是一种数据类型, 和整数, 字符串, <br>符号等等一样。一个函数并不必然有一个名字, 这和C++或者Java语言的情形很不相同。<br>在这里函数自己代表自己。事实上它是一个指向代码块的指针, 附带有一些其他信息(例<br>如一组参数变量)。只有在把函数赋予其他符号时, 它才具有了名字, 就像把一个数值或<br>字符串赋予变量一样的道理。你可以用一个内置的专门用于创建函数的函数来创建函数,<br>然后把它赋值给符号fn, 用伪码来表示就是:<br><br>&nbsp;&nbsp;&nbsp; fn [a]<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return *(a, 2);<br>&nbsp;&nbsp;&nbsp; }<br><br>这段代码返回一个具有一个参数的函数, 函数的功能是计算参数乘2的结果。这个函数还<br>没有名字, 你可以把此函数赋值给别的符号:<br><br>&nbsp;&nbsp;&nbsp; set(times-two, fn [a] {return *(a, 2)})<br><br>我们现在可以这样调用这个函数:<br><br>&nbsp;&nbsp;&nbsp; time-two(5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 返回10<br><br>我们先跳过符号和函数, 讲一讲表。什么是表? 你也许已经听过好多相关的说法。表, 一<br>言以蔽之, 就是把类似XML那样的数据块, 用s表达式来表示。表用一对括号括住, 表中元<br>素以空格分隔, 表可以嵌套。例如(这回我们用真正的Lisp语法, 注意用分号表示注释):<br><br>&nbsp;&nbsp;&nbsp; ()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 空表<br>&nbsp;&nbsp;&nbsp; (1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 含一个元素的表<br>&nbsp;&nbsp;&nbsp; (1 "test")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 两元素表, 一个元素是整数1, 另一个是字符串<br>&nbsp;&nbsp;&nbsp; (test "hello")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 两元素表, 一个元素是符号, 另一个是字符串<br>&nbsp;&nbsp;&nbsp; (test (1 2) "hello")&nbsp;&nbsp;&nbsp; ; 三元素表, 一个符号test, 一个含有两个元素1和2的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 表, 最后一个元素是字符串<br><br>当Lisp系统遇到这样的表时, 它所做的, 和Ant处理XML数据所做的, 非常相似, 那就是试<br>图执行它们。其实, Lisp源码就是特定的一种表, 好比Ant源码是一种特定的XML一样。<br>Lisp执行表的顺序是这样的, 表的第一个元素当作函数, 其他元素当作函数的参数。如果<br>其中某个参数也是表, 那就按照同样的原则对这个表求值, 结果再传递给最初的函数作为<br>参数。这就是基本原则。我们看一下真正的代码:<br><br>&nbsp;&nbsp;&nbsp; (* 3 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 相当于前面列举过的伪码*(3,4), 即计算3乘4<br>&nbsp;&nbsp;&nbsp; (times-two 5)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回10, times-two按照前面的定义是求参数的2倍<br>&nbsp;&nbsp;&nbsp; (3 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, 3不是函数<br>&nbsp;&nbsp;&nbsp; (time-two)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, times-two要求一个参数<br>&nbsp;&nbsp;&nbsp; (times-two 3 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, times-two只要求一个参数<br>&nbsp;&nbsp;&nbsp; (set + -)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 把减法函数赋予符号+<br>&nbsp;&nbsp;&nbsp; (+ 5 4)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 依据上一句的结果, 此时+表示减法, 所以返回1<br>&nbsp;&nbsp;&nbsp; (* 3 (+ 2 2))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 2+2的结果是4, 再乘3, 结果是12<br><br>上述的例子中, 所有的表都是当作代码来处理的。怎样把表当作数据来处理呢? 同样的,<br>设想一下, Ant是把XML数据当作自己的参数。在Lisp中, 我们给表加一个前缀'来表示数<br>据。<br><br>&nbsp;&nbsp;&nbsp; (set test '(1 2))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; test的值为两元素表<br>&nbsp;&nbsp;&nbsp; (set test (1 2))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 错误, 1不是函数<br>&nbsp;&nbsp;&nbsp; (set test '(* 3 4))&nbsp;&nbsp;&nbsp;&nbsp; ; test的值是三元素表, 三个元素分别是*, 3, 4<br><br>我们可以用一个内置的函数head来返回表的第一个元素, tail函数来返回剩余元素组成的<br>表。<br><br>&nbsp;&nbsp;&nbsp; (head '(* 3 4))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回符号*<br>&nbsp;&nbsp;&nbsp; (tail '(* 3 4))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回表(3 4)<br>&nbsp;&nbsp;&nbsp; (head (tal '(* 3 4)))&nbsp;&nbsp; ; 返回3<br>&nbsp;&nbsp;&nbsp; (head test)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; 返回*<br><br>你可以把Lisp的内置函数想像成Ant的任务。差别在于, 我们不用在另外的语言中扩展<br>Lisp(虽然完全可以做得到), 我们可以用Lisp自己来扩展自己, 就像上面举的times-two<br>函数的例子。Lisp的内置函数集十分精简, 只包含了十分必要的部分。剩下的函数都是作<br>为标准库来实现的。<br><br>Lisp宏<br><br>我们已经看到, 元编程在一个类似jsp的模板引擎方面的应用。我们通过简单的字符串处<br>理来生成代码。但是我们可以做的更好。我们先提一个问题, 怎样写一个工具, 通过查找<br>目录结构中的源文件来自动生成Ant脚本。<br><br>用字符串处理的方式生成Ant脚本是一种简单的方式。当然, 还有一种更加抽象, 表达能<br>力更强, 扩展性更好的方式, 就是利用XML库在内存中直接生成XML节点, 这样的话内存中<br>的节点就可以自动序列化成为字符串。不仅如此, 我们的工具还可以分析这些节点, 对已<br>有的XML文件做变换。通过直接处理XML节点。我们可以超越字符串处理, 使用更高层次的<br>概念, 因此我们的工作就会做的更快更好。<br><br>我们当然可以直接用Ant自身来处理XML变换和制作代码生成工具。或者我们也可以用Lisp<br>来做这项工作。正像我们以前所知的, 表是Lisp内置的数据结构, Lisp含有大量的工具来<br>快速有效的操作表(head和tail是最简单的两个)。而且, Lisp没有语义约束, 你可以构造<br>任何数据结构, 只要你原意。<br><br>Lisp通过宏(macro)来做元编程。我们写一组宏来把任务列表(to-do list)转换为专用领<br>域语言。<br><br>回想一下上面to-do list的例子, 其XML的数据格式是这样的:<br><br>&nbsp;&nbsp;&nbsp; &lt;todo name = "housework"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;item priority = "high"&gt;Clean the hose&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;item priority = "medium"&gt;Wash the dishes&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;item priority = "medium"&gt;Buy more soap&lt;/item&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/todo&gt;<br><br>相应的s表达式是这样的:<br><br>&nbsp;&nbsp;&nbsp; (todo "housework"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (item (priority high) "Clean the house")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (item (priority medium) "Wash the dishes")<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (item (priority medium) "Buy more soap"))<br><br>假设我们要写一个任务表的管理程序, 把任务表数据存到一组文件里, 当程序启动时, 从<br>文件读取这些数据并显示给用户。在别的语言里(比如说Java), 这个任务该怎么做? 我们<br>会解析XML文件, 从中得出任务表数据, 然后写代码遍历XML树, 再转换为Java的数据结构<br>(老实讲, 在Java里解析XML真不是件轻松的事情), 最后再把数据展示给用户。现在如果<br>用Lisp, 该怎么做?<br><br>假定要用同样思路的化, 我们大概会用Lisp库来解析XML。XML对我们来说就是一个Lisp <br>的表(s表达式), 我们可以遍历这个表, 然后把相关数据提交给用户。可是, 既然我们用<br>Lisp, 就根本没有必要再用XML格式保存数据, 直接用s表达式就好了, 这样就没有必要做<br>转换了。我们也用不着专门的解析库, Lisp可以直接在内存里处理s表达式。注意, Lisp <br>编译器和.net编译器一样, 对Lisp程序来说, 在运行时总是随时可用的。<br><br>但是还有更好的办法。我们甚至不用写表达式来存储数据, 我们可以写宏, 把数据当作代<br>码来处理。那该怎么做呢? 真的简单。回想一下, Lisp的函数调用格式:<br><br>&nbsp;&nbsp;&nbsp; (function-name arg1 arg2 arg3)<br><br>其中每个参数都是s表达式, 求值以后, 传递给函数。如果我们用(+ 4 5)来代替arg1, <br>那么, 程序会先求出结果, 就是9, 然后把9传递给函数。宏的工作方式和函数类似。主要<br>的差别是, 宏的参数在代入时不求值。<br><br>&nbsp;&nbsp;&nbsp; (macro-name (+ 4 5))<br><br>这里, (+ 4 5)作为一个表传递给宏, 然后宏就可以任意处理这个表, 当然也可以对它求<br>值。宏的返回值是一个表, 然后有程序作为代码来执行。宏所占的位置, 就被替换为这个<br>结果代码。我们可以定义一个宏把数据替换为任意代码, 比方说, 替换为显示数据给用户<br>的代码。<br><br>这和元编程, 以及我们要做的任务表程序有什么关系呢? 实际上, 编译器会替我们工作, <br>调用相应的宏。我们所要做的, 仅仅是创建一个把数据转换为适当代码的宏。<br><br>例如, 上面曾经将过的C的求三次方的宏, 用Lisp来写是这样子:<br><br>&nbsp;&nbsp;&nbsp; (defmacro triple (x)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; `(+ ~x ~x ~x))<br><br>(译注: 在Common Lisp中, 此处的单引号应当是反单引号, 意思是对表不求值, 但可以对<br>表中某元素求值, 记号~表示对元素x求值, 这个求值记号在Common Lisp中应当是逗号。<br>反单引号和单引号的区别是, 单引号标识的表, 其中的元素都不求值。这里作者所用的记<br>号是自己发明的一种Lisp方言Blaise, 和common lisp略有不同, 事实上, 发明方言是<br>lisp高手独有的乐趣, 很多狂热分子都热衷这样做。比如Paul Graham就发明了ARC, 许多<br>记号比传统的Lisp简洁得多, 显得比较现代)<br><br>单引号的用处是禁止对表求值。每次程序中出现triple的时候, <br><br>&nbsp;&nbsp;&nbsp; (triple 4)<br><br>都会被替换成:<br><br>&nbsp;&nbsp;&nbsp; (+ 4 4 4)<br><br>我们可以为任务表程序写一个宏, 把任务数据转换为可执行码, 然后执行。假定我们的输<br>出是在控制台:<br><br>&nbsp;&nbsp;&nbsp; (defmacro item (priority note)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; `(block <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (print stdout tab "Prority: " ~(head (tail priority)) endl)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (print stdout tab "Note: " ~note endl endl)))<br><br>我们创造了一个非常小的有限的语言来管理嵌在Lisp中的任务表。这个语言只用来解决特<br>定领域的问题, 通常称之为DSLs(特定领域语言, 或专用领域语言)。<br><br>特定领域语言<br><br>本文谈到了两个特定领域语言, 一个是Ant, 处理软件构造。一个是没起名字的, 用于处<br>理任务表。两者的差别在于, Ant是用XML, XML解析器, 以及Java语言合在一起构造出来<br>的。而我们的迷你语言则完全内嵌在Lisp中, 只消几分钟就做出来了。<br><br>我们已经说过了DSL的好处, 这也就是Ant用XML而不直接用Java的原因。如果使用Lisp, <br>我们可以任意创建DSL, 只要我们需要。我们可以创建用于网站程序的DSL, 可以写多用户<br>游戏, 做固定收益贸易(fixed income trade), 解决蛋白质折叠问题, 处理事务问题, 等<br>等。我们可以把这些叠放在一起, 造出一个语言, 专门解决基于网络的贸易程序, 既有网<br>络语言的优势, 又有贸易语言的好处。每天我们都会收获这种方法带给我们的益处, 远远<br>超过Ant所能给予我们的。<br><br>用DSL解决问题, 做出的程序精简, 易于维护, 富有弹性。在Java里面, 我们可以用类来<br>处理问题。这两种方法的差别在于, Lisp使我们达到了一个更高层次的抽象, 我们不再受<br>语言解析器本身的限制, 比较一下用Java库直接写的构造脚本和用Ant写的构造脚本其间<br>的差别。同样的, 比较一下你以前所做的工作, 你就会明白Lisp带来的好处。<br><br>接下来<br><br>学习Lisp就像战争中争夺山头。尽管在电脑科学领域, Lisp已经算是一门古老的语言, 直<br>到现在仍然很少有人真的明白该怎样给初学者讲授Lisp。尽管Lisp老手们尽了很大努力,<br>今天新手学习Lisp仍然是困难重重。好在现在事情正在发生变化, Lisp的资源正在迅速增<br>加, 随着时间推移, Lisp将会越来越受关注。<br><br>Lisp使人超越平庸, 走到前沿。学会Lisp意味着你能找到更好的工作, 因为聪明的雇主会<br>被你与众不同的洞察力所打动。学会Lisp也可能意味着明天你可能会被解雇, 因为你总是<br>强调, 如果公司所有软件都用Lisp写, 公司将会如何卓越, 而这些话你的同事会听烦的。<br>Lisp值得努力学习吗? 那些已经学会Lisp的人都说值得, 当然, 这取决于你的判断。<br><br>你的看法呢?<br><br>这篇文章写写停停, 用了几个月才最终完成。如果你觉得有趣, 或者有什么问题, 意见或<br>建议, 请给我发邮件coffeemug@gmail.com, 我会很高兴收到你的反馈。 <br>
<script type=text/javascript>
if ($ != jQuery) {
$ = jQuery.noConflict();
}
var isLogined = false;
var cb_blogId = 21153;
var cb_entryId = 762180;
var cb_blogApp = "Leap-abead";
var cb_blogUserGuid = "e35e360b-63cf-dd11-9e4d-001cf0cd104b";
var cb_entryCreatedDate = '2007/5/28 10:42:00';
</script>
<img src ="http://www.cppblog.com/ccl0326/aggbug/140071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2011-02-15 09:29 <a href="http://www.cppblog.com/ccl0326/archive/2011/02/15/140071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>懂得</title><link>http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Sun, 12 Dec 2010 13:55:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/136236.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/136236.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/136236.html</trackback:ping><description><![CDATA[<p>第一次周六周日在加班<br>第一次担起责任<br>第一次怕自己不行<br>第一次有那么一点理解到底什么是主程<br>这一天仅作纪念:)</p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/136236.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-12-12 21:55 <a href="http://www.cppblog.com/ccl0326/archive/2010/12/12/136236.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏服务端程序员群</title><link>http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 19 Oct 2010 08:35:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/130461.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/130461.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/130461.html</trackback:ping><description><![CDATA[群号:16043631 已经250人了 还差250<br>帮朋友宣传一下:)<br>
<img src ="http://www.cppblog.com/ccl0326/aggbug/130461.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-10-19 16:35 <a href="http://www.cppblog.com/ccl0326/archive/2010/10/19/130461.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最近规划</title><link>http://www.cppblog.com/ccl0326/archive/2010/09/15/126667.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Wed, 15 Sep 2010 07:55:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2010/09/15/126667.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/126667.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2010/09/15/126667.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/126667.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/126667.html</trackback:ping><description><![CDATA[<p>最近比较闲<br>于是决定规划一下这些日子的生活<br><br>每一场Topcoder都尽量参加<br>无视rating。<br>每天坚持切2-3道sgu,再苦再难都要坚持<br>看unix网络编程<br>学习ace</p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/126667.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2010-09-15 15:55 <a href="http://www.cppblog.com/ccl0326/archive/2010/09/15/126667.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>感慨</title><link>http://www.cppblog.com/ccl0326/archive/2009/11/22/101646.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Sun, 22 Nov 2009 13:24:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2009/11/22/101646.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/101646.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2009/11/22/101646.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/101646.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/101646.html</trackback:ping><description><![CDATA[最近的事还真多啊..<br>今天好友说他进world final了..<br>真心的为他高兴..<br>哪怕我曾经做过不是一个好朋友应该做的事情...<br>在这里..真的赞一下他..<br>算起来..我应该能算是亲眼目睹着他从一个菜鸟..逐渐实现了梦想,进入了world final..<br>这也真的证明了只要为着自己的梦想一直努力下去,迟早有一天会实现的..<br>我想,等明年2月的哈尔滨..我无法去现场给你加油..<br>但一定会为你祝福的.<br>无论成绩如何,你都是一个胜利者^_^<br>而我,也会努力的..朝着我的梦想前进.
<img src ="http://www.cppblog.com/ccl0326/aggbug/101646.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2009-11-22 21:24 <a href="http://www.cppblog.com/ccl0326/archive/2009/11/22/101646.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ITAT复赛</title><link>http://www.cppblog.com/ccl0326/archive/2009/10/31/99898.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Sat, 31 Oct 2009 06:38:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2009/10/31/99898.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/99898.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2009/10/31/99898.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/99898.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/99898.html</trackback:ping><description><![CDATA[题很简单简单简单简单..有人说是书上原题...orz..<br>其实这不重要..重要的是真的很easy...<br>但是我没考好...粗心大意..<br>我原来认为能力与心态是各占50%的.<br>现在觉得这个是不对的..心态至少也得60%...<br>好了..<br>积累下rp..希望能进决赛.. <br>虽然我实在不想参加java的...
<img src ="http://www.cppblog.com/ccl0326/aggbug/99898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2009-10-31 14:38 <a href="http://www.cppblog.com/ccl0326/archive/2009/10/31/99898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SRM450</title><link>http://www.cppblog.com/ccl0326/archive/2009/10/18/98857.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Sat, 17 Oct 2009 17:34:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2009/10/18/98857.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/98857.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2009/10/18/98857.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/98857.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/98857.html</trackback:ping><description><![CDATA[<p>算上有道..第三次玩tc..<br>不知道是div1...看第一题挺简单的..就得意忘形了...也没有仔细看看..想当然了..最后终于被cha了..</p>
不过有幸在room里发现了Petr..怎是orz啊..<br>大牛就是大牛..报了名..没参加比赛..<br>哎.血一般的教训..下回努力
<img src ="http://www.cppblog.com/ccl0326/aggbug/98857.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2009-10-18 01:34 <a href="http://www.cppblog.com/ccl0326/archive/2009/10/18/98857.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ITAT</title><link>http://www.cppblog.com/ccl0326/archive/2009/10/17/98813.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Sat, 17 Oct 2009 02:36:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2009/10/17/98813.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/98813.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2009/10/17/98813.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/98813.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/98813.html</trackback:ping><description><![CDATA[<p>预赛66....orz..只能说是个很吉利的数字...<br>长期放置java..这个成绩还在接受范围呢..<br>进复赛应该没问题吧...<br>话说当初我就说了..这样的题60分就应该能进..除非专门天天去背来背去..<br>现在果然灵验了..<br>好了..吃点东西去..下午还有ural..晚上还有topcoder..forza<br><br><br>ural做了2个半小时..<br>做的很糟..只切了2道题...<br>除了能力,另一个就是英语问题..<br>根据提交来看..应该有4-5道水题...<br>题目大致能看懂...但几个细节一直看不懂..<br>于是一个wa on 5..一个wa on 3...orz啊..orz..<br>B. Sandro's Book..<br>这题最水..枚举字符串就完了..<br>不过我wa了一次..因为有个细节没看懂...- -我都快怀疑我在猜题目了..<br>J. Dill..<br>最最最最最简单的构造..对于第一组箱子..可以把1-n给第一组..<br>那么第二组的m个可以构造成n+1,n+1+n,n+1+2*n.....<br>因为代码都很短..就不帖了..囧<br></p>
<img src ="http://www.cppblog.com/ccl0326/aggbug/98813.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2009-10-17 10:36 <a href="http://www.cppblog.com/ccl0326/archive/2009/10/17/98813.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谁之罪</title><link>http://www.cppblog.com/ccl0326/archive/2009/10/13/98504.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 13 Oct 2009 09:37:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2009/10/13/98504.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/98504.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2009/10/13/98504.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/98504.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/98504.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 因为不是什么好事，而是丑事，所以具体是哪发生的等等信息我就不写出来了。<br>&nbsp;&nbsp;&nbsp; 中学时因为oi的失利，现在上着一所很普通的学校。因为无聊报了北大青鸟。而我也有幸目睹了作弊。幸亏我很早做完就离开了考场，事实上我也的确因为受不了同学的企求，而差点拿u盘把答案传给他。不过u盘当时竟然神奇了坏了。很orz,或者说是我的幸运&#8230;&#8230;于是我离开了考场。但我离开了，还有别人在呢。作弊这种事总会发生的。事实上也的确发生了，而且似乎发生了不少起。最终学校决定严查这件事。最终的结果至今未知。<br>&nbsp;&nbsp;&nbsp; 谈起作弊，我想起了我的童年，我不知道是不是有人一生之中都没作过弊，反正我是的确做过的。不过随着年龄的增长，我越发的讨厌作弊这个词，以及作弊的人。作弊，百害而真的无一利，自欺欺人，不诚实，这些品质夹杂在身上的人，又如何能在这个社会上真正的立足。没有真才实学而只知糊弄的人又如何能成为真正的程序员。<br>&nbsp;&nbsp;&nbsp; 最终，我也没有勇气或魄力去对着我的那些作弊了的同学们说些什么，我不知道他们是如何想的，我是的确有无力感的，因此而只能在这里写些东西来警示自己。<br>&nbsp;&nbsp;&nbsp; 当我们凭借着虚假来获得一些东西的时候，我们又如何能相信获得的东西是真实的，永恒的呢？凭什么&#8230;&#8230;
<img src ="http://www.cppblog.com/ccl0326/aggbug/98504.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2009-10-13 17:37 <a href="http://www.cppblog.com/ccl0326/archive/2009/10/13/98504.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>囧</title><link>http://www.cppblog.com/ccl0326/archive/2009/10/06/97946.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 06 Oct 2009 08:21:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2009/10/06/97946.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/97946.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2009/10/06/97946.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/97946.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/97946.html</trackback:ping><description><![CDATA[poj挂了<br><img src="http://www.cppblog.com/images/cppblog_com/ccl0326/poj.jpg" border=0>
<img src ="http://www.cppblog.com/ccl0326/aggbug/97946.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2009-10-06 16:21 <a href="http://www.cppblog.com/ccl0326/archive/2009/10/06/97946.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>第一篇日志</title><link>http://www.cppblog.com/ccl0326/archive/2009/07/28/91515.html</link><dc:creator>Vincent</dc:creator><author>Vincent</author><pubDate>Tue, 28 Jul 2009 10:22:00 GMT</pubDate><guid>http://www.cppblog.com/ccl0326/archive/2009/07/28/91515.html</guid><wfw:comment>http://www.cppblog.com/ccl0326/comments/91515.html</wfw:comment><comments>http://www.cppblog.com/ccl0326/archive/2009/07/28/91515.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ccl0326/comments/commentRss/91515.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ccl0326/services/trackbacks/91515.html</trackback:ping><description><![CDATA[<p style="COLOR: red">Orz..<br></p>
在这里按家了。<br>曾经换过数不清的博客，自己也架设过博客。<br>但是很不争气，因为各种各样的原因，都没有坚持下去。<br>但自己还总是想写些东西，因为总觉得写些东西心里才会踏实，才觉得像活着一样。<br>所以最终我选择了这里。<br>在这里按家。<br>希望这能是一个足够长久的家。
<img src ="http://www.cppblog.com/ccl0326/aggbug/91515.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ccl0326/" target="_blank">Vincent</a> 2009-07-28 18:22 <a href="http://www.cppblog.com/ccl0326/archive/2009/07/28/91515.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>