Когда использовать '(или цитату) в Лиспе? - PullRequest
102 голосов
/ 25 сентября 2008

Пройдя через основные части вводной книги по Лиспу, я все еще не мог понять, что делает функция специального оператора (quote) (или эквивалент '), но это было во всем коде Лисп, который я видел.

Что это делает?

Ответы [ 9 ]

162 голосов
/ 26 сентября 2008

Краткий ответ Обходите правила оценки по умолчанию и выполняйте , а не оценку выражения (символ или s-exp), передавая его функции точно в том виде, в котором оно было напечатано.

Длинный ответ: правило оценки по умолчанию

Когда вызывается обычная (я вернусь к этому позже) функция, все передаваемые ей аргументы оцениваются. Это означает, что вы можете написать это:

(* (+ a 2)
   3)

Что, в свою очередь, оценивает (+ a 2), оценивая a и 2. Значение символа a ищется в текущем наборе привязок переменной, а затем заменяется. Скажем, a в настоящее время привязан к значению 3:

(let ((a 3))
  (* (+ a 2)
     3))

Мы получили бы (+ 3 2), + затем вызывается на 3 и 2, получая 5. Наша первоначальная форма теперь (* 5 3), получая 15.

Объясните quote Уже!

Хорошо. Как показано выше, все аргументы функции оцениваются, поэтому, если вы хотите передать символ a, а не его значение, вы не хотите его оценивать. Символы Лиспа могут удваиваться как в качестве их значений, так и в качестве маркеров, когда вы в других языках использовали бы строки, например ключи к хеш-таблицам.

Вот тут и приходит quote. Предположим, вы хотите построить график распределения ресурсов из приложения Python, а лучше сделать это в Lisp. Сделайте, чтобы ваше приложение Python сделало что-то вроде этого:

print("'(")
while allocating:
    if random.random() > 0.5:
        print(f"(allocate {random.randint(0, 20)})")
    else:
        print(f"(free {random.randint(0, 20)})")
    ...
print(")")

Вывод, который выглядит примерно так (слегка симпатично):

'((allocate 3)
  (allocate 7)
  (free 14)
  (allocate 19)
  ...)

Помните, что я сказал о quote ("галочка"), в результате чего правило по умолчанию не применяется? Хорошо. В противном случае произойдет поиск значений allocate и free, и мы этого не хотим. В нашем Лиспе мы хотим сделать:

(dolist (entry allocation-log)
  (case (first entry)
    (allocate (plot-allocation (second entry)))
    (free (plot-free (second entry)))))

Для данных, приведенных выше, была бы выполнена следующая последовательность вызовов функций:

(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)

А как же list?

Ну, иногда вы делаете хотите оценить аргументы. Скажем, у вас есть изящная функция, управляющая числом и строкой и возвращающая список полученных ... вещей. Давайте сделаем фальстарт:

(defun mess-with (number string)
  '(value-of-number (1+ number) something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))

Эй! Это не то, что мы хотели. Мы хотим выборочно оценить некоторые аргументы и оставить другие как символы. Попробуйте №2!

(defun mess-with (number string)
  (list 'value-of-number (1+ number) 'something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)

Не только quote, но backquote

Намного лучше! Кстати, этот шаблон настолько распространен в (в основном) макросах, что для этого есть специальный синтаксис. Обратная цитата:

(defun mess-with (number string)
  `(value-of-number ,(1+ number) something-with-string ,(length string)))

Это похоже на использование quote, но с возможностью явной оценки некоторых аргументов, ставя их перед запятой. Результат эквивалентен использованию list, но если вы генерируете код из макроса, вам часто нужно оценивать только небольшие части возвращаемого кода, поэтому обратная цитата больше подходит. Для более коротких списков list может быть более читабельным.

Эй, ты забыл про quote!

Так, где это оставляет нас? Ах да, что на самом деле делает quote? Он просто возвращает свой аргумент (ы) без оценки! Помните, что я сказал в начале о регулярных функциях? Оказывается, что некоторые операторы / функции должны , а не оценивать свои аргументы. Например, IF - вы бы не хотели, чтобы ветвь else оценивалась, если она не была взята, верно? Так работают так называемые специальные операторы вместе с макросами. Специальные операторы также являются «аксиомой» языка - минимальным набором правил - по которым вы можете реализовать остальную часть Lisp, комбинируя их вместе различными способами.

Вернуться к quote, хотя:

Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL

Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL

Сравнить с (на Сталь-Банк Common Lisp):

Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING   {A69F6A9}>:
  The variable SPIFFY-SYMBOL is unbound.

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0] 

Потому что в текущей области видимости spiffy-symbol нет!

Подведение итогов

quote, backquote (с запятой) и list - это некоторые из инструментов, которые вы используете для создания списков, которые не только являются списками значений, но, как вы видели, могут использоваться как легкие (не нужно) определить struct) структуры данных!

Если вы хотите узнать больше, я рекомендую книгу Питера Сейбела Practical Common Lisp для практического подхода к изучению Lisp, если вы уже в программировании в целом. Со временем в вашем путешествии по Лиспу вы тоже начнете использовать пакеты. Руководство идиота по пакетам Common Lisp Рона Гаррета даст вам хорошее объяснение этого.

Счастливого взлома!

49 голосов
/ 25 сентября 2008

Там написано "не оценивай меня". Например, если вы хотите использовать список как данные, а не как код, вы должны поставить перед ним цитату. Например,

(print '(+ 3 4)) печатает "(+ 3 4)", тогда как (print (+ 3 4)) печать "7"

17 голосов
/ 23 февраля 2009

Другие люди ответили на этот вопрос превосходно, и Матиас Бенкард выдвинул отличное предупреждение.

НЕ ИСПОЛЬЗУЙТЕ ЦИТАТУ ДЛЯ СОЗДАНИЯ СПИСКОВ, КОТОРЫЕ ВЫ ИЗМЕНИТЕ ПОЗЖЕ. Спецификация позволяет компилятору обрабатывать списки в кавычках как константы. Часто компилятор оптимизирует константы, создавая для них единственное значение в памяти, а затем ссылается на это единственное значение из всех мест, где появляется константа. Другими словами, она может рассматривать константу как анонимную глобальную переменную.

Это может вызвать очевидные проблемы. Если вы изменяете константу, она может очень хорошо изменить другие варианты использования этой же константы в совершенно не связанном коде. Например, вы можете сравнить некоторую переменную с '(1 1) в некоторой функции, а в совершенно другой функции начать список с' (1 1), а затем добавить к нему больше материала. После запуска этих функций вы можете обнаружить, что первая функция больше не соответствует вещам должным образом, потому что теперь она пытается сравнить переменную с '(1 1 2 3 5 8 13), что и было возвращено второй функцией. Эти две функции совершенно не связаны, но они влияют друг на друга из-за использования констант. Могут произойти даже более безумные плохие эффекты, как совершенно нормальная итерация списка, неожиданно бесконечный цикл.

Используйте кавычки, когда вам нужен постоянный список, например, для сравнения. Используйте список, когда вы будете изменять результат.

14 голосов
/ 26 сентября 2008

В одном ответе на этот вопрос говорится, что QUOTE «создает структуры данных списка». Это не совсем верно. Цитата является более фундаментальным, чем это. Фактически, QUOTE является тривиальным оператором: его цель состоит в том, чтобы предотвратить что-либо вообще происходящее. В частности, он ничего не создает.

То, что (QUOTE X) говорит, в основном «ничего не делай, просто дай мне X». X не обязательно должен быть списком, как в (QUOTE (A B C)), или символом, как в (QUOTE FOO). Это может быть любой объект. Действительно, результат оценки списка, который создается (LIST 'QUOTE SOME-OBJECT) всегда будет просто возвращать SOME-OBJECT, каким бы он ни был.

Теперь, причина (QUOTE (A B C)) в том, что он создал список, элементами которого являются A, B и C, заключается в том, что такой список действительно является тем, что он возвращает; но на момент оценки формы QUOTE список, как правило, уже существует некоторое время (как компонент формы QUOTE!), создаваемый либо загрузчиком, либо читателем до выполнения кода.

Одним из следствий этого, который довольно часто запутывает новичков, является то, что очень неразумно изменять список, возвращаемый формой QUOTE. Данные, возвращаемые QUOTE, для всех намерений и целей должны рассматриваться как часть выполняемого кода и поэтому должны рассматриваться как доступные только для чтения!

11 голосов
/ 25 сентября 2008

Цитата предотвращает выполнение или оценку формы, превращая ее вместо этого в данные. В общем случае вы можете выполнить данные, затем оценив их.

quote создает структуры данных списка, например, следующие эквиваленты:

(quote a)
'a

Может также использоваться для создания списков (или деревьев):

(quote (1 2 3))
'(1 2 3)

Вы, вероятно, лучше всего получите вводную книгу по lisp, такую ​​как Practical Common Lisp (которая доступна для чтения в режиме онлайн).

2 голосов
/ 28 февраля 2016

В Emacs Lisp:

Что можно процитировать?

Списки и символы.

Цитирование числа оценивает само число: '5 совпадает с 5.

Что происходит, когда вы цитируете списки?

Например:

'(one two) оценивается как

(list 'one 'two), что соответствует

(list (intern "one") (intern ("two"))).

(intern "one") создает символ с именем «one» и сохраняет его в «центральной» хэш-карте, поэтому каждый раз, когда вы говорите 'one, символ с именем "one" будет просматриваться в этой центральной хеш-карте.

Но что такое символ?

Например, в ОО-языках (Java / Javascript / Python) символ может быть представлен как объект с полем name, которое представляет собой имя символа, например "one" выше, а также данные и / или код С ним может быть связан этот объект.

Таким образом, символ в Python может быть реализован как:

class Symbol:
   def __init__(self,name,code,value):
       self.name=name
       self.code=code
       self.value=value

В Emacs Lisp, например, символ может иметь 1) данные, связанные с ним И (в то же время - для того же символа) 2) код, связанный с ним - в зависимости от контекста вызываются либо данные, либо код .

Например, в Элиспе:

(progn
  (fset 'add '+ )
  (set 'add 2)
  (add add add)
)

оценивается как 4.

Потому что (add add add) оценивается как:

(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4

Так, например, используя класс Symbol, который мы определили в Python выше, этот add ELisp-Symbol может быть записан в Python как Symbol("add",(lambda x,y: x+y),2).

Большое спасибо ребятам из IRC #emacs за объяснение символов и цитат.

2 голосов
/ 07 июня 2014

Когда мы хотим передать сам аргумент вместо передачи значения аргумента, мы используем кавычку. В основном это связано с прохождением процедуры при использовании списков, пар и атомов которые не доступны на языке программирования C (большинство людей начинают программирование с использованием программирования C, поэтому мы запутались) Это код на языке программирования Scheme, который является диалектом lisp, и я думаю, вы можете понять этот код.

(define atom?              ; defining a procedure atom?
  (lambda (x)              ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f

Последняя строка (atom? 'Abc) передает abc, как и в процедуре проверки, является ли abc атомом или нет, но когда вы передаете (atom? Abc), он проверяет значение abc и проходит значение для этого. С тех пор мы не предоставили ему никакой ценности

0 голосов
/ 22 февраля 2016

Короткий ответ Anoter:

quote означает без оценки, а обратная цитата является цитатой, но оставляет задние двери .

Хорошая ссылка:

Справочное руководство по Emacs Lisp делает это очень ясным

9,3 Цитирование

Специальная кавычка формы возвращает единственный аргумент, как написано, без его оценки. Это позволяет включать в программу постоянные символы и списки, которые не являются самооценочными объектами. (Нет необходимости указывать самооценочные объекты, такие как числа, строки и векторы.)

Специальная форма: объект цитаты

This special form returns object, without evaluating it. 

Поскольку кавычки так часто используются в программах, Lisp предоставляет удобный синтаксис для чтения. Символ апострофа (‘'’), за которым следует объект Lisp (в синтаксисе чтения), расширяется до списка, первым элементом которого является кавычка, а вторым элементом является объект. Таким образом, синтаксис чтения 'x является сокращением для (кавычка x).

Вот несколько примеров выражений, которые используют кавычки:

(quote (+ 1 2))
     ⇒ (+ 1 2)

(quote foo)
     ⇒ foo

'foo
     ⇒ foo

''foo
     ⇒ (quote foo)

'(quote foo)
     ⇒ (quote foo)

9,4 Backquote

Конструкции обратных кавычек позволяют вам заключать в кавычки список, но выборочно оценивать элементы этого списка. В простейшем случае он идентичен кавычке специальной формы (описанной в предыдущем разделе; см. Цитирование). Например, эти две формы дают одинаковые результаты:

`(a list of (+ 2 3) elements)
     ⇒ (a list of (+ 2 3) elements)

'(a list of (+ 2 3) elements)
     ⇒ (a list of (+ 2 3) elements)

Специальный маркер ‘,’ внутри аргумента для обратной кавычки указывает значение, которое не является постоянным. Оценщик Emacs Lisp оценивает аргумент ‘,’ и помещает значение в структуру списка:

`(a list of ,(+ 2 3) elements)
     ⇒ (a list of 5 elements)

Замена на «,» также разрешена на более глубоких уровнях структуры списка. Например:

`(1 2 (3 ,(+ 4 5)))
     ⇒ (1 2 (3 9))

Вы также можете объединить оцененное значение в результирующий список, используя специальный маркер ‘, @’. Элементы списка сращивания становятся элементами на том же уровне, что и другие элементы результирующего списка. Эквивалентный код без использования ‘` ’часто не читается. Вот несколько примеров:

(setq some-list '(2 3))
     ⇒ (2 3)

(cons 1 (append some-list '(4) some-list))
     ⇒ (1 2 3 4 2 3)

`(1 ,@some-list 4 ,@some-list)
     ⇒ (1 2 3 4 2 3)
0 голосов
/ 12 мая 2015

Цитата возвращает внутреннее представление своих аргументов. После того, как мы слишком много объяснили, что цитата не делает , именно тогда загорелась лампочка. Если REPL не конвертировал имена функций в UPPER-CASE, когда я их цитировал, он, возможно, меня не осенил.

Итак. Обычные функции Lisp преобразуют свои аргументы во внутреннее представление, оценивают аргументы и применяют функцию. Цитата преобразует свои аргументы во внутреннее представление и просто возвращает это. Технически правильно сказать, что цитата говорит: «не оценивайте», но когда я пытался понять, что он сделал, то, что он не делал, разочаровывал. Мой тостер также не оценивает функции Lisp; но это не то, как вы объясняете, что делает тостер.

...