设为首页收藏本站订阅更新

无忧脚本

 找回密码
 加入无忧

QQ登录

只需一步,快速开始

查看: 18524|回复: 49

[原创]极致之美——百行代码实现全新智能语言! [复制链接]

Rank: 8Rank: 8

注册时间
2005-3-9
威望
1952
阅读权限
150
积分
4347
帖子
1577
精华
9
UID
24714
状态
当前离线
发表于 2005-11-7 19:39:06 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ 查看个人网站
一键分享 一键分享
首先要解释一下:


“极致之美”不是说月儿的这篇文章,因为本人还没有自大到这种程度:P,它形容的是Lisp和javascript结合的优美形态。

本来以下内容是要在无优首发的,但是不巧完成文章的当天忽然发现无优“弹”了,直到上周末才恢复=.=,由于不能等那么久,所以就先放到月儿在CSDN上的博客里去了。

正如标题所描述的,下文是关于用javascript实现类Lisp语言的技巧,然而重点不在于如何实现一门编程语言,而是在于通过思考和实现过程展示javascript的简洁灵活和Lisp的优美。

或许这里接触Lisp的人不多,因此不少人一定会对以下的内容或形式感到奇怪,如果你完全没有接触过它,不必过分惊讶,Lisp的确与以前你见过得所有编程语言不同,因为,呃,它是Lisp,独一无二的Lisp,一段优雅、简洁、完整、独立的奇妙思想,也许你会觉得它很难懂,但是一旦你懂了,你会喜欢上它的。

好了,下面开始我们的LispScript之旅~


最近在网上偶然看到一篇文章,说javascript = C+Lisp,于是思考这样的问题,既然javascript包含着部分Lisp的血统,那么用javascript来实现一个类似于Lisp的人工智能脚本又会是什么样子?

LISt Processing语系作为一种“函数式”语系,自从诞生之日起便以其简单优美的风格和简洁高效的结构征服了许许多多的研究者和爱好者。

目前这种古老的语言和文法仍然被许许多多的人使用着并热爱着,而且在人工智能等领域发挥着非常巨大的作用。

我认为,javascript的灵活加上Lisp的简洁,应该能够创造出一种非常优美的语言,不过这种语言是什么样子的呢?相信大家也很想知道,那么下面我们一起来研究一下这个非常吸引人的问题。

(在仔细阅读下面的内容之前,建议大家先倒杯热茶,坐下来平静一下自己的心情,深呼吸一下,集中起精神来,因为下面的过程将是有趣而又颇耗脑细胞的...^^)

在进入Lisp王国之前,让我们先来做一些javascrip的准备工作...请仔细阅读下面的代码

  1. NIL = [];

  2. Array.prototype.toEvalString = function()
  3. {
  4. if(this.length <= 0) return "NIL";
  5. var str = "";
  6. for (var i = 0; i < this.length; i++)
  7. {
  8.   if(this[i] instanceof Array)
  9.    str += "," + this[i].toEvalString();
  10.   else str += "," + this[i];
  11. }
  12. return "[" + str.slice(1) + "]";
  13. };

  14. (function(){

  15. LispScript = {
  16.   Run : run
  17. };

  18. function run(code)
  19. {
  20.   if(code instanceof Array)
  21.   {
  22.    var elements = new Array();
  23.    for (var i = 0; i < code.length; i++)
  24.    {
  25.     code[i] = run(code[i]); //递归向下读取
  26.     if(code[i] instanceof Function)  //解析表达式
  27.     {
  28.      if(code[i].length <= 0) //无参函数可省略[]直接以函数名称调用
  29.      {
  30.       code[i] = code[i].call(null);
  31.      }
  32.      else if(i == 0)  //调用带参数的函数[funcall,args...]
  33.      {
  34.       return code[i].apply(null, code.slice(1));
  35.      }
  36.     }
  37.    }

  38.    return code;
  39.   }
  40.   return Element(code);
  41. };
  42. })();

  43. function Assert(msg, cond)
  44. {
  45. if(cond)
  46.   return true;
  47. else
  48.   {
  49.    alert(msg);
  50.    throw new Error(msg);
  51.   }
  52. };

  53. function Element(arg)
  54. {
  55. if(arg == null)
  56.   return [];
  57. else if(arg instanceof Function && arg.length <= 0)
  58.   return arg.call(null);
  59. else
  60.   return arg;
  61. };

  62. __funList = new Array();
复制代码运行代码另存代码


以上这段简简单单不过数十行的javascript代码由三个辅助函数、一个主体对象、一个常量NIL(后面我们会知道它表示一个空表或者逻辑false),以及一个存放函数名称的堆栈组成。

LispScript静态对象构成了LispScript解析器的主体,它只有一个Run方法,该方法用向下递归的方式解析传递进来的LispScript代码,代码的类型——相信细心的读者已经发现了——直接用的是javascript的数组,也就是一系列“[”、“]”和分隔符“,”构成的序列。

用javascript天然的数组特性,使得我们的解析器可以设计得十分简洁——不用去拆分和解析每一个token,于是一段简短到不到50行的代码惊人地实现了整个LispScript解析器的核心!

三个辅助函数的作用分别是为函数迭代提供解析(toEvalString),检测序列异常(Assert,后面的具体实现中其实并没有用到),以及解析指令单词(Element)

接下来我们先定义表达式.表达式或是一个原子[atom],它是一个字母序列(如 foo),或是一个由零个或多个表达式组成的表(list), 表达式之间用逗号分开, 放入一对中括号中. 以下是一些表达式:
(注:原Lisp语法的表达式用空格隔开,放入一对括号中。因是javascript的实现,所以用中括号和逗号较为简洁)

foo
[]
[foo]
[foo,bar]
[a,b,[c],d]

最后一个表达式是由四个元素组成的表, 第三个元素本身是由一个元素组成的表.
在算术中表达式 1 + 1 得出值2. 正确的Lisp表达式也有值. 如果表达式e得出值v,我们说e返回v. 下一步我们将定义几种表达式以及它们的返回值.

如果一个表达式是表,我们称第一个元素为操作符,其余的元素为自变量.我们将定义七个原始(从公理的意义上说)操作符: quote,atom,eq,car,cdr,cons,和 cond.

[quote,x] 返回x. 我们把[quote,x]简记为[_,x].

> [quote,a]
a
> [_,a]
a
> [quote,[a b c]]
[a,b,c]


  1. quote = _ = function(args)
  2. {
  3. if(arguments.length < 1)
  4.   return [];
  5. else if(arguments.length >= 1)
  6. {
  7.   return arguments[0];
  8. }
  9. };
复制代码运行代码另存代码


[atom,x]返回原子true如果x的值是一个原子或是空表,否则返回[]. 在Lisp中我们按惯例用原子true表示真, 而用空表表示假.

> [atom,[_,a]]
true
> [atom,[_,[a,b,c]]]
[]
> [atom,[_,[]]]
true


  1. atom = function(arg)
  2. {
  3. var tmp = LispScript.Run(arg); //先对参数求值

  4. if(!(tmp instanceof Array) || tmp.length <= 0)
  5.   return true;
  6. else
  7.   return [];
  8. };
复制代码运行代码另存代码


既然有了一个自变量需要求值的操作符, 我们可以看一下quote的作用. 通过引用(quote)一个表,我们避免它被求值. 一个未被引用的表作为自变量传给象 atom这样的操作符将被视为代码:

> [atom,[atom,[_,a]]]
true

反之一个被引用的表仅被视为表, 在此例中就是有两个元素的表:

> [atom,[_,[atom,[_,a]]]]
[]

这与我们在英语中使用引号的方式一致. Cambridge(剑桥)是一个位于麻萨诸塞州有90000人口的城镇. 而"Cambridge"是一个由9个字母组成的单词.

引用看上去可能有点奇怪因为极少有其它语言有类似的概念. 它和Lisp最与众不同的特征紧密联系:代码和数据由相同的数据结构构成, 而我们用quote操作符来区分它们.

[eq,x,y]返回t如果x和y的值是同一个原子或都是空表, 否则返回[].

> [eq,[_,a],[_,a]]
true
> [eq,[_,a],[_,b]]
[]
> [eq,[_,[]],[_,[]]]
true


  1. equal = eq = function(arg1, arg2)
  2. {
  3. var tmp1 = LispScript.Run(arg1);
  4. var tmp2 = LispScript.Run(arg2);   //先对参数求值

  5. if(!(tmp1 instanceof Array) && !(tmp2 instanceof Array) &&
  6.   tmp1.toString() == tmp2.toString() ||
  7.   (tmp1 instanceof Function) && (tmp2 instanceof Function) && tmp1.toString() == tmp2.toString() ||
  8.   (tmp1 instanceof Array) && (tmp2 instanceof Array) && (tmp1.length == 0) && (tmp2.length == 0))
  9.   return true;
  10. else
  11.   return [];
  12. };
复制代码运行代码另存代码


[car,x]期望x的值是一个表并且返回x的第一个元素.

> [car,[_,[a b c]]]
a


  1. car = function(arg)
  2. {
  3. var tmp = LispScript.Run(arg);  //先对参数求值

  4. if(tmp instanceof Array && tmp.length > 0)
  5.   return tmp[0];
  6. else
  7.   return [];
  8. };
复制代码运行代码另存代码


[cdr,x]期望x的值是一个表并且返回x的第一个元素之后的所有元素.

> [cdr,[_,[a b c]]]
[b,c]


  1. cdr = function(arg)
  2. {
  3. var tmp = LispScript.Run(arg);  //先对参数求值

  4. if(tmp instanceof Array && tmp.length > 0)
  5.   return tmp.slice(1);
  6. else
  7.   return [];
  8. };
复制代码运行代码另存代码


[cons,x,y]期望y的值是一个表并且返回一个新表,它的第一个元素是x的值, 后面跟着y的值的各个元素.

