Когда использовать ' (или кавычку) в Лиспе?

StackOverflow https://stackoverflow.com/questions/134887

  •  02-07-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

Короткий ответОбходите правила оценки по умолчанию и выполните нет оцените выражение (символ или 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 оценивалась, если она не была занята, верно?Так называемые специальные операторы, вместе с макросами работают так.Специальные операторы также являются «аксиомой» языка — минимальным набором правил, на основе которых вы можете реализовать остальную часть Лиспа, комбинируя их различными способами.

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

Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL

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

Сравните с (на Steel-Bank 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) структуры данных!

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

Удачного взлома!

Другие советы

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

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

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

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

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

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

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

Что (цитата X) говорит, что в основном «не делай ничего, просто дай мне х». X Не нужно быть списком, как в (QUOTE (ABC)) или символ, как в (цитате Foo).Это может быть любой объект.Действительно, результат оценки списка, созданного (LIST 'QUOTE SOME-OBJECT), всегда будет просто возвращать SOME-OBJECT, каким бы он ни был.

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

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

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

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

(quote a)
'a

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

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

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

Когда мы хотим передать сам аргумент вместо передачи значения аргумента, мы используем кавычку.Это в основном связано с прохождением процедуры при использовании списков, пар и атомов, которые не доступны на языке программирования C (большинство людей начинают программирование с использованием программирования C, следовательно, мы запутались) Это код на языке программирования схемы, который является диалектом 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

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

В Emacs Лисп:

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

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

Цитирование числа эквивалентно самому числу:'5 такой же как 5.

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

Например:

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

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

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

(intern "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 за объяснение мне символов и кавычек.

Quote возвращает внутреннее представление своих аргументов.После слишком большого количества объяснений того, что цитата не делает сделать, вот тогда лампочка загорелась.Если бы REPL не преобразовывал имена функций в ПРОПИСНЫЕ РЕГИСТРЫ, когда я их цитировал, меня бы это, возможно, не осенило.

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

Еще один короткий ответ:

quote значит без оценки, и обратная кавычка это цитата, но оставь задние двери.

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

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

9.3 Цитирование

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

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

This special form returns object, without evaluating it. 

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

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

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

(quote foo)
     ⇒ foo

'foo
     ⇒ foo

''foo
     ⇒ (quote foo)

'(quote foo)
     ⇒ (quote foo)

9.4 Обратная кавычка

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

`(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)
Code is data and data is code.  There is no clear distinction between them.

Это классическое утверждение, известное любому программисту на Лиспе.

Когда вы цитируете код, этот код будет данными.

1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)

1 ]=> (+ 2 3 4)
;Value: 9

Когда вы цитируете код, результатом будут данные, представляющие этот код.Итак, когда вы хотите работать с данными, представляющими программу, вы цитируете эту программу.Это также справедливо для атомарных выражений, а не только для списков:

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

1 ]=> '"ok"
;Value: "ok"

1 ]=> code
;Unbound variable: code

Предположим, вы хотите создать язык программирования, встроенный в Lisp. Вы будете работать с программами, указанными в схеме (например, '(+ 2 3)) и которые интерпретируются как код на создаваемом вами языке, придавая программам семантическую интерпретацию.В этом случае вам необходимо использовать кавычки для сохранения данных, иначе они будут оцениваться на внешнем языке.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top