【问题标题】:Why a parameter of an Emacs lisp function is not evaluated?为什么不评估 Emacs lisp 函数的参数?
【发布时间】:2014-07-18 00:01:36
【问题描述】:

我想用 Emacs Lisp 定义一个累加器列表并编写以下代码,但我得到一个错误,说 initV 是一个 void 变量。似乎initV 没有在函数define-accum 中进行评估。我在哪里犯了错误? (我只是想知道为什么,虽然我知道还有其他方法可以达到我的目标。)

(defun define-accum (name initV)
  (defalias name (lambda (v) (+ v initV))))

(setq accums '((myadd1 . 1)
               (myadd2 . 2)))

(dolist (a accums)
  (define-accum (car a) (cdr a)))

(message "result = %d" (+ (myadd1 1) (myadd2 1)))

【问题讨论】:

    标签: emacs elisp metaprogramming


    【解决方案1】:

    除了使用反引号之外,您还可以activate lexical binding(如果您使用的是 Emacs 24 或更高版本)。例如,如果我将您的代码放在 .el 文件中并将其放在第一行:

    ;; -*- lexical-binding: t -*-
    

    然后我得到输出:

    result = 5
    

    这是可行的,因为define-accum 中的 lambda 函数将在定义它的环境中引用 initV(因此选择参数列表中的变量),并在此变量上创建一个闭包。使用动态绑定(默认),该函数将在调用它的环境中查找initV

    【讨论】:

      【解决方案2】:

      您需要正确使用反引号。例如,这对您有用:

      (defun define-accum (name initV)
        (defalias name `(lambda (v) (+ v ,initV))))
      

      请参阅here 了解说明

      【讨论】:

      • 谢谢。我知道反引号的用法,但我仍然不明白为什么要引用。
      • 问题是lambda 是自引用的。因此,initV 字面意思就是别名。在评估别名时未定义。
      【解决方案3】:

      补充一点其他人所说的-

      1. 如果变量 (initV) 实际上从未被用作变量,那么实际上它在定义累加器时的 是所有需要的,那么就不需要封装该变量及其值的词法闭包。在这种情况下,@juanleon 描述的方法就足够了:它仅在定义时使用值 - 调用函数时变量不存在(如您所见)。

      2. 另一方面,词法闭包方法允许对函数进行字节编译。在反引号方法中,函数在运行时简单地由表示 lambda 形式的 list 表示。如果 lambda 形式代表昂贵的代码,那么使用词法闭包方法是有意义的,即使(在这种情况下)变量并不是真正需要的(作为变量)。

      3. 但您始终可以显式地对函数进行字节编译(例如,define-accum 中的##NAME##。这将解决上面 #2 中提到的低效率问题。

      【讨论】:

      • 请注意,闭包不喜欢“变量”,而不是反引号的 lambda。唯一的区别是字节编译器会在需要时为您重写代码(例如,它将(let ((a 1)) ... (setq a ..) ... a ...) 转换为(let ((a (list 1))) ... (setcar a ..) ... (car a) ...))。
      猜你喜欢
      • 1970-01-01
      • 2015-07-28
      • 1970-01-01
      • 2013-08-25
      • 2012-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多