destructuring-bind
объединяет деструкторы с привязкой. Деструктор - это функция, которая позволяет вам получить доступ к части структуры данных. car
и cdr
- простые деструкторы для извлечения головы и хвоста списка. getf
- это общая структура деструкторов. Связывание чаще всего выполняется let
. В этом примере fns
равно (#'list #'round #'sqrt)
(аргументы compose
), поэтому (reverse fns)
равно (#'sqrt #'round #'list)
. Тогда
(destructuring-bind (fn1 . rest) '(#'sqrt #'round #'list)
...)
эквивалентно
(let ((tmp '(#'sqrt #'round #'list)))
(let ((fn1 (car tmp))
(rest (cdr tmp)))
...))
за исключением того, что он не связывает tmp
, конечно. Идея destructuring-bind
заключается в том, что это конструкция сопоставления с шаблоном: ее первый аргумент - это шаблон, которому должны соответствовать данные, а символы в шаблоне связаны с соответствующими частями данных.
Итак, fn1
равно #'sqrt
, а rest
равно (#'round #'list)
. Функция compose
возвращает функцию: (lambda (&rest args) ...)
. Теперь рассмотрим, что происходит, когда вы применяете эту функцию к некоторому аргументу, например 4
. Лямбда может применяться, давая
(reduce #'(lambda (v f) (funcall f v))
'(#'round #'list)
:initial-value (apply #'sqrt 4)))
Функция apply
применяет fn1
к аргументу; поскольку этот аргумент не является списком, это просто (#'sqrt 4)
, что составляет 2
. Другими словами, у нас есть
(reduce #'(lambda (v f) (funcall f v))
'(#'round #'list)
:initial-value 2)
Теперь функция reduce
выполняет свою работу, которая заключается в последовательном применении #'(lambda (v f) (funcall f v))
к #'round
и #'list
, начиная с 2
. Это эквивалентно
(funcall #'list (funcall #'round 2))
→ (#'list (#'round 2))
→ '(2)