Разница между (применить #'аргументы 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 используется (см. спецификацию), так что все элементы списка объединяются вместе, а затем преобразуются в атом.

Другие вопросы по тегам