【问题标题】:Difference between (apply #'somefunc args) and (somefunc args)(apply #'somefunc args) 和 (somefunc args) 之间的区别
【发布时间】:2016-12-12 21:02:13
【问题描述】:

在阅读 Paul Graham 的 On Lisp 时,我在 第 4 章,实用函数中发现了以下 function

(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 ...)

【问题讨论】:

  • 其余参数ARGS 将包含给定参数的列表(示例中为(NIL T :A))。 APPLY 将“拼接”该列表,因此 (apply #'mkstr args)(mkstr nil t :a) 相同。如果没有APPLY,您会将列表作为单个参数传递给MKSTR

标签: common-lisp on-lisp


【解决方案1】:

当您调用(f args) 时,您调用f 时带有一个参数。

使用(apply #'f args),您可以使用args 列表包含的参数数量调用f。所以如果args(1 2),那么(apply #'f args)就等价于(f 1 2)

APPLY

【讨论】:

  • (f args) 将等价于(f '(1 2))
【解决方案2】:

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

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

WRITE 的可能关键字参数:

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

让我们将参数列表读取为使用READ 的列表,并使用参数列表通过APPLY 调用WRITE

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

【讨论】:

    【解决方案3】:

    我们来看看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)"
    

    同样,symbsymb1 函数采用可变数量的参数,args 将包含由它们形成的列表。所以,symb1 使用 single 参数调用mkstr,将参数列表传递给symb1,以便mkstr 从列表中创建一个唯一的字符串,最后列表是实习将其转化为原子。而在symb 中,函数mkstr 应用于列表中提取的所有参数,因为使用了apply(参见specification),因此所有的元素列表被连接在一起,然后转换成一个原子..

    【讨论】:

      猜你喜欢
      • 2021-06-14
      • 1970-01-01
      • 2011-08-25
      • 2021-01-30
      • 2021-07-29
      • 2013-09-16
      • 2015-09-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多