思考范式的转变

计算技术、认知科学、脑科学等等领域的发展,总让我们觉得下面几十年,会有一场深刻的思考方式的转变……

纽约时报:“奥巴马政府正在规划一项为期10年的科学研究,检查人脑的活动,并绘制一张全面的人脑活动图,希望能在大脑研究领域做出类似人类基因组计划(Human Genome Project)为遗传学所做的贡献。”  美国的人脑地图计划 – cn.nytimes.com – Readability readability.com

加拿大滑铁卢大学的神经科学家和软件工程师称,他们构建出了目前最大的人类大脑模拟系统—Spaun。它不仅有获取视觉信号的电子眼和可以作出相应反应的机械臂,它还能通过基础的智商测验。它拥有250万只虚拟神经元,可以执行8项不同的任务。
Spaun:最逼真的人工大脑 – www.guokr.com – Readability readability.com

更有意思的是计算技术和生物技术的相互渗透,比如下面这个有意思的科技发展给人所展现出来的可能性就非常丰富—用计算机模拟生命现象。
人类离”补完”生命还有多远? – www.guokr.com – Readability readability.com

这些科学和技术的前沿,或许离我们已经很近了。作为工程师的我们,有必要加强对认知科学和脑科学的学习了。

编译中的依赖性消解和Bootstrap

项目中要做两种实际编程语言的编译。遇到两个层面的翻译:纯粹语法层面的翻译;基础类型系统的翻译。后者很复杂,有时候需要在目标语言建立初始语言的模拟类库。初始语言类库的各个类之间有非常严重的依赖性,导致模拟类库各个类之间也有严重的依赖性。如何消解依赖性,把工程顺利的Bootstrap起来的问题,就成为一个基础问题。

项目中要做两种实际编程语言的编译。遇到两个层面的翻译:纯粹语法层面的翻译;基础类型系统的翻译。后者很复杂,有时候需要在目标语言建立初始语言的模拟类库。

1、但这个类库具体的形态依赖于怎么翻译;而怎么翻译又反过来依赖模拟类库提供的支持。
2、初始语言类库的各个类之间有非常严重的依赖性,导致模拟类库各个类之间也有严重的依赖性。

实际编程语言的类库都比较庞大,由于这种依赖关系,一方不就绪,另一方就无法运行和调试程序。这种情况下,一个开发团队要怎么样分工合作呢?难道要大家代码都就绪了,全部做一次联调,再去改各自的bug吗?这样显然行不通。如何消解依赖性,把工程顺利的Bootstrap起来的问题,就成为一个基础问题。我们的架构师为此规划了一个迭代、累进的路线,实践证明非常成功。

第一遍:翻译程序没有就绪。首先确定一个起步的API集合,小范围手工实现一些模拟类,提供基本操作和大部分接口,这些接口可以没有给出实现,但都是编译通过的。

第二遍:翻译程序初步就绪。模拟类库自动翻译生成,手工改错。由于第一遍已经生成了一些模拟类,当大家分头开发时,都依赖于第一遍的结果,而不是彼此依赖,这样就把依赖性给消除掉了。

第三遍:翻译程序就绪,模拟类库完全自动翻译生成。

这种Bootstrap的工程思路,是我这次美国之行最大的收获。诚如同事指出,Bootstrap不只是编译技术上才遇到,任何团队运作都会遇到类似的问题。

基本功

最近没有去用太多时间研究Haskell,而是又把很多精力放回到已经工作了将近10年的Java了。原因很简单,这些日子和公司的一位老手合作,才豁然发现其实自己编程的基本功很差。对程序员来说,语言的选择只是问题的一个方面,更重要的是他如何对待自己写出的每一行代码,境界的差别全在其中。就拿我这一两天学到的为例。

本文原发在巴别诺瓦,把上篇BLOG没有细说的设计说清楚了。

最近没有去用太多时间研究Haskell,而是又把很多精力放回到已经工作了将近10年的Java了。原因很简单,这些日子和公司的一位老手合作,才豁然发现其实自己编程的基本功很差。

对程序员来说,语言的选择只是问题的一个方面,更重要的是他如何对待自己写出的每一行代码,境界的差别全在其中。就拿我这一两天学到的为例。

public static String format(Object[] arguments, String pattern) {
return java.text.MessageFormat.format(pattern, arguments);
}

