如何在Lisp中的变量中存储函数并使用它

时间:2013-10-15 07:14:24

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

我想在变量中存储类似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 2中的3Clisp

*** - 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

3 个答案:

答案 0 :(得分:9)

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

这更好用:

(defvar p 'print)

(funcall p "Hello world")

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

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

(p "Hello world")

答案 1 :(得分:6)

Common Lisp为函数提供了一个单独的命名空间,这使得这样的操作比使用Scheme更加冗长。如果你想要与CL中的顶级 (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

答案 2 :(得分:5)

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

  

尝试使用符号意味着什么? p绝对是一个象征;误报?

按字面意思阅读错误消息:(EVAL (ENVIRONMENT) P)(EVAL P)(未评估)不是符号,而是列表。在Common Lisp中,不评估表单的汽车。

  

eval有什么用? p的评估不会产生程序打印吗?

您的代码永远不会调用

eval(请参阅上一个答案)。即使它是,结果将是符号symbol-value的{​​{1}},而不是print / symbol-function

  

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

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

fdefinition

修改

额外问题的答案:

  

我想知道为什么(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

  

另外,为什么它不适用于defvar而不是像这样的setf:

print
  

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

(defvar (symbol-function 'p) #'print) 为地点分配值。术语setf表示作为符号(symbol-function 'p)的函数单元格的位置。

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