美国大兵的22条作战条例
- 第一条你不是超人;
- 第二条如果一个愚蠢的方法有效,那它就不是愚蠢的方法;
- 第三条不要太显眼,因为那样会引人攻击(这就是航母被称为“炸弹磁铁”的原因);
- 第四条别和比你勇敢的家伙待在一个掩体里;
- 第五条别忘了你手上的武器是由出价最低的承包商制造的;
- 第六条如果你的攻击很顺利,那你一定是中了圈套;
- 第七条没有任何计划能在遇敌后继续执行;
- 第八条所有5秒的手榴弹的引线都会在3秒内烧完;
- 第九条装成无关紧要的人,因为敌人的弹药可能不够了(所以它会只打重要的人);
- 第十条那支你不加注意的敌军部队其实是攻击的主力;
- 第十一条重要的事总是简单的;
- 第十二条简单的事总是难做的;
- 第十三条好走的路总会被布上雷;
- 第十四条如果你除了敌人什么都缺,那你一定在交战中;
- 第十五条飞来的子弹有优先通过权(挡它的道你就倒大霉啦);
- 第十六条如果敌人在你的射程内,别忘了你也在他的射程内;
- 第十七条要一起用才能生效的装备通常不会一起运来;
- 第十八条无线电总会在你急需火力支援时断掉;
- 第十九条你做的任何事都有可能让你挨枪子儿——什么都不做也一样;
- 第二十条惟一比敌方炮火还精确的是友军的炮火;
- 第二十一条专业士兵的行为是可以预测的,但世上却充满了业余玩家;
- 第二十二条当两军都觉得自己快输时,那他们可能都是对的。
Ruby++
最近在MSN, GTalk上和一些朋友聊起近况,他们问我是不是“消失”了? 很多地方都见不到人影。那么说说近况吧。
去年下半年投了几个简历,莫名其妙的去了Vobile。杭州一家小创业公司,专注于video DNA领域。因为可以使用Ruby,所以留下来做到现在。目前主要构建一个普适的分布式运算环境,涉及一些有意思也比较有挑战的问题。开发小组不大,所以要扛起这么个大项目,必须投入更多的时间和精力。
在Vobile认识不少优秀的人,也学到不少东西。挺忙,希望以后能多些时间来思考一些东西
Google’s Engineering Philosophy [Funny]
Last year’s Google Engineering Open House listed 12 principles that guide programming at Google:
- All developers work out of a ~single source depot; shared infrastructure!
- A developer can fix bugs anywhere in the source tree.
- Building a product takes 3 commands ("get, config, make")
- Uniform coding style guidelines across company
- Code reviews mandatory for all checkins
- Pervasive unit testing, written by developers
- Unit tests run continuously, email sent on failure
- Powerful tools, shared company-wide
- Rapid project cycles; developers change projects often; 20% time
- Peer-driven review process; flat management structure
- Transparency into projects, code, process, ideas, etc.
- Dozens of offices around world => hire best people regardless of location
Fiber and Pipeline
Ruby 1.9发布后终于出现了关于Fiber的一个应用,发现自己很缺乏想象力了。Dave Thomas 写了使用Fiber来实现pipeline (Part I 和 Part II)。Nice catch!
Pipeline有两个特征:
- 前一段pipeline和后一段pipeline不会并行
- 前一段pipeline的输出是下一段pipeline的输入。
所以一个producer-consumer模型加上Coroutines/Fibers就可以简单的实现。当然是用thread也可以来实现
Coroutines
一直没写Coroutines, 因为在思考一个问题就是: Coroutines到底有什么用?其实Coroutines概念在1963年就被提出来,它最初的描述是: subroutines who act as the master program。我的理解是像主程序一样可以由用户调度的过程。
说到Coroutines,我们可以牵扯出multi-thread的概念。当然我们都知道multi-thread主要想解决的问题是并发,如果写过并部署过多线程的程序,你应该明白线程数目不应该超过CPU的数目,否则没什么意义。然而Coroutines恰恰不能够做到并发。因为没有并发,所以也就没有了condition race,临界区这些概念。而且Coroutines也没有thread那样复杂的状态迁移。

