0

在阅读 Paul Graham 的On Lispfunction时,我在第 4 章实用函数中发现了以下内容。

(defun symb (&rest args)
  (values (intern (apply #'mkstr args)))) ;; mkstr function is "applied"
;; which evaluates like in the following example:
> (symb nil T :a)
NILTA

我想了解以下功能有什么区别,略有不同:

(defun symb1 (&rest args)
  (values (intern (mkstr args)))) ;; directly calling mkstr
;; which evaluates like in the following example:
> (symb1 nil T :a)
|(NIL T A)|

在这第二个版本中,mkstr直接用args参数进行评估,但我不明白为什么我们需要(apply #'mkstr ...)在原始版本中这样做。

4

3 回答 3

4

APPLY 的目的是调用具有计算参数列表的函数。

想象一下用户输入了一些参数,我们想要调用函数WRITEWRITE需要很多可能的论点。第一个参数是要打印的对象,其余的是关键字值选项:

可能的关键字参数WRITE

array base case circle escape gensym
length level lines miser-width pprint-dispatch
pretty radix readably right-margin stream

让我们将参数列表读取为列表,READ并使用参数列表调用WRITEvia APPLY

CL-USER 30 > (loop for input = (read)
                   while input
                   do
                   (format t "~%# ")
                   (apply #'write input)
                   (format t "~%~%"))
((1 5 10 30 55 26 12 17))
# (1 5 10 30 55 26 12 17)

((1 5 10 30 55 26 12 17) :base 16)
# (1 5 A 1E 37 1A C 11)

((1 5 10 30 55 26 12 17) :base 12)
# (1 5 A 26 47 22 10 15)

((1 5 10 30 55 26 12 17) :length 5)
# (1 5 10 30 55 ...)

((1 5 10 30 55 26 12 17) :base 16 :length 5)
# (1 5 A 1E 37 ...)

另一种实现类似目标的方法是使用 EVAL。

CL-USER 35 > (let ((f #'+)
                   (args '(20 22)))
               (eql (eval (list* 'funcall f args))
                    (apply f args)))
T
于 2016-12-13T00:22:01.047 回答
4

当你打电话时(f args),你打电话f给一个参数。

使用(apply #'f args),您调用的参数与列表包含f的参数一样多。args所以 if argsis (1 2), then(apply #'f args)等价于(f 1 2)

APPLY

于 2016-12-12T21:21:26.860 回答
2

让我们看一下 的定义mkstr

CL-USER> (defun mkstr (&rest args)
           (with-output-to-string (s)
             (dolist (a args) (princ a s))))
MKSTR

它是一个函数,它接受可变数量的任何类型的参数,将它们打包在一个列表中,并将这个列表分配给形式参数args(由于参数的&rest规范)。然后,该函数使用 打印此列表的所有元素printc,生成一个字符串,该字符串是连接它们的所有打印表示的结果(没有中间空格)。因此,例如:

CL-USER> (mkstr '(a b c))
"(A B C)"
CL-USER> (mkstr 3 'A '(A b 4))
"3A(A B 4)"

同样,函数接受可变数量的参数symb并将包含由它们形成的列表。因此,使用单个参数调用,传递给的参数列表,以便从列表中创建一个唯一的字符串,最后该列表被实习以将其转换为原子。相反,该函数应用于从列表中提取的所有参数,因为使用了(参见规范),因此列表的所有元素连接在一起,然后转换为一个原子。symb1argssymb1mkstrsymb1mkstrsymbmkstrapply

于 2016-12-12T21:32:51.160 回答