我正在尝试理解以下两个代码片段:
(defun make-adder1 (n) `(lambda (x) (+ ,n x)))
(defun make-adder2 (n) (lexical-let ((n n)) (lambda (x) (+ n x))))这两个似乎都会产生可伸缩代码:
(funcall (make-adder1 3) 5) ;; returns 8
(funcall (make-adder2 3) 5) ;; returns 8这两种方法都有效。我有两个主要问题:
1)我不理解这两种方法在“引用级别”上的差异。在第一种情况下,lambda表达式被引用,这意味着返回的是“符号本身”,而不是值。在第二种情况下,似乎包含lambda的语句将被计算,因此将返回lambda的值。然而,这两种方法都适用于funcall。当在defun'ed函数上使用funcall时,它必须被引用。lexical-let会自动进行某种引用吗?这不是有点令人惊讶吗?
2)阅读关于这个主题的其他文章,我了解到第一种方法在某些情况下会失败,并与使用lambda和其他语言的高阶函数时的预期背道而驰,因为elisp默认情况下具有动态作用域。有没有人能给出一个具体的代码例子,让这种差异变得明显,并解释一下?
发布于 2016-07-27 09:12:59
在第一个示例中,结果函数中没有变量n,它只是(lambda (x) (+ 3 x))。它不需要词法绑定,因为lambda中没有自由变量,也就是说,闭包的绑定中没有需要保留的变量。如果在使用函数时不需要变量n可用,也就是说,如果在函数定义时它的值(=3)就是您所需要的,那么您只需要第一个示例。
(fset 'ad1 (make-adder1 3))
(symbol-function 'ad1)返回:
(lambda (x) (+ 3 x))第二个示例实际上创建了一个函数,它创建并应用了一个复杂的闭包。
(fset 'ad2 (make-adder2 3))
(symbol-function 'ad2)返回
(lambda (&rest --cl-rest--)
(apply (quote (closure ((--cl-n-- . --n--) (n . 3) t)
(G69710 x)
(+ (symbol-value G69710) x)))
(quote --n--)
--cl-rest--))第三种选择是使用lexical-binding文件局部变量,并使用最直接的定义。这将创建一个简单的闭包。
;;; foo.el --- toto -*- lexical-binding: t -*-
(defun make-adder3 (n) (lambda (x) (+ n x)))
(fset 'ad3 (make-adder3 3))
(symbol-function 'ad3)返回:
(closure ((n . 3) t) (x) (+ n x))(symbol-function 'make-adder1)返回:
(lambda (n)
(list (quote lambda)
(quote (x))
(cons (quote +) (cons n (quote (x))))))
(symbol-function 'make-adder2)返回:
(closure (t)
(n)
(let ((--cl-n-- (make-symbol "--n--")))
(let* ((v --cl-n--)) (set v n))
(list (quote lambda)
(quote (&rest --cl-rest--))
(list (quote apply)
(list (quote quote)
(function
(lambda (G69709 x)
(+ (symbol-value G69709) x))))
(list (quote quote) --cl-n--)
(quote --cl-rest--)))))
(symbol-function 'make-adder3)返回
(closure (t) (n) (function (lambda (x) (+ n x))))https://stackoverflow.com/questions/38598176
复制相似问题