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

Seg*_*ult 2 scheme eval common-lisp first-class-functions lisp-2

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

(define print display)
(print "Hello world\n")
;; alternate way
(define print 'display)
((eval print) "Hello world\n")
Run Code Online (Sandbox Code Playgroud)

同样的方法似乎不起作用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
Run Code Online (Sandbox Code Playgroud)

Attempt 1上面得到这个错误:

*** - EVAL: undefined function P
Run Code Online (Sandbox Code Playgroud)

而这与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
Run Code Online (Sandbox Code Playgroud)

并与gcl:

Error: (EVAL P) is invalid as a function.
Error: (EVAL (ENVIRONMENT) P) is invalid as a function.
Run Code Online (Sandbox Code Playgroud)

所以:

  • 什么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
Run Code Online (Sandbox Code Playgroud)

我知道尖锐的符号(#)应该在函数和
具有相同名称的变量之间消除歧义,但在这种情况下,只有一个print函数.

此外,为什么它不会这样,defvar而不是setf这样:

(defvar (symbol-function 'p) #'print)
Run Code Online (Sandbox Code Playgroud)

然而defvar,setf它们都为变量赋值.
相关的错误是:

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

Lar*_*off 9

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

这效果更好:

(defvar p 'print)

(funcall p "Hello world")
Run Code Online (Sandbox Code Playgroud)

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

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

(p "Hello world")
Run Code Online (Sandbox Code Playgroud)


Syl*_*ter 6

Common Lisp为函数提供了一个单独的命名空间,这使得这样的操作比使用Scheme更加冗长.如果你想要与CL中的顶级 相似,(define p display)你应该制作一个宏:

(defmacro defun-alias (name original-name)
   `(setf (symbol-function ',name) #',original-name))
Run Code Online (Sandbox Code Playgroud)

这适用于:

(defun-alias pc princ)
(pc "Hello") ; prints hello
Run Code Online (Sandbox Code Playgroud)

与Scheme不同,define这只会覆盖全局绑定.从而:

(flet ((test (x) (+ x x))) 
   (defun-alias test +)
   (test 10))
Run Code Online (Sandbox Code Playgroud)

将设置全局定义#'test#'+和返回20.例如 它有点像defun.


Rör*_*örd 5

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

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

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

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

eval永远不会被您的代码调用(参见上一个答案).即使它是,结果将是symbol-value符号print,而不是symbol-function/ fdefinition.

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

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

(let ((p #'print))
  (funcall p "hello world"))
Run Code Online (Sandbox Code Playgroud)

编辑:

额外问题的答案:

我想知道为什么(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)
Run Code Online (Sandbox Code Playgroud)

但defvar和setf都为变量赋值.

setf为地点分配值.该术语(symbol-function 'p)表示作为符号的功能单元的位置p.

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