内容提要
作者推出《Coffee Break Clojure》系列,以轻松方式介绍Clojure与Lisp。Lisp即列表处理,函数调用、定义和数据都写成列表,代码与数据形式相同。核心概念是eval:代码默认被求值,quote(')可阻止求值,将形式当作数据处理。由此得出“代码即数据”,可用cons、apply等操作代码,并借助宏在求值前变换代码,扩展语言。
延伸解读
为什么 Lisp 的代码看起来像列表?
文章指出,Lisp 即 LISt Processing,函数调用、定义、宏、条件语句乃至数据本身都写成列表形式。这种统一性让代码与数据在语法上几乎没有区别,除了少量语法糖。理解这一点是读懂 Clojure 代码的关键,也解释了为什么初学者会觉得括号很多、缩进奇怪。
Eval 与 Quote:代码何时被求值?
Clojure 代码默认会被求值一次。eval 对原子求值得到原子本身,对形式求值则把第一个元素当作操作、其余当作参数。quote(')是特殊形式,阻止求值,把形式当作数据处理。文章通过对比 (eval '(list ...)) 和 (eval (list ...)) 的报错,说明不加 quote 时列表会被误认为函数调用。
代码即数据带来的实际能力
因为代码和数据形式相同,可以用 cons、apply 等操作代码。例如 (cons '+ (list 1 2 3)) 生成 (+ 1 2 3),再 eval 得到 6;apply 也能达到类似效果。文章强调,能对数据做的变换就能对代码做,宏则允许在求值前变换代码,从而扩展语言。这为后续深入宏和元编程打下基础。
Q&A
Clojure 和 Lisp 是什么关系?
Clojure 是 Lisp 的一种方言。Lisp 的意思是 LISt Processing(列表处理),Clojure 和所有 Lisp 一样,代码和数据都写成列表形式。
Clojure 中函数调用和函数定义怎么写?
函数调用写作 (函数名 参数1 参数2 ...),例如 (get webhooks keyw-or-urls)。函数定义用 defn,例如 (defn function-name [arg1 arg2] (println "I'm a function!") (str arg1 arg2))。
Clojure 里的 eval 是做什么的?
eval 用于对形式求值。对原子求值得到原子本身,对形式求值则把第一个原子当作操作,其余原子当作参数。Clojure 代码在运行时默认被求值一次。
Clojure 中 quote 或单引号 ' 有什么作用?
quote(或 ')是特殊形式,用于阻止求值,把形式当作数据而不是代码来处理。例如 '(list "a" "b" "c") 不会执行 list 函数,而是得到列表数据。
为什么说 Clojure 中代码即数据?
因为代码不过是被 eval 的数据,数据不过是没有被 eval 的代码,两者都是括号包起来的形式,区别仅在于是否被求值。因此可以用 cons、apply 等操作代码,例如 (cons '+ (list 1 2 3)) 得到 (+ 1 2 3)。
Clojure 的宏有什么作用?
宏可以在求值前变换代码,从而构造方便的写法,按自己的想法扩展语言。具体内容会在系列后续文章中介绍。