Year: 2009

I, IT Newbie

据说,如果被人问道“该怎么办”,一般有两种应对:一种是单纯的说理,把问题剖析解理,抓住“本质”找到出路,遇上与科学关系紧密的问题,此类解法最是合适;另一种则是“答非所问”,给你讲一个故事,相比“干巴巴”地探究法则,娓娓而来的叙事更“罗嗦”,但也更丰厚、更温暖、更亲切——“听完这个故事,你就会感觉好多了”,对于人生的困惑,此类问题或许更加合适。实际上自古也是如此:从上古的传说,从《荷马史诗》到《伊索寓言》,再到晚近流传的各种故事、小说,人类一直从重复的叙事中获得安全感,获得启示。下面要说的,也是一本与叙事有关的书。

近年来,迷惘和无助,似乎成了大学生活的主要色彩。“我该怎么办?”、“我的人生究竟何去何从?”这样的问题困扰着许多正处于人生最美好年华的同学。甚至拙译《精通正则表达式》出版之后,也有同学给我留下的勘误邮箱来信诉说这样的痛苦,问题的严重性由此可见一斑。这里暂不去追究现象的原因,更紧要的问题似乎是:面对这些迫切希望解脱的同学,我们到底能做点什么?
面对这样的问题,单纯的说理难免显得乏力,尤其在现代,它往往带有荒谬的色彩——电影《顽主》里的“德育专家”,就是最好的注解。这种时候,叙事的解答反而更加合适:通过讲述那些经历过这些困扰,但又成功挣脱的“普通人”的经历,给还身陷其中的孩子以力量和启示。这样的好处是双重的:一方面,它真切展现了“挣脱”的过程,仍然身陷其中的人,往往能够在这样的故事中复现自己,这是“设身处地地讲道理”也无法提供的;另一方面,它又不同于大众传媒所包装的“成功人士”,它不但给你看到蝴蝶的美丽,也给你看到化蝶的辛苦,让你相信明白成功者并非“天赋异禀”,于是能够重塑对自己的信心。
当然,要做到这一点非常困难:如今我们看到更多的是成功之后的“全面包装”,有几个人愿意把自己曾经的血泪和疮疤无保留地展现给大家呢?也正因为如此,这本《我是一只IT小小鸟》,才显得难能可贵。

Continue reading I, IT Newbie

《正则表达式傻瓜书》第一章:通配符

第一章:通配符

我们已经说过,这本《正则表达式傻瓜书》并非把读者当傻瓜,而是保证“傻瓜都能看懂”。如果你到现在还没听说过“通配符”或是“正则表达式”,那么,请看这一章。
要说明的另一点是,因为一般的Linux/Unix用户都熟悉通配符,所以,本章假设读者工作于Windows平台下,所举的例子也全部面向Windows平台。

从Windows的搜索谈起

正则表达式是进行文本处理的工具。那么,它到底进行哪些“处理”?简而言之,正则表达式的主要功能就是对文本进行查找(匹配)和替换(修改)。在这一章里,我们先从最简单的文本查找说起。
正则表达式所“搜索/查找”功能的对象,就是我们说的“文本”——它可以是Word文档、Excel表格、浏览器看到的网页等等,也可以是文件名(工作日报20090925.doc)、电话号码(400-82055555)、电子邮件地址([email protected])等等。所以也有这样一种说法:正则表达式处理的是“字符串”——也就是一系列的字符。想想也是,Word文档的内容、Excel表格的内容、网页的内容、文件名、电话号码、电子邮件等等,无非都是“连接起来”的字符,也就是“字符串”了。
几乎每种文本处理工具(Word、Excel、记事本、写字板)都提供了查找(和替换)功能:


图1-1 Word中的查找

Continue reading 《正则表达式傻瓜书》第一章:通配符

《正则表达式傻瓜书》前言

按:《精通正则表达式》是一本好书,我翻译之后,一直都奢望写本关于正则表达式的书,为《精通正则表达式》接上地气,今年终于有机会把“奢望”变成“苦差”。下面是本书(暂定名《正则表达式傻瓜书》,大家对此有意见或建议也请直说)的前言,其中介绍了本书的结构、读者和价值,请大家多提建议,在这里先行谢过。

前言

正则表达式简介

“正则表达式”,这个名字看起来有点古怪。不过别着急,我们先看看它到底是有什么用,再解释这古怪名字的来历。

简而言之,正则表达式就是一套专门处理文本的强大工具。“学术”地说,它能够做的事情主要是:

  • 复杂的文本查找/匹配/提取
  • 复杂的文本替换

请注意,这里说的是“复杂”的操作,而不是“简单”的查找/匹配/提取/替换(几乎任何一种文本处理工具,例如Word和记事本,都提供了这种功能)。或者,通俗地说,正则表达式能够做的事情是这样的:

如果你是一般用户:

  • 把多行的文本迅速拼成用逗号分隔的一行文本(群发邮件时这非常有用);
  • 把一长篇文章里的手机号码都找出来(除了匹配13x15x18x开头的号码,还可以处理开头有‘0’和/或有‘+86’的情况);
  • 把一篇文章里可能拼写错的某个单词。比如把separetesaparatesaperete之类“自动纠正”到separate,而且不受大小写限制(seParate, separaTe也可以纠正);

如果你是专业用户:

  • 验证用户输入的手机号、邮件地址是否合法(还记得填写网页表单时常见的提示吗?);
  • 提取网页源代码中的所有图片链接、超链接(搜索引擎就是这么干的);
  • 提取文本中的邮件地址(现在你知道自己的邮件地址怎么被“抓”走了吧?);
  • 进行复杂的格式检查(把各种小数“统一”成精度为0.01的格式,去掉重复的单词);