那么Coroutines为什么被提出来呢?我们来考虑下面一个问题:我有两个数组,一个数组里面装着a-z,一个数组里面装着1-26,现在输出结果是两个数组内容的交叉。这个问题在我写blog前请教了不少人,大多数人觉得应该先把两个数组交叉合并,然后输出。Well,是解决了问题!我反问:为什么不用线程呢?当然很多人想用,但是这里没有并发,而且thread的切换(状态的迁移)需要时间。Emm… 这就是Coroutines出现的一个理由吧,它能够像thread一样在用户空间进行调度,而且context的切换比thread要快的多。上面问题的一个解法可能如下:
if __FILE__ == $0 then
$c1 = Coroutine::new do
for i in ‘a’..’z’ do
printf "%s ", i
$c1.switch($c2)
end
end
$c2 = Coroutine::new do
for i in 1..26 do
printf "%i ", i
$c2.switch($c1)
end
end
$c1.start
printf "\n"
end
Coroutines的分类
Coroutines的分类标准有很多,一种常见的就是control transfer mechanism。按照这个分类可以有两种Coroutines。一种是symmetric coroutines另外一种是asymmetric coroutines(semi-coroutines)。他们的区别在于symmetric coroutines只提供一个control-transfer的操作,比如上面例子中的switch。而semi-coroutines则是提供两个control-transfer operations。一个是唤醒coroutines(resume),一个则是挂起coroutines(yield)。Ruby 1.9中的Fiber和Lua中的Coroutines都是属于semi-coroutines.
太晚了,有时间说说Coroutines的实现把。
Continuation
Fiber -> Contiuation -> call/cc -> CoRoutine
最初本想学习一下Fiber的内部实现,结果拉出了一大堆的东西。这也许是我不喜欢看书的原因吧,书上的东西虽然有旁征博引,但是却很难去寻找源头。互联网的好处在于我可以一直找下去。比较适合我这种有好奇心的人 =.=
上面的路线图只是表示我是如何发现这些名词的,并不是相互之间的关系。Fiber, Continuation, CoRoutine之间的关系其实我还是处于random thoughts的状态,所以不可能写出很深刻的东西。但是无论如何写这些东西主要帮助我自己整理一些思路。
这个Post主要说Continuation,因为Pluskid已经说了很多了。我嫌他说的不直白,自己陈述如下:
所谓Continuation就是保存即将运算的内容(the rest of the computation)。举个简单例子,我在玩游戏,突然接到电话要外出,这时我应该存档(存档是让我继续即将的游戏)。存档的数据就是Continuation。
上面的例子是给非CS专业的人听的。如果是CS专业应该明白函数调用栈的现场保护以及一些pop/push的操作。
对于一个Continuation,主要有下面三个步骤(使用Ruby语言说明,因为比较容易懂):
- 获得Continuation。在Ruby中使用callcc (call with continuation)。你可以把Continuation(记住,这后面是以后想运行的代码)付给某个变量。
- 继续运行。callcc并不会改变程序的任何状态,它只是插入了一个Continuation对象。
- 调用Continuation。就是把当前的call stack用Contiuation object替换掉。在Ruby中,程序将在callcc获得Continuation对象的那一行后面继续执行。
来看下面的例子:
1. def bar
2. c=nil
3. (1..4).each {|x|
4. puts x
5. callcc{|cont| c=cont} if x==3
6. puts "hello"
7. }
8. puts "world"
9. return c
10. end
当你运行 b=bar的时候,输出和没有callcc是一样的:
1
hello
2
hello
3
hello
4
hello
world
有趣的事情是你可以继续调用b.call,这时候的输出是:
hello
4
hello
world
解释是当x=3的时候,调用栈(call stack)以及一些变量(c和x)被保存到continuation对象中。
说完这里,有兴趣的可以继续去看Pluskid写的Continuation,看完了再接下去看我下面部分
我在Pluskid那里留了两个问题,其实还缺少一个,就是Continuation到底有什么用?这是一个很重要的问题,Continuation也算是双利刃吧,它可以跳转来跳转去,用它定义的break可以跳出很多层。我们注意到Contiuation是一个保存现场/状态的东西。什么地方需要保存状态呢?OK, 工作流引擎需要,还有Web Server也有可能需要(还记得session的作用吧)。使用Continuation的Web Server有什么好处呢?用户可以后退到某些状态然后继续执行。
loop do
request = latest_request
if request.is_new?
start_new_handler(request)
else
cont = continuations[request.id]
cont.call(request)
end
end
当然其中的tradeoff是Server保存多少状态是合适的,全部保存显然是一个糟糕的决策。
Call/cc最早出现在Scheme中,后来是Smalltalk,而且Seaside(web framework in Smalltalk) 就是一个Continuation Server
我会继续关注Continuation的应用,下次说CoRoutine …
Current method name
在前面很多post里面我都表达一个观点 —- 动态语言比较适合多年写程序的程序員。宽松的约束需要需要更好的习惯来平衡。比如在动态语言中,参数个数/类型的检查,强制实现方法的申明是很多人往往忽略的东西。
Ruby中没有类似java里面的interface关键字,但是我们仍然可以模拟实现类似的强制实现。在javascript中我们实现强制执行method的方法就是抛出exception或者alert出类型的信息,这种想法可以借鉴到ruby的实现中,关键是我们要清楚的告诉用户异常在那里,要做什么事情。所以最好的我们可以获得当前method的名字,javascript可以采用一个"假"调用,把exception stack都dump出来。在ruby中要获得当前method的名字,可以在Kernel module中加入下面的方法:
module Kernel
private
def current_method_name
caller[0] =~ /`([^’]*)’/ and $1
end
end
我们当然可以在每个需要强制实现的地方写如下的代码
raise NotImplementedError, "’#{current_method_name}’ should be implemented in subclass".
但是我们会发现很多时候我们在写重复的代码,除了方法名不一样。所以我们希望类似的代码抽象到最顶端的module。所以在Kernel module中加入下面的方法:
module Kernel
def interface_methods(method_names)
method_names.each do |method_name|
define_method(method_name.to_sym) do
puts caller
raise NotImplementedError, "The method \"#{current_method_name}\" should be implemented in subclass. Please check the exception source #{caller[0].inspect}\n"
end
end
end
end
这样一来你就可以在任何地方申明interface method:
interface_method %w(run fly)
Enjoy it
Federate Query Engine
一年前因为若干的vertical search engine项目,自己曾经写过WebSQL的DSL. 本想写篇论文,后来还是一拖再拖,到现在还没出来,想想真是感到惭愧。
WebSQL是我做的第一个联合信息提取引擎。基本的想法是把每个web page当作一个datasource, web page中的dom tree的某个集合看作table, 当多个dom集合(可以在同一个page或者多个pages)之间存在关系需要join的时候,那么使用dom的关系衔接起来,最后得到一组结果。
在NextLib同样有联合查询引擎的设计,不过两套东西的设计想法却是截然不同 =.=!!, NextLib中我使用一种类似pipeline的概念,这也许是因为我们遇到更多的情况是URL流动的场景。最后的用户代码看起来像下面:
fqe = FederateQueryEngine.new
start_point = URLPipeline.new(:base => "http://www.shop.com/", :type => "pagination")
fqe.url_clouds = start_point.provide(:product_category).
connect_to("http://www.shop.com/id=:product_category").
provide(:all).
connect_to(%w(http://www.shop.com/detail?id=:id http://www.weather.com/query_city=:city))
fqe.query_mode = :multi_threads
fqe.result
WebSQL也想做个Ruby的实现。联合查询引擎打算使用MIT license开源出来,到时候KDT的网站可以下载。如果你有好的想法,也可以联系我
email: himars@gmail.com
Two tools help you to smooth ruby refactoring
动态语言的重构有点类似噩梦,Javascript经历太多,这次面对Ruby竟然有点怯怯的感觉。
以前我对动态语言的重构都是基于铅笔和纸来完成,也就是
- 在纸上记录下重构的起点。包括具体文件以及所在的行数。
- 搜索可能存在的引用。
- 运行测试。
- 失败则检查原因或者回滚重构轨迹。回滚有时候借助版本控制的一些功能。
Ruby的重构我一直关注着,应该说这是能够广泛应用的一个重要因素。有两个工具是可以参考一下的 —— Ruby Refactroing Browser 和 RDT eclipse plugin。RDT是我推荐的,因为Java的重构是我觉得比较实用的,尽管.Net后来有很多花哨的重构。而RDT是借助了JRuby的AST(Abstract Synatx Tree)。有兴趣的可以看ruby refactroing的设计文档。这里是更多的关于重构的演示。让我兴奋的还是一个undo可以把在做的重构轨迹回滚
求职,准备好了么?
国内高校的校园招聘在9月中悄然拉开序幕,越来越多的学子们也带着迷茫和一丝的微微的恐惧加入到求职的队列。
看着众多的校园招聘信息,看着如我当年一样的面庞。我想做点什么 —— 最后出来的结果就是Life Go! Campus,一个旨在帮助大学生求职的公益项目。
在Life Go! Campus中,收集了中国一些企业的校园招聘行程,包含着详细的时间和地点。通过行程表的形式发布出来,希望能够帮助大家安排好自己的时间,把握住属于自己的机会。
另外NextLib中也有不少人分享着他们自己求职的一些知识和经验,我用NextLib小册子的形式把知识挂在校园招聘的旁边,希望这些知识可以给你带来自信,积极和好运!