首页 > 其他 > 详细

解释器求值的那个夏天

时间:2014-05-14 03:05:57      阅读:451      评论:0      收藏:0      [点我收藏+]

求值从何始?

开始思考求值的实际意义是在自己要实现一个解释器的时候,那个时候很头疼,一直在思考求值的自然含义,怎样求值,求值会遇到的问题。甚至思考值本身的意义是什么?
这可能是个很令人不屑一想的问题,试想在使用编程语言的时候,大多数时候是在思考算法,实现业务逻辑,架构设计,思考这个语言本身的问题,既不能加快开发,也不能明悟。

我为什么思考?

在我开始学习SICP的时候,我的小伙伴已经学到一半,并开始实现一个解释器,本来没打算写什么解释器,但是出于暑假无聊,我也"跟风"写了一个scheme解释器。
我coding过半,完成了词法,语法解析部分之后,开始为这个空的global环境增加一些基本的过程,例如最基本的:

+  -  *  /  lambda  define

但是这时候我开始思考这些基本过程的意义是什么,scheme这门语言是允许overwrite一些方法的,比如用户可以:

(define + -)

这就意味着+的功能被修改成了原来-的功能,那么我开始反问: 既然这些方法可以被重写,那么我现在设置这些值是为了什么,反正这些规则是可以改变的,到底有什么意义?如果我这样做是错的,那么到否意味着我应该把握住自己的“权威”,禁止用户去修改这些我设定的初始值;如果是对的,那么如果用户胡乱重写这些方法,那么最后可能这门语言什么用都没了,再也找不到+的意义,也再也找不到-的意义,最后没有意义。
这个问题我想了以后,发现是永远不可能有一个正确的答案,有的只是设计者的一些态度,世界本来就始于虚无,能够存在就是因爲创造,所以最后我将这些算数操作依然处理为用户可重写的,但是却保留了define,lambda这些关键字,纵然可以重新归于0,但不能缺乏创造。

在哪求值?

刚才我提到了global环境,环境便是值所在处,所谓猪肉长在猪身上,若要在环境中求值,必定先要在环境中注入值,譬如在global环境增加一个最常见的求阶乘的函数:

(define factorial (lambda (n)
    (if (= n 1)
        1
        (factorial (- n 1)))))

在下次调用时只要身处这个global环境,就可以找到这个factorial函数:

(factorial 3) => 6

细心的朋友会问

  • n是怎么传到这个函数的计算里的?
  • 同时,还应该问=,-,甚至factorial是怎么被访问到的?

计算需要求值环境,所以这一切的值的访问,需要环境,在计算时任何变量都会绑定到一个环境中,如果在环境中搜索到值,那么求值ok,否则undefined。(factorial 3)在计算的时候绑定了global环境。

环境链

环境如果是单一的,那么对于各个对象的私有状态是个巨大的挑战。试想coder1将name修改为‘coder1‘,coder2将name修改为‘coder2‘,当coder1读取name时,意外地发现竟然不是自己的名字。而为了实现两个coder各自的状态,就得在变量命名上要求不重复,更坏的情况是coder越来越多,这个问题越来越突出。
利用私有环境可以维护各个对象的私有状态,coder1设置读取自己的name属性,就可以去这个环境中获得,而其它的变量依然可以去global中获取,所以环境链成了一个自然的解决方案,以下是(factorial 3)的计算环境:

-GLOBAL
^       - => [procedure:minus]
|       * => [procedure:multiple]
|       factorial => [procedure:factorial]
-ANONYMOUS COMPUTING ENVIRONMENT
^       n => 3
|
CURRENT

(factorial 3)在求值函数体的时候,将当前的环境指向了这个为了当前计算而产生的匿名计算环境,里面有的就是通过参数传入的n,名字为n的变量便绑定到了这个值,而当求值失败后,解释器判断是否存在下一个环境,如果存在,那么求值将在环境链下个环境中求值,直到求值成功或环境链遍历结束,返回undefined。

一个求值例子: javascript的this求值

js的求值本质适合scheme一样的,不过是语法的略微不同,而js中的this的求值比较好玩,它是一个异数,它是和当前对象绑定的,解释器根据当前执行这个函数的对象来判断这个this的值,this是只读的。
可以有几个有趣的例子来对this有个认识:

  1. global this

    var name = ‘global‘;
    alert(this);       // [Object:Window]
    alert(this.name);  // ‘gloabl‘
    
  2. 新建对象里的this

    var name = ‘global‘;
    var Coder = {
        name : ‘srggggg‘,
        name2 : this.name,
        name3 : name,
        getName : function() {
            return this.name;
        },
    };
    alert(Coder.getName());  // ‘srggggg‘
    alert(Coder.name);  // ‘srggggg‘
    alert(Coder.name2);  // ‘global‘
    alert(Coder.name3());  // ‘global‘
    
  3. 嵌套的function的this

    var name = ‘global‘;
    var Coder = {
        name : ‘srggggg‘,
        getNameFunc : function() {
            return function() {
                return this.name;
            };
        }
    };
    alert(Coder.getNameFunc()());  // ‘global‘
    

第一种情况是最直白的由于在global环境中求值,this就是global环境下的Window对象。
第二种情况,this显式出现2次,隐式出现1次。

  • name2的定义中this是绑定到global中的Window对象,定义本身是一种求值过程,实际上是这次的define执行被绑定到了global环境,所以name2是‘global‘
  • name3的定义中没有this,但是由于name从global中被解析出来,所以可以看作this.name
  • getName函数定义中用到了this,这个this是真正绑定到Coder对象的,getName的函数体执行肯定是在Coder对象定义之后,因此this就不会在Coder定义过程中被绑定到global,而是绑定到最接近自己Coder对象。

第三种情况,this对象绑定的是Window对象,说明了function本身不能作为this对象,Coder.getNameFunc返回的是一个function,Coder.getNameFunc()返回的是执行这个function返回的function,而如果function本身如何可以作为对象的话,那么根据第二个例子this绑定的是最近的object,那么this应该绑定的是getNameFunc这个"function object",可惜javascript并不把function真正作为一个可以充当this的对象,既然如此this就被绑定到了执行这个Coder.getNameFunc()的[Window Object]上了。

// Another sample
// Array Object
var array = new Array();
array.name = ‘array‘;
array[0] = function() { return this.name; };
alert(array[0]()); // ‘array‘

聊以此文,怀念我那纠结但是执着的暑假,在我的人生道路上留下一个印记

解释器求值的那个夏天,布布扣,bubuko.com

解释器求值的那个夏天

原文:http://www.cnblogs.com/pier2/p/eval.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!