-[?]-- Lisp 的 lambda 表达式

发布于 2022-08-20 14:12:36 字数 1945 浏览 32 评论 6

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

獨角戲 2022-08-20 23:53:50

function 是 elisp 的,common lisp 里不一定有。

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

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

弱骨蛰伏 2022-08-20 23:45:04

(list 'function (cons  'lambda cdr))  
请教楼主,我想知道这里的“function”也是宏吗?在哪里定义的啊?

晨曦÷微暖 2022-08-20 23:44:34

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

还在原地等你 2022-08-20 23:36:28

这里头还是有个判断标准的,但我不清楚它在哪里。

又用 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 编辑 ]

夏日落 2022-08-20 21:54:03

我觉的lambda表达式就是函数常量. 引不引用都一样.

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

瑾夏年华 2022-08-20 20:34:15

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文