你所知道的并不是你真的知道 —— 程序语言的精髓

来源:互联网 发布:tp路由器访客网络 编辑:程序博客网 时间:2024/05/28 16:23
当夜幕已近闭合,沉睡的晚灯张起结界,昏黄中没半点柔情,仿佛在告诉行人:非诚勿扰

夜晚11:11分,注定孤独的时分。Carl倚靠窗台失神了半晌,回过来就顺手拉紧了窗帘。拧开台灯,桌上静静地躺着一本书,是日本人西尾太和的《代码之髓》。他翻开扉页,签上自己的大名,满足地撇撇嘴,开始看起来。

Lisp是这个世界上最精练,最美观的语言

(setq f      (let ((x 1))        (lambda (y) (* x y))))

直观上,这个函数f的结果就是(* 1 y),也就是y的值。那么实际调用的结果如何呢?

let ((x 2))  (funcall f 2))

即f(2)的结果是2?实际运行的结果让人大跌眼镜,是4!

Carl揉揉被蹂躏多时的眼球,问自己:为什么是这样,像梦一场?

行文到这里,我们的黑暗主角慢慢展露头角——作用域(Scope)。作用域在程序的世界里,见怪不怪,谁都知道它的第一要务就是防止命名冲突!

Carl点点头,道:嗯,教材里是这么说的。他抬头看了一下光,阴影里有种烦躁的情绪在跳动。

其实,作用域是分类别的。Lisp中用到了其中之一的动态作用域(Dynamic Scope),动态作用域最根本的特点是从时间维度上判断进入和离开某个函数具有独立的作用域。上面Lisp的例子当中,let ((x 2)) (funcall f 2)表示一个函数,也就是独立的作用域。它调用了f这个函数,但从时间上看,调用点依旧处于原来的作用域当中,所以x=2这样的变量在f函数里是有效的,最后的结果是(* 2 y),等于4也就理所应当了。

Carl接受了这样的解释,他想:既然存在,那么一定合理。闭目养神的时间里,他的思维遨游了很久,有溺水的迹象,仿佛得到了神启。他想到了人生,理想,家人,婚姻和python2

python2里面,这个世界里有怪物存在——嵌套的函数和外部作用域的再绑定。

x = "global"def foo():    x = "foo"    def bar():        print x    bar()foo()

答案是global,而不是想当然的foo。python2里头使用了静态作用域(Static Scope), 与动态作用域相对。静态作用域的显著特点是源码级别上拥有独立的作用域,简单地可以理解成每一个函数都是独立的作用域。然后,2011年发布的python2.1修改了逻辑,最终返回了foo,他们显然认为嵌套的函数不是函数,而是形同if/else这样的嵌套结构体。感觉上,又回到了当初动态作用域的年代。

Carl咽了口水,他讨厌动态作用域如同父母窥视了孩子狭小私人空间,这让他很不舒服。有一首诗:阴郁的日子总会过去。接下来,事情总算不太坏。

def foo():    x = "old"    def bar():        x = "new"    bar()    print xfoo()

这回得到的还是old。

Carl笑了,这才像话。

def foo():    x = "old"    def bar():        nonlocal x        x = "new"    bar()    print xfoo()

这回得到的却是new。

Carl哭了。他想起来了,java里的类,private, protected修饰符,还有静态变量。他触摸到了作用域的本质,如同一直看不见的手,抚摸那薄膜面纱背后吹弹可破的脸蛋。至于有没有被赐予洪亮的耳光,那已经是后话了。

0 0
原创粉丝点击