Разница между (применить #'аргументы somefunc) и (аргументами somefunc)
Читая книгу Пола Грэма " На Лиспе", я обнаружил следующее function
в главе 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 ...)
в оригинале.
3 ответа
Когда вы звоните (f args)
, ты звонишь f
с одним аргументом.
С (apply #'f args)
, ты звонишь f
с таким количеством аргументов, как args
список содержит. Так что если args
является (1 2)
, затем (apply #'f args)
эквивалентно (f 1 2)
,
Увидеть APPLY
,
Назначение APPLY - вызывать функции с вычисленными списками аргументов.
Представьте, что пользователь вводит некоторые аргументы, и мы хотим вызвать функцию WRITE
, WRITE
принимает много возможных аргументов. Первый аргумент - это объект для печати, остальные параметры значения ключевых слов:
возможные ключевые аргументы для WRITE
:
array base case circle escape gensym
length level lines miser-width pprint-dispatch
pretty radix readably right-margin stream
Давайте читать список аргументов как список, используя READ
и позвонить WRITE
с помощью 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
Давайте посмотрим на определение 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
а также symb1
принять переменное количество аргументов и args
будет содержать сформированный ими список. Так, symb1
звонки mkstr
с одним аргументом, список аргументов, переданных symb1
, чтобы mkstr
создает уникальную строку из списка, и, наконец, список интернируется, чтобы преобразовать его в атом. В symb
вместо этого функция mkstr
применяется ко всем аргументам, извлеченным из списка, так как apply
используется (см. спецификацию), так что все элементы списка объединяются вместе, а затем преобразуются в атом.