> [cons,[_,a],[_,[b,c]]]
[a,b,c]
> [cons,[_,a],[cons,[_,b],[cons,[_,c],[_,[]]]]]
[a,b,c]
> [car,[cons,[_,a],[_,[b c]]]]
a
> [cdr,[cons,[_,a],[_,[b,c]]]]
[b,c]


  1. cons = function(arg1, arg2)
  2. {
  3. var tmp1 = LispScript.Run(arg1);
  4. var tmp2 = LispScript.Run(arg2);   //先对参数求值

  5. if(tmp2 instanceof Array)
  6. {
  7.   var list = new Array();
  8.   list.push(tmp1);
  9.   return list.concat(tmp2);
  10. }
  11. else
  12.   return [];
  13. };
复制代码运行代码另存代码


[cond [...] ...[...]] 的求值规则如下. p表达式依次求值直到有一个返回t. 如果能找到这样的p表达式,相应的e表达式的值作为整个cond表达式的返回值.

> [cond,[[eq,[_,a],[_,b]],[_,first]],
      [,[atom,[_,a]], [_,second]]]
second


  1. cond = function(args)
  2. {
  3. for (var i = 0; i < arguments.length; i++)
  4. {
  5.   if(arguments[i] instanceof Array)
  6.   {
  7.    var cond = LispScript.Run(arguments[i][0]);  //先对参数求值
  8.    //alert(cond);
  9.    if(cond == true && arguments[i][1] != null)
  10.     return LispScript.Run(arguments[i][1]);
  11.   }
  12. }
  13. return [];
  14. };
复制代码运行代码另存代码


当表达式以七个原始操作符中的五个开头时,它的自变量总是要求值的.2 我们称这样 的操作符为函数.

接着我们定义一个记号来描述函数.函数表示为[lambda, [...], e],其中 ...是原子(叫做参数),e是表达式. 如果表达式的第一个元素形式如上
[[lambda,[...],e],...]

则称为函数调用.它的值计算如下.每一个表达式先求值,然后e再求值.在e的求值过程中,每个出现在e中的的值是相应的在最近一次的函数调用中的值.

> [[lambda,['x'],[cons,'x',[_,[c]]]],[_,a]]
[a,c]
> [[lambda,['x','y'],[cons,'x',[cdr,'y']]],[_,z],[_,[a,b,c]]]
[z,b,c]


  1. lambda = function(args, code)
  2. {
  3. if(code instanceof Array)
  4. {
  5.   var fun = new Function(args,
  6.    "for(var i = 0; i < arguments.length; i++) arguments[i] = LispScript.Run(arguments[i]);return LispScript.Run("+code.toEvalString()+");");

  7.   var globalFuncName = __funList.pop();
  8.   
  9.   fun._funName = globalFuncName;

  10.   if(globalFuncName != null)
  11.    self[globalFuncName] = fun;

  12.   return fun;
  13. }

  14. return [];
  15. };
复制代码运行代码另存代码


如果一个表达式的第一个元素f是原子且f不是原始操作符
[f ...]

并且f的值是一个函数[lambda,[...]],则以上表达式的值就是

[[lambda,[...],e],...]

的值. 换句话说,参数在表达式中不但可以作为自变量也可以作为操作符使用:

