-[?]-- Lisp 的 lambda 表达式
Lisp 的 lambda 表达式,有个地方想不清楚。 我用的解释器是 clisp。
在 Common Lisp 中,用 defun 定义的函数有一个函数名和一个函数对象。
(defun f (x) x) 生成了一个函数对象,它的名字叫 f。用 #'f 可以获取 f 关联的函数对象。
- [1]> (defun f (x) x)
- F
- [2]> #'f
- #<FUNCTION F (X) (DECLARE (SYSTEM::IN-DEFUN F)) (BLOCK F X)>
复制代码
在 Common Lisp 中,函数名和变量名有各自的空间,互不干涉。
(f 1) 以 1 为参数调用函数 f。 f 是函数名。clisp 通过 f 找到对应的函数对象,并执行。
但写成 (funcall f 1) 就会出错,因为此时 f 出现在表的第二个位置,clisp 会在变量名空间中查找 f。
- [3]> (f 1)
- 1
- [4]> (funcall f 1)
- *** - EVAL: variable F has no value
- The following restarts are available:
- USE-VALUE :R1 You may input a value to be used instead of F.
- STORE-VALUE :R2 You may input a new value for F.
- ABORT :R3 ABORT
复制代码
出错信息是:variable F hsa no value.
(funcall f 1) 错误,正确的写法是 (funcall #'f 1)
如果把函数对象绑定到一个变量上而不是函数名字上,那么调用方式就要改变。
- [6]> (setq g #'f)
- #<FUNCTION F (X) (DECLARE (SYSTEM::IN-DEFUN F)) (BLOCK F X)>
复制代码
此时 (g 1) 会出错,因为 g 处在函数名的位置上,clisp 会到函数名空间去查找它。查不到当然出错,
但如果函数名空间中也有一个 g,那就会调用错误的函数。
正确的写法是 (funcall g 1),或等价地 (funcall #'f 1)
如果我们再定义一个值为 1 的变量 f,就可以获的很晕的效果
- [1]> (setq f 1)
- 1
- [2]> (defun f (x) x)
- F
- [3]> (f f)
- 1
- [4]> (funcall #'f f)
- 1
- [5]> (apply #'f (list f))
- 1
复制代码
总而言之,表的第一项放函数名,其它地方放函数对象。
[ 本帖最后由 retuor 于 2009-2-18 16:21 编辑 ]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
function 是 elisp 的,common lisp 里不一定有。
function 是个 special form,类似宏,可以不对参数求值。
[ 本帖最后由 retuor 于 2009-2-18 16:34 编辑 ]
(list 'function (cons 'lambda cdr))
请教楼主,我想知道这里的“function”也是宏吗?在哪里定义的啊?
(lambda () (lambda () 1) 是一个 lambda 表达式,对其求值,得到
((lambda () (lambda () 1)) -> (lambda () 1)
再把这个表达式求值应该得到 1,但
(((lambda () (lambda () 1)))) 得到:
Debugger entered--Lisp error: (invalid-function ((lambda nil (lambda nil 1))))
(((lambda nil ...)))
eval((((lambda nil ...))))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp nil nil)
而 (funcall ((lambda () (lambda () 1)))) 就得到 1。
所以从函数返回的 lambda 表达式已经是个函数对象,跟直接输入
((lambda (x) 1) 1) 不一样。
[ 本帖最后由 retuor 于 2009-2-18 14:01 编辑 ]
这里头还是有个判断标准的,但我不清楚它在哪里。
又用 emacs lisp 试了一下。 在 elisp 中,lamba 是一个宏。代码为:
复制代码
注释真多呀,去掉后为:
复制代码
lambda 宏造出 lambda 表达式:一个以符号 'lambda 起头的表。参考注释中的:
我发现 lambda 宏和表达式中的符号 lambda 似乎是不相干的。如果把 lambda 宏定义成其它东西,然后执行以下代码:
(funcall (list 'lambda '(x) x) 1)
得到 1.
仿照 lambda 宏的定义,我可以写一个自己的宏
复制代码
(funcall (make-lambda (x) x) 1) 得到 1。即使同时取消 lambda 宏的原有定义也不影响这个 make-lambda
但我定义出来的 make-lambda 并不能复制 lambda 的全部能力。首先 “self-quoting” 就做不到,
其次,make-lambda 出来的东西不能放在表的第一项。
((lambda (x) x) 1) 是合法的,但 ((make-lambda (x) x) 1) 就不合法。出错信息为:
复制代码
似乎宏没有被展开。不明白为什么。如果 make-lambda 被展开,则结果与 lambda 宏被展开无异。
(macroexpand (lambda (x) x)) 和 (macroexpand (make-lambda (x) x)) 的值都是
复制代码
另外,根据注释,上面的 function 换成 quote 也是可以的。
[ 本帖最后由 retuor 于 2009-2-18 13:39 编辑 ]
我觉的lambda表达式就是函数常量. 引不引用都一样.
所以求值为函数对象的地方lambda表达式都可以出现.
但 lambda 表达式的行为有点不一样。
复制代码
它出现在什么位置都可以。《 On lisp 》 说:“A lambda-expression can also be considered as the name of a function.”
所以 lambda 表达式能出现在表的第一项,也能出现在后面的项。是这样吗?
[ 本帖最后由 retuor 于 2009-2-17 14:19 编辑 ]