这样的任务可能并不是我们日常工作的主要内容,“不幸”遇上了却非常烦人——简单重复劳动往往要耗费我们大量的时间。所以,在《卓有成效的程序员》(Neal Ford著,机械工业出版社2009年版)中,作者写道:

在我刚才提到的例子中,开发人员用了1小时58分建立正确的(正则表达式)语法,然后用了不足两分钟运行。在一些未曾培训过的人眼里,他的大多数时间都没有效率(这就是为什么他们反对使用正则表达式的原因),但最后,他节省的是几天的时间!

正则表达式还可以玩出更炫更酷的花样;不过现在,你只需要记得一点:不管任务有多么复杂,多么“不可思议”,只要掌握了关于正则表达式的基础知识,就能“以不变应万变”。这有点像搭积木,如果你学会了搭积木,就能随心所欲地搭出各种结构。而这本书的目的,就是教会你“搭积木”,也就是学会正则表达式;如果加以锻炼,相信不久你就能游刃有余地解决各种古怪的问题了。

现在我们回头说说正则表达式的古怪名字。我估计,古怪主要的原因来自“正则”——我们的生活中很少见到“正则”这个词。其实,“正则”的原文是英文单词regular,也就是“规则”、“规范”的意思。谈到这个regular,就必须提到一个概念:“正规语言(regular language)”,概略地说,“正规语言”就是严格遵照一系列规则构造出来的“语言”(不同于规则模糊且“可能出错”的自然语言)。“正则表达式”的“正则”,也就是这个“正规”的意思,它可以进行精确(很细致但也很“繁琐”)的描述:举个例子,我们都能一眼判断出,一串字符是不是“邮政编码”,但用正规语言来描述,大概是这样的——“邮政编码是前后相连的六个十进制数字字符(也就是说,每个字符都是0123456789之一),而且在这六个字符前后不能再出现数字字符”,如果我们能这样“规范”地表达自己的意图,计算机就可以匹配邮政编码字符串了。正则表达式的功能之所以强大,正是因为它可以“正规(规范)”的方式进行精确的描述/表达自己操作文本的意图——有了规矩才有方圆,计算机才能完整准确“理解”我们的想法。

回过头来,无论你看“正则表达式”这个名字是否顺眼,现在只要明白它是用来处理文本的强大工具,就可以了。

谁适合阅读这本书

本书名为《正则表达式傻瓜书》,并非假设读者都是傻瓜,而是保证“傻瓜都能看懂”。从这个定义出发,我们假设读者对正则表达式没有任何基础。如果您真的“不幸”成为其中一员,那么恭喜你,整本书都是为你而写的,如果你能循序渐进,一路修炼下来,一定能把正则表达式玩弄于股掌之间。

如果你之前已经稍微了解了正则表达式,那么更要恭喜你。我发现,许多接触过正则表达式的开发人员,并不真正理解正则表达式。要用的时候,他们往往从网上搜索一个正则表达式应急,如果不能用就改改——却往往被“这个字符是否要转义呢?”、“这门语言是否支持这个功能”之类的问题困扰。如果你能细心读完本书的基础章节,一定能厘清概念、正本清源,功力再上层楼。

如果你自认为在这两种人之外,或者毫无兴趣,也不必“自绝于”本书。你可能会在很多地方遇到正则表达式:你的浏览器是否有插件屏蔽某些网站的广告?如果有,你想不想设定应当屏蔽“哪些”广告?你在访问一些网站时是否遇到了困难,而需要为“某些”网站设定使用代理服务器?如果是,那么你不妨学一点正则表达式。俄罗斯有句谚语是这么说的:年轻人,多学一点本领总不是坏事!

学习方法

