Common Lisp实现可变参数接收及展开调用format函数的方法
Common Lisp中可变参数传递给format的实现方法
用过不少语言都支持函数签名里声明可变参数,比如Go里用func doSomething(args ...int)定义,调用时传doSomething(2, 3, 4);Python里用def do_something(*args)定义,调用写do_something(1, 2, 3)。这些语言还都有参数展开/解包语法,比如Go的things = append(things, others...)、Python的do_something_else(*args)。
我想写一个包装函数,接收格式字符串和任意数量的参数,把它们传递给format t。知道format支持可变参数,但没找到正确的实现方式。
尝试的错误代码1:
(defun printf (fstr vals) (format t (concatenate 'string fstr "~%") vals))
这个在调用单参数格式字符串时能正常工作,但多参数就报错“too many arguments given to printf”,比如(printf "~d is not ~a" 12 "13")就不行。
又试了这段代码:
(defun printf (fstr &rest vals) (format t (concatenate 'string fstr "~%") vals))
这次报错“there are not enough arguments left for this format directive”,显然vals是参数列表,但不知道怎么展开后传给format t。
问题解答
- 使用
&rest接收可变参数是完全正确的,这正是Common Lisp中定义可变参数函数的标准方式。 - 要把
vals这个参数列表展开后传递给format,需要用apply函数——它的作用就是把函数调用的参数列表展开,将列表中的每个元素作为单独的参数传给目标函数。
正确的实现代码如下:
(defun printf (fstr &rest vals) (apply #'format t (concatenate 'string fstr "~%") vals))
错误原因解释
- 第一段错误代码的问题是函数只定义了两个参数,无法接收多个可变参数,所以调用时参数数量不匹配报错。
- 第二段错误代码里,直接把
vals传给format,相当于把整个列表作为单个参数传递,但格式字符串需要多个独立参数,所以会提示参数不足。而apply会把vals列表里的元素逐个拆解出来,作为format的后续参数,这样就和直接调用format t "~d is not ~a~%" 12 "13"的效果一致了。
内容的提问来源于stack exchange,提问作者Jonathan Voss
相关产品推荐
相关产品推荐

