【问题标题】:elisp: Test an object is a functionelisp:测试一个对象是一个函数
【发布时间】:2016-07-03 16:48:09
【问题描述】:

我正在尝试找出一种安全的方法来检查对象是否为函数(命名或匿名)。
由于functionp 或fboundp 无法正常工作,因此我正在尝试:

(defun function-check  (x)
  (and (boundp 'x)
       (if (symbolp x) (fboundp x)
     (functionp x))))

显然它适用于几种类型的对象:

(setq lfun (lambda () "hello"))
(function-check lfun) ; -> t
(setq nfun 'buffer-name)
(function-check nfun) ; -> t
(setq slfun '(lambda () "hello"))
(function-check slfun) ; -> t

(function-check 'not-bound) ; -> safe nil

无论如何,看看我的代码,对于这样一个简单的任务来说,它似乎过于冗长和复杂。
有没有可能让它变得更好?

更新:

如被问及,我澄清了“functionp,fboundp 现在按预期工作”的意思。

假设我们要检测一个有效的钩子。这不起作用:

(setq var 'buffer-name)
(functionp  'var)         ;nil
(fboundp  'var)           ;nil

我们需要使用:

(functionp  var)          ;t
(fboundp  var)            ;t

虽然这可行,但我们需要确保var 不是无效的,否则会出现错误:

(functionp  void-var)     ;Lisp-error
(fboundp  void-var)       ;Lisp-error 

视情况而定,这意味着添加额外的控制代码、编译代码等。

有效的钩子可以是任何可调用的对象:宏、函数、lambdas 都是有效的钩子。无论如何functionp 不适用于宏:

(defmacro mac () "hello")
(functionp 'mac) ;nil

虽然fbound 不适用于 lambda 表达式:

(functionp '(lambda () t)) ;t 
(functionp (lambda () t))  ;t
(fboundp '(lambda () t))   ;Lisp error
(fboundp (lambda () t))    ;Lisp error 

如果将表达式分配给变量,也会发生这种情况:

(setq var '(lambda () t))
(functionp var)     ;t
(fboundp var)       ;Lisp error 

如果var 是一个符号,可能需要测试。

据我了解,没有直接的方法来测试可调用对象,因此我尝试了。

【问题讨论】:

  • 我不能直接代表 elisp,但这看起来非常糟糕:你可能想要 (or (functionp x) (and (symbolp x) (fboundp x))):你当然不想引用 x。即便如此,我还是忘记了 (funcall '(lambda ....) ...) 是否在 elisp 中工作,或者 functionp 对此类列表的看法。
  • functionp 有什么问题?那就是测试一个对象是否是一个函数的函数。你在说什么错误案例?如果您没有使用与functionp 相同的定义,您如何定义“对象是函数”?
  • @antonio 那么你想要functionp。从您的问题看来,您似乎对绑定和宏等问题感到困惑,但 SE 上的 cmets 不是解决这个问题的正确论坛:也许是 irc 或其他东西。
  • “一个 void 对象不是一个函数”:你误解了一些东西。 Emacs Lisp 没有 void 对象。如果var 是一个未绑定的变量,那么(myfunction var) 应该 为任何函数myfunction 抛出一个错误。它不是一个值为“空对象”的变量,没有这样的东西。它是一个未绑定变量,尝试访问未绑定变量的值是错误的,因为未绑定变量没有值。我坚持,functionp 是你要找的,你只是用错了,因为你不懂语言。
  • 请不要用' 引用lambda:总是用#' 或什么都不引用。

标签: function emacs lambda lisp elisp


【解决方案1】:

(boundp 'x) 检查在这里有点重复。如果您使用动态绑定编译代码,(boundp 'x) 将始终为t,因为在输入函数时会绑定x。如果您使用词法绑定编译代码,(boundp 'x) 可能是nil,除非您以某种方式创建了一个名为x 的“全局”变量。在这两种情况下,结果都不会取决于您传递给函数的参数。

所以我认为你只需要这个:

(defun function-check (x)
  (if (symbolp x)
      (fboundp x)
    (functionp x)))

也就是说,检查x 是具有函数绑定的符号还是 lambda 函数。

【讨论】:

  • 这怎么不逊色于functionp?您的函数返回 t 的符号,该符号具有不可作为函数执行的函数绑定。当传递一个符号时,fboundp 正确地检查它的函数绑定是一个函数。
【解决方案2】:

我发现了一个在 AUCTeX 包中使用的函数谓词的有趣示例。

(defun TeX-function-p (arg)
  "Return non-nil if ARG is callable as a function."
  (or (and (fboundp 'byte-code-function-p)
       (byte-code-function-p arg))
      (and (listp arg)
       (eq (car arg) 'lambda))
      (and (symbolp arg)
       (fboundp arg))))

此测试在调用通过解析 alist TeX-expand-list 获得的 TeX 命令(组件)之前在 AUCTeX 中使用。
如果在 alist 中找到的 扩展 是一个函数,则调用它:

(apply expansion arguments)

TeX-function-p 是彻底的,因为TeX-expand-list 是巨大的且可自定义的,扩展也可能导致对象不是符号或未绑定的符号。

这个函数回答了我的问题,它表明functionp 可能并不总是足以测试可调用对象。

【讨论】:

  • functionp 还没有处理哪一部分? TeX-function-p 在编写时可能需要,但现在对我来说似乎是多余的。
  • @phils:经过更多研究后,我提交了bug report。消息一到,我会相应地编辑这篇文章。
猜你喜欢
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 1970-01-01
  • 2013-11-04
  • 2011-04-13
  • 1970-01-01
  • 2010-10-22
  • 1970-01-01
相关资源
最近更新 更多