楼上诸位展开太深了。
如果只是回答:
很简单,一个字:懒
懒惰的写法:
(add-hook 'xxx-mode-hook
(lambda ()
...))
不厌其烦的写法:
(defun foo (lambda ()
...))
(add-hook 'xxx-mode-hook 'foo)
楼主后面又问了,为什么 add-hook
第二参数必须是 FUNCTION,不能是 S 表达式。答案也很简单:因为 add 之后,将由 (funcall FUNCTION)
来执行传进来的函数,如果不是函数就会出错。而参数列表本身不具备约束力,传什么都可以。
接下来就是函数名的问题了。函数执行之前,首先要对其参数进行求值,所以如果直接写:
(add-hook 'xxx-mode-hook foo)
那么 foo
就会被当作变量处理了,可以通过 M-x ielm
来观察这一求值过程:
ELISP> (defun foo () (message "foo"))
foo
ELISP> foo
*** Eval error *** Symbol’s value as variable is void: foo
ELISP> 'foo
foo
ELISP> (defvar bar "bar")
bar
ELISP> bar
"bar"
ELISP> 'bar
bar
所以写成 'foo
确保传进去的是 SYMBOL:
(add-hook 'xxx-mode-hook 'foo)
;; 等效
;; (add-hook (quote xxx-mode-hook) (quote foo))
虽然传递函数名和 lambda 执行效果没什么区别,但还是有些需要注意的,例如:
(add-hook 'xxx-mode-hook 'foo)
之后,xxx-mode-hook
的内容是
(foo ;; <-- 最近添加
bar
quux
...)
而 (add-hook 'xxx-mode-hook (lambda () (message "foo")))
之后,其内容则是
((lambda () (message "foo")) ;; <-- 最近添加
bar
quux
...)
所以如果是传递的是函数名,将来需要改变函数 foo 的行为,直接修改就可以了。但如果传递的是 (lambda ...)
,要修改就比较麻烦了,必须整个 (lambda ..)
一字不漏抄一遍:
(remove-hook 'xxx-mode-hook (lambda () (message "foo")))
然后,修改,重新添加:
(add-hook 'xxx-mode-hook (lambda () (message "bar")))
所以说,偷懒是有代价的。
(defun ...)
和 (lambda ...)
的关系,对应到 c 语言,我感觉因类似指针和结构体的关系(毕竟 c 不能把函数当值传递):
(add-hook 'xxx-mode-hook 'foo) ;; 指针(传引用)
(add-hook 'xxx-mode-hook (lambda ()...)) ;; 结构体(传值,一大拖东西)
实际上,(defun ...)
展开之后也是个 lambda,只不过有了一个别名,例如 foo:
(defun foo ()
(message "foo"))
;; 展开 =>
(defalias (quote foo)
(function
(lambda nil (message "foo"))))