介绍一下Lisp中的变量与环境的概念。

变量与环境

在lisp语言中,定义一个环境变量所使用的语句如下:

1
(let [<symbol> <value>] <exper>)

在方括号里面可以定义多个符号和值。

然而为了简单起见,我们可以将这个函数写成只能定义一个变量的形式:

1
(let <symbol> <value> <exper>)

然后我们可以通过多个let语句来定义多个变量:

1
(let <symbol1> <value1> (let <symbol2> <value2> <...>))

这个语句的实现我们可以通过引入环境这个概念来实现。

所谓环境,就是一个存储符号和对应值的键值对的存储结构。

而且我们可以在Clojure里面试一下这个语句:

1
2
3
4
(let [x 1]
(let [y 2]
(let [x 3]
x)))

它的输出应该为3,也就是说,环境这个存储结构应该是一个先进后出的栈,它会在栈中寻找一个最先匹配到的变量,得到他的值然后输出。

Clojure语法

Clojure 中定义数据结构的语法是这样的:

1
(defstruct <struct name> :arg1 :arg2)

我们需要定义一个包含符号-值的键值对的数据结构:

1
(defstruct Pair :sym :value)

环境是一个存储键值对的空间。我们可以用lisp的列表来存储它。在Clojure里,这个叫做Sequences

定义一个空的Sequences像是下面这样:

1
(def emptyEnv '())

我们对环境所需要进行的操作只有两个:

存储查找并取得值

在存储函数中我们构造一个Pair结构体,然后把他们放到环境中返回环境。这两个分别对应的就是Clojure里面的(struct Pair *)(cons *)

1
2
3
(defn binding
[sym value en]
(cons (struct Pair sym value) en))

取值函数则是顺序查找环境,然后返回第一个匹配的数据结构的:value属性,这个对应的是Clojure里面的some函数。我们可以这样写:

1
2
3
(defn getValue
[x env]
(some (symEq x) env))

它会返回第一个为逻辑真值的值。

我们定义的symEq函数,应该是接受一个参数,返回一个函数。返回的函数接受一个Pair的参数,并判断它的:sym与symEq参数是否相等,相等则返回这个参数本身。

其定义应该如下:

1
2
3
4
5
(defn symEq
[sym]
(fn [item]
(if (= (item :sym) sym)
(item :value))))

其中fn是定义一个匿名函数,接受item参数,然后比较它的值并返回

这样我们就处理好了解释器和变量之间的关系了。