免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5859 | 回复: 6
打印 上一主题 下一主题

-[?]-- Lisp 的 lambda 表达式 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-02-17 14:16 |只看该作者 |倒序浏览
Lisp 的 lambda 表达式,有个地方想不清楚。 我用的解释器是 clisp。


在  Common Lisp 中,用 defun 定义的函数有一个函数名和一个函数对象。

(defun f (x) x) 生成了一个函数对象,它的名字叫 f。用 #'f 可以获取 f 关联的函数对象。


  1. [1]> (defun f (x) x)
  2. F
  3. [2]> #'f
  4. #<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。


  1. [3]> (f 1)
  2. 1
  3. [4]> (funcall f 1)

  4. *** - EVAL: variable F has no value
  5. The following restarts are available:
  6. USE-VALUE      :R1      You may input a value to be used instead of F.
  7. STORE-VALUE    :R2      You may input a new value for F.
  8. ABORT          :R3      ABORT
复制代码


出错信息是:variable F hsa no value.

(funcall f 1) 错误,正确的写法是 (funcall #'f 1)

如果把函数对象绑定到一个变量上而不是函数名字上,那么调用方式就要改变。


  1. [6]> (setq g #'f)
  2. #<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. [1]> (setq f 1)
  2. 1
  3. [2]> (defun f (x) x)
  4. F
  5. [3]> (f f)
  6. 1
  7. [4]> (funcall #'f f)
  8. 1
  9. [5]> (apply #'f (list f))
  10. 1
复制代码


总而言之,表的第一项放函数名,其它地方放函数对象。

[ 本帖最后由 retuor 于 2009-2-18 16:21 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2009-02-17 14:17 |只看该作者

--?---

但 lambda 表达式的行为有点不一样。


  1. [1]> ((lambda (x) x) 1)
  2. 1
  3. [2]> (funcall (lambda (x) x) 1)
  4. 1
  5. [3]> (funcall #'(lambda (x) x) 1)
  6. 1
复制代码


它出现在什么位置都可以。《 On lisp 》 说:“A lambda-expression can also be considered as the name of a function.”

所以 lambda 表达式能出现在表的第一项,也能出现在后面的项。是这样吗?

[ 本帖最后由 retuor 于 2009-2-17 14:19 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2009-02-17 17:30 |只看该作者
我觉的lambda表达式就是函数常量. 引不引用都一样.

所以求值为函数对象的地方lambda表达式都可以出现.

论坛徽章:
0
4 [报告]
发表于 2009-02-18 12:41 |只看该作者
这里头还是有个判断标准的,但我不清楚它在哪里。

又用 emacs lisp 试了一下。 在 elisp 中,lamba 是一个宏。代码为:


  1. (defmacro lambda (&rest cdr)
  2.   "Return a lambda expression.
  3. A call of the form (lambda ARGS DOCSTRING INTERACTIVE BODY) is
  4. self-quoting; the result of evaluating the lambda expression is the
  5. expression itself.  The lambda expression may then be treated as a
  6. function, i.e., stored as the function value of a symbol, passed to
  7. `funcall' or `mapcar', etc.

  8. ARGS should take the same form as an argument list for a `defun'.
  9. DOCSTRING is an optional documentation string.
  10. If present, it should describe how to call the function.
  11. But documentation strings are usually not useful in nameless functions.
  12. INTERACTIVE should be a call to the function `interactive', which see.
  13. It may also be omitted.
  14. BODY should be a list of Lisp expressions.

  15. \(fn ARGS [DOCSTRING] [INTERACTIVE] BODY)"
  16.   ;; Note that this definition should not use backquotes; subr.el should not
  17.   ;; depend on backquote.el.
  18.   (list 'function (cons 'lambda cdr)))
复制代码


注释真多呀,去掉后为:


  1. (defmacro lambda (&rest cdr)
  2.   (list 'function (cons 'lambda cdr)))
复制代码


lambda 宏造出 lambda 表达式:一个以符号 'lambda 起头的表。参考注释中的:

A call of the form (lambda ARGS DOCSTRING INTERACTIVE BODY) is
self-quoting; the result of evaluating the lambda expression is the
expression itself.  


我发现 lambda 宏和表达式中的符号 lambda 似乎是不相干的。如果把 lambda 宏定义成其它东西,然后执行以下代码:

(funcall (list 'lambda '(x) x) 1)

得到 1.

仿照 lambda 宏的定义,我可以写一个自己的宏


  1. (defmacro make-lambda (&rest cdr)
  2.   (list 'function (cons 'lambda cdr)))
复制代码


(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) 就不合法。出错信息为:


  1. Register 1 contains the text:
  2. Debugger entered--Lisp error: (invalid-function (make-lambda (x) x))
  3.   ((make-lambda (x) x) 1)
  4.   eval(((make-lambda (x) x) 1))
  5.   eval-last-sexp-1(nil)
  6.   eval-last-sexp(nil)
  7.   call-interactively(eval-last-sexp nil nil)
复制代码


似乎宏没有被展开。不明白为什么。如果 make-lambda 被展开,则结果与 lambda 宏被展开无异。

(macroexpand (lambda (x) x))  和 (macroexpand (make-lambda (x) x)) 的值都是


  1. (function (lambda (x) x))
复制代码


另外,根据注释,上面的 function 换成 quote 也是可以的。

[ 本帖最后由 retuor 于 2009-2-18 13:39 编辑 ]

论坛徽章:
0
5 [报告]
发表于 2009-02-18 14:00 |只看该作者

由函数返回一个 lambda 表达式

(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 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2009-02-18 16:10 |只看该作者
(list 'function (cons  'lambda cdr))  
请教楼主,我想知道这里的“function”也是宏吗?在哪里定义的啊?

论坛徽章:
0
7 [报告]
发表于 2009-02-18 16:24 |只看该作者
function 是 elisp 的,common lisp 里不一定有。

function 是个 special form,类似宏,可以不对参数求值。

[ 本帖最后由 retuor 于 2009-2-18 16:34 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP