【问题标题】:How to store a function in a variable in Lisp and use it如何将函数存储在 Lisp 中的变量中并使用它
【发布时间】:2013-10-22 21:44:45
【问题描述】:

我想将像 print 这样的函数存储在一个变量中,这样我就可以输入像 p 这样的简短内容,例如:
Scheme:

(define print display)
(print "Hello world\n")
;; alternate way
(define print 'display)
((eval print) "Hello world\n")

同样的方法似乎在Common Lisp 中不起作用:

(defvar p 'print)
;;(print (type-of p))
(p "Hello world") ;; Attempt 1
((eval p) "Hello world") ;; >> Attempt 2
((eval (environment) p) "Hello world") ;; Attempt 3

上面的Attempt 1 出现此错误:

*** - EVAL: undefined function P

这与Attempt 23Clisp 中:

*** - EVAL: (EVAL (ENVIRONMENT) P) is not a function name; try using a 
            symbol instead
*** - EVAL: (EVAL P) is not a function name; try using a symbol instead

还有gcl:

Error: (EVAL P) is invalid as a function.
Error: (EVAL (ENVIRONMENT) P) is invalid as a function.

所以:

  • try using a symbol 是什么意思? p 绝对是symbol;假阳性?
  • eval 怎么了? p 的评估不会产生过程print 吗?
  • 我认为Lisp 程序是first class objects。为什么Attempt 1 不像Scheme 那样工作?

编辑
(移自下面的评论)

我想知道为什么(setf (symbol-function 'p) #'print) 不能这样工作
(setf (symbol-function 'p) 'print)。我收到以下(不是很有帮助)错误:

*** - SYSTEM::%PUTD: PRINT is not a function ;; CLisp
Error: PRINT is not of type LIST. ;; Gcl

我知道尖号 (#) 应该在函数和变量之间消除歧义
具有相同的名称,但在本例中,只有一个 print,即函数。

另外,为什么不能像这样使用defvar 而不是setf

(defvar (symbol-function 'p) #'print)

然而defvarsetf 都为变量赋值。
相关的错误是:

*** - DEFVAR: non-symbol (SYMBOL-FUNCTION 'P) cannot be a variable ;; Clisp
Error: (SYMBOL-FUNCTION (QUOTE P)) is not of type SYMBOL. ;; Gcl

【问题讨论】:

标签: scheme common-lisp eval first-class-functions lisp-2


【解决方案1】:

Common Lisp 是 "Lisp-2"。除此之外,函数调用中的第一个位置是在“函数命名空间”中评估的。在您的情况下,符号 p 命名一个变量,而不是一个函数。

这样效果更好:

(defvar p 'print)

(funcall p "Hello world")

或者可能,但您可能不想这样做:

(setf (symbol-function 'p) #'print)

(p "Hello world")

【讨论】:

  • 鉴于这可能适用于某些本地范围,我还要指出 fletlabels
【解决方案2】:

Common Lisp 有一个单独的函数命名空间,这使得这样的操作比使用 Scheme 更冗长。如果你想在 CL 中类似于 top level (define p display) 你应该做一个宏:

(defmacro defun-alias (name original-name)
   `(setf (symbol-function ',name) #',original-name))

这样工作:

(defun-alias pc princ)
(pc "Hello") ; prints hello

与 Scheme define 不同,这只会覆盖全局绑定。因此:

(flet ((test (x) (+ x x))) 
   (defun-alias test +)
   (test 10))

#'test 的全局定义设置为#'+ 并返回20。例如。它的工作方式类似于defun

【讨论】:

  • 我喜欢这个宏,它接近Schemedefine
【解决方案3】:

用直接回答您的问题来补充其他好的答案:

尝试使用符号是什么意思? p 绝对是一个符号;误报?

逐字阅读错误消息:(EVAL (ENVIRONMENT) P)(EVAL P)(未计算)不是符号,而是列表。在 Common Lisp 中,不会对表单的汽车进行评估。

eval 怎么了? p 的求值不产生过程打印吗?

eval 永远不会被您的代码调用(请参阅上一个答案)。即使是这样,结果也会是符号 printsymbol-value,而不是 symbol-function/fdefinition

我认为 Lisp 程序是一流的对象。为什么尝试 1 不像在 Scheme 中那样工作?

这与函数无关(我认为 Common Lisp 标准不像 Scheme 标准那样使用术语“过程”。)是一流的对象。这适用于 Common Lisp:

(let ((p #'print))
  (funcall p "hello world"))

编辑

额外问题的答案:

我想知道为什么 (setf (symbol-function 'p) #'print) 不能这样工作 (setf (symbol-function 'p) 'print).

正如您稍后所写的那样,“尖号 (#) 应该消除函数和同名变量之间的歧义”并不是真的。 'print 扩展为 (quote print),因此它的计算结果为符号 print,而不是作为变量的值。 #'print 扩展为 (function print),因此它的计算结果为符号 print 的函数单元格的值。 print 当前是否有一个值作为变量与 #'print 的计算结果完全无关。

(symbol-function 'p) 设置为符号print 显然不会使p 调用函数print,因为符号print 与绑定到符号print 的函数不同。

另外,为什么它不能像这样使用 defvar 而不是 setf:

(defvar (symbol-function 'p) #'print)

然而 defvar 和 setf 都为变量赋值。

setf 为地点赋值。术语(symbol-function 'p) 表示符号p 的功能单元的位置。

defvar 定义了新的全局变量。它的第一个参数需要是一个命名变量的符号,不能是任何地方。

【讨论】:

  • 那么,评论中问题的答案是什么?
  • 为什么不对cars 的表单进行评估?
  • @Segfault:这是标准的相关部分:lispworks.com/documentation/HyperSpec/Body/03_abab.htm。所以实际上,形式的汽车有两种有效类型,符号或 lambda 表达式。这会起作用:((lambda (s) (funcall p s)) "hello world").
  • @Segfault:关于您在 cmets 中对 LarsBrinkhoff 的回答提出的问题,请澄清您仍然不清楚的地方。
  • 抱歉,如果不清楚。我已经编辑了问题。
猜你喜欢
  • 2017-08-14
  • 2016-11-27
  • 1970-01-01
  • 2022-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
相关资源
最近更新 更多