> [[lambda,[f],[f,[_,[b,c]]],[_,[lambda,[x],[cons,[_,a],x]]]
[a,b,c]

有另外一个函数记号使得函数能提及它本身,这样我们就能方便地定义递归函数.记号

[label,f,[lambda,[...],e]]

表示一个象[lambda,[...],e]那样的函数,加上这样的特性: 任何出现在e中的f将求值为此label表达式, 就好象f是此函数的参数.

假设我们要定义函数[subst,x,y,z], 它取表达式x,原子y和表z做参数,返回一个象z那样的表, 不过z中出现的y(在任何嵌套层次上)被x代替.

> [subst,[_,m],[_,b],[_,[a,b,[a,b,c],d]]]
[a,m,[a,m,c],d]

我们可以这样表示此函数

[label,subst,[lambda,[x,y,z],
               [cond,[[atom,z],
                      [cond,[[eq,z,y],x],
                            true,z]]],
                     [true,[cons,[subst,x,y,[car,z]],
                               [subst,x,y,[cdr,z]]]]]]]

  1. label = function(funName, funDef)
  2. {
  3. __funList.push(funName);

  4. return LispScript.Run(funDef);
  5. };
复制代码运行代码另存代码


我们简记f=[label,f,[lambda,[...],e]]为
[defun,f,[...],e]

  1. defun = function(funName, args, code)
  2. {
  3. __funList.push(funName);

  4. if(code instanceof Array)
  5. {
  6.   var fun = new Function(args,
  7.    "for(var i = 0; i < arguments.length; i++) arguments[i] = LispScript.Run(arguments[i]);return LispScript.Run("+code.toEvalString()+");");

  8.   var globalFuncName = __funList.pop();

  9.   fun._funName = globalFuncName;

  10.   if(globalFuncName != null)
  11.    self[globalFuncName] = fun;

  12.   return fun;
  13. }

  14. return [];
  15. };
复制代码运行代码另存代码


于是

[defun,subst,[x,y,z],
  [cond,[[atom,z],
         [cond,[[eq,z,y],x],
               [true,z]]],
     [true,[cons,[subst,x,y,[car,z]],
                  [subst,x,y,[cdr,z]]]]]]

偶然地我们在这儿看到如何写cond表达式的缺省子句. 第一个元素是't的子句总是会成功的. 于是
[cond,[x,y],[[_,true],z]]

等同于我们在某些语言中写的
if x then y else z

对于函数调用,具有如下结构:[FunName,[_,args]]
其中FunName是函数名称,[_,args]是指定参数引用列表args
注意[FunName,args]也是合法的,但是和[FunName,[_,args]]有所区别,对于前者,指令在被调用之前先计算args的值,把计算出的值作为参数列表代入函数计算(期望args计算结果为List),而后者的args参数列表在函数指令调用时才被计算


到这里为止我们很高兴地看到LispScript已经可以不依赖于javascript来扩展了

现在我们可以直接用LispScript定义一些新函数了:

函数:[isNull,x]测试它的自变量是否是空表.

  1. LispScript.Run(
  2. [defun,'isNull',['x'],
  3.   [eq,'x',[_,NIL]]]
  4. );
复制代码运行代码另存代码



> [isNull,[_,a]]
[]
> [isNull. [_,[]]]
t

函数:[and,x,y]返回t如果它的两个自变量都是t, 否则返回[].

  1. LispScript.Run(
  2. [defun,'and',['x','y'],
  3.   [cond,['x',[cond,['y',true],[true,NIL]],
  4.    [true,NIL]]]]
  5. );
复制代码运行代码另存代码


> [and,[atom,[_,a]],[eq,[_,a],[_,a]]]
t
> [and,[atom,[_,a]],[eq,[_,a],[_,b]]]
[]

函数:[not,x]返回t如果它的自变量返回[],返回[]如果它的自变量返回t.

  1. LispScript.Run(
  2. [defun,'not',['x'],
  3.   [cond,['x',NIL],
  4.         [true,true]]]
  5. );
复制代码运行代码另存代码


> [not,[eq,[_,a],[_,a]]]
[]
> [not,[eq,[_,a],[_,b]]]
t

函数:[append,x,y]取两个表并返回它们的连结.

  1. LispScript.Run(
  2. [defun,'append',['x','y'],
  3.   [cond,[[isNull,'x'],'y'],
  4.     [true,[cons,[car,'x'],['append',[cdr,'x'],'y']]]]]
  5. );
复制代码运行代码另存代码


> [append,[_,[a,b]],[_,[c,d]]]
[a,b,c,d]
> [append,[], [_,[c,d]]]
[c,d]

函数:[pair,x,y]取两个相同长度的表,返回一个由双元素表构成的表,双元素表是相应位置的x,y的元素对.

  1. LispScript.Run(
  2. [defun,'pair',['x','y'],
  3.   [cond,
  4.    [[and,[isNull,'x'],[isNull,'y']],NIL],
  5.    [[and,[not,[atom,'x']],[not,[atom,'y']]],
  6.     [append,[[[car,'x'],[car,'y']]],['pair',[cdr,'x'],[cdr,'y']]]
  7.    ]]]
  8. );
复制代码运行代码另存代码



> [pair,[_,[x,y,z]],[_,[a,b,c]]]
[[x,a],[y,b],[z,c]]

[assoc,x,y]取原子x和形如pair函数所返回的表y,返回y中第一个符合如下条件的表的第二个元素:它的第一个元素是x.

  1. LispScript.Run(
  2. [defun,'assoc',['x','y'],
  3.   [cond,[[eq,[car,[car,'y']],'x'],[car,[cdr,[car,'y']]]],
  4.    [[isNull,'y'],NIL],[true,['assoc','x',[cdr,'y']]]]]
  5. );
复制代码运行代码另存代码


> [assoc,[_,x],[_,[[x,a],[y,b]]]]
a
> [assoc,[_,x],[_,[[x,new],[x,a],[y,b]]]]
new

[ret,e]返回表达式计算结果

  1. LispScript.Run(
  2. [defun,'ret',['e'],[car,['e']]]
  3. );
复制代码运行代码另存代码


[str,e]返回表达式计算结果的引用

  1. LispScript.Run(
  2. [defun,'str',['e'],[_,[_,'e']]]
  3. );
复制代码运行代码另存代码


我们来看一下为什么要定义ret函数:
我想通过前面的解释和实际应用大家已经理解了引用(quote)的重要性,并且很容易证明:[[_,e]] = [e]
现在的问题是我们必须要定义一个引用的反函数f,令[f,[_,e]] = e
而显然地ret正是这样一个函数

[map,x,y]期望x是原子,y是一个表,如果[assoc,x,y]非空返回[assoc,x,y]的值否则返回x

  1. LispScript.Run(
  2. [defun,'map',['x','y'],
  3.   [cond,[[isNull,[assoc,'x','y']],'x'],[true,[assoc,'x','y']]]]
  4. );
复制代码运行代码另存代码


[maplist,x,y]期望x和y都是表,返回由x中的每个元素t求[map,t,y]的结果构成的表

  1. LispScript.Run(
  2. [defun,'maplist',['x','y'],
  3.   [cond,
  4.    [[atom,[_,'x']],[map,'x','y']],
  5.    [true,[cons,['maplist',[car,[_,'x']],'y'],['maplist',[cdr,[_,'x']],'y']]]
  6.   ]
  7. ]
  8. );
复制代码运行代码另存代码


因此我们能够定义函数来连接表,替换表达式等等.也许算是一个优美的表示法, 那下一步呢? 现在惊喜来了. 我们可以写一个函数作为我们语言的解释器:此函数取任意Lisp表达式作自变量并返回它的值. 如下所示:

  1. LispScript.Run(
  2. [defun,'_eval',['e','a'],
  3.   [ret,[maplist,[_,'e'],'a']]
  4. ]
  5. );
复制代码运行代码另存代码


_eval.的简洁程度或许超出了我们原先的预想,于是这样我们获得了LispScrip实现的一个完整的自身的解析器!
让我们回过头考虑一下这意味着什么. 我们在这儿得到了一个非常优美的计算模型. 仅用quote,atom,eq,car,cdr,cons,和cond, 我们定义了函数_eval.,它事实上实现了我们的语言,用它可以定义和(或)动态生成任何我们想要的额外的函数和各种文法(这一点比较重要)


其他(略为复杂)的扩展:

下面我们定义变量的赋值操作[setq,paraName,paraValue]

  1. LispScript.Run(
  2. [defun,'setq',['para','val'],
  3.   [ret,[defun,'para',[],[_eval,'val']]]]
  4. );
复制代码运行代码另存代码


增加逻辑操作符or,[or,x,y]返回t如果它的自变量有一个为t,否则返回[]

  1. LispScript.Run(
  2. [defun,'or',['x','y'],
  3.   [not,[and,[not,'x'],[not,'y']]]]
  4. );
复制代码运行代码另存代码


增加循环控制foreach,[foreach,v,[paralist],[expr]]
foreach期望list是一个表,依次取表中的每一个原子作为expr的参数进行计算,返回计算结果的表


  1. LispScript.Run(
  2. [defun,'foreach',['v','list','expr'],
  3.   [cond,
  4.    [[isNull,'list'],[]],
  5.    [true,[cons,[_eval,[_,'expr'],[['v',[car,'list']]]],['foreach','v',[cdr,'list'],[_,'expr']]]]
  6.   ]
  7. ]
  8. );
复制代码运行代码另存代码


增加批量赋值操作let,[let,[[a1,v1],[a2,v2]...]]

  1. LispScript.Run(
  2.   [defun,'let',['paralist'],
  3.    [foreach,"'v'",'paralist',[_,[setq,[car,"'v'"],[car,[cdr,"'v'"]]]]]
  4.   ]
  5. );
复制代码运行代码另存代码


总结

现在该回过头来看看我们究竟做了什么,以及这么做有什么意义了。
首先我们用javascript实现了一个简单的向下递归的词法分析器,它能对嵌套数组的每个原子进行简单处理,加上几个辅助函数(toEvalString(),Assert(),Element()和一个存放函数名称的堆栈...简单来说我们仅用了数十行代码实现了一种全新的“函数式”语言??LispScript的完整内核。
接着我们定义了7种原始操作,它们分别是quote,atom,eq,car,cdr,cons和cond
然后(相对较复杂地),我们定义了三种用来描述和调用函数的标记,它们分别是lambda, label以及defun,于是我们成功地用另外不到百行代码实现了LispScript语言的核心环境。
接着(接下来的部分已经可以完全独立于javascript)我们用7种原始操作符和函数定义标记defun定义出一些新的函数,分别是:isNull,and,not,append,pair,assoc,ret和str
然后我们惊喜地发现,可以仅用一行LispScript指令定义出自身的“解析器”??_eval函数
最后我们在此基础上定义出一些略为复杂的函数,它们包括:or,setq,foreach和let,其中一些新函数带给我们的新语言定义变量和处理循环的能力,加上前面实现的一些函数,一个比较完善的基础环境就搭建成了。

写在最后:LispScript和Lisp

事实上我们依照[ref. Paul Graham.]的精彩描述用javascript实现了LispScript,毫无疑问,它是一种Lisp(或者Lisp风格的函数式语言),尽管功能上还十分简陋,但它确实是符合Lisp的基本思想和拥有Lisp的基本特性。由于javascript数组文法的特点,我用[]取代了[ref. Paul Graham]中的(),用逗号取代了空格作为分隔符。同[ref. Paul Graham]的文章以及目前一些标准(或者相对标准)的Lisp不同的是,我根据javascript灵活的特点有意弱化了LispScript的语法结构,这样使得LispScript更加灵活,也更加方便实现,然而代价是一小部分的可维护性和安全性。
最后,LispScript还有许多需要完善的内容,例如,最明显地是它基本上还不具有基本的数值运算能力(相对而言,符号操作能力已经比较完善),另外对原子操作参数合法性的检验、副作用, 连续执行 (它得和副作用在一起才有用), 动态可视域、复杂数据结构支持以及注释文法(这相当重要!)也都是它所欠缺的,不过这些功能“都可以令人惊讶地用极少的额外代码来补救”。

感谢约翰麦卡锡,这位天才早在数十年前就向我们展示了一种程序设计领域内至今无人能超越的“极致的美”,他于1960年发表了一篇非凡的论文,他在这篇论文中对编程的贡献有如欧几里德对几何的贡献.1 他向我们展示了,在只给定几个简单的操作符和一个表示函数的记号的基础上, 如何构造出一个完整的编程语言. 麦卡锡称这种语言为Lisp, 意为List Processing, 因为他的主要思想之一是用一种简单的数据结构表(list)来代表代码和数据.

感谢保罗格雷厄姆,他用浅显易懂的语言将Lisp的根源和实质展现在我们面前,令我们能够幸运地零距离体验Lisp的这种“超凡的美”

如果你理解了约翰麦卡锡的eval, 那你就不仅仅是理解了程序语言历史中的一个阶段. 这些思想至今仍是Lisp的语义核心. 所以从某种意义上, 学习约翰麦卡锡的原著向我们展示了Lisp究竟是什么. 与其说Lisp是麦卡锡的设计,不如说是他的发现. 它不是生来就是一门用于人工智能, 快速原型开发或同等层次任务的语言. 它是你试图公理化计算的结果(之一).

随着时间的推移, 中级语言, 即被中间层程序员使用的语言, 正一致地向Lisp靠近. 因此通过理解eval你正在明白将来的主流计算模式会是什么样.

References
The Roots of Lisp Paul Graham. Draft, January 18, 2002.
LISt Primer Colin Allen & Maneesh Dhagat.Tue Feb 6, 2001.(http://mypage.iu.edu/~colallen/lp/lp.html)

[ 本帖由 月影 最后编辑于 2005-11-7 23:36 ]

Rank: 8Rank: 8

注册时间
2005-3-9
威望
1952
阅读权限
150
积分
4347
帖子
1577
精华
9
UID
24714
状态
当前离线
发表于 2005-11-7 19:54:58 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ 查看个人网站
上面的那些放在文本区域里的代码并不是可以运行的,只是为了让大家看得清楚些

要试着体验LispScript的话,可以运行下面的代码:

  1. <script>
  2. NIL = [];

  3. Array.prototype.toEvalString = function()
  4. {
  5.         if(this.length <= 0) return "NIL";
  6.         var str = "";
  7.         for (var i = 0; i < this.length; i++)
  8.         {
  9.                 if(this[i] instanceof Array)
  10.                         str += "," + this[i].toEvalString();
  11.                 else str += "," + this[i];
  12.         }
  13.         return "[" + str.slice(1) + "]";
  14. };

  15. (function(){
  16.        
  17.         LispScript = {
  18.                 Run : run
  19.         };
  20.        
  21.         function run(code)
  22.         {
  23.                 if(code instanceof Array)
  24.                 {
  25.                         var elements = new Array();
  26.                         for (var i = 0; i < code.length; i++)
  27.                         {
  28.                                 code[i] = run(code[i]);        //递归向下读取
  29.                                 if(code[i] instanceof Function)  //解析表达式
  30.                                 {
  31.                                         if(code[i].length <= 0)        //无参函数可省略[]直接以函数名称调用
  32.                                         {
  33.                                                 code[i] = code[i].call(null);
  34.                                         }
  35.                                         else if(i == 0)  //调用带参数的函数[funcall,args...]
  36.                                         {
  37.                                                 return code[i].apply(null, code.slice(1));
  38.                                         }
  39.                                 }
  40.                         }

  41.                         return code;
  42.                 }
  43.                 return Element(code);
  44.         };
  45. })();

  46. function Assert(msg, cond)
  47. {
  48.         if(cond)
  49.                 return true;
  50.         else
  51.                 {
  52.                         alert(msg);
  53.                         throw new Error(msg);
  54.                 }
  55. };

  56. function Element(arg)
  57. {
  58.         if(arg == null)
  59.                 return [];
  60.         else if(arg instanceof Function && arg.length <= 0)
  61.                 return arg.call(null);
  62.         else
  63.                 return arg;
  64. };

  65. __funList = new Array();

  66. //七个原始操作符
  67. //开始我们先定义表达式.表达式或是一个原子[atom],它是一个字母序列(如 foo),或是一个由零个或多个表达式组成的表(list), 表达式之间用逗号分开, 放入一对中括号中. 以下是一些表达式:
  68. //(注:原Lisp语法的表达式用空格隔开,放入一对括号中。因是javascript的实现,所以用中括号和逗号比较较为简洁)

  69. //foo
  70. //[]
  71. //[foo]
  72. //[foo,bar]
  73. //[a,b,[c],d]

  74. //最后一个表达式是由四个元素组成的表, 第三个元素本身是由一个元素组成的表.
  75. //在算术中表达式 1 + 1 得出值2. 正确的Lisp表达式也有值. 如果表达式e得出值v,我们说e返回v. 下一步我们将定义几种表达式以及它们的返回值.

  76. //如果一个表达式是表,我们称第一个元素为操作符,其余的元素为自变量.我们将定义七个原始(从公理的意义上说)操作符: quote,atom,eq,car,cdr,cons,和 cond.

  77. //[quote,x] 返回x. 我们把[quote,x]简记为[_,x].

  78. //> [quote,a]
  79. //a
  80. //> [_,a]
  81. //a
  82. //> [quote,[a b c]]
  83. //[a,b,c]

  84. quote = _ = function(args)
  85. {
  86.         if(arguments.length < 1)
  87.                 return [];
  88.         else if(arguments.length >= 1)
  89.         {
  90.                 return arguments[0];
  91.         }
  92. };

  93. //[atom,x]返回原子t如果x的值是一个原子或是空表,否则返回[]. 在Lisp中我们按惯例用原子t表示真, 而用空表表示假.

  94. //> [atom,[_,a]]
  95. //t
  96. //> [atom,[_,[a,b,c]]]
  97. //[]
  98. //> [atom,[_,[]]]
  99. //t

  100. atom = function(arg)
  101. {
  102.         var tmp = LispScript.Run(arg); //先对参数求值

  103.         if(!(tmp instanceof Array) || tmp.length <= 0)
  104.                 return true;
  105.         else
  106.                 return [];
  107. };

  108. //既然有了一个自变量需要求值的操作符, 我们可以看一下quote的作用. 通过引用(quote)一个表,我们避免它被求值. 一个未被引用的表作为自变量传给象 atom这样的操作符将被视为代码:


  109. //> [atom,[atom,[_,a]]]
  110. //t

  111. //反之一个被引用的表仅被视为表, 在此例中就是有两个元素的表:

  112. //> [atom,[_,[atom,[_,a]]]]
  113. //[]

  114. //这与我们在英语中使用引号的方式一致. Cambridge(剑桥)是一个位于麻萨诸塞州有90000人口的城镇. 而"Cambridge"是一个由9个字母组成的单词.

  115. //引用看上去可能有点奇怪因为极少有其它语言有类似的概念. 它和Lisp最与众不同的特征紧密联系:代码和数据由相同的数据结构构成, 而我们用quote操作符来区分它们.

  116. //[eq,x,y]返回t如果x和y的值是同一个原子或都是空表, 否则返回[].

  117. //> [eq,[_,a],[_,a]]
  118. //t
  119. //> [eq,[_,a],[_,b]]
  120. //[]
  121. //> [eq,[_,[]],[_,[]]]
  122. //t

  123. equal = eq = function(arg1, arg2)
  124. {
  125.         var tmp1 = LispScript.Run(arg1);
  126.         var tmp2 = LispScript.Run(arg2);   //先对参数求值

  127.         if(!(tmp1 instanceof Array) && !(tmp2 instanceof Array) &&
  128.                 tmp1.toString() == tmp2.toString() ||
  129.                 (tmp1 instanceof Function) && (tmp2 instanceof Function) && tmp1.toString() == tmp2.toString() ||
  130.                 (tmp1 instanceof Array) && (tmp2 instanceof Array) && (tmp1.length == 0) && (tmp2.length == 0))
  131.                 return true;
  132.         else
  133.                 return [];
  134. };

  135. //[car,x]期望x的值是一个表并且返回x的第一个元素.

  136. //> [car,[_,[a b c]]]
  137. //a

  138. car = function(arg)
  139. {
  140.         var tmp = LispScript.Run(arg);  //先对参数求值

  141.         if(tmp instanceof Array && tmp.length > 0)
  142.                 return tmp[0];
  143.         else
  144.                 return [];
  145. };

  146. //[cdr,x]期望x的值是一个表并且返回x的第一个元素之后的所有元素.
  147. //> [cdr,[_,[a b c]]]
  148. //[b,c]

  149. cdr = function(arg)
  150. {
  151.         var tmp = LispScript.Run(arg);  //先对参数求值

  152.         if(tmp instanceof Array && tmp.length > 0)
  153.                 return tmp.slice(1);
  154.         else
  155.                 return [];       
  156. };

  157. //[cons,x,y]期望y的值是一个表并且返回一个新表,它的第一个元素是x的值, 后面跟着y的值的各个元素.

  158. //> [cons,[_,a],[_,[b,c]]]
  159. //[a,b,c]
  160. //> [cons,[_,a],[cons,[_,b],[cons,[_,c],[_,[]]]]]
  161. //[a,b,c]
  162. //> [car,[cons,[_,a],[_,[b c]]]]
  163. //a
  164. //> [cdr,[cons,[_,a],[_,[b,c]]]]
  165. //[b,c]

  166. cons = function(arg1, arg2)
  167. {
  168.         var tmp1 = LispScript.Run(arg1);
  169.         var tmp2 = LispScript.Run(arg2);   //先对参数求值

  170.         if(tmp2 instanceof Array)
  171.         {
  172.                 var list = new Array();
  173.                 list.push(tmp1);
  174.                 return list.concat(tmp2);
  175.         }
  176.         else
  177.                 return [];
  178. };

  179. //[cond [...] ...[...]] 的求值规则如下. p表达式依次求值直到有一个返回t. 如果能找到这样的p表达式,相应的e表达式的值作为整个cond表达式的返回值.

  180. //> [cond,[[eq,[_,a],[_,b]],[_,first]],
  181. //      [,[atom,[_,a]], [_,second]]]
  182. //second

  183. cond = function(args)
  184. {
  185.         for (var i = 0; i < arguments.length; i++)
  186.         {
  187.                 if(arguments[i] instanceof Array)
  188.                 {
  189.                         var cond = LispScript.Run(arguments[i][0]);  //先对参数求值
  190.                         //alert(cond);
  191.                         if(cond == true && arguments[i][1] != null)
  192.                                 return LispScript.Run(arguments[i][1]);
  193.                 }
  194.         }
  195.         return [];
  196. };

  197. //当表达式以七个原始操作符中的五个开头时,它的自变量总是要求值的.2 我们称这样 的操作符为函数.

  198. //函数的表示

  199. //接着我们定义一个记号来描述函数.函数表示为[lambda, [...], e),其中 ...是原子(叫做参数),e是表达式. 如果表达式的第一个元素形式如上
  200. //[[lambda,[...],e],...]

  201. //则称为函数调用.它的值计算如下.每一个表达式先求值,然后e再求值.在e的求值过程中,每个出现在e中的的值是相应的在最近一次的函数调用中的值.

  202. //> [[lambda,['x'],[cons,'x',[_,[b]]]],[_,a]]
  203. //[a,b]
  204. //> [[lambda,['x','y'],[cons,'x',[cdr,'y']]],[_,z],[_,[a,b,c]]]
  205. //[z,b,c]

  206. lambda = function(args, code)
  207. {
  208.         if(code instanceof Array)
  209.         {
  210.                 var fun = new Function(args,
  211.                         "for(var i = 0; i < arguments.length; i++) arguments[i] = LispScript.Run(arguments[i]);return LispScript.Run("+code.toEvalString()+");");

  212.                 var globalFuncName = __funList.pop();
  213.                
  214.                 fun._funName = globalFuncName;

  215.                 if(globalFuncName != null)
  216.                         self[globalFuncName] = fun;

  217.                 return fun;
  218.         }

  219.         return [];
  220. };

  221. //如果一个表达式的第一个元素f是原子且f不是原始操作符
  222. //[f ...]

  223. //并且f的值是一个函数[lambda,[...]],则以上表达式的值就是

  224. //[[lambda,[...],e],...]

  225. //的值. 换句话说,参数在表达式中不但可以作为自变量也可以作为操作符使用:


  226. //> [[lambda,[f],[f,[_,[b,c]]],[_,[lambda,[x],[cons,[_,a],x]]]
  227. //[a,b,c]

  228. //有另外一个函数记号使得函数能提及它本身,这样我们就能方便地定义递归函数.记号

  229. //[label,f,[lambda,[...],e]]

  230. //表示一个象[lambda,[...],e]那样的函数,加上这样的特性: 任何出现在e中的f将求值为此label表达式, 就好象f是此函数的参数.

  231. //假设我们要定义函数[subst,x,y,z], 它取表达式x,原子y和表z做参数,返回一个象z那样的表, 不过z中出现的y(在任何嵌套层次上)被x代替.

  232. //> [subst,[_,m],[_,b],[_,[a,b,[a,b,c],d]]]
  233. //[a,m,[a,m,c],d]

  234. //我们可以这样表示此函数
  235. //[label,subst,[lambda,[x,y,z],
  236. //               [cond,[[atom,z],
  237. //                      [cond,[[eq,z,y],x],
  238. //                            [_,t],z]]],
  239. //                     [[_,t],[cons,[subst,x,y,[car,z]],
  240. //                               [subst,x,y,[cdr,z]]]]]]]

  241. label = function(funName, funDef)
  242. {
  243.         __funList.push(funName);

  244.         return LispScript.Run(funDef);
  245. };

  246. //我们简记f=[label,f,[lambda,[...],e]]为
  247. //[defun,f,[...],e]

  248. defun = function(funName, args, code)
  249. {
  250.         __funList.push(funName);
  251.        
  252.         if(code instanceof Array)
  253.         {
  254.                 var fun = new Function(args,
  255.                         "for(var i = 0; i < arguments.length; i++) arguments[i] = LispScript.Run(arguments[i]);return LispScript.Run("+code.toEvalString()+");");

  256.                 var globalFuncName = __funList.pop();

  257.                 fun._funName = globalFuncName;

  258.                 if(globalFuncName != null)
  259.                         self[globalFuncName] = fun;

  260.                 return fun;
  261.         }

  262.         return [];
  263. };

  264. //于是

  265. //[defun,subst,[x,y,z],
  266. //  [cond,[[atom,z],
  267. //         [cond,[[eq,z,y],x],
  268. //               [[_,t],z]]],
  269. //                                        [[_,t],[cons,[subst,x,y,[car,z]],
  270. //                  [subst,x,y,[cdr,z]]]]]]

  271. //偶然地我们在这儿看到如何写cond表达式的缺省子句. 第一个元素是't的子句总是会成功的. 于是
  272. //[cond,[x,y],[[_,true],z]]

  273. //等同于我们在某些语言中写的
  274. //if x then y else z

  275. //现在我们可以直接用LispScript定义一些新函数了:

  276. //对于函数调用,具有如下结构:[FunName,[_,args]]
  277. //其中FunName是函数名称,[_,args]是指定参数引用列表args
  278. //注意[FunName,args]也是合法的,但是和[FunName,[_,args]]有所区别,对于前者,指令在被调用之前先计算args的值,把计算出的值作为参数列表代入函数计算(期望args计算结果为List),而后者的args参数列表在函数指令调用时才被计算

  279. //函数:[null,x]测试它的自变量是否是空表.
  280. LispScript.Run(
  281.         [defun,'isNull',['x'],
  282.                 [eq,'x',[_,NIL]]]
  283. );

  284. //> [null,[_,a]]
  285. //[]
  286. //> [null. [_,[]]]
  287. //t

  288. //函数:[and,x,y]返回t如果它的两个自变量都是t, 否则返回[].
  289. LispScript.Run(
  290.         [defun,'and',['x','y'],
  291.                 [cond,['x',[cond,['y',true],[true,NIL]],
  292.                         [true,NIL]]]]
  293. );

  294. //> [and,[atom,[_,a]],[eq,[_,a],[_,a]]]
  295. //t
  296. //> [and,[atom,[_,a]],[eq,[_,a],[_,b]]]
  297. //[]

  298. //函数:[not,x]返回t如果它的自变量返回[],返回[]如果它的自变量返回t.
  299. LispScript.Run(
  300.         [defun,'not',['x'],
  301.                 [cond,['x',NIL],
  302.         [true,true]]]
  303. );

  304. //> [not,[eq,[_,a],[_,a]]]
  305. //[]
  306. //> [not,[eq,[_,a],[_,b]]]
  307. //t

  308. //函数:[append,x,y]取两个表并返回它们的连结.
  309. LispScript.Run(
  310.         [defun,'append',['x','y'],
  311.                 [cond,[[isNull,'x'],'y'],
  312.                          [true,[cons,[car,'x'],['append',[cdr,'x'],'y']]]]]
  313. );

  314. //> [append,[_,[a,b]],[_,[c,d]]]
  315. //[a,b,c,d]
  316. //> [append,[], [_,[c,d]]]
  317. //[c,d]

  318. //函数:[pair,x,y]取两个相同长度的表,返回一个由双元素表构成的表,双元素表是相应位置的x,y的元素对.
  319. LispScript.Run(
  320.         [defun,'pair',['x','y'],
  321.                 [cond,
  322.                         [[and,[isNull,'x'],[isNull,'y']],NIL],       
  323.                         [[and,[not,[atom,'x']],[not,[atom,'y']]],
  324.                          [append,[[[car,'x'],[car,'y']]],['pair',[cdr,'x'],[cdr,'y']]]
  325.                         ]]]
  326. );

  327. //> [pair,[_,[x,y,z]],[_,[a,b,c]]]
  328. //[[x,a],[y,b],[z,c]]

  329. //[assoc,x,y]取原子x和形如pair函数所返回的表y,返回y中第一个符合如下条件的表的第二个元素:它的第一个元素是x.

  330. LispScript.Run(
  331.         [defun,'assoc',['x','y'],
  332.                 [cond,[[eq,[car,[car,'y']],'x'],[car,[cdr,[car,'y']]]],
  333.                         [[isNull,'y'],NIL],[true,['assoc','x',[cdr,'y']]]]]
  334. );

  335. //> [assoc,[_,x],[_,[[x,a],[y,b]]]]
  336. //a
  337. //> [assoc,[_,x],[_,[[x,new],[x,a],[y,b]]]]
  338. //new

  339. //[ret,e]返回表达式计算结果
  340. LispScript.Run(
  341.         [defun,'ret',['e'],[car,['e']]]
  342. );

  343. //[str,e]返回表达式计算结果的引用
  344. LispScript.Run(
  345.         [defun,'str',['e'],[_,[_,'e']]]
  346. );

  347. //我们来看一下为什么要定义ret函数:
  348. //我想通过前面的解释和实际应用大家已经理解了引用(quote)的重要性,并且很容易证明:[[_,e]] = [e]
  349. //现在的问题是我们必须要定义一个引用的反函数f,令[f,[_,e]] = e
  350. //而显然地ret正是这样一个函数

  351. //[map,x,y]期望x是原子,y是一个表,如果[assoc,x,y]非空返回[assoc,x,y]的值否则返回x
  352. LispScript.Run(
  353.         [defun,'map',['x','y'],
  354.                 [cond,[[isNull,[assoc,'x','y']],'x'],[true,[assoc,'x','y']]]]
  355. );

  356. //[maplist,x,y]期望x和y都是表,返回由x中的每个元素t求[map,t,y]的结果构成的表
  357. LispScript.Run(
  358.         [defun,'maplist',['x','y'],
  359.                 [cond,
  360.                         [[atom,[_,'x']],[map,'x','y']],
  361.                         [true,[cons,['maplist',[car,[_,'x']],'y'],['maplist',[cdr,[_,'x']],'y']]]
  362.                 ]
  363.         ]
  364. );

  365. //一个惊喜

  366. //因此我们能够定义函数来连接表,替换表达式等等.也许算是一个优美的表示法, 那下一步呢? 现在惊喜来了. 我们可以写一个函数作为我们语言的解释器:此函数取任意Lisp表达式作自变量并返回它的值. 如下所示:


  367. LispScript.Run(
  368.         [defun,'_eval',['e','a'],
  369.                 [ret,[maplist,[_,'e'],'a']]
  370.         ]
  371. );

  372. //_eval.的简洁程度或许超出了我们原先的预想,于是这样我们获得了LispScrip实现的一个完整的自身的解析器!
  373. //让我们回过头考虑一下这意味着什么. 我们在这儿得到了一个非常优美的计算模型. 仅用quote,atom,eq,car,cdr,cons,和cond, 我们定义了函数_eval.,它事实上实现了我们的语言,用它可以定义和(或)动态生成任何我们想要的额外的函数和各种文法(这一点比较重要)

  374. //其他的扩展

  375. //下面我们定义变量的赋值操作[setq,paraName,paraValue]
  376. LispScript.Run(
  377.         [defun,'setq',['para','val'],
  378.                 [ret,[defun,'para',[],[_eval,'val']]]]
  379. );


  380. //增加逻辑操作符or,[or,x,y]返回t如果它的自变量有一个为t,否则返回[]
  381. LispScript.Run(
  382.         [defun,'or',['x','y'],
  383.                 [not,[and,[not,'x'],[not,'y']]]]
  384. );

  385. //增加循环控制foreach,[foreach,v,[list],[expr]]
  386. //foreach期望list是一个表,依次取表中的每一个原子作为expr的参数进行计算,返回计算结果的表
  387. LispScript.Run(
  388.         [defun,'foreach',['v','list','expr'],
  389.                 [cond,
  390.                         [[isNull,'list'],[]],
  391.                         [true,[cons,[_eval,[_,'expr'],[['v',[car,'list']]]],['foreach','v',[cdr,'list'],[_,'expr']]]]
  392.                 ]
  393.         ]
  394. );

  395. //增加批量赋值操作let,[let,[[a1,v1],[a2,v2]...]]
  396. LispScript.Run(
  397.                 [defun,'let',['paralist'],
  398.                         [foreach,"'v'",'paralist',[_,[setq,[car,"'v'"],[car,[cdr,"'v'"]]]]]
  399.                 ]
  400. );

  401. //总结

  402. //现在该回过头来看看我们究竟做了什么,以及这么做有什么意义了。
  403. //首先我们用javascript实现了一个简单的向下递归的词法分析器,它能对嵌套数组的每个原子进行简单处理,加上几个辅助函数(toEvalString(),Assert(),Element()和一个存放函数名称的堆栈...简单来说我们仅用了数十行代码实现了一种全新的“函数式”语言??LispScript的完整内核。
  404. //接着我们定义了7种原始操作,它们分别是quote,atom,eq,car,cdr,cons和cond
  405. //然后(相对较复杂地),我们定义了三种用来描述和调用函数的标记,它们分别是lambda, label以及defun,于是我们成功地用另外不到百行代码实现了LispScript语言的核心环境。
  406. //接着(接下来的部分已经可以完全独立于javascript)我们用7种原始操作符和函数定义标记defun定义出一些新的函数,分别是:isNull,and,not,append,pair,assoc,ret和str
  407. //然后我们惊喜地发现,可以仅用一行LispScript指令定义出自身的“解析器”??_eval函数
  408. //最后我们在此基础上定义出一些略为复杂的函数,它们包括:or,setq,foreach和let,其中一些新函数带给我们的新语言定义变量和处理循环的能力,加上前面实现的一些函数,一个比较完善的基础环境就搭建成了。

  409. //写在最后:LispScript和Lisp

  410. //事实上我们依照[ref. Paul Graham.]的精彩描述用javascript实现了LispScript,毫无疑问,它是一种Lisp(或者Lisp风格的函数式语言),尽管功能上还十分简陋,但它确实是符合Lisp的基本思想和拥有Lisp的基本特性。由于javascript数组文法的特点,我用[]取代了[ref. Paul Graham]中的(),用逗号取代了空格作为分隔符。同[ref. Paul Graham]的文章以及目前一些标准(或者相对标准)的Lisp不同的是,我根据javascript灵活的特点有意弱化了LispScript的语法结构,这样使得LispScript更加灵活,也更加方便实现,然而代价是一小部分的可维护性和安全性。
  411. //最后,LispScript还有许多需要完善的内容,例如,最明显地是它基本上还不具有基本的数值运算能力(相对而言,符号操作能力已经比较完善),另外对原子操作参数合法性的检验、副作用, 连续执行 (它得和副作用在一起才有用), 动态可视域、复杂数据结构支持以及注释文法(这相当重要!)也都是它所欠缺的,不过这些功能“都可以令人惊讶地用极少的额外代码来补救”。

  412. //感谢约翰麦卡锡,这位天才早在数十年前就向我们展示了一种程序设计领域内至今无人能超越的“极致的美”,他于1960年发表了一篇非凡的论文,他在这篇论文中对编程的贡献有如欧几里德对几何的贡献.1 他向我们展示了,在只给定几个简单的操作符和一个表示函数的记号的基础上, 如何构造出一个完整的编程语言. 麦卡锡称这种语言为Lisp, 意为List Processing, 因为他的主要思想之一是用一种简单的数据结构表(list)来代表代码和数据.

  413. //感谢保罗格雷厄姆,他用浅显易懂的语言将Lisp的根源和实质展现在我们面前,令我们能够幸运地零距离体验Lisp的这种“超凡的美”

  414. //如果你理解了约翰麦卡锡的eval, 那你就不仅仅是理解了程序语言历史中的一个阶段. 这些思想至今仍是Lisp的语义核心. 所以从某种意义上, 学习约翰麦卡锡的原著向我们展示了Lisp究竟是什么. 与其说Lisp是麦卡锡的设计,不如说是他的发现. 它不是生来就是一门用于人工智能, 快速原型开发或同等层次任务的语言. 它是你试图公理化计算的结果(之一).

  415. //随着时间的推移, 中级语言, 即被中间层程序员使用的语言, 正一致地向Lisp靠近. 因此通过理解eval你正在明白将来的主流计算模式会是什么样.

  416. //References
  417. //The Roots of Lisp Paul Graham. Draft, January 18, 2002.
  418. //LISt Primer Colin Allen & Maneesh Dhagat.Tue Feb 6, 2001.(http://mypage.iu.edu/~colallen/lp/lp.html)
  419. </script>
  420. LispScript指令:(以分号分隔每条指令)<br>
  421. <textarea cols="80" rows="20" id="code">
  422. </textarea><br>
  423. 运行结果:<br>
  424. <textarea cols="80" rows="5" readonly id="result">
  425. </textarea><br>
  426. <input type="button" value="运行" onclick="RunCode()">
  427. <script>
  428. function RunCode()
  429. {
  430.         var codes = code.value.split(';');
  431.         result.value = "";
  432.         for (var i = 0; i < codes.length; i++)
  433.         {
  434.                 try
  435.                 {
  436.                         var res = LispScript.Run(eval(codes[i]));
  437.                 }
  438.                 catch(e)
  439.                 {
  440.                         result.value += "错误的LispScript语法!(" + e.message + ")";
  441.                         continue;
  442.                 }
  443.                 if(res instanceof Array)
  444.                         result.value += res.toEvalString() + "\n";
  445.                 else if(res instanceof Function)
  446.                         result.value += "Function " + res._funName + "\n";
  447.                 else
  448.                         result.value += res + "\n";
  449.         }
  450. }
  451. </script>
复制代码运行代码另存代码


你可以在这个简易的运行环境中输入LispScript的语句,多条语句请用分号分开

例如:你可以输入
[cons,1,[2,3]]
然后运行,结果是[1,2,3]

你可以输入
[setq,'x',1];
[setq,'y',[2,3]];
[cons,x,y]
然后运行,结果仍然是[1,2,3]

你可以输入
[defun,'mycons',['x','y'],[cons,'x','y']];
[mycons,1,[2,3]]
然后运行,结果也仍然是[1,2,3]

使用道具 举报

Rank: 2

升级  62.67%

注册时间
2004-2-16
威望
76
阅读权限
20
积分
144
帖子
75
精华
0
UID
8575
状态
当前离线
发表于 2005-11-7 20:08:38 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 查看个人网站
不推不行
JavaScript:
世界上最被误解的语言
http://www2.uuzone.com/blog/555080192/18957.htm
http://www.cnblogs.com/shiningray/articles/143004.html

[ 本帖由 lint 最后编辑于 2005-11-7 20:23 ]

使用道具 举报

超级版主

我在哪?

Rank: 8Rank: 8

注册时间
2004-4-14
威望
416
阅读权限
150
积分
1562
帖子
2031
精华
7
UID
10635
状态
当前离线
发表于 2005-11-7 21:53:54 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ Yahoo! 查看个人网站
现在在上班,没办法如何研究,只能回家再仔细研究一下,简略看了一下。。郁闷。。看得冷汗直流。。。单从思想上来看,评为精华三并无夸大之处,先评后快。。
我一直在追求着一个答案
可是谁又能够告诉我问题是什么呢?
请支持无忧原创文章版

使用道具 举报

Rank: 8Rank: 8

注册时间
2005-3-9
威望
1952
阅读权限
150
积分
4347
帖子
1577
精华
9
UID
24714
状态
当前离线
发表于 2005-11-7 23:37:34 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ 查看个人网站
引用内容由 泣红亭 发表于 2005-11-7 09:53 PM
现在在上班,没办法如何研究,只能回家再仔细研究一下,简略看了一下。。郁闷。。看得冷汗直流。。。单从思想上来看,评为精华三并无夸大之处,先评后快。。


上夜班啊...好辛苦哦...=.=

使用道具 举报

Rank: 2

升级  76.67%

注册时间
2004-4-6
威望
99
阅读权限
20
积分
165
帖子
79
精华
1
UID
10229
状态
当前离线
发表于 2005-11-8 00:02:43 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ
为了学习,测试上面的ListScript,我写了一个命令环境

<script>
NIL = [];

Array.prototype.toEvalString = function()
{
if(this.length <= 0) return "NIL";
var str = "";
for (var i = 0; i < this.length; i++)
{
  if(this instanceof Array)
   str += "," + this.toEvalString();
  else str += "," + this;
}
return "[" + str.slice(1) + "]";
};

(function(){

LispScript = {
  Run : run
};

function run(code)
{
  if(code instanceof Array)
  {
   var elements = new Array();
   for (var i = 0; i < code.length; i++)
   {
    code = run(code); //递归向下读取
    if(code instanceof Function)  //解析表达式
    {
     if(code.length <= 0) //无参函数可省略[]直接以函数名称调用
     {
      code = code.call(null);
     }
     else if(i == 0)  //调用带参数的函数[funcall,args...]
     {
      return code.apply(null, code.slice(1));
     }
    }
   }

   return code;
  }
  return Element(code);
};
})();

function Assert(msg, cond)
{
if(cond)
  return true;
else
  {
   alert(msg);
   throw new Error(msg);
  }
};

function Element(arg)
{
if(arg == null)
  return [];
else if(arg instanceof Function && arg.length <= 0)
  return arg.call(null);
else
  return arg;
};

__funList = new Array();


/////////////////
quote = _ = function(args)
{
if(arguments.length < 1)
  return [];
else if(arguments.length >= 1)
{
  return arguments[0];
}
};

//[atom,x]返回原子true如果x的值是一个原子或是空表,否则返回[]. 在Lisp中我们按惯例用原子true表示真, 而用空表表示假.
atom = function(arg)
{
var tmp = LispScript.Run(arg); //先对参数求值

if(!(tmp instanceof Array) || tmp.length <= 0)
  return true;
else
  return [];
};

//[eq,x,y]返回t如果x和y的值是同一个原子或都是空表, 否则返回[].
equal = eq = function(arg1, arg2)
{
var tmp1 = LispScript.Run(arg1);
var tmp2 = LispScript.Run(arg2);   //先对参数求值

if(!(tmp1 instanceof Array) && !(tmp2 instanceof Array) &&
  tmp1.toString() == tmp2.toString() ||
  (tmp1 instanceof Function) && (tmp2 instanceof Function) && tmp1.toString() == tmp2.toString() ||
  (tmp1 instanceof Array) && (tmp2 instanceof Array) && (tmp1.length == 0) && (tmp2.length == 0))
  return true;
else
  return [];
};

//[car,x]期望x的值是一个表并且返回x的第一个元素.
car = function(arg)
{
var tmp = LispScript.Run(arg);  //先对参数求值

if(tmp instanceof Array && tmp.length > 0)
  return tmp[0];
else
  return [];
};

//[cdr,x]期望x的值是一个表并且返回x的第一个元素之后的所有元素.
cdr = function(arg)
{
var tmp = LispScript.Run(arg);  //先对参数求值

if(tmp instanceof Array && tmp.length > 0)
  return tmp.slice(1);
else
  return [];
};

//[cons,x,y]期望y的值是一个表并且返回一个新表,它的第一个元素是x的值, 后面跟着y的值的各个元素.
cons = function(arg1, arg2)
{
var tmp1 = LispScript.Run(arg1);
var tmp2 = LispScript.Run(arg2);   //先对参数求值

if(tmp2 instanceof Array)
{
  var list = new Array();
  list.push(tmp1);
  return list.concat(tmp2);
}
else
  return [];
};

/*
[cond [...] ...[...]] 的求值规则如下. p表达式依次求值直到有一个返回t. 如果能找到这样的p表达式,相应的e表达式的值作为整个cond表达式的返回值.

*/
cond = function(args)
{
for (var i = 0; i < arguments.length; i++)
{
  if(arguments instanceof Array)
  {
   var cond = LispScript.Run(arguments[0]);  //先对参数求值
   //alert(cond);
   if(cond == true && arguments[1] != null)
    return LispScript.Run(arguments[1]);
  }
}
return [];
};

//则称为函数调用.它的值计算如下.每一个表达式先求值,然后e再求值.在e的求值过程中,每个出现在e中的的值是相应的在最近一次的函数调用中的值.
lambda = function(args, code)
{
if(code instanceof Array)
{
  var fun = new Function(args,
   "for(var i = 0; i < arguments.length; i++) arguments = LispScript.Run(arguments);return LispScript.Run("+code.toEvalString()+");");

  var globalFuncName = __funList.pop();
  
  fun._funName = globalFuncName;

  if(globalFuncName != null)
   self[globalFuncName] = fun;

  return fun;
}

return [];
};

</script>
<script>
        var prompt = '>';
        function initPrompt()
        {
                if(cmd.value == '')
                        cmd.value = prompt;
        }
        function runLastCmd()
        {
                if(window.event.keyCode == 13)
                {
                        window.event.returnValue = false;
                       
                        var idx = cmd.value.lastIndexOf('\n') + 1;
                        idx += prompt.length;

                        var strCmd = cmd.value.substr(idx);
                        var output = '';
                        try
                        {
                                var lisp = eval(strCmd);
                                if(lisp instanceof Array)
                                {
                                         output = LispScript.Run(lisp);
                                }
                                else
                                {
                                        output = '未知命令';
                                }
                        }
                        catch(e)
                        {
                                output = '语法错误';
                        }
                        if(output instanceof Array)
                        {
                                output = '[' + output + ']';
                        }
                        cmd.value += '\n' + output + '\n' + prompt;
                }
               
        }
</script>
<textarea id=cmd style="width:600px;height:600px" onkeydown="runLastCmd()" onclick="initPrompt()">
</textarea>

  1. <script>
  2. NIL = [];

  3. Array.prototype.toEvalString = function()
  4. {
  5. if(this.length <= 0) return "NIL";
  6. var str = "";
  7. for (var i = 0; i < this.length; i++)
  8. {
  9.   if(this[i] instanceof Array)
  10.    str += "," + this[i].toEvalString();
  11.   else str += "," + this[i];
  12. }
  13. return "[" + str.slice(1) + "]";
  14. };

  15. (function(){

  16. LispScript = {
  17.   Run : run
  18. };

  19. function run(code)
  20. {
  21.   if(code instanceof Array)
  22.   {
  23.    var elements = new Array();
  24.    for (var i = 0; i < code.length; i++)
  25.    {
  26.     code[i] = run(code[i]); //递归向下读取
  27.     if(code[i] instanceof Function)  //解析表达式
  28.     {
  29.      if(code[i].length <= 0) //无参函数可省略[]直接以函数名称调用
  30.      {
  31.       code[i] = code[i].call(null);
  32.      }
  33.      else if(i == 0)  //调用带参数的函数[funcall,args...]
  34.      {
  35.       return code[i].apply(null, code.slice(1));
  36.      }
  37.     }
  38.    }

  39.    return code;
  40.   }
  41.   return Element(code);
  42. };
  43. })();

  44. function Assert(msg, cond)
  45. {
  46. if(cond)
  47.   return true;
  48. else
  49.   {
  50.    alert(msg);
  51.    throw new Error(msg);
  52.   }
  53. };

  54. function Element(arg)
  55. {
  56. if(arg == null)
  57.   return [];
  58. else if(arg instanceof Function && arg.length <= 0)
  59.   return arg.call(null);
  60. else
  61.   return arg;
  62. };

  63. __funList = new Array();


  64. /////////////////
  65. quote = _ = function(args)
  66. {
  67. if(arguments.length < 1)
  68.   return [];
  69. else if(arguments.length >= 1)
  70. {
  71.   return arguments[0];
  72. }
  73. };

  74. //[atom,x]返回原子true如果x的值是一个原子或是空表,否则返回[]. 在Lisp中我们按惯例用原子true表示真, 而用空表表示假.
  75. atom = function(arg)
  76. {
  77. var tmp = LispScript.Run(arg); //先对参数求值

  78. if(!(tmp instanceof Array) || tmp.length <= 0)
  79.   return true;
  80. else
  81.   return [];
  82. };

  83. //[eq,x,y]返回t如果x和y的值是同一个原子或都是空表, 否则返回[].
  84. equal = eq = function(arg1, arg2)
  85. {
  86. var tmp1 = LispScript.Run(arg1);
  87. var tmp2 = LispScript.Run(arg2);   //先对参数求值

  88. if(!(tmp1 instanceof Array) && !(tmp2 instanceof Array) &&
  89.   tmp1.toString() == tmp2.toString() ||
  90.   (tmp1 instanceof Function) && (tmp2 instanceof Function) && tmp1.toString() == tmp2.toString() ||
  91.   (tmp1 instanceof Array) && (tmp2 instanceof Array) && (tmp1.length == 0) && (tmp2.length == 0))
  92.   return true;
  93. else
  94.   return [];
  95. };

  96. //[car,x]期望x的值是一个表并且返回x的第一个元素.
  97. car = function(arg)
  98. {
  99. var tmp = LispScript.Run(arg);  //先对参数求值

  100. if(tmp instanceof Array && tmp.length > 0)
  101.   return tmp[0];
  102. else
  103.   return [];
  104. };

  105. //[cdr,x]期望x的值是一个表并且返回x的第一个元素之后的所有元素.
  106. cdr = function(arg)
  107. {
  108. var tmp = LispScript.Run(arg);  //先对参数求值

  109. if(tmp instanceof Array && tmp.length > 0)
  110.   return tmp.slice(1);
  111. else
  112.   return [];
  113. };

  114. //[cons,x,y]期望y的值是一个表并且返回一个新表,它的第一个元素是x的值, 后面跟着y的值的各个元素.
  115. cons = function(arg1, arg2)
  116. {
  117. var tmp1 = LispScript.Run(arg1);
  118. var tmp2 = LispScript.Run(arg2);   //先对参数求值

  119. if(tmp2 instanceof Array)
  120. {
  121.   var list = new Array();
  122.   list.push(tmp1);
  123.   return list.concat(tmp2);
  124. }
  125. else
  126.   return [];
  127. };

  128. /*
  129. [cond [...] ...[...]] 的求值规则如下. p表达式依次求值直到有一个返回t. 如果能找到这样的p表达式,相应的e表达式的值作为整个cond表达式的返回值.

  130. */
  131. cond = function(args)
  132. {
  133. for (var i = 0; i < arguments.length; i++)
  134. {
  135.   if(arguments[i] instanceof Array)
  136.   {
  137.    var cond = LispScript.Run(arguments[i][0]);  //先对参数求值
  138.    //alert(cond);
  139.    if(cond == true && arguments[i][1] != null)
  140.     return LispScript.Run(arguments[i][1]);
  141.   }
  142. }
  143. return [];
  144. };

  145. //则称为函数调用.它的值计算如下.每一个表达式先求值,然后e再求值.在e的求值过程中,每个出现在e中的的值是相应的在最近一次的函数调用中的值.
  146. lambda = function(args, code)
  147. {
  148. if(code instanceof Array)
  149. {
  150.   var fun = new Function(args,
  151.    "for(var i = 0; i < arguments.length; i++) arguments[i] = LispScript.Run(arguments[i]);return LispScript.Run("+code.toEvalString()+");");

  152.   var globalFuncName = __funList.pop();
  153.   
  154.   fun._funName = globalFuncName;

  155.   if(globalFuncName != null)
  156.    self[globalFuncName] = fun;

  157.   return fun;
  158. }

  159. return [];
  160. };

  161. </script>
  162. <script>
  163.         var prompt = '>';
  164.         function initPrompt()
  165.         {
  166.                 if(cmd.value == '')
  167.                         cmd.value = prompt;
  168.         }
  169.         function runLastCmd()
  170.         {
  171.                 if(window.event.keyCode == 13)
  172.                 {
  173.                         window.event.returnValue = false;
  174.                        
  175.                         var idx = cmd.value.lastIndexOf('\n') + 1;
  176.                         idx += prompt.length;

  177.                         var strCmd = cmd.value.substr(idx);
  178.                         var output = '';
  179.                         try
  180.                         {
  181.                                 var lisp = eval(strCmd);
  182.                                 if(lisp instanceof Array)
  183.                                 {
  184.                                          output = LispScript.Run(lisp);
  185.                                 }
  186.                                 else
  187.                                 {
  188.                                         output = '未知命令';
  189.                                 }
  190.                         }
  191.                         catch(e)
  192.                         {
  193.                                 output = '语法错误';
  194.                         }
  195.                         if(output instanceof Array)
  196.                         {
  197.                                 output = '[' + output + ']';
  198.                         }
  199.                         cmd.value += '\n' + output + '\n' + prompt;
  200.                 }
  201.                
  202.         }
  203. </script>
  204. <textarea id=cmd style="width:600px;height:600px" onkeydown="runLastCmd()" onclick="initPrompt()">
  205. </textarea>
复制代码运行代码另存代码
附件: 你需要登录才可以下载或查看附件。没有帐号?加入无忧

使用道具 举报

Rank: 3Rank: 3

升级  12.67%

注册时间
2003-4-10
威望
127
阅读权限
30
积分
238
帖子
122
精华
0
UID
1806
状态
当前离线
发表于 2005-11-8 10:48:20 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ
看得云里雾里的,看来需要先去了解LISP
...0_o...

使用道具 举报

Rank: 4

升级  22%

注册时间
2005-9-12
威望
353
阅读权限
50
积分
610
帖子
359
精华
0
UID
37085
状态
当前离线
发表于 2005-11-8 11:54:49 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ 查看个人网站

e

so cool
┍┉┉┉┉┉┉┉┉┉┉┉┉┑
[做眼保健操喽]         │
│  ..innov提供的公益广告    │
┕┉┉┉┉┉┉┉┉┉┉┉┉┙

使用道具 举报

Rank: 7Rank: 7Rank: 7

注册时间
2004-4-22
威望
469
阅读权限
100
积分
722
帖子
431
精华
2
UID
10830
状态
当前离线
发表于 2005-11-8 14:13:41 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 查看个人网站
看的好吐血,建议楼主开头写下能具体实现什么让人惊叹的结果。
我最烦两种人:一是帖子乱发,二是标题乱写。
http://rexsong.com

使用道具 举报

Rank: 6Rank: 6

升级  94.7%

注册时间
2004-1-15
威望
820
阅读权限
70
积分
2894
帖子
781
精华
0
UID
8043
状态
当前离线
发表于 2005-11-8 14:45:11 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料
研究了 3 个月 用 js 实现 C++ 的可能性(搞VC出身,还是喜欢 C 的风格)
限制太多。。。

使用道具 举报

Rank: 2

升级  6%

注册时间
2005-11-8
威望
44
阅读权限
20
积分
59
帖子
23
精华
0
UID
40603
状态
当前离线
发表于 2005-11-8 15:32:03 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 查看个人网站
我这里有一种实现方式,不用eval,因为eval的效率比较差,我用了别的办法,大家看看是否可行
  1. var NIL = [];
  2. var __globals = {};
  3. Array.prototype.eval = function(){
  4.     var len = this.length;
  5.     if( len <= 0)
  6.         return NIL;
  7.     var f = this.shift();
  8.     len--;
  9.     var i = 0;
  10.     while(i < len){
  11.         if(this[i] instanceof Array){
  12.             this[i] = this[i].eval();
  13.         }
  14.         i ++;
  15.     }
  16.     if(f instanceof Function){
  17.         return f.apply(null, this);
  18.     }else if(f instanceof String && typeof __globals[f] == "function" ){
  19.         return __globals[f].apply(null, this);
  20.     }else {
  21.         throw "cannot find function";
  22.     }
  23. }

  24. var quote = _ = function (){
  25.     var len = arguments.length;
  26.     var i = 0;
  27.     var list = [];
  28.     while(i < len){
  29.         list.push(arguments[i++]);
  30.     }
  31.     return list;
  32. }

  33. function say(){
  34.     WScript.Echo(arguments[0]);
  35. }
  36. function define(name, value){
  37.     if(typeof __globals[name] != "undefined"){
  38.         throw "name already used";
  39.     }
  40.     __globals[name] = value;
  41. }
复制代码运行代码另存代码

我觉得用JavaScript原生代码执行Lisp确实比较奇怪,http://www.crockford.com/javascript/little.html 上面有一个Scheme解释器,便是用JavaScript实现的。
另外,楼上那位,JavaScript本来是为了嵌在别的语言如C++中执行的,本身不提供这种平台,而你却颠倒过来,想要JavaScript实现C++,分明是本末倒置。

使用道具 举报

Rank: 8Rank: 8

注册时间
2005-3-9
威望
1952
阅读权限
150
积分
4347
帖子
1577
精华
9
UID
24714
状态
当前离线
发表于 2005-11-8 17:54:10 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ 查看个人网站
引用内容由 ShiningRay 发表于 2005-11-8 03:32 PM
我这里有一种实现方式,不用eval,因为eval的效率比较差,我用了别的办法,大家看看是否可行
[code]
var NIL = [];
var __globals = {};
Array.prototype.eval = function(){
    var len = this.length;
  ...



eval??
我的代码里也没有用javascript的eval的...
我用LispScript自身实现了它自己的eval函数...

另外我觉得楼上和我说的概念并不完全相同...:)

或许是另外一种思路了...

使用道具 举报

Rank: 2

升级  6%

注册时间
2005-11-8
威望
44
阅读权限
20
积分
59
帖子
23
精华
0
UID
40603
状态
当前离线
发表于 2005-11-8 22:49:43 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 查看个人网站
你可能并没有看清楚我的代码

使用道具 举报

Rank: 8Rank: 8

注册时间
2005-3-9
威望
1952
阅读权限
150
积分
4347
帖子
1577
精华
9
UID
24714
状态
当前离线
发表于 2005-11-9 00:05:07 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ 查看个人网站
引用内容由 ShiningRay 发表于 2005-11-8 10:49 PM
你可能并没有看清楚我的代码


你的Array.prototype.eval()方法基本上是实现了一个递归下降的语法分析器,然而并没有处理所有的问题。

首先,实现基本的操作如"quote","define"(这个似乎没有必要其实...=.=)并不复杂,其实真正有些复杂的是实现闭包,函数声明,_eval,setq,以及foreach...
这里需要考虑许多细节问题。

...
呃...不知道我这样说对不对...
其实单从Array.prototype.eval本身的思路来看和我的并没有太大区别...
我的也没有用到Jscript的eval啊...效率上两者应该是差不多的...

如果有兴趣的话,你可以按你的方法完善一下...也许能给我很大启发呢...

另外...稍微提议下...你的quote函数对于我来说是错误的(可能按你的实现你会认为有道理)...:P
因为我的LispScript中规定[quote,1]应当返回"1"而不是"[1]"
[quote,[1]]才是返回"[1]"
quote只接受一个参数...
至于为什么这样...
是因为我并没有打算让原子操作接受不定数量的参数(cond除外)...尽管我依然可以通过它们定义出可变参数的函数...
其实这里有一个“系统自治”的问题...描述起来还是比较复杂的...:P

[ 本帖由 月影 最后编辑于 2005-11-9 00:17 ]

使用道具 举报

Rank: 8Rank: 8

注册时间
2005-3-9
威望
1952
阅读权限
150
积分
4347
帖子
1577
精华
9
UID
24714
状态
当前离线
发表于 2005-11-9 00:20:45 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 QQ 查看个人网站
对不起...另外还得提一下...有一个非常致命的问题...

按你的方法没法实现[cond,[t1,v1],[t2,v2]...]

因为按要求当tn为true的时候应当返回vn并且不再计算v(n+1)的值(这很重要!!对于终止“迭代”来说)

另外“闭包”的实现你也欠考虑...闭包(lambda)比宏(define)要复杂得多.

具体来说,LispScript中应当支持下面这种迭代...

> [[lambda,[f],[f,[_,[b,c]]],[_,[lambda,[x],[cons,[_,a],x]]]
[a,b,c]

也就是说操作符、形参和程序体都应当可以是表达式,这反映了Lisp的一个基本思想——代码也是数据

同样的...LispScript中支持
[defun,[expr1],[expr2],[expr3]]
这里expr1的计算结果为函数名称,expr2的计算结果为形参列表,expr3的计算结果为函数体
而且它们可以重复迭代甚至递归!
也就是说我可以“将一个函数的返回值赋给它的形参列表”,这在任何其他语言中都是做不到的...

再者,Lisp还支持下面这样的...
[_eval,[expr],[maplist]]
实际上可以看作是另外一种函数形式,但是更接近于“闭包”...
你可以把expr看作指令(机器),maplist看作数据(环境),反之亦然(这才是Lisp惊人的地方)!
有了_eval之后,LispScript是自我解析的...也就是说它可以自我解释数据为指令,从而构成一个自治的完整系统。

[ 本帖由 月影 最后编辑于 2005-11-9 00:34 ]

使用道具 举报

Rank: 2

升级  6%

注册时间
2005-11-8
威望
44
阅读权限
20
积分
59
帖子
23
精华
0
UID
40603
状态
当前离线
发表于 2005-11-9 07:30:04 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 查看个人网站
呵呵,我不精通Lisp

使用道具 举报

Rank: 6Rank: 6

升级  94.7%

注册时间
2004-1-15
威望
820
阅读权限
70
积分
2894
帖子
781
精华
0
UID
8043
状态
当前离线
发表于 2005-11-9 10:28:31 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料
终于看完,看的有点明白
的确精练,简洁

山外青山楼外楼

使用道具 举报

Rank: 1

升级  46%

注册时间
2004-7-2
威望
22
阅读权限
10
积分
23
帖子
2
精华
0
UID
13778
状态
当前离线
发表于 2006-3-4 11:31:33 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料

Lisp,Prolog人工智能语言

本人在大学很是喜欢这类语言
人工智能语言主要使用递归方式(优化后)进行匹配而求解
不错,精华
(Javascript模拟其他程序或界面,基本上都列为精华,关键在于把Javascript运用的凌厉尽致)
尝试理性地看待一切问题...

使用道具 举报

Rank: 1

升级  4%

注册时间
2006-5-22
威望
1
阅读权限
10
积分
2
帖子
1
精华
0
UID
52033
状态
当前离线
发表于 2006-5-23 17:44:38 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料

好熟悉的文章!!!

如题

使用道具 举报

超级版主

新手上路

Rank: 8Rank: 8

注册时间
2004-5-22
威望
3336
阅读权限
150
积分
6241
帖子
3329
精华
3
UID
11749
状态
当前离线
发表于 2006-5-23 21:49:39 |显示全部楼层 |串个门|加好友|打招呼|发消息 |
查看详细资料 查看个人网站
不解:
(function(){
LispScript = {
  Run : run
};

function run(code)
{
  return code;
};
})();
跟这一段:
LispScript=new Object;
LispScript.Run=function(code)
{
  return code;
}
有什么区别?

[ 本帖由 Rimifon 最后编辑于 2006-5-23 21:50 ]

使用道具 举报

您需要登录后才可以回帖 登录 | 加入无忧

Archiver|手机版|无忧脚本 ( 苏ICP备05080427号 )|值班电话:027-62300445  

GMT+8, 2012-2-7 21:29 , Processed in 0.114820 second(s), 15 queries , Gzip On, Memcache On.

Powered by Discuz! X2

© 1999-2011 无忧脚本

回顶部