public static void test() {
format(new Object[]{"a", "b"}, "{0}_{1}");
}

比如,上面这段普通不过的代码如何改进?我看到了下面的代码才明白自己没有真正理解变参函数的长处:

public static String format(String pattern, Object... arguments) {
return java.text.MessageFormat.format(pattern, arguments);
}

public static void test() {
format("{0}_{1}", "a", "b");
}

这只是一个小的细节。下面再举一个设计的例子,关于一个代码生成器:

需求很简单,输入一个格式类似 “${a.b.c}” 的字串,把它按照一定的业务规则,输出为另外一段代码

上面描述的过程在业务类里面大量重复出现,一个可以设想的解决办法是把重复的代码提升到父类里面。

public String generate(Context ctx) {
Reference ref = resolveReference(m_ref);

String clazz = ctx.getClass(ref.getFirstPart());
String variable = getVariable(ref, "url");
String scriptlet = getScriptlet (ref, clazz, "url");

ctx.setAttribute(variable, scriptlet);
return "${" + variable + "}";
}

然而因为父类导致抽象层次的增加,很多时候这种做法并不一定导致最好的解决方案,反而让代码变得难懂。有时候引入一个恰当的记法和工具类,可以使得代码变得异常的清晰。

public String generate(Context ctx) {

Object[] parts = FormatHelper.parse(m_ref, "$'{'{0}.{1}.{2}'}'");

String clazz = ctx.getClass((String) parts[0]);
String variable = FormatHelper.format("_url_{0}_{1}_{2}", parts);
String scriptlet = FormatHelper.format( "{0}.url({3}.{1}.{2})", parts[0], parts[1], parts[2], clazz);

ctx.setAttribute(variable, scriptlet);
return "${" + variable + "}";
}

这样是不是清晰了很多呢?更多的好处还在于工具类没有破坏业务类的完整,对业务逻辑的控制权始终掌握在业务类里面,提高了代码的内聚性。

这位老手的话不多,但他评审代码的意见都很到位,让我心悦诚服。

更多想说的是,热爱编程的程序员应该要精益求精,追求代码的完美。只有多磨练,才能出真功夫。

业务类、工具类和内聚

工作里的小小心得—能设计出相对复杂的系统,却不一定能写得好一段简单的代码。

在写代码的过程中,经常会遇到业务类里面大量的代码重复,一个可以设想的解决办法是把重复的代码提升到父类里面。然而因为父类导致抽象层次的增加,很多时候这种做法并不一定导致最好的解决方案,反而让代码变得难懂。

有时候一个恰当的工具类,可以使得代码变得异常的清晰。但在写工具类时一定要注意:工具类不能生硬插入业务的逻辑,这会破坏业务类的完整;对业务逻辑的控制权一定要掌握在业务类里面。

这些都是看似简单的原则,可在平时写代码的时候,因为时间、进度,我们往往忽略了代码的美感。写代码的感觉还是要多磨练呀。

Ruby上的管理工具:Capistrano與God

公司網站的后端應用比較多,于是后台程序的部署和后台進程的監控便成為當務之急。搜索了一些解決方案后,最終鎖定到Ruby平台上的Capistrano與God兩個工具。

