Лучше всего избегать # в большинстве случаев, потому что это «в основном» не нужно и делает ваш код более многословным.Есть несколько исключений, когда необходима некоторая форма цитирования (см. Пример 4 ниже).
Примечание: Все примеры в этом посте были протестированы в Emacs Lisp (GNU Emacs 25.2.1), но они должны работать одинаково для любого общего ANISP-интерфейса.Основные понятия одинаковы на обоих диалектах.
ПРОСТОЕ ОБЪЯСНЕНИЕ
Сначала давайте рассмотрим случай, когда лучше всего избегать цитирования.Функции являются объектами первого класса (например, обрабатываются как любой другой объект, включая возможность передавать их функциям и назначать их переменным), которые оценивают сами себя.Анонимные функции (например, лямбда-формы) являются одним из таких примеров.Попробуйте следующее на Emacs Lisp (Mx ielm RET) или на ЛЮБОМ общем ANSI ANSI.
((lambda (x) (+ x 10)) 20) -> 30
Теперь попробуйте указанную версию
(#'(lambda (x) (+ x 10)) 20) -> "function error" or "invalid function..."
Если вы используете настаивать на использовании # ', вы должны написать
(funcall #'(lambda (x) (+ x 10)) 20) -> 30
ПОДРОБНОЕОБЪЯСНЕНИЕ
Чтобы по-настоящему понять, когда требуется цитирование, нужно знать, как Лисп оценивает выражения.Читать дальше.Я обещаю сделать это кратким.
Вам нужно знать несколько основных фактов о Лиспе:
- Лисп "всегда" оценивает каждое выражение.Хорошо, если выражение не заключено в кавычки, в этом случае оно возвращается без оценки.
- Атомы оценивают сами себя.Атомарные выражения НЕ являются списками.Примеры включают числа, строки, хеш-таблицы и векторы.
- Символы (имена переменных) хранят два типа значений.Они могут содержать регулярные значения и функциональные определения.Следовательно, символы Lisp имеют два слота, называемых ячейками для хранения этих двух типов.Нефункциональный контент обычно хранится в ячейке значения символа и функционирует в функциональной ячейке.Возможность одновременно хранить как нефункциональные, так и функциональные определения помещает Emacs Lisp и Common Lisp в категорию 2-Lisp.Какая из двух ячеек используется в выражении, зависит от того, как используется символ, а точнее от его положения в списке.В отличие от символов в некоторых диалектах Лисп, Схема является наиболее известным, может содержать только одно значение.Схема не имеет понятия ячейки значения и функции.Такие Лиспы в совокупности называются 1-Лиспс.
Теперь вам необходимо приблизительное понимание того, как Лисп оценивает S-выражения (выражения в скобках).Каждое S-выражение оценивается примерно следующим образом:
- Если указано, вернуть его без оценки
Если не указано в кавычках, получить его CAR (например, первый элемент) и оценить его, используяследующие правила:
a.если атом, просто вернуть его значение (например, 3 -> 3, "pablo" -> "pablo")
b.если S-выражение, оцените его, используя ту же самую общую процедуру
c.если символ, вернуть содержимое его функциональной ячейки
Оценить каждый из элементов в CDR S-выражения (например, все, кроме первого элемента списка).
- Примените функцию, полученную из CAR, к значениям, полученным от каждого из элементов в CDR.
Вышеуказанная процедура подразумевает, что любой символ в CAR из UNQUOTED S-выражение должно иметь действительное функциональное определение в своей функциональной ячейке.
Теперь давайте вернемся к примеру с начала поста.Почему
(#'(lambda (x) (+ x 10)) 20)
генерирует ошибку?Это происходит потому, что # '(lambda (x) (+ x 10)), CAR S-выражения, не оценивается интерпретатором Lisp из-за функциональной кавычки #'.
#'(lambda (x) (+ x 10))
isне функция, но
(lambda (x) (+ x 10))
есть.Имейте в виду, что целью цитирования является предотвращение оценки.С другой стороны, лямбда-форма оценивает себя, функциональную форму, которая действительна как CAR списка UNQUOTED .Когда Lisp оценивает CAR
((lambda (x) (+ x 10)) 20)
оно получает (лямбда (х) (+ х 20)), что является функцией, которая может применяться к остальным аргументам в списке (при условии, что длина CDR равна числу аргументов, разрешенных лямбда-выражением)выражение).Следовательно,
((lambda (x) (+ x 10)) 20) -> 30
Таким образом, возникает вопрос, когда заключать в кавычки функции или символы, содержащие функциональные определения.Ответ почти НИКОГДА, если вы не делаете вещи "неправильно".Под «неправильно» я подразумеваю, что вы помещаете функциональное определение в ячейку значения символа или функциональную ячейку, когда вы должны сделать обратное.Для лучшего понимания см. Следующие примеры:
ПРИМЕР 1 - Функции, хранящиеся в ячейках значений
Предположим, вам нужно использовать «apply» с функцией, которая ожидает переменное числоаргументы.Одним из таких примеров является символ +.Лисп рассматривает + как обычный символ.Функциональное определение хранится в функциональной ячейке +.Вы можете присвоить значение его ячейке значения, если хотите, используя
(setq + "I am the plus function").
Если вы оцениваете
+ -> "I am the plus function"
Однако (+ 1 2) по-прежнему работает, как и ожидалось.
(+ 1 2) -> 3
Функция apply весьма полезна в рекурсии.Предположим, вы хотите суммировать все элементы в списке.Вы НЕ МОЖЕТЕ писать
(+ '(1 2 3)) -> Wrong type...
Причина в том, что + ожидает, что его аргументы будут функциями.apply решает эту проблему
(apply #'+ '(1 2 3)) -> (+ 1 2 3) -> 6
Почему я цитировал + выше?Помните правила оценки, которые я изложил выше.Лисп оценивает применяемый символ путем извлечения значения, хранящегося в его функциональной ячейке.он получает функциональную процедуру, которую может применить к списку аргументов.Однако, если я не буду заключать в кавычки +, Lisp будет извлекать значение, хранящееся в его ячейке значения, потому что это НЕ первый элемент в S-выражении.Поскольку для ячейки значения + установлено значение «Я - функция плюс», Лисп не получает функционального определения, содержащегося в ячейке функции +.На самом деле, если бы мы не установили для его ячейки значения значение «Я являюсь функцией плюс», Лисп получил бы значение nil, которое НЕ является функцией, как того требует применение.
Есть ли способ использовать + без кавычек?с применением.Да, есть.Вы можете просто оценить следующий код:
(setq + (symbol-function '+))
(apply + '(1 2 3))
Это даст оценку 6, как и ожидалось, потому что, как оценивает Lisp (apply + '(1 2 3)), теперь он находит функциональное определение +, хранящееся в +Ячейка значения.
ПРИМЕР 2 - Сохранение функциональных определений в ячейках значений
Предположим, вы сохраняете функциональное определение в ячейке значений символа.Это достигается следующим образом:
(setq AFunc (lambda (x) (* 10 x)))
Оценка
(AFunc 2)
генерирует ошибку, потому что Lisp не может найти функцию в функциональной ячейке AFunc.Вы можете обойти это, используя funcall, который говорит Лиспу использовать значение в ячейке значения символа в качестве функционального определения.Вы делаете это с помощью «funcall».
(funcall AFunc 2)
Если допустимо функциональное определение, хранящееся в ячейке значения символа,
(funcall AFunc 2) -> 20
Вы можете избежать использования funcall, поместив лямбда-формув функциональной ячейке символа с помощью fset:
(setf AFunc (lambda (x) (* 10 x)))
(AFunc 2)
Этот кодовый блок вернет 20, потому что lisp находит функциональное определение в функциональной ячейке AFunc.
ПРИМЕР 3 - Локальные функции
Скажем, вы пишете функцию и вам нужна функция, которая больше нигде не будет использоваться.Типичным решением является определение функции, действительной только внутри основной области.Попробуйте это:
(defun SquareNumberList (AListOfIntegers)
"A silly function with an uncessary
local function."
(let ((Square (lambda (ANumber) (* ANumber ANumber))))
(mapcar Square AListOfIntegers)
)
)
(SquareNumberList '(1 2 3))
Этот блок кода вернет
(1 4 9)
Причина, по которой в приведенном выше примере Square не указана, заключается в том, что S-выражение вычисляется в соответствии с правилами, изложенными выше.,Сначала Лисп использует функциональное определение mapcar.Затем Lisp извлекает содержимое ячейки значения своего второго аргумента (например, 'Square).Наконец, он возвращает '(1 2 3) без оценки для третьего аргумента.
ПРИМЕР 4 - Содержание значения символа и функциональных ячеек
Вот один случай, когда требуются кавычки.
(setq ASymbol "Symbol's Value")
(fset 'ASymbol (lambda () "Symbol's Function"))
(progn
(print (format "Symbol's value -> %s" (symbol-value 'ASymbol)))
(print (format "Symbol's function -> %s" (symbol-function 'ASymbol)))
)
Приведенный выше код будет иметь значение
"Symbol's value -> Symbol's Value"
"Symbol's function -> (lambda nil Symbol's Function)"
nil
Кавычки требуются как в
(fset 'ASymbol (lambda () "Symbol's Function"))
, так и в
(symbol-value 'ASymbol)
и
(symbol-function 'ASymbol)
, поскольку в противном случае Lisp получит значение ASymbol в каждом случаепредотвращение правильной работы fset, symbol-value и symbol-function.
Надеюсь, этот длинный пост окажется полезным для кого-то.