美文网首页Lispemacs lisp语言介绍
[Emacs Lisp] 变量和符号

[Emacs Lisp] 变量和符号

作者: 何幻 | 来源:发表于2017-05-10 16:46 被阅读374次

Lisp程序是用Lisp对象表示的,
但是代码却是用文本形式来书写的,
Lisp读取器会通过对象的read syntax来将文本读取为对象。
变量就是symbol对象的read syntax,
例如:x是一个变量,它表示一个symbol。

即,变量是程序中的一个名字,它用于表示一个值。
在Emacs Lisp中,每一个变量对应一个symbol,
变量名就是symbol的name,变量的值保存在symbol的value cell中。

1. 全局变量

使用defvardefconst来定义全局变量。
setq可用于改变一个变量的值,如果没有就创建全局变量。
全局变量在整个Emacs Lisp程序生命周期内都有效,除非你改写它的值。

例如:

(setq x '(a b))
x    ; (a b)

(setq x 4)
x    ; 4

2. 局部变量

全局变量直到被设置新的值,否则一直保持原来的值不变,
然而,很多时候,仅在局部给变量使用某个其他的值是有用的。
这个值只在这一段程序中有效,当控制流离开了这块代码,变量又恢复为进入之前的值。
我们说这种行为是,局部变量遮挡(shadow)了它以前的值。

例如,当一个函数被调用时,它的参数变量就是局部变量,
局部变量的值只在函数体中有效,
同一个函数的不同调用,参数变量可能会被赋予不同的值。

let也可以用于创建局部变量,
且只在let体中有效。

3. 变量绑定的作用域规则

每一个局部变量的绑定都具有两方面的属性,
作用域(scope)和生存期(extent)。

作用域表示,在源代码文本中,绑定在什么地方(where)有效。
生存期表示,在程序执行的过程中,绑定在什么时候(when)有效。

Emacs Lisp支持两种形式的绑定,
动态绑定(dynamic binding)和静态绑定(lexical binding)。

动态绑定具有动态作用域和动态生存期,
动态作用域(dynamic scope),任何一段代码都可能访问变量的绑定,
动态生存期(dynamic extent),只有只有在绑定结构(例如let)执行的过程中,绑定才有效。

静态绑定具有词法作用域和无限的生存期,
词法作用域(lexical scope),绑定在绑定结构的源代码文本范围中有效,
无限生存期(indefinite extent),某些情况下,绑定可能永远有效。

(1)动态绑定的具体实现

动态绑定在Emacs Lisp中通过以下方式实现,
每一个symbol都有一个value cell,表示变量的当前值(current dynamic value),
当一个symbol被给定一个局部绑定时(dynamic local binding),
Emacs会把原来的value cell记录在一个栈上,然后把新值放入value cell中。
当绑定结构执行完后,Emacs进行弹栈操作,取出旧的值放回value cell中。

例子:

(defvar x 0)
(defun getx ()
    x)

(let ((x 1))
    (getx))    ; 1

(getx)    ; 0
(2)静态绑定的具体实现

每一个绑定结构会创建一个词法环境(lexical environment),
在这个环境中保存了,变量名和它所对应值之间的对应关系,
当Lisp求值器对某个变量求值的时候,它首先从词法环境中寻找值,如果找到了,就用这个值。
否则就认为这个symbol是一个动态变量,读取symbol的value cell作为变量的值。

; -*- lexical-binding: t -*-

(setq test (let ((foo "bar"))
         (lambda () 
           foo)))

(let ((foo "something-else"))
  (funcall test))    ; "bar"

(funcall test)    ; "bar"

注:
(1)动态绑定变量的值总是从symbol的value cell中获取,
而静态绑定变量的值从词法环境中获取,
所以,无法使用symbol-value获取静态绑定变量的值。

; -*- lexical-binding: t -*-

(let ((x 1))
  (symbol-value 'x))    ; Symbol’s value as variable is void: x

(2)全局变量是动态绑定的,
即使启用了词法绑定规则,let并没有引入新的静态变量x
而是,建立了局部动态变量x,然后用局部动态变量遮挡了全局动态变量的值。

; -*- lexical-binding: t -*-
(setq test (let ((x 1))
         (lambda () 
           x)))

(funcall test)    ; 1
; -*- lexical-binding: t -*-
(defvar x 0)

(setq test (let ((x 1))
         (lambda () 
           x)))

(funcall test)    ; 0

在进行试验时,需要在全新的buffer中,分别测试,
否则(defvar x 0)一旦执行,即使再重新M-x eval-bufferx的值已经被定义了。

4. Symbol

symbol是一个对象,它具有唯一的名字,
symbol内,包含4个组成部分,称为cell,
(1)name:symbol的名字
(2)value cell:作为一个动态变量,symbol的值
(3)function cell:作为一个函数,它的函数值
(4)property list:属性列表

defvardefconst会创建一个全局symbol,并设置value cell。
defundemacro会创建全局symbol,并设置function cell。

当Lisp读取器遇到一个symbol的时候,它会从源代码中读取到symbol的名字,
然后在一个带索引的数据结构中查找symbol,这个数据结构称为obarray,
其索引是symbol名字的哈希值。

在Emacs Lisp中,obarray实际上是一个向量(vector),
根据哈希值,会查找到obarray的某一个元素,obarray的元素是一个桶(bucket),
里面包含了用链表存储的具有相同哈希值的symbol。

Emacs默认有一个obarray,用户也可以创建自己的obarray,
通过make-vector可以创建一个新的obarray。

(make-vector 7 0)    ; LENGTH=7,INIT=0

每个obarray中symbol的名字不能相同,
相同名字的symbol可以放入不同的obarray中。
obarray中的symbol称为interned symbol,
还有symbol不在任何obarray中,称为uninterned symbol。

通过make-symbol可以创建uninterned symbol,

(setq sym (make-symbol "foo")
(eq sym 'foo)    ; nil

通过intern可以将名字放入默认或指定的obarray中。

(defvar other-obarray
  (make-vector 7 0))

(setq sym (intern "foo")
(eq sym 'foo)    ; t

(setq sym1 (intern "foo" other-obarray)
(eq sym1 'foo)    ; nil

通过unintern可以从默认或指定的obarray中删除symbol。


参考

GNU Emacs Lisp Reference Manual

相关文章

  • [Emacs Lisp] 变量和符号

    Lisp程序是用Lisp对象表示的,但是代码却是用文本形式来书写的,Lisp读取器会通过对象的read synta...

  • [Emacs] Emacs之魂(四):标识符,符号和变量

    1. 符号 上文我们提到了Emacs Lisp是一种Lisp-2,即同一个符号(symbol)在不同的上下文中,可...

  • 简介

    1. 为什么学习 Emacs Lisp 2. Lisp 的历史 3. Lisp 的特点 4. E...

  • Emacs资料汇总

    Emacs 官方手册 Emacs Lisp 15 分钟入门 从零开始——Emacs 安装配置使用教程 2015 E...

  • Emacs Lisp 学习(2)

    昨天, 学习了 Emacs Lisp 的一些基本的概念, 今天学习如何定义函数, 如何使用函数. 在 Emacs ...

  • emacs笔记

    前言 最初接触emacs是因为org-mode,后来学习lisp又把玩了几把。我还是无法放弃Lisp,就从emac...

  • Macos下spacemacs+slime+sbcl配置comm

    基本工具 spacemacs: 优秀的第三方 emacs 配置 slime: The Superior Lisp ...

  • [Emacs Lisp] 启用变量的词法作用域

    1. setq lexical-binding失效 从Emacs 24开始,Emacs可以启用变量的lexical...

  • **lisp** 学习笔记

    开发环境 开发环境: SBCLEmacsSlime:在emacs里面帮助进行common lisp开发的扩展qui...

  • Emacs Lisp 学习(1)

    这是一个读书笔记, 是我学习 Emacs Lisp 的笔记. 这是这个笔记的第一篇. 使用 Emacs 做编辑器的...

网友评论

    本文标题:[Emacs Lisp] 变量和符号

    本文链接:https://www.haomeiwen.com/subject/fzhgtxtx.html