Capistrano( http://capify.org/ )是Jamis Buck的力作。總的來說,Capistrano就是一個建立在ssh之上的、多服務器環境下的部署工具。它與SVN也有良好的集成;通過ssh,它可以自由的執行任何一台服務器上的命令。

God( http://god.rubyforge.org/ )則是一個后台進程監控的工具。它可以用來啟動指定的進程,監控其后的狀態,并可以依據CPU/Memory等條件重啟進程。它的命令行界面很干淨,讓人喜愛。

現在只是剛剛把這兩個工具結合在一起使用,至于God是否穩定還有待考察。

最后想說的是,Ruby語言之上并不只有Ruby on Rails這樣的開發平台,也有許多系統管理工具可供大家選擇。

维基入门(三):中立的观点

这里的“中立”是指公平包容各方不同的观点,而不去判断各方观点的正误,把评判的工作交给读者自己去做。同时,“中立”也是一种过程,一种永远敞开、没有完结的、本着互敬精神和公平原则的对话。

上一篇讲到善意,然而善意只说明双方互相尊重的态度,并不蕴含彼此观点的一致。实际上,Wiki里面有专门的一个术语——“编辑战”,用来描述不断地互相删改对方的编辑内容。在维基百科早期的历史上,有一些比较有名的争议,比如神创论者对条目“进化论”的争议,这些争议直接导致了“中立观点”(NPOV, sovaldi Neutrual Point Of View)政策的出现。此后,“中立”便成为了维基百科一个重要的特点。

需要提醒大家注意的是,维基百科的“中立”并不是说它的条目已经没有偏差了,相反,条目的偏差永远都不可能避免。“中立”也不是说我们在各种互异观点之下取一个中间观点。这里的“中立”是指公平包容各方不同的观点,而不去判断各方观点的正误,把评判的工作交给读者自己去做。同时,“中立”也是一种过程,一种永远敞开、没有完结的、本着互敬精神和公平原则的对话。

围绕“中立的观点”还有一些有趣的小论点:

  • 我们应该试图刻画争辩,而不是自己参与争辩。
  • 不是“科学的观点”,而是“中立的观点”?
  • 为敌人写作

维基入门(二):善意与友爱

任何新来者都会有一个疑问—任何人都可以编辑,这不就乱套了吗?实际上,所有语言的维基百科已经积累到1000万篇条目了,许多条目的质量也可以大英百科全书媲美。为什么没有乱套呢?除了一些维持社群运作秩序的规则之外,说到底,还是因为参与这个计划的大多数人都是在善意的修改。

上一篇讲到维基百科是热爱知识者的乐园,这一篇接着讲维基社群存在的基石和一些行为的准则。我不打算在这个入门系列里介绍各种编辑的规则和技巧,而主要介绍社群文化方面。因为正是维基百科的社群文化才是它对国人最有价值的部分。

任何新来者都会有一个疑问—任何人都可以编辑,这不就乱套了吗?实际上,所有语言的维基百科已经积累到1000万篇条目了,许多条目的质量也可以大英百科全书媲美。为什么没有乱套呢?除了一些维持社群运作秩序的规则之外,说到底,还是因为参与这个计划的大多数人都是在善意的修改。这就是“善意推定”(Assume Good Faith)这一项指引要说的。

“既然我们允许任何人编辑,这必然蕴涵着:我们假定大多数进行编辑的人都是在帮助这个计划,而不是伤害这个计划。如果不是这样的话,象维基百科这样的计划从一开始就会失败。”

本着善意才可能有效的展开合作(包括和异己者的合作,也就是中立观点的政策),甚至我们可以达到维基友爱的境界。

“大家来到这里都是为了一个共同目的:我们热衷于积累、整理、组织我们的知识,并以空前规模的百科全书的形式,将这些知识自由地提供给大众。维基百科不只是一个普通论坛,它是用来叙述、累积我们所知的一切的一项工程。如果我们谨记“热爱知识”这一目的;如果我们竭尽所能尽量达至中立的观点;如果我们试图真正理解对方所说的话;那么,我们就可以达到维基友爱的境界。”

我们的理想是善意、友爱,然而实际上这是一个充满冲突的世界,下面计划介绍编辑战、中立原则、共识与投票、争议的解决和维基上不同理念的冲突。

维基入门(一):乐知者的大熔炉

今天下午许多地方报告可以访问中文维基百科了,虽然我仍有些怀疑解封究竟能持续多久,但仍然多了一丝热情,于是准备写一些小文章来介绍维基百科。

为什么参与维基百科?有什么样的乐趣呢?最简单的说就是乐于求知、喜欢分享,见识持有不同观点的人并与之合作

今天下午许多地方报告可以访问中文维基百科了,虽然我仍有些怀疑解封究竟能持续多久,但仍然多了一丝热情,于是准备写一些小文章来介绍维基百科。

为什么参与维基百科?有什么样的乐趣呢?最简单的说就是乐于求知、喜欢分享,见识持有不同观点的人并与之合作。

先让我们看看维基上几个好写手的个人页面:

User:Lecter
参观一下他的客厅、画廊、书房,看看他翻译的条目

User:SElephant
看看这位来自台湾卖酒人多姿多彩的生活和渊博的知识

User:Mhss
一位信仰佛教的程序员如何几年如一日翻译大量理论文章

还有很多很多……

可以看得出来,维基百科的贡献者都是热爱知识的人。我们在维基百科为了一个目标而工作——“让全世界的人都可以自由的获取人类全部的知识。”

你有知道港台人如何理解“封建社会”吗?台湾的朋友也逐渐了解到“白色恐怖”这个词在现代汉语里真正的渊源。等等……

原本彼此遥远,互相只有一个刻板的印象,可当你和活生生的人交流并成为朋友,你会发现好多事情都不一样啦。

维基百科是一个熔炉,不同观点在这里碰撞、融合。

軟件搆造的復雜性

今天在arXiv讀到一篇有趣的文章《軟件圖與程序員意識》(Software graphs and programmer awareness)。作者探討的是程序不同模塊間相互依賴搆成的圖的統計性質。作者試圖從微觀的程序員開發行為來闡釋程序的宏觀統計性質。

今天在arXiv讀到一篇有趣的文章《軟件圖與程序員意識》(Software graphs and programmer awareness)。作者探討的是程序不同模塊間相互依賴搆成的圖的統計性質。作者試圖從微觀的程序員開發行為來闡釋程序的宏觀統計性質。

以前,我曾經做過一個統計,計量一些關鍵性系統模塊的復雜性。當時寫到:

硬件的飛速發展,網絡的出現,人機交互方式的變化正劇烈的改變着軟件產業的形態。那么影響軟件產業形態的因素有什么呢?從某一個視角來看,復雜性和經濟學是影響軟件產業形態的重要因素。

軟件搆造是一個非常復雜的過程。由于經濟學的原因,在攻克軟件搆造的復雜性過程中,發生了系統程序和應用程序的裂解,于是軟件產業也隨之分化成了不同的行業。在分化過程中值得注意的是一些關鍵性的系統,它們居于核心的,具有某種平台性質。比如操作系統內核、桌面系統、瀏覽器、Web服務器、數據庫服務器。

寫了一個小程序測試了一些系統的源程序行數,想看看現在的關鍵性系統究竟有多復雜。測試結果如下:

  • openoffice-1.0.2 3786802
  • linux-2.4.20 2964125
  • mozilla 2459987
  • sapdb 711151 (script for system tables not include)
  • j2sdk1.4.0 486376
  • amaya 7.2 452057
  • mysql 428500*
  • gcc-2.7.2.2 331209
  • lesstif-0.93.40 323526
  • httpd-2.0.44 297110
  • jfc 210022
  • HotSpot JVM 1.1 119093
  • javatool 101041 (estimated from bytecode)
  • hurd-0.2 97674
  • jigsaw 96061
  • berkleydb-3.2.9 86862
  • aspectj-1.0.6 84405
  • tomcat-4.1.10 64495
  • libwww 61347
  • zlib-1.1.4 9388

從上面的結果,我們可以看到:

  • 一個實用的基本的開發環境(編譯、調試、庫等)的代碼量應該在 100~500 kloc 附近,才有可能被廣泛使用。
  • 復雜的應用需要有 ~1 mloc 級別的代碼量。
  • 一台良好工作的 PC 所必須的支持軟件大致需要有 ~10 mloc 級別的代碼量。

今天再把當年的舊文翻出來,只是覺得這是一個可以繼續深究下去的課題。

從程序設計語言的角度看,一個程序設計語言可以通過其基礎的語言搆造來獲得普遍的計算能力,這解決了可計算性的問題,僅僅是第一步,后繼而來的是復雜性和經濟學的問題。在語言這個層次上,我們可以看到函數、類、模塊、包等分層,以及對分別編譯的支持,這是用抽象、分治、復用的手段解決復雜性。

除了這些手段,還有沒有其他的方法?我能想到的,還有meta-programming和combinator兩種途徑的DSL,以及generic programming。這些也是某種語言層次上的方法,但大的思路依然是抽象、分治、復用。

RSS導入后臺的實現

來到Linkist的第一個項目就是RSS的自動導入,經過一個月的努力我們實現了這個功能。簡單記一下所得:

  • 空間的分散化:RSS的抓取本身是可以很容易分布到不同機器上去的,所以我們需要支持多臺機器分布工作。(勢必涉及分布算法,比如領導者選舉)
  • 時間的分散化:RSS源有不同的更新頻率,我們的抓取頻率必須可以根據更新頻率來調整。同樣頻率的抓取又可以在時間上隨機均勻分布于整個時段。
  • RSS源的特異性:RSS標準多,各個RSS源的實現不標準,特別是pubDate字段的實現更是千奇百怪。

這三點是我們RSS自動導入后臺實現的主要考慮。