关于正则表达式,最重要的概念就是“规定可变的范围”:它匹配和处理的文本的是“变化”的(如果你只想寻找“中华人民共和国”之类的固定字符串,直接进行简单的查找就是了;电子邮件地址则不同,它的样子千变万化,这时候就得正则表达式上场了),但这些“变化”又必须遵循一定的规则(“电子邮件地址不就是“用户名 + @ + 主机名”嘛,超文本链接不就是“http:// + 主机名 + 路径 + 参数[可能出现]”嘛,而“用户名”、“主机名”之类都有明确的规定:可以按什么次序,出现什么字符),只要想明白了这回事,再弄明白正则表达式的规则和门道,把这些“变化规则”准确描述出来,就可做到“万变不离其宗”——任文本的形式怎么变化,都逃不出你的手掌心。

要学会正则表达式的规则和门道,可以分三步,据此,本书也分为三个部分:

第一步,基础,提供“入门”和“基础”。如果你从未接触过“正则表达式”,我们将从最简单的通配符的概念介绍起,帮你建立最基本的感性认识,并介绍正则表达式中最最基本的功能,它们不但可以用在编程语言里,也可以应用在平常的工具中。所以,看完这一部分,你应该能了解最基本最常用的关于正则表达式的概念,并把它们应用的日常工作中去。

第二步,进阶,在这里,我们会跟进一步,介绍普通文字编辑软件没有提供,但正则表达式中常用的重要知识、譬如多选结构、分组等等。并辅以恰当的例子,让读者通过对比,深刻牢固地认识这些概念。可以说,如果能充分认识、恰当运用这些概念,就能完成80%基本的字符处理任务,这也是本书的重中之重。

第三步,深入,向读者介绍更复杂、更高级的功能。包括其它类型的量词、匹配模式、锚点等,并会讲解一些常见的应用思路。了解了这些概念,你就能玩出更炫更酷的花样,也能解决更复杂、更罕见的问题。另外,在本章,我们会简单介绍不同的语言对于正则表达式的实现,以及各自的特点。

对于一般人,掌握这些已经完全足够了,如果学完这些,你还不满足,要“百尺竿头更进一步”,推荐你阅读本人翻译的《精通正则表达式(第三版)》(Jeffery Friedl著,电子工业出版社2007年版)的高级内容,相信你一定会有新的收获。

本书的价值

可能有读者会问,现在关于正则表达式的书那么多,有《精通正则表达式》,有《正则表达式必知必会》……为什么还要有“傻瓜书”?

确实,为什么要有“傻瓜书”呢?我学正则表达式的时候,也就是生吞活剥了《精通正则表达式》的影印版,就大致掌握了这东西嘛。

不过,在后来的开发中,我逐渐发现,对于处于开发前线、需要亲手写正则表达式的广大同行,从阅读关于正则表达式的经典作品,到熟练解决手上的难题,还有一个艰难的过程。比如,我曾遇到过这样的任务:要把一段文本里的“湖南株洲”智能地替换为“湖南省株洲市”,简单的文本替换在这里是行不通的,因为原文中可能也存在“湖南株洲市”,简单替换之后就成了“湖南省株洲市市”(出现了两个重复的“市”)(程序员可能会说,用程序检查“株洲”之后的那个字符就可以了嘛,但这是不行的,因为可能遇到越界错误:如果文本最后的两个字符就是“株洲”,后面不存在任何字符,检查“后一个”字符的程序就会报错退出)。再比如,不同的语言/环境在对正则表达式的支持上也有细微的区别,许多时候我们明白“理论上”应该怎么做,但实际操作起来又有诸多掣肘(譬如在ApacheRewrite规则中,怎样用一条表达式匹配“除aaa.somesite.combbb.somesite.com之外somesite.com的所有子域名”呢),该怎样绕过它们,很多书都没有讲,只能依靠猜测和经验来解决。本书在讲解相关的知识点时,会尽可能贴近实际的应用,举出针对具体开发环境的例子,降低大家从“阅读外版书借鉴国外经验”到“消化、吸收,应用到具体开发”之间的学习成本。

另一方面,不少关于正则表达式的书籍往往追求全面,把各条知识事无巨细地罗列出来。然而广大应用正则表达式的人员,却不太可能有精力通读这些“百科全书”,对他们来说,最重要的就是在遇到问题的时候迅速地找到最合适表达式,或者看懂某个复杂的表达式,加以改造,服务于自己的工作。这一点,我自己也深有体会。因此,本书会着重讲解一些常用的正则表达式,并提供它们在各种语言中的版本,并讲解一些常见问题的解决思路(常见的“与、或、非”的逻辑要怎么表达,掌握了这些,你就可以解决“长度在8到16之间,同时包含字母、数字、符号的密码字符串怎么用一条正则表达式验证”的问题了);即便您找不到直接使用的例子,也可以触类旁通。从某种意义上说,您也可以将本书放在案头,当成“正则表达式速查手册”。

最后,在《精通正则表达式》中文版出版之后,我收到了许多热心读者的来信,也看到许多读者的评论。有不少读者觉得,《精通》一书确实不错,但详略安排可以更好:许多简单的问题往往花费了太多时间,生怕读者不懂,而复杂问题的讲解又不够到位,读来总有十全九美的遗憾;也有读者觉得,《精通》的篇幅太长,价格太贵,并非人人都要“精通”,降低点成本,“够用”就好。有鉴于此,我会努力根据读者的意见,尽力调整这本书的详略安排。当然,贯彻始终的原则是“简明扼要”。毕竟,学习正则表达式就是为了节省时间,如果让读者在“不值得”的内容上浪费时间,就违背了本书的写作初衷。

勘误

我向读者保证会尽力避免犯错,但是我不敢保证这本书里没有错误。所以,如果您在阅读时发现有错误,请一定来信告知,您的意见,经过确认,会进入勘误列表,并在下次重印时进行订正。当然,我也欢迎您来信告知建议、感想。我的电子邮箱是 [email protected],诚挚地等待您的邮件。

怎样翻译更地道:翻译如铺路

我们所使用的绝大多数语言都具有这样的特性:既有规则(譬如句子要有完整的结构,过去发生的动作要用过去式),又没有规则(譬如不规则动词,以及某些“妙手偶得”的奇妙搭配)。可以说,语言的创造性,就在于能够灵活游走于这种有/无规则的矛盾之间,“产生”出历史上未曾有过,但意义完整的通顺句子。这种“游走”,在我们使用母语时,往往是凭借本能完成的:“千锤百炼”可以调换成“百炼千锤”,但不能变成“千炼百锤”。
但是,译者在翻译时则不容易做到这一点,究其原因,一方面是译者受到原语言规则的影响,往往把原文的思维“照搬”过来;另一方面,词典提供的往往是僵化或者说“过度统一”的解释,结果造成译文生硬晦涩——或许能看懂,但是很别扭。这方面最典型的例子,就是er/or后缀的翻译。

er/or是英文中常见的后缀,其英文解释是the person/thing that(does the action indicated by verb),在英文中,这是方便、简单而且统一的用法:无论什么动作,加上er/or后缀,就可以表示执行的人/器物。翻查英汉词典,通常将这个翻译为“xx者/xx的人”,这个翻译,意思是对的,但译法却不一定合适。
我们不妨来想想,中文语境中各种er/or对应的各种说法,总结一下这个后缀的多种翻译:

  • ~师:培训师(trainer),律师(lawyer)
  • ~者:译者(translator),读者(reader),作者(author)
  • ~员:演员(actor),官员(officer),接线员(operator)
  • ~官:指挥官(commander),法官(judger),翻译官(translator)
  • ~手:水手(sailor)
  • ~士:战士(soldier),辩护士(defender)
  • ~民:农民(farmer),选民(voter)
  • ~人:工人(worker),诗人(songster)
  • ~方:资方(employer)
  • ~主:雇主(employer)
  • ~工:水管工(plumber)
  • ~匠:木匠(carpenter),工匠(artificer),油漆匠(painter)
  • ~家:画家(painter),摄影家(photographer)

此外,还有些名称,似乎完全不包含“与人相关的元素”:导演(director),司机(driver),园丁(gardener)……

这样一个“通用”的er/or,意思也不难理解,在翻译时,竟然可以变出这么多种的花样,的确很麻烦,但却是译者不容推辞的责任:“鬼子带了个翻译官来”不能说成“鬼子带了个译者来”,“劳方/资方关系”远远比“雇佣者/被雇佣者关系”要简捷得多,“对选民负责”也好过“对投票者负责”。
从这里推演开去,相同的语言成分(甚至相同意象)在翻译时要变化对待的例子还有许多:radicalism 有时应该翻译成“激进派”而不是“激进主义者”(虽然-ist似乎约定俗成就是“主义者”,international community也不是“国际社群”而是“国际社会”(虽然community似乎跟“社会”没关系)。

看到这里,有些人要说,这还不是中文太麻烦了,英文就没有这样的问题——整齐划一,规律好记。可是,事实果真如此吗?单单“星期1”到“星期6”,和“1月”到“12月”这样的简单名词,就没法像中文一样“归化”到整齐的形式,更不用说那一大堆的不规则动词,每个都得记住过去式、过去分词的麻烦了。依我看,问题并不是中文英文孰优孰劣,而是说话的人,身上的懒筋在作怪——我总觉得,语言中的每个元素,都好像一块独特而不规律的石头,译者的责任,就是找到(或者说“发现”)这些石头彼此合适的一面,重新铺就一条坦途,倘若偷懒照统一的方式来摆放,铺出来的路肯定是会硌脚的。

怎样翻译更地道:补语篇

题记:“怎样翻译更地道”是我在翻译中的随想,没有固定的更新间隔,供有兴趣的读者参考。

我们学英语,都追求“地道”,也就是“洋味浓”,要做到这一点,就得突破“意思”的层面,把握英语在形式上的某些特点。譬如下面的句子:Please buy two tickets for me,I think to do this is my honor,虽然“意思正确”,但明显不够地道;Please buy me two tickets和I grant it my honor to do this,洋味就“浓”多了。

汉语也是如此,只是我们以汉语为母语,此时“意思”和“形式”常常是混为一体的。但是,在翻译中,往往由于形式奇特,就出现了“欧化中文”和“翻译体”。为了解决这个问题,需要在平时多用心注意汉语在表达上的特点。今天我们要讨论的补语(及其用法),就是其中之一。

一般认为,补语就是补充说明述语的结果、程度、趋向、可能、状态、数量等的成分。具体的解释非常多,这里略不赘述。作为译者,我们只要记得三点:

  1. 补语一般是放在它补充的对象之后的(类似英语中的从句),因此有时候需要把对象“切开”,把补语“塞”进去;
  2. 句子去掉补语也有可能结构完整(譬如“晚会开”),但在实际语境中,补语和它补充的对象需要组合在一起,表达才有“意义”(譬如“晚会开得很热闹”。“晚会开”这样的句子虽然结构完整,却没有人用);
  3. 在肯定时,补语多在对象后添加“”字结构,在否定时,多把对象切开,把“”字塞进去。

不妨来看下面这几个句子:

  • His handwriting is pretty good.

不要小看这个句子,handwriting之类的名词,往往是翻译中的难点,因为它是从动词“演变”而来,而汉语中缺乏对应的形式(“他的写字”就像日语了),译者往往会只能另找词汇,handwriting还好,可以翻译成“书法”、“字迹”,不过也不太方便:

  • 他的书法很不错
    他的字迹很漂亮。

这样的句子,无论怎么修饰,还是去不掉“翻译腔”,原因就在于handwriting“对应”不到合适的名词。然而我们仔细想想就会发现,handwriting这样的“动词变形而来的名词”,往往不会在句子中单独出现,而必须与其它成分并用。这时候,如果用“动词+补语”的形式来翻译,就解决了“寻找对应名词”的困难:我们在handwriting对应的“写字”颠倒一下,后面加上“得”字结构:

  • 他的字写得很漂亮

这就是补语的肯定形式,下面我们再看看否定形式。

  • I can no longer walk.

这个句子通常翻译为:

  • 我实在不能走了。

类似的句子也很多,尤其是涉及到can not之类的情态动词结构,第一反应往往就是“不能”、“无法”,所以,无论下面是什么动作,什么成分,前面都要加一个“不能”、“无法”。其实,这种结构非常合适用补语来翻译,只是需要动一点脑筋,根据具体的动词来选择“不”后面的字。具体到这个句子,我们不妨在“走”和“不”之后添加一个“动”字,就可以翻译为:

  • 我再也走不动了。

beta技术沙龙:大型网站的Lucene应用

beta技术沙龙越办越有意思了,上次错过了阙宏宇的mod_cache(还有关于线程进程的讨论)就很可惜,这次关于Lucene的演讲,是无论如何不应该错过了。

到目前为止,全文检索已经完全不算高技术门槛了,记得以前看过一本书里面写:“今天,任何程序员,都可以很容易地构造一个全文检索应用”。是的,全文检索的基本原理大家都知道差不多了,剩下的只是实践。我见过纯粹自己开发的,具有AS(Advanced Search)、BS(Basic Search)、DI(Digest)等结构,“像模像样”的全文检索架构,不过应用更多的,却是在开源项目上完善、定制而来的,Apache的Lucene就是众多开源全文检索项目中,名气最大、资格最老、应用也最广泛的一个。本期beta技术沙龙,讲的就是大型网站中lucene的应用,主讲人是手机之家团队的唐福林(“手机之家”总是有些东东来共享,比如上次的DAL,这真是不错)。

众所周知,用Lucene构造一个“索引-查询”的应用是非常简单的,搭好环境,参照(修改)示范代码,很容易就可以成功。但是,要构造一个真正大规模、稳定、可靠的应用,就不说这么简单。程序的编写、模块的分布、架构的设计,都有许多费心思的讲究。按照PPT提供的数据,手机之家目前的Lucene应用,采用的是Lucene 2.4.1 + JDK 1.6(64 bit)的组合,运行在8 CPU, 32G内存的机器上,数据量超过3300万条,原始数据文件超过14G,每天需要支持超过35万次的查询,高峰时期QPS超过20。单看这些数据可能并没有大的亮点,但它的重建和更新都是自动化完成,而且两项任务可以同时运行,另一方面,在不影响服务可靠性的前提下,尽可能快地更新数据(如果两者发生冲突,则优先保证可用性,延迟更新),其中的工作量还是非常大的。

演讲的主要内容都PPT里,非常丰富,我就不再赘述了。要补充的是,这份PPT做得非常清楚,需求-目标-进度-设计-上线-测试-上线,整个流程非常清楚,给出的数据同样非常精当,我想,这也反映了手机之家团队的开发规范。

因为对Lucene的使用稍微有些经验,我在这里补充几句,权当狗尾续貂:

  1. 在大规模的应用中,Lucene更适合用于狭义的“搜索”,而不应当负责数据的存储。我们看看Lucene的源代码也可以知道,Document和Field的存储效率是不够好看的。手机之家的团队也发现了这一点,他们的办法是,用Lucene存放索引,用Memcache + Berkeley DB(Java Edition)负责存储。这样有两个好处,一是减小了Lucene的数据规模,提高了程序的效率;另一方面,这套系统也可以提供某些类似SQL的查询功能。实际上,Lucene Project自己似乎也注意到了这个问题,在Store中新增了一个db选项,其实也是利用的Berkeley DB。如果仅仅用Lucene存放索引,而不存放Document,并且合理配置,一台机器可以支持几十G甚至上百G的索引;如果需要用Lucene存放索引,最好在读取时使用FieldSelector,只读取需要的Field,如果使用恰当,性能会有10%左右的提升。
  2. 在大规模应用中,Cache是非常重要的。PPT中也提到,可以在程序提供服务之前,进行几次”预热“搜索,填充Searcher的Cache。据我们(银杏搜索)的经验,也可以在应用程序中,再提供针对Document的Cache,这样对性能有较大的改善(同一个JVM内部的Cache,速度更快一些)。Lucene自己似乎也注意到了这个问题,在2.4版本中提供了Cache,并提供了一个LRU Cache实现。不过据我们测试,在极端情况下,这个Cache可能会突破大小限制,一路膨胀最后吃光内存,甚至从网络上找的许多LRU Cache实现在极端条件下都有可能出现这样的问题(这也是我们百思不得其解的地方:反复检查程序的逻辑都没有问题),最终自己写了一个LRU Cache,并修改多次,目前来看是稳定的。
  3. 在编写Java服务程序的时候,记得设置退出的钩子函数(RunTime.getRunTime.addShutdownHook)是一个非常好的习惯。许多Java程序员都没有这种意识,或者有,也只是写一个finalize函数,结果程序非正常退出时,可能造成某些外部资源的状态不稳定。拿Lucene来说,之前的IndexWriter是默认autoCommit的,这样每添加一条记录,就提交一次,好处是如果中断,则之前添加的记录都是可用的,坏处则是,索引的速度非常低。在新版本中autoCommit默认为False,速度提升明显(我们测试的结果是,提高了大约8倍),但如果中途异常退出,则前功尽弃。如果我们添加了退出的钩子函数,捕获到退出信号则自动调用writer.close()方法,就可以避免这个问题。
  4. 目前的Lucene是兼容JDK 1.4的,它的binary版本也是JDK1.4编译的,如果对性能要求比较高,可以自行下载Lucene Source Code,用更新版本的JDK编译出.jar文件,据我测试,速度大约有30%的提升。
  5. 如果对并发的要求较高,可以考虑采用多IndexSearcher的技术,也就是在一个应用服务中,开启多个IndexReader(可以对同样的索引开启多个),每个IndexReader再生成一个IndexSearcher,将这些Searcher放在一个“池”里头,给搜索请求调用。这样可以大幅度提高并发的性能,代价是在写程序的时候就要考虑到这一点,进行相应的调整。

P.S. 据我观察,国内公司内部的项目,一般取的名字都中规中矩,以’er’结尾的比较多,多是Indexer, Crawler, Layer之类。好像很少有外国那种“天马行空”的奇特名字,譬如Hadoop(这是一个“没来由”的名字)、Lucene(这是个少见的姓)。国内我接触过不多,以前抓虾有个重要的DB叫tudui(“土堆”),目前银杏有个项目叫LaserTank,都是跟实际用途毫不相关的,印象反而深刻。

你的香蕉怎样剥?

我第一次吃香蕉的时候,顺手把蕉把(也就是互相连结的那一头)一揪,就开始剥皮。父亲看了说:怎么能这样剥香蕉呢,一定要从另外一头开始剥的。在父母面前,小孩子当然不敢当面顶撞,而且,我并没有尝试过从另一头开始剥。可是比较两种方法之后,我觉得还是自己的方法更好,于是去找父亲理论。父亲又试了试,说:“真怪,从这一头来剥,果然是要省事一些,可是我们从小到大,都是从那头开始剥皮的呀。”

这件事过去已经有二十多年了,我却时常想起它,开始只是告诉自己“不要迷信权威”,到后来,我逐渐发现,在生活中,一些看似简单的事情,似乎有“想当然(甚至都不用‘想’)”的做法,可是这做法未必好,许多时候,我们甚至需要意识到“我这是在想当然”、“想当然其实并不是好的办法”,于是或者另辟蹊径别出心裁,或者放下身段重新学习,结果要么遇见别样洞天,要么由此再上层楼;这样的事情,在我的生活中,一次又一次地被验证。

最近一次的经验,来自读书。
我最开始读书,主要看重的就是故事情节,哪几个人,发生了一回怎样的事情,了解了这些,就差不多够了。那时候,除了情节,学校语文课顶多教教“拿个小本子,把优美词句、名人名言记录下来”。这种做法,我从来没有尝试过,所以它有什么效果,我也不得而知。“读书这样简单的事情,还有什么道理可言吗?”许多年里,这就是我“想当然”的观点。
印象里,第一次启蒙来自在大学学到姚斯的“接受美学理论”:审美经验原来分为三个层次!原来普通的“愉悦”只停留在最表面的层次!这可以算“歪打正着”吧,姚斯说的本是对于文艺作品的接受,可是我却想到了自己读书的方法原来那么“原始”,并一下子明白了,为什么同样读一本书,不同的人能谈出来的内容完全不一样。
原来,这道理就和剥香蕉一样,即便是“读书”这样“再简单不过”、“人人都会”的事情,居然也可以有门道可言,居然也有规矩可讲。在这些门道和规矩面前,我这个“读”了许多年书的人,似乎根本“没有上道”!于是我开始有意识地学习和锻炼读书的方法:记住作者的名字,了解作品的背景,寻找文字的特点,并且,要针对不同的文本,选择不同的方法和侧重点……这样下来,成效大增(去年有位朋友与我谈及读书,最后问我:你这样的阅读境界,是怎样来的呢?)
读书这样简单的事情,自己探索和反思都可以收到这样的成果,那么已经成型的“门道”和“规矩”,岂不是帮助更大?可是,这些“门道”和“规矩”,究竟在哪里呢,是不是只能自己反省、参悟?这个问题我一直不很清楚,直到最近接触了《如何阅读一本书》才想到,所谓“门道”和“规矩”,大概就存在于这样的书里头罢。

《如何阅读一本书》
,就是一本关于“读书”的书(是的,它就好像一口咬住自己尾巴的蛇)。这本初版六十多年来不断重印、再版的书,就是要教会读者读书的方法。大致来说,整本书分为三个部分:第一部分可以视作纵向探究,主要向读者介绍阅读层次的概念,由下而上依次介绍了基础阅读(也就是初步的,毫无结构的阅读)和检视阅读(带有结构意识,能够抓住主题的阅读)。第二部分则着重讲解阅读的第三层次,也就是分析阅读,即如何“通透”——紧紧抓住这本书,做到“我注六经”,读到这本书“真正属于自己”为止。有过亲身经历的人都知道,这并非易事,大学里的一些课程,老师会带领学生,把大量的精力倾注到少数的经典上——可惜,许多人即便花了大量的时间精力,因为没有掌握合理的阅读方法和技巧,也无法达到“通透”的境界;我想,作者之所以愿意花三分之一的篇幅来讲分析阅读,原因也部分在于分析阅读是如此重要,而它本身又需要大量的技巧和锻炼。全书的第三部分则可以视为横向解剖,在这一部分,作者分门别类地讲解了各类书籍的阅读方法:实用型的书籍要如何阅读,想象文学要如何阅读,历史、科学、数学、哲学类书籍,又应当怎样阅读……虽然我们未必要完全遵照其中的方法去阅读各类文本,但是相比自己从头开始积累,书中提到的各种办法,无疑是具有相当价值的,不容错过。
另外,这本书也有一些总览性建议,我觉得很受用,譬如阅读书籍时,要仔细观察目录,要读一读作者的前言,要仔细想想每一部分的标题……这些都是我之前读书不太重视,其实又非常重要的技巧(当然,这也可能与我国特色的前言、序言有关,不信,看看《中华人民共和国宪法》),以后应当努力改进。

也许有人会说:读书就是读书,要那么多条条框框干什么,太累了。
没错,一千个读者就有一千个汉姆雷特,极端地说,作品的意义完全存在于“接受”的过程之中。可是,我又分明看到,同样的一本书,同样的时间,有人还在刀耕火种,也有人读书却已经开上了联合收割机——虽说都是“接受”,可是接受的深度和广度,却有如此的差别。造成这种差别的,恐怕并非悟性,而是清晰的思维,和长期有意识的锻炼。

朋友,你的香蕉是怎样剥的?你的书,又是怎样读的呢?

《精通正则表达式》第四次重印

出版两年之后,《精通正则表达式》马上要第四次重印了,这个消息很是让我兴奋。

我读大学的时候,有幸接触到侯捷老师的许多文章,尤其是他谈关于选择技术书籍的言论,感觉受益匪浅——正是从此,我深刻认识到,“学习”的宾语不应该是“教材”,而是“知识”。认识到这一点,就豁然开朗了;当然,也无比真切地知道了好的书籍是多么重要。

另一方面,我也深信,总的来说,知识的价值是在传播中实现的。我经历过“有了新的收获自己保密,一人独享”,也经历过众人把自己的心得拿出来分享、彼此协作的环境,两厢对比,后者提供的满足感远远超越前者。因此,有更多的人迅速学会“卑之无甚高论”的正则表达式,不需要重走我自己当初学习的弯路,对我来说,也是一种不小的满足(相比之下,帮人写各种表达式所得到的满足,实在是“很小很小”)。

在这里还要感谢博文视点的编辑许莹,她细心地把目前勘误列表列出的所有错误都做了订正。因为《精通》一书中存在的错误,始终是我的一块心病。

另外,要兑现我年初的计划,今年要写一本关于正则表达式的书,正好在这里征集大家的意见:你们是期望它更加“下里巴人”,包括Word, EditPlus等等常用软件的应用例子,以应付更广泛的工作呢;还是希望更加“阳春白雪”,与狭义的“IT行业”(也就是开发)靠的更近呢? 或者有什么别的想法,还请不吝赐教。

6月26日看片会:人工智能如何改变世界

虽然医生嘱咐要静养,可谁让“小姬看片会”那么好玩呢?周六下午两点钟,我顶着大太阳来到微软亚洲研究院地下一层的会议厅,加入了第9期小姬看片会。

本次看片会的主题是“人工智能如何改变世界”,选的影片是BBC的VISIONS OF THE FUTURE。这样“科技含量高”(用松鼠的行话说,就是“高级”),又比较偏IT的主题,放在这里举办(尤其还有免费饮料提供),真是再合适也没有了。
参加的人很多,看来大家热情都比较高,我首先找了个僻静地方“潜伏”下来,过会儿居然有人来打招呼,一看居然是刘未鹏,不久又遇到图灵的刘江老师。寒暄一阵,眼看影片要开始了,我提议搬三把椅子坐到中间的过道里,这样比较靠前,大家一致同意,于是搬了三把椅子,在过道里前后排成一列,影片正好开始。

影片分好几个方面,介绍了广义上的“人工智能”:模拟人类行为、虚拟现实、机器与生物的结合等等。关于狭义的“人工智能”,也就是与“智慧”相关的内容,并没有占太多的比重。我觉得,作为科普影片,这样的安排,是没有错的。
影片结束之后,照例是嘉宾与观众的互动环节,这次的嘉宾都很“高级”,几乎都是IT专业人士,即便有研究科幻非IT人士,来头也相当大。难能可贵的是,他们回答起问题来,丝毫没有普通人想象中的“死板”,而是妙趣横生,许多时候台上台下笑成一片,这正好应了松鼠会的口号:让科学变得有趣。另一方面,它也说明,活动办得非常成功。

我不是研究人工智能的,只是以前稍微看过一点点资料,参加完这次活动,也有两点想法,可惜小姬没给我当场提问的机会(观众实在是太热情了),写在自己blog上吧。
关于“人工智能”,我想大概可以分为好几个方面,之一就是“仿真”(也就是模拟生物体的机能),在片中我们可以看到,现在已经有机器人可以惟妙惟肖地模仿人类的许多行为,譬如会走路的机器人Asimo;换句话说,神秘而简单的生物行为,可以通过物理模型、数学公式来分解,加以实现。人类“潜意识”里的某些“感性”动作,譬如“向前走五步”,可以转化为一系列精妙的指令,以另一种方式来“理性”实现。当然,这个方面也存在困难,譬如王启宁博士所说,用电气元件模拟生物行为,总是存在若干困难,因而有时候,“电气元件–生物”混合的方式,反而效率更高。这一点在影片中也有提及,研究人员记录了小白鼠脑内的海马区(与短期记忆相关的区域)在不同情况下的电流脉冲,再照这些规则制造出芯片,植入小白鼠体内,据称“反应速度提高了40%左右”。我对这个例子很有兴趣,可惜,它在影片中只是一带而过。
另一个方面,也是大家非常感兴趣的方面,就是“机器能否具有智慧”,或者说“机器能否像人一样思维”。这个问题,关于这个问题,前面有过著名的图灵检验,后来塞尔教授又提出过“中文屋子”问题,于是产生了“强人工智能/弱人工智能”的分野。按照程序的逻辑,我们关心的是“接口”,而不是“实现”。也就是说,不管我们面对的“其实”是机器,还是人,只要我们无法做出区分,就可以认定,对方“就是”人,虽然就其“本质”上来说仍然是机器。我之所以对那个小白鼠的例子感兴趣,原因也在这里:如果我们不去关心这些脉冲所“承载”的意义,而只是在“黑屋子”外,模拟我们观察到的一切,能产生完全相同的结果,那么或许可以说,电脑模拟的这些信号,本身也“承载”了同样的意义?
关于这个问题,现场的毛老师也提到,关于“什么是人(智慧)”,我们目前可能很难给出一个确定的定义,而只能通过多个特征加以描述和归纳,如果机器实现了这些特征,我们仍然“愿意”觉得,这还是与人有区别的,或者说,大家心中,其实多少还有一点对“人类本质”的偏执。在我看来,这有点类似波普尔所说的“本质论”和“标签论”的关系,通过“贴标签”而不是“追求本质”的方式(譬如把某种现象定义为“重力”而不是研究“重力的本质是什么”),科学已经取得了长足的进展,未来有一天,如果我们彻底无法区分面对的是人还是机器,我们是认定“这就是人”,还是心智紊乱,或者苦心积虑地找到另一种“定义”,把对方排除出“人”的范畴?这是个有意思的问题。
另说一点,在看片的时候,我忽然想到象棋与围棋,机器“深蓝”已经可以在国际象棋上打败人类世界冠军,但是对于围棋,目前人工智能却束手无策,最好的围棋程序,也敌不过一般的围棋爱好者。大家认为,主要原因是围棋的可能性太多,决策树太过复杂,超过了目前计算机的计算能力。如果人类的大脑能够处理这样两类计算量迥然不同的任务,这到底说明,我们理解棋类游戏的算法思路有问题(也就是说,大脑的运算能力是一定的,只是我们还没找到围棋的高效算法),还是人类大脑的运算(思维)能力,其实是不能以单一维度衡量的(从某个维度上来说,在解决围棋的问题上,大脑可以展现出远远高于象棋的计算能力)?

好了,闲话这么多,就此打住。最后给松鼠会提两点意见:
1.我看片的时候,听讲解的同时看了看字幕,发现字幕有一些翻译错误,所幸不是很严重。因为字幕是合成在影片一起的,所以,这样的问题也不能怪罪松鼠会。不过以后准备材料时,多注意一些这样的细节(如果同一部片子有多个字幕,不妨选择质量最好的那个版本),另外互动环节如果出现问题应该当时纠正(比如有人说“吃转基因食品,我们的基因就会随之变化”,应当有人马上澄清),可能更好点。
2.如果能够在影片结束后,做一些入门性的背景介绍(譬如这一次的,可以介绍图灵检验,中文屋,图灵完备性等概念),这样可以真正“以科普为中心”,而不会“以影片为中心”,对主题起到辅助和补充的作用(而不限于影片所介绍的知识),在专业的嘉宾和热情的观众之间夯实基础的沟通桥梁,效果可能更好。

身份证掉了怎么办?捡起来

大概是1992年左右,我第一次见到“脑筋急转弯”,马上就被这种新鲜玩意迷住了。可惜,当年那些稀奇古怪的问题,如今都忘得差不多了,记得的只有一道题——身份证掉了怎么办?捡起来。
记得这道题,并不是因为它很难,而是因为,这些年来的许多经历,事后细细咀嚼起来,答案就象“身份证掉了怎么办?捡起来”一样简单、直白,又荒诞、无奈——或者怨天尤人,或者凭空慨叹,或者沉溺空想;总之,薄薄的一张身份证,就是没法捡起来。

2006年末我着手翻译《精通正则表达式(第三版)》的经历,就是如此。
试译的时候,我一晚上就交出了六页译稿。接下来约定全书的交稿时间,我不假思索地说,最多六个月。可书拿到手的那一瞬间,整个人就傻了——五百页,整整五百页,以前是轻松畅快的阅读体验,现在是堆积如山的任务列表!
照计划,我每天应该翻译四到五页,然而我只坚持了两天就中止了——每天下班累的要死,还得做这劳什子翻译,五百页何时能结束啊?
于是干脆自我放纵,过了一周“轻松加愉快”的生活,到周末,我甚至在想,原来生活这么美妙,干脆取消合同,交违约金算了吧。
然而,就在这一瞬间,我忽然又想到了那个脑筋急转弯——“身份证掉了怎么办?捡起来”。现在的这张身份证,无非就是在六个月内完成这本书的翻译嘛。而捡起来的方式,就是坚持每天翻译而已。答案简单到完全不存在其它的可能,简单到毫无风险。
于是我开始忍住内心的厌倦,慢慢捡起这张掉落的身份证。五页,十页;一章,两章……每做一点点,我都安慰自己说,对了,我在一点点地捡起那张身份证。
说来也奇怪,日子一天天地过去,我反倒习惯了这样的生活,厌倦的感觉也渐渐消失了。
全书翻译就要结束的时候,一次误操作弄丢了上万字的译稿,怎样也无法恢复。痛惜了半小时之后,我忽地又一次想到了“身份证掉了怎么办”这个问题。好吧,既然无法挽回,重新来过就是了。

这一回,为什么恢复得这么快?是习惯使然吗?
这个问题,一直保持到去年翻译温伯格的《技术领导之路》:在第四章,温伯格用现身说法讲解了,我们在成长过程中必然会遭遇“峡谷”,而且总结说:

高原-峡谷模型描述了一个轮回周期,也描述了一个元周期——也就是周期的周期,这是个螺旋,就好像鹦鹉螺上一圈圈的纹路。走出低谷,不仅仅是登上另一个高原,同时也是在另一个高原上前进,这就是学习如何成长的高原。我的确是在学习新的语言,但更重要的是,我在学习“如何学习计算机语言”。这种元学习(meta-learning) ,清楚地说明了我对学习新语言的情绪。我不再焦虑,不再抵触,不再觉得毫无价值,而是感到激动,充满创造活力,能够处理几乎任何事情。

译到这里,我豁然开朗了:是了,我能更快地接受现实的解决办法,直接开始“捡身份证”,原因就在这里;我也发现,自己开始翻译《技术领导之路》的时候,已经没有了当初的煎熬:划分好进度,每日执行就是了。

《技术领导之路》一路翻译下来,我越来越赞同序言的说法:温伯格的这本书“卑之无甚高论”,并没有太多稀奇。如果“成为技术领导”是掉落的那张身份证,这本书不是传说中的《九阴真经》:温伯格只是告诉你,怎样才能把它“捡”起来——你仍然需要弯腰,仍然需要伸手。如此说来,更大的问题或许在于,你是否愿意(是否能)按部就班地去做。
譬如在第七章,温伯格介绍了一种加深自我认识的“不起眼”的办法,写日记:每天五分钟就可以,写下自己想写的内容,过一个月再回顾;第十章介绍了一种办法,照它一步步地分析,我们发现“小小的”误会背后,原来有长长的逻辑链条;第十三章给出了协调“自己跟自己过不去”的那些“做人原则”与现实的途径……凡此种种,不胜枚举。许多做法普通到不可思议,然而根据我的亲身实践,它们的确有效——退一万步说,至少能作为颇具价值的参考。

类似的例子,在这本书中还有许多。我也逐渐发现,许多时候我们希望“取其精华、去其糟粕”,然而这世界上的许多问题的答案,并没有“精华”那般完美,反而就像“捡起来”那样直接、平凡,这时候,困扰我们的,已经不再是一个智力的问题,而是自制力、毅力和决心的问题了。

读温伯格的《技术领导之路》,会看到那张掉落的身份证。那么,让我们一起捡起它吧。