Reputation: 5870
While reading Paul Graham's On Lisp I found the following function
in Chapter 4, Utility Functions.
(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
I would like to understand what is the difference with the following function, slightly different:
(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)|
In this second version, mkstr
is directly evaluated with args
arguments, but I don't understand why we need to do (apply #'mkstr ...)
in the original.
Upvotes: 0
Views: 119
Reputation: 139251
The purpose of APPLY is to call functions with computed argument lists.
Imagine the user enters some arguments and we want to call the function WRITE
. WRITE
takes a lot of possible arguments. The first argument is the object to print the rest are keyword value options:
possible keyword arguments for WRITE
:
array base case circle escape gensym
length level lines miser-width pprint-dispatch
pretty radix readably right-margin stream
Let's read the argument list as a list using READ
and call WRITE
via APPLY
with the argument list:
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 ...)
Another way to achieve something similar would have been using EVAL.
CL-USER 35 > (let ((f #'+)
(args '(20 22)))
(eql (eval (list* 'funcall f args))
(apply f args)))
T
Upvotes: 4
Reputation: 27424
Let’s look at the definition of mkstr
:
CL-USER> (defun mkstr (&rest args)
(with-output-to-string (s)
(dolist (a args) (princ a s))))
MKSTR
it is a function that takes a variable number of arguments, of any type, packs them in a list, and assigns this list to the formal parameter args
(due to the &rest
specification of the parameter). Then, the function prints all the elements of this list with printc
, producing a string that is the result of concatenating all the printed representations of them (without intervening spaces). So, for instance:
CL-USER> (mkstr '(a b c))
"(A B C)"
CL-USER> (mkstr 3 'A '(A b 4))
"3A(A B 4)"
Similarly, the functions symb
and symb1
take a variable numbers of arguments and args
will contain the list formed by them. So, symb1
calls mkstr
with a single argument, the list of the arguments passed to symb1
, so that mkstr
creates a unique string from the list, and finally the list is interned to transform it in an atom. In symb
, instead, the function mkstr
is applied to all the arguments extracted from the list, since apply
is used (see the specification), so that all the elements of the list are concatened together and then transformed into an atom..
Upvotes: 2