Карта, резкая цитата и замыкания
Я немного новичок в CL и сейчас пытаюсь обернуть голову mapcan
, #'
, funcall
и закрытия. Вот закрытие, которое применяет предикат к числу n и, если оно верное, возвращает (list n)
иначе nil
:
(defun all-those (predicate)
(lambda (n)
(if (funcall predicate n) (list n))))
Я понимаю, что мне нужно позвонить funcall
превратить это закрытие в функцию. Это отлично работает:
> (funcall (all-those #'evenp) 8)
(8)
Теперь я попытался передать созданную таким образом функцию в качестве аргумента mapcan:
> (mapcan #'(funcall (all-those #'evenp)) '(1 2 3 4))
Я получаю ошибку времени компиляции: (FUNCALL (ALL-THOSE #'EVENP)) is not a legal function name
,
Но это работает, если я опущу #'
так же как funcall:
> (mapcan (all-those #'evenp) '(1 2 3 4))
(2 4)
Теперь я в замешательстве. Это было мое понимание того, что при использовании mapcan
следовать привязке функции символа (*) и что мне нужно вызвать funcall
когда "закрытие закрытия".
Это потому что #'
а также funcall
гасите друг друга или почему я должен опустить их обоих в приведенном выше примере? Заранее благодарю за любые ответы.
(*) Я знаю, что в этом примере у меня нет символа, для которого можно следовать привязке функции. Но если я использую анонимную функцию и mapcan
Мне все еще нужно процитировать это: (mapcan #'(lambda ...
2 ответа
Для mapcar, funcall и т. Д. Вам нужно передать либо функциональный объект, либо символ. Если вы передаете символ, то символ-функция символа используется в качестве функции. Если вы передаете объект функции, то он используется в качестве функции.
Ваша все-эта функция возвращает функцию. Это означает, что (mapcan (все-те...)...) в порядке.
Точная кавычка (#') является просто сокращением для формы функции. То есть #'foo - это то же самое, что и (функция foo):
Значение функции - это функциональное значение имени в текущей лексической среде.
Если name - это имя функции, функциональное определение этого имени - это определение, которое устанавливается самой внутренней лексической формой flet, label или macrolet, если таковая имеется. В противном случае возвращается глобальное функциональное определение имени функции.
Если name является лямбда-выражением, возвращается лексическое замыкание. В ситуациях, когда замыкание по одному и тому же набору привязок может производиться более одного раза, различные результирующие замыкания могут или не могут быть эквивалентны.
Таким образом, вы используете только # или функцию с именем функции. Это означает или символ (например, #'автомобиль) или лямбда-выражение (например, #' (lambda (x) x)). Это означает, что следующее не работает (или даже имеет смысл):
#'(funcall (all-those #'evenp))
Теперь я в замешательстве. Насколько я понимаю, мне нужно заключать в кавычки функцию при использовании mapcan, чтобы следовать привязке функции символа (*), и что мне нужно вызывать funcall при "закрытии замыкания".
Документация для mapcar и т. Д. Гласит, что первый аргумент:
function --- указатель на функцию, которая должна принимать столько аргументов, сколько существует списков.
Из глоссария:
обозначение функции n. обозначение функции; то есть объект, который обозначает функцию и является одним из: символа (обозначающего функцию, названную этим символом в глобальной среде) или функции (обозначающей себя). Последствия не определены, если символ используется в качестве обозначения функции, но он не имеет глобального определения в качестве функции или имеет глобальное определение в виде макроса или специальной формы. Смотрите также расширенное обозначение функции.
Таким образом, вы можете передать функцию непосредственно в mapcar, funcall и т. Д., И это именно то, что вы делаете:
(mapcan (all-those …) …)
Вы также можете сделать:
(mapcan (lambda (x) ...) ...)
(mapcan #'(lambda (x) ...) ...)
(mapcan 'car ...)
(mapcan #'car ...)
(mapcan (symbol-function 'car) ...)
Резюме
(function foo)
это специальная форма, возвращающая объект функции. Здесь взято из названия foo
, Мы используем его, чтобы получить функциональный объект.
(funcall foo)
используется для вызова функций, переданных в качестве аргумента - здесь значение переменной foo
, funcall = FUNction CALL = вызвать функцию. Мы используем его для вызова функционального объекта.
подробности
Вот закрытие, которое применяет предикат к числу n и, если правильно, возвращает (список n), иначе nil:
(defun all-those (predicate) (lambda (n) (if (funcall predicate n) (list n))))
Нет, это не закрытие. all-those
возвращает закрытие, но само по себе оно не одно.
? #'all-those
#<Compiled-function ALL-THOSE #x302000C9631F>
? (all-those #'evenp)
#<COMPILED-LEXICAL-CLOSURE (:INTERNAL ALL-THOSE) #x302000E5ECFF>
Я понимаю, что мне нужно вызвать funcall, чтобы превратить это замыкание в функцию.
Закрытие является функциональным объектом.
? (functionp (all-those #'evenp))
T
Примечание: все замыкания также являются функциональными объектами. Не все функциональные объекты являются замыканиями.
Замыкание - это функция и связанные привязки переменных. Это функциональный объект.
Обратите внимание, что анонимные функции не обязательно являются замыканиями. (function (lambda () ()))
не возвращает замыкание, так как нет привязок переменных. Вы могли бы сказать, что это замыкание с пустыми привязками, но на языке CL это не называется замыканием.
Обратите внимание, что в стандартном Common Lisp нет способа определить, является ли объект функции на самом деле замыканием, и нет способа получить доступ к его привязкам через имена переменных.
Я понимаю, что мне нужно вызвать funcall, чтобы превратить это замыкание в функцию.
funcall
используется для вызова функциональных объектов (или функциональных объектов, которые будут извлечены из значения функции символа) с аргументами.
Помните, что есть разные способы вызова функции:
вызвать именованную глобальную функцию:
(foo bar baz)
вызвать именованную лексическую функцию:
(foo bar bar)
вызвать именованную глобальную функцию через символ:
(funcall 'foo bar baz)
вызвать объект функции из значения переменной:
(funcall foo bar baz)
вызвать объект функции из имени функции (лексического или глобального):
(funcall #'foo bar baz)
вызвать объект анонимной функции:
(funcall (function (lambda (foo) foo)) 'bar)
или же(funcall #'(lambda (foo) foo) 'bar)
или же(funcall (lambda (foo) foo) 'bar)
вызвать анонимную функцию:
((lambda (foo) foo) 'bar)
Тогда есть APPLY
который похож на FUNCALL
но принимает аргументы из списка.
(apply #'+ 1 2 3 (list 4 5 6)) is similar to (funcall #'+ 1 2 3 4 5 6)
FUNCALL сам по себе является функцией. Все формы аргументов будут оценены. Первый аргумент должен оцениваться как символ или объект функции. funcall
вызовет объект функции (или объект функции, полученный из значения функции символов с аргументами.
ФУНКЦИЯ - это специальный оператор. Это синтаксис. Это не сама функция. FUNCTION
ожидает символ или лямбда-выражение в качестве своей подчиненной формы. FUNCTION
form возвращает объект функции, соответствующий символу (либо из глобальной функции, либо из лексической функции) или лямбда-выражению.
? (defun foo (bar) (list bar 'baz))
FOO
? (function foo) ; a function object from the global function
#<Compiled-function FOO #x302000CC0D1F>
? #'foo ; the same, differently written
#<Compiled-function FOO #x302000CC0D1F>
? (funcall #'foo 42) ; calling the function object
(42 BAZ)
? (funcall 'foo 42) ; calling the symbol-function of the symbol
(42 BAZ)
? (funcall (symbol-function 'foo) 42) ; as above
(42 BAZ)
? (flet ((foo (arg) (list arg :foo))) ; a local lexical function
(list (foo 43) ; calling the local lexical function
(funcall #'foo 42) ; calling a function object,
; from the local lexical function
(funcall 'foo 41))) ; calling the symbol-function of the symbol
((43 :FOO) (42 :FOO) (41 BAZ))