还是在Lisp中引用

时间:2013-11-25 14:40:56

标签: lisp quote

我是Lisp的新手,在业余时间慢慢学习......几个月前,我对Lisp REPL的错误报告感到困惑,以下表达式不起作用:

((if (> 2 1) + -) 1 2)

通过环顾四周然后我知道Lisp不是Scheme ...在Lisp中,我需要做任何一个:

(funcall (if (> 2 1) '+ '-) 2 1)

(funcall (if (> 2 1) #'+ #'-) 2 1)

我还看了一下关于lisp-1和lisp-2的介绍性材料,虽然我无法在那里完成所有的东西...无论如何,我知道quote会阻止评估,因为评估规则的例外。

最近我正在阅读有关reduce的内容......然后作为练习,我想编写自己的reduce版本。虽然我设法让它工作(至少看起来有效),但我意识到我仍然无法准确解释为什么在defun的主体中需要某些地方funcall,并且在某些地方不

以下是elisp中的myreduce

    (defun myreduce (fn v lst)
    (cond ((null lst) v)
          ((atom lst) (funcall fn v lst))
          (t (funcall fn (car lst) (myreduce fn v (cdr lst))))))

(myreduce '+ 0 '(1 2 3 4))

我的问题是关于第3和第4行:

  1. 第3行:为什么我需要funcall?为什么不只是(fn v lst)?我的“论证”是在(fn v lst)中,fn是列表中的第一个元素,因此lisp可以使用此位置信息将其视为一个函数......但事实并非如此。所以我肯定错过了一些东西。

  2. myreduce的递归调用中的第4行:将fn传递给myreduce的递归调用? '++或其他什么?

  3. 我想应该有一些非常基本的东西我不知道...我想知道,当我按照第6行/最后一行所示调用myreduce时,事后发生了什么(至少在如何传递'+,有没有办法在任何REPL环境中跟踪它?

    非常感谢,

    /布鲁因

2 个答案:

答案 0 :(得分:4)

Common Lisp是一个LISP-2,有两个名称空间。一个用于函数,一个用于变量。参数绑定在变量名称空间中,因此函数名称空间中不存在fn

(fn arg) ; call what fn is in the function namespace
(funcall fn ...) ; call a function referenced as a variable

'+是一个符号,funcallapply会在全局函数命名空间中查看它是符号而不是函数对象时查找它。 #'+(function +)的缩写,它从本地函数名称空间解析函数。由于#'+需要查找,因此大量调用'+'+快。符号和函数都可以作为fn传递给myreduce,传递的任何内容都与第4行传递的相同。

(myreduce '+ 0 '(1 2 3 4)) ; here funcall might lookup what '+ is every time (CLISP does it while SBLC caches it)
(myreduce #'+ 0 '(1 2 3 4)); here funcall will be given a function object looked up in the first call in all consecutive calls

现在,如果你传递'+,它将被评估为+并绑定到fn。 在myreduce中,我们在递归中传递fn,它也将被评估为+

对于#'+,它评估函数并绑定到fn。 在myreduce中,我们在递归中传递fn,它将被计算到变量名称空间中绑定的函数对象fn

Common Lisp具有添加到函数名称空间的构造。例如。

(flet ((double (x) (+ x x))) ; make double in the function namespace
  (double 10)) ; ==> 20

但你可以编写它并在变量名称空间中使用它:

(let ((double #'(lambda (x) (+ x x)))) ; make double in the variable namespace
  (funcall double 10))

答案 1 :(得分:2)

Common Lisp有两个(实际上超过两个)命名空间:一个用于变量,一个用于函数。这意味着一个名称可能意味着不同的东西,具体取决于上下文:它可以是一个变量,它可以是一个函数名称。

(let ((foo 42))    ; a variable FOO
  (flet ((foo (n) (+ n 107)))   ; a function FOO
    (foo foo)))    ; calling function FOO with the value of the variable FOO

如何定义变量的一些例子:

(defun foo (n) ...)   ; n is a variable
(let ((n 3)) ...)     ; n is a variable
(defparameter *n* 41) ; *n* is a variable

因此,无论何时定义和使用变量,名称都在变量名称空间中。

定义了函数:

 (defun foo (n) ...)         ; FOO is a function
 (flet ((foo (n) ...)) ...)  ; FOO is a function

因此,无论何时定义和使用函数,名称都在函数名称空间中。

由于函数本身是一个对象,因此可以将函数作为变量值。如果要调用这样的值,则需要使用FUNCALL或APPLY。

  (let ((plus (function plus)))
    (funcall plus 10 11)) 

现在为什么会这样? ; - )

  • 两个名称空间允许我们使用名称作为已经是函数的变量。

示例:在Lisp-1中我无法写:

(defun list-me (list) (list list))

在Common Lisp中,上述代码没有冲突。

  • 一个单独的函数命名空间使编译的代码更简单:

在通话(foo 42)中,名称FOO只能是未定义的或它是一个功能。另一种替代方案不存在。所以在运行时我们永远不必检查FOO的函数值实际上是一个函数对象。如果FOO具有函数值,则它必须是函数对象。原因是:Common Lisp中不可能用函数来定义函数。

在Scheme中你可以写:

(let ((list 42))
  (list 1 2 3 list))

上面需要在某个时候检查并导致错误,因为LIST是42,这不是一个函数。

在Common Lisp中,代码只定义了一个变量LIST,但函数LIST仍然可用。