My Blogs
Posts tagged with "Lisp"
零基础构建语言解释器
Published 2015年04月01日 23:00 by james
在编写Interpreter之前,我们需要先了解Lexer(词法分析器),Parser(语法解析器),AST(抽象语法树)。
一般情况下,Interpreter在解释执行程序时,一般会经过如下步骤。
- Lexer读入程序代码,把代码转换token序列。
- Parser把读到的token序列转换为AST(大部分情况下,Lexer内嵌为Parser的一部分)。
- 对AST进行Lowering(化简AST)或者desugar(把语法糖的AST节点转换为标准等价AST节点)处理。
- Interpreter递归执行AST,AST决定了代码的执行顺序。
介绍完了基本的一些概念,我们现在开始来实现语言解释器。
首先,我们需要先定制文法规则,一般情况下,我们只要制定好了文法规则,就可以找一些工具来帮我们生成AST,对于复杂的文法规则,手写Parser是非常麻烦并且乏味的。 这里,为了简单,我们选用S-Expression来作为我们的文法规则来实现Lambda Calculus(Lambda演算)的解释器。 通过该Interpreter的实现,大家可以很容易就搞明白程序设计语言中耳熟能详却又未必深刻了解的一些概念,比如:类型,Lexical Scope(词法作用域),Dynamic Scope(动态作用域),闭包等概念。
Lambda Calculus
虽然规则简单,Lambda演算却是一门强大的语言。它的三个元素分别是是:变量,函数,调用。用传统的表达法,它们看起来就是:
变量:x
函数:λx.t
调用:t1 t2
每个程序语言里面都有这三个元素,只不过具体的语法不同,所以你其实每天都在使用 lambda calculus。换成S-Expression就是:
变量:x
函数:(lambda (x) e)
调用:(e1 …
Lambda演算之Y-Combinator的推导
Published 2014年09月18日 12:00 by james
上一节中,我们讲到了如何使用λ演算来描述自然数,可以看出λ演算的表现力确实非常强大,然而遗憾的是,由于lambda演算中使用的都是匿名函数,所以它无法很直观地表述递归。 如果缺少了递归,λ演算的能力无疑会大打折扣。
所有基于λ演算的语言中,其实都是支持对过程进行命名的,那为什么这里我们还需要探讨匿名函数的递归呢?
- 为了保持纯洁性,我们希望仅仅通过λ演算的规则本身来解决递归的问题。
- 部分语言对过程的命名必须等到该过程定义完成后才可以进行,这个时候我们必须找到一种方式使其能够很容易地获得递归的能力。
下面我们就是用factorial函数为原型,来看看如何使用lambda演算基本的规则,为其增加递归的能力。
传统定义方式,不过这个方式不符合我们的需求,因为它使用到了函数自身fact。
(defn fact [n] (if (<= n 0) 1 (* n (fact (dec n))))) …
Lambda演算之自然数
Published 2014年09月10日 12:00 by james
λ演算(英语:lambda calculus,λ-calculus)是一套用于研究函数定义、函数应用和递归的形式系统。它由阿隆佐·邱奇和他的学生斯蒂芬·科尔·克莱尼在20世纪30年代引入。这种演算可以用来清晰地定义什么是一个可计算函数。
λ演算规则
<expression> := <name> | <function> | <application>
<function> := λ<name>.<expression>
<application …