老潘的Eurotrip VII - 最后周末

本来计划之前一个周末在布鲁塞尔购物给大家买礼物,最后一个周末两天去阿姆斯特丹转转。结果项目传来噩耗,客户下了最后通牒,XX日之内搞不定就退款。北京公布的应对政策十分简单:到那天之前一天休息都没有,周末全部加班。其中一个理由是摆出诚恳的姿态来给客户看。我认为这一点十分徒劳。对一个大公司客户,不是有诚意就可以解决问题的。北京毫不犹豫的这么做,说到底还是让员工加班成本太低了 - 几乎没什么成本。即使新劳动合同法颁布了,中国人多劳力便宜的事实还是没有改变。要想和资本家斗争,还有很长的路要走。等到80后都混成老板了,老板也就不那么好当了吧。每一代人都有每一代人的难处,比起抱怨,还是找到让自己舒服的活法儿最重要。

于是我在欧洲的最后一个周末休息也就告吹。其间还发生不愉快。只是作为一个职业人,我不会在博客里说这些的,心里有数就行了。

越临近归期,对你的想念就越强烈。

关于虚函数,多重继承,Python中的super

继承和多重继承(http://en.wikipedia.org/wiki/Multiple_inheritance)是language features,是面向对象技术为了使程序向现实贴近的一个手段。不过在面向对象理论中多重继承也不是必须的,基本上所有可以用多重继承实现的,都可以通过单继承来work around。这句话更多来自于实践而不是严格的理论。

从继承的实现角度来说,当子类对象被初始化时,实际上有一个父类对象也被初始化并且组合到子类对象中。这种来自继承的组合和普通的组合的不同之处主要在于,我们是仅仅需要使用父类提供的功能,还是说也需要使用父类提供的接口。

有继承就有多态。C++为了实现多态引入了late/dynamic/runtime binding(Connecting a function call to a function body is called binding):编译时,编译器不知道通过一个基类引用调用的某函数,具体调用的到底是那段代码。所以传统的非OOP编译器的early/static binding机制就行不通了。C++中实现late binding的手段是虚函数。Python和Java中所有的函数/方法实际上都是虚的。所以说虚函数只是实现多态的一个(并不唯一的)技术手段。

具体的实现机制是,编译器为每一个包含虚函数的类(包括通过继承得来虚函数的类)都生成一段分配空间并初始化VTABLE(就是所谓的虚函数表)的代码。于是在运行的时候内存中就会有这样的VTABLE,它们包含了该类中所有虚函数的实现代码的地址。这是非常合乎逻辑的,因为同一个具体类的所有对象,其虚函数实现都是一样的,所以一个类只需要一个虚函数表。

接下来,每当编译器在源代码中看到new一个对象的时候,都会做一些小偷小摸的事:它会生成一些额外的代码,在对象中在分配空间给一个指针成员变量VPTR(对程序员是不可见的!!!),通常被放在对象内存布局最开始的地方,也就是this指针指向的地址。这个VPTR指向本对象的类所属的VTABLE。这部分代码被十分自然的放在构造器中,所以运行时new一个对象出来,对象被初始化后,这个VPTR就已经ready好了。明白了吧!这就是为什么如果程序员不写构造器,编译器会搞个默认构造器出来。Java中也是这样。

但是VTABLE的地址是在运行时才available的。初始化VPTR这段代码必须在运行时获得本对象的类型信息才能知道正确的VTABLE是哪个!所以编译器确实是插入了类型信息到对象中的。在运行时的时候可以获得。题外话:这也是RTTI的本质。

前面的说法有一个hole,就是如果一个对象多重继承怎么办?这样对于同一个对象就会有多个VTABLE。最直觉也是最家常的做法,就是搞多个VPTR出来,也是都放在对象头部。但这时就需要pointer fixups即thunks(http://en.wikipedia.org/wiki/Virtual_table),即当用不同的类型去引用该对象时,使用不同的VTABLE。

接下来的事情就简单了:当通过一个基类指针调用虚函数时,不管对象的具体类型是啥,总能找到正确的VTABLE,通过其找到被调用函数的正确地址,并调用之。

从语言使用者的角度来看,virtual只是一个关键字。使用者实际上不需要知道虚函数是怎么实现的。但由于虚函数调用会有额外的开销,有心的程序员会去研究它的实现并且在适当的时候才使用。

所谓纯虚函数,就是只有声明没有实现的函数。包含至少一个纯虚函数的类就是抽象类。

多重继承引入了二义性问题。最典型的菱形问题http://en.wikipedia.org/wiki/Diamond_problem。

C++使用虚函数来解决多态调用,使用了虚基类来解决菱形问题。

Python则规定了一个特别的method resolution order来摒除二义性:http://en.wikipedia.org/wiki/Diamond_problem。

Python中的super http://fuhm.net/super-harmful/ 

GUI framework基础原理(占位)

用户直接触发的事件(点击按钮是最经典的例子),用户间接触发的事件(例如resize引起的paint事件),系统事件(某个窗口自动弹出?)

多线程

事件队列,事件分派、传播规则

Vi与Lisp: 从小工到专家,告诉你Vi到底好在哪里

用Vi也有段历史了吧,总是记不住那些命令。但最近在客户这边不知道是怎么了,Vi熟练度暴涨,现在整天玩macro不亦乐乎。大概缘由还是找了个比较全的reference card,双面儿打印好,就放跟前儿,想干什么一定忍住别换别的编辑器,逼着自己用Vi。几个回合下来,我现在在Eclipse里都直按hjkl。。。

说笑。但是现在真是离不开Vi了。在北京的时候分析日志文件就很头痛。有的时候我们过于放任服务器上的应用,结果端下来的日志有几百兆。。。很多编辑器看见这个就趴下了。。。UE倒是还行但毕竟是盗版,不爽。而且我上学的时候就用出过UE的bug,从此基本敬而远之。Eclipse的编辑器也不错,但是要把文件搞到workspace里又嫌麻烦,即使是通过链接的方式。再说Eclipse还是太重了点。剩下能开大文件的也就是基于Scintilla的Scite和Notepad++了。Notepad++的搞笑默认字体我不太能忍。Scite倒是不错但功能又过于简单了。。。

好吧,我知道我事儿多。这不还有Vi呢么。原来不理解为什么,到底为什么要用hjkl这种诡异的方式来移动光标。其实Vi的哲学是把指令用一个字符序列来表示,并且指令可以互相组合。哎?你有没有觉得这个像什么?

我觉得像Lisp:

1. 指令(代码)即字符(数据),字符(数据)即指令(代码)。

2. 至于互相组合的指令,就像高阶函数那样,是一种过程抽象。

这样的好处是什么呢?第一点简单来说,就是由于指令本身也是数据,因此可以被编辑也便于记录;另外由于数据可以被映射为指令,你可以使用创建和操纵数据的方式去创建和操纵指令。第二点稍微高深一些,就是Vi指令体现了一种抽象的思想,其功能是作用在某个抽象层次上。 

有点元编程的味道吧?我先解释第二点。想想看,在Notepad++里,假设你要对一些文件做一组相同的操作,比如你要去掉一些log文件的头部,这个操作要删掉每个文件的前39行,那你就要开始录制macro,用鼠标划上39行,或者用shift+end再加down arrow39行,然后点delete。往好了说,你有个删除当前行的快捷键ctrl+d(Eclipse里就是),那你也得按39次!

Vi呢?四个字符:39dd。什么叫抽象?这就叫抽象!原来我有一朋友讲过一事儿,她回学校看老师,到办公室看见n个老师,挨个叫:黄老师好,张老师好,王老师好。。。然后教数学的老师发话了:教你那点儿数学都忘光了吧?合并同类项:老师们好!

其实就是这么回事儿。Vi之所以神奇就因为它有个normal mode。这种模式下你按键盘输入的不是字符,而是指令。如果说删39行还体现不出差别,那么删1000行呢??用过普通编辑器的人可以想想,要删1000行怎么办:你不得不用鼠标拉滚动条,看看1000到了没有,还要小心不要划多了或者划少了。Vi的指令是可以互相组合的,像39就好像是一个高阶函数一样:39dd是删39行,39iILOVEYOU<Esc>是写39遍我爱你。。。关键是它把39这个特征抽象出来了!

接下来解释第一点。所有这些指令,包括组合指令,都可以表示为字符序列。你可以把这些指令存起来,比如存在Vi的register里,然后想用的时候拿出来用。你还可以根据需要随意修改它们。想想看在Notepad++里你一样可以录macro,但你能修改这个macro么?我相信Notepad++一定有某种内部方式,某种数据结构记录macro,可能很简单也可能很复杂,但whatever你是看不到的你也改不了。这样假设你费了挺大劲录了个删39行的macro,发现下一拨要处理的log文件都要删40行,你咋办?

Vi就方便了。你可以找到存macro的那个register - 你会发现它只存了一个字符序列 - 39dd。然后你把它改成40dd,就行了。

我举的例子简单。如果这个操作除了删几行之外再包括很多其他操作,Vi的优势会更大。用Vi的高手老是说Vi有“禅”(Zen)在里面,呵呵。我不想说那么玄,说白一点,和Lisp一样,Vi就是以极大的灵活性,应对变化。程序,软件,项目,莫不过如此。

SICP-2.2 solution

(define (print-point p)
  (newline)
  (display "(")
  (display (x-point p))
  (display ",")
  (display (y-point p))
  (display ")"))

(define (make-segment start end)
  (cons start end))
;; or (define make-segment cons)

(define (start-segment seg)
  (car seg))
;; or (define start-segment car)

(define (end-segment seg)
  (cdr seg))
;; or (define end-segment cdr)

(define (make-point x y)
  (cons x y))
;; or (define make_point cons)

(define (x-point p)
  (car p))
;; or (define x-point car)

(define (y-point p)
  (cdr p))
;; or (define y-point cdr)

(define (midpoint-segment seg)
  (let ((start-point (start-segment seg))
        (end-point (end-segment seg))
        (avg (lambda (a b) (/ (+ a b) 2))))
    (make-point (avg (x-point start-point) (x-point end-point))
                (avg (y-point start-point) (y-point end-point)))))

(print-point
 (midpoint-segment
  (make-segment
   (make-point 1 1) (make-point 4 4))))

老潘的Eurotrip VI - 巴黎

啊,巴黎,终于到了激动人心的巴黎!

等我溜达完回来再写。

-----------------可以被预料到的令人泄气的分割线-----------------

我是大年初三去的。仗着自己是北京人,长这么大第一次不在家过春节。但也因为年年在家过,过年都没感觉了,出来躲躲空气和噪声污染也好。

头一回做Thalys火车。并没觉得好太多。下了车心里恶俗的想,啊巴黎我来了!凭常识先买地图。卖地图的小二根本不抬眼看人,收了两欧把地图扔出来完事儿。奔厕所。抬头一看大牌子写着收费一欧大洋。我心说tmd不愧是巴黎,这物价就是高啊,这狗眼就是看人低啊。我在别的地方上厕所一般都是30到50分,地图也是50分左右,这儿就变两块了!来之前法国朋友已经提醒我了:你可能不会喜欢巴黎人!果然第一印象不能说好。接下来进厕所,发现没小便池,甚慌,心说莫非我进了女厕?再一看没错啊!等出来一看旁边有一小入口写着小便60分。我就日!大便的挂那么大个牌子,难道因为大便大么!!!

接下来奔青年旅店。我住的应该是非常有名的一家了,就在卢浮宫旁边。一路看风景晒太阳,甚得意。我去的那个周末赶巧天气极好,二月阳光下的塞纳河非常令人陶醉。路过卢浮宫不过因为是第二天的计划,所以没进去。在外面转了转,气势恢宏。把东西撂在hostel,轻装出行,经卢浮宫进入西边那个巨大的花园儿,然后惬意的一直走一直走,在水边坐了一会儿看水鸟,然后找了个公园里的饭馆儿吃了个crepe,就是一种类似煎饼的东西,不咋好吃,比中餐差远了。不过能坐着欣赏巴黎春光就非常值了。

然后一直向西。。。向西。。。经协和广场到了香榭丽舍大街。协和广场上立着一3300年历史的方尖碑,上面全是埃及象形文字。一开始我还以为是现代复制的,一翻书才知道那是真货。。。说是法国殖民北非的时候,埃及总督送给法国的。埃及人太贱了吧也!这国宝都能送人!!!卖国贼!卖国贼!

人家卖国跟我也没关系。反正我到了香榭丽舍。路北很热闹,各种店铺。路南主要是一些超级品牌的专卖店,包括一LV的大楼,真受不了。。。比号称姐妹街的王府井强多了,但也就是一商业街。

向西走到头就是戴高乐环岛,中间矗立着凯旋门,要经过地下通道才能到达环岛。凯旋门的售票口就在地下通道里。卖票的黑妞儿英语十分差劲,搞得我不想浪费时间问她巴黎通票的事了,那样会耽误好多人的时间。反正我就呆两天也转不了太多地方。验票的那哥们儿特拽,说了一句法语我说sorry I don't speak French,结果丫还来劲了,说了好几遍,我就看着他,他才跟要教我法语似的,拿英语说,那句话的意思是open your bag。我心说操你大爷的我跟你丫说中文你听得懂么?!不跟他计较,不过巴黎人我算是见识了。

凯旋门没啥太多可说的,就是实物比原来想象要大一些。下来的途中我碰上俩日本青年,我又痒痒了就开始跟他们拽火影忍者。我拿日语说影分身之术,他们十分高兴,哈哈!

接下来顺着某街杀到巴黎左岸,埃菲尔铁塔。巴黎的很多街名都是以名人命名的。比如去戴高乐环岛出来我就经过了维克多雨果大街。嗯嗯!

从北面接近埃菲尔铁塔,路过了很好的博物馆,但不想进去了。博物馆旁边有个非常有活力的广场!跳街舞的(跳得非常牛逼!),卖小吃的,谈情说爱的,唱歌的,玩儿极限运动的,各占一方。我在这儿度过了一些不错的时光,感受到了巴黎的活力和热情。

接下来就是铁塔了。那队排出。。。几十米。铁塔的四个脚其实都是售票口,分别出售腿儿票和电梯票。这时候都下午四点多了,我看着那塔,崇拜着在腿儿票窗口排队的群众们,毅然加入了等待电梯票的行列。这队还是挺有效率的,我上到铁塔最高层的时候正赶上夕阳西下,可以看到最后那一个像素的红色消逝在地平线,特别漂亮,没少拍照。

铁塔应该是巴黎的制高点了。四周景色尽收眼底,尤其是横穿巴黎的塞纳河,给这个城市增添了多少情趣!

夜幕下我穿过战神广场和拿破仑毕业的军事学院,回到了右岸。略微欣赏了一下夜幕下的塞纳河与卢浮宫,就回hostel了。同屋是一个香港人,一个美国人和一个德国人。当夜无话。

第二天的重头戏就是卢浮宫了。。。这东西实在是太变态了。。。看过达芬奇密码不?里面说把卢浮宫馆藏都正经看完得花一年。绝对不夸张!进玻璃金字塔其实是不收钱的。那里面下到地下有卖票的,买了票进去。我万幸自己是带着地图和指南针进去的。实在是太大了!各种艺术品令人眼花缭乱。就跟你一下吃了太多甜的就吃不出甜的妙处来一样(坂田银时除外),一下让你看这么多珍宝,也就匆匆掠过了。比较搞笑的是卢浮宫现在非常迎合大众口味,从一进馆开始就有标识:看蒙娜丽莎请这边走。靠!太能揣摩人的心意了。不是说大家庸俗,是你进去了就知道肯定转不完,还是先把那几个掰着手指头就能数出来的都看了,给自己一个交代。

我在里面整整转了4个小时!下午2点才出来,巨饿。不仅是肚子饿,是精神被强烈冲击了!出来奔左岸走,准备找萨特波伏娃左拉他们呆过的小馆儿歇会儿。还是先找了个饭馆儿吃午饭。遇到几个和善而爱开玩笑的女士,这才让我对巴黎人的印象有些好转。午饭是经典的牛排配红酒。然后到了文化名人曾经聚集的Flore de Cafe喝了他们的特饮热巧克力。这东西太赞了!难以形容的好喝!就这样边喝边欣赏街边的景色,非常惬意啊!

向东走,离开左岸登上西提岛。巴黎圣母院就在眼前了!啊!卡西莫多!啊?真有个卡西莫多在人群中乱窜。仔细一看是个人戴着个卡西莫多的橡胶面具,在巴黎圣母院附近的人群中吓唬人搞笑,然后向排队买票的人要钱。

巴黎圣母院挺漂亮的,不过我并不觉得比安特卫普大教堂大。登上去转了一圈,也就是这样了。

接下来回到右岸,去了貌似工地钢管儿堆的蓬皮杜中心,不过没买票参观,就在外围和大厅里转了一圈,逛了一下其贵得离谱并且毫无道理的商店。出来天黑了。沿着塞纳河走到巴士底广场,曾经的巴士底狱已经没了。这地方相当于北京的菜市口,当年斩了不少人(萝卜斯皮尔是谁还记得么?)。

接下来一路走到火车站,完成了巴黎之旅。回来以后英国朋友问我玩得怎么样我说我不喜欢巴黎人。他说所有巴黎以外的法国人都不喜欢巴黎人,但那城市很好很漂亮。我说是啊,城市很漂亮。

老潘的Eurotrip V - 安特卫普

呃,安特卫普是半个月之前去的了。这段时间比较懒得写字,照片就更懒得整理,想回去再说吧。

一开始我问比利时同事Antwerp跟蚂蚁有啥关系,呵呵,其实我就是故意搞笑。他们是怎么说的我忘了,反正就是跟水有关系。

天气还是挺好的,所以坐在岸边with dangling legs,晒着太阳,看看野鸭鸽子一众水鸟在旁边啄食,挺放松的。

老火车站很好看。购物街很时尚,顶级名牌比布鲁塞尔多。鲁本斯故居很值得去(看丫住得多舒服!!!)。船舶博物馆一般,但小城堡有点像北海团城,挺好看的。地下城市遗迹应该是非常好的,但臭气扑鼻+要走5个小时所以我放弃了。本来就累得够呛不想再打着旅游的旗号受罪了。第二天去的动物园也很让人happy。钻石博物馆我就没去,去了也买不起。

印象最深的应该是大教堂。整个低地国家地区最大的Cathedral,气势宏伟。事实上后来我去了巴黎圣母院,至少并不觉得比它大。附近中央广场著名的的Max炸薯条是我在欧洲吃过的最好的fries! 不骗你!一定要去吃!太好吃了!

比较不爽的一点就是在安特卫普的第一天晚上遇到掏包的。是属于忽悠型的那种,跟你说一堆你听不懂的法语,然后在你面前搞点儿小动作分散你注意力,然后手指头就伸到你口袋里。还好我警惕性很高没让他得逞。这事发生后我意识到之前在布鲁塞尔遇到的一个家伙其实也是贼,因为他们做了一样的事。但那次我口袋里什么都没有,他自然也就无从下手了。

所以一个人旅行还是有一定风险的,呵呵。

分页共1页 1