修改变量值的常规方式是使用特殊形式 setq。如果你需要在运行时动态计算要操作的变量,则使用函数 set。
该特殊形式是最常用的变量赋值方式。每个 symbol 会被赋予一个新值,该值为对应 form 表达式的求值结果。符号的当前绑定会被修改。
setq 不会对 symbol 求值,而是直接设置你写出的符号。我们称这个参数是 自动引用(automatically quoted) 的。setq 中的 ‘q’ 代表 “quoted(引用)”。
setq 形式的值,等于最后一个 form 表达式的值。
(setq x (1+ 2))
⇒ 3
x ; x now has a global value.
⇒ 3
(let ((x 5))
(setq x 6) ; The local binding of x is set.
x)
⇒ 6
x ; The global value is unchanged.
⇒ 3
需要注意的是,第一个 form 会先被求值,随后第一个 symbol 被赋值;接着第二个 form 被求值,然后第二个 symbol 被赋值,依此类推:
(setq x 10 ; Notice thatxis set before y (1+ x)) ; the value ofyis computed. ⇒ 11
该函数会将 value 存入 symbol 的值单元(value cell)中。由于它是一个函数而非特殊形式,程序会对 symbol 对应的表达式求值,以确定要赋值的符号。函数的返回值为 value。
当动态变量绑定生效时(默认行为),除了「set 会对其 symbol 参数求值,而 setq 不会」这一点外,set 与 setq 的作用效果完全相同。但当变量为词法绑定时,set 会修改该变量的 动态(dynamic) 值,而 setq 会修改其当前的(词法)值。See Scoping 变量绑定的作用域规则。
(set one 1) error→ Symbol's value as variable is void: one
(set 'one 1)
⇒ 1
(set 'two 'one)
⇒ one
(set two 2) ; two evaluates to symbol one.
⇒ 2
one ; So it isonethat was set. ⇒ 2 (let ((one 1)) ; This binding ofoneis set, (set 'one 3) ; not the global value. one) ⇒ 3
one
⇒ 2
若 symbol 实际上并非符号,则会触发 wrong-type-argument 错误。
(set '(x y) 'z) error→ Wrong type argument: symbolp, (x y)
该宏的作用与 setq 类似(见上文),但专用于用户可配置项(user options)。此宏会调用自定义机制(Customize machinery)来设置变量(可指定多个)(see 定义自定义变量)。值得注意的是,setopt 会执行与该变量关联的设置函数(setter function)。例如,若你编写了如下代码:
(defcustom my-var 1
"My var."
:type 'number
:set (lambda (var val)
(set-default var val)
(message "We set %s to %s" var val)))
那么下面的代码除了会把 my-var 设置为 ‘2’ 之外,还会输出一条提示信息:
(setopt my-var 2)
setopt 还会检查该值是否对这个用户可配置项有效。例如,若使用 setopt 将一个定义为 number 类型的用户可配置项设置为字符串,会触发错误。
与 defcustom 及相关的自定义命令(如 customize-variable)不同,setopt 适用于非交互式场景,尤其常用于用户的初始化文件(init file)中。因此,它不会记录变量的标准值、保存值和用户设定值,也不会将该变量标记为可保存到自定义文件(custom file)的候选项。
setopt 宏也可用于普通的非用户可配置项变量,但效率远低于 setq。该宏的主要使用场景是在用户的初始化文件中设置用户可配置项。