Коли користуватися '(або цитатою) в Lisp?


114

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

Що це робить?

Відповіді:


178

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

Це місце, де 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шукаються, і ми цього не хочемо. У нашому Lisp ми хочемо зробити:

(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 - ви б не хотіли, щоб інша гілка була оцінена, якби вона не була взята, правда? Так звані спеціальні оператори разом з макросами працюють так. Спеціальні оператори - це також "аксіома" мови - мінімальний набір правил, за якими можна реалізувати решту Lisp, комбінуючи їх разом різними способами.

Повернення до 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) структури даних!

Якщо ви хочете дізнатися більше, я рекомендую книгу Пітера Сейбела « Практичний звичайний лісп» для практичного підходу до вивчення Ліспа, якщо ви вже займаєтесь програмуванням. Зрештою, у подорожі Lisp ви також почнете використовувати пакети. Посібник Рона Гаррета «Ідіотський посібник до загальних пакунків Lisp » дасть вам хороше пояснення.

Щасливий злом!


У моїх програмах emacs SBCL - це налаштування, і коли я набираю '' це 'є' правда ', він повертається лише останнім, тобто ІСТИНА у виході. Навіть у порталі я отримую такий же вихід
Totoro

@Totoro Повернене значення функції або просто декілька висловлювань у lisp - це останній вираз, тому воно фактично повертається this, потім is, потім true, але ви бачите лише останній, який повернувся. (це правда і є окремими твердженнями)
Wezl

52

Там написано "не оцінюй мене". Наприклад, якщо ви хочете використовувати список як дані, а не як код, ви поставите цитату перед ним. Наприклад,

(print '(+ 3 4))друкує "(+ 3 4)", тоді як (print (+ 3 4))друкує "7"


Як це можна оцінити, наприклад, чи є unquoteкоманда?
Лайм

3
@William лепече має зручну функцію під назвою eval: (print (eval '(+ 3 4))). Саме це робить Lisps настільки великим: списки - це код, а код - списки, тому програма Lisp може маніпулювати собою.
darkfeline

18

Інші люди чудово відповіли на це питання, і Маттіас Бенкард висуває чудове попередження.

НЕ ВИКОРИСТОВУЙТЕ ЦІТУ, ЩО СТВОРИТИ СПИСОКИ, ЩО ВИ БУДЕ МОВЛЕНО. Специфікація дозволяє компілятору трактувати списки, що цитуються, як константи. Часто компілятор оптимізує константи, створивши для них одне значення в пам'яті, а потім посилаючи це єдине значення з усіх місць, де з'являється константа. Іншими словами, він може трактувати константу, як анонімну глобальну змінну.

Це може спричинити очевидні проблеми. Якщо ви модифікуєте константу, вона може цілком модифікувати інші види використання тієї самої константи у абсолютно незв'язаному коді. Наприклад, ви можете порівняти певну змінну з '(1 1) в якійсь функції, а в зовсім іншій функції, запустити список з' (1 1), а потім додати до нього більше матеріалів. Запустивши ці функції, ви можете виявити, що перша функція більше не відповідає речам, тому що зараз вона намагається порівняти змінну до '(1 1 2 3 5 8 13), що повернуло другу функцію. Ці дві функції абсолютно не пов'язані між собою, але вони впливають одна на одну через використання констант. Навіть божевільні негативні наслідки можуть трапитися, як і зовсім нормальна ітерація списку, раптом нескінченна циклічність.

Використовуйте цитату, коли вам потрібен постійний список, наприклад для порівняння. Використовуйте список, коли ви будете змінювати результат.


Тож це звучить так, що слід використовувати (list (+ 1 2)) більшу частину часу. Якщо так, як ви заважаєте оцінювати (+ 1 2)всередині такого прикладу? Чи є unquoteкоманда?
Лайм

1
Ви хочете еквівалент '((3))або еквівалент '((+ 1 2))? В останньому випадку, ви повинні використовувати більш list: (list (list '+ 1 2)). Або якщо ви хотіли еквівалента '(+ 1 2), просто (list '+ 1 2). І пам’ятайте, якщо ви не змінюєте список, сміливо використовуйте цитату: нічого поганого в тому, '(+ 1 2)якщо ви просто порівнюєте його чи щось.
Ксантір

1
Ви не проти посилатися на те, де котирувані списки повинні розглядатися як константи?
Лайм

HyperSpec clhs.lisp.se/Body/s_quote.htm говорить, що поведінка не визначена, якщо цитований об'єкт деструктивно модифікований. Мається на увазі, що це дозволити імпульсам трактувати значення як атомні величини.
Ксантір

14

Одна з відповідей на це питання говорить про те, що ЦИТКА "створює структури даних зі списку". Це не зовсім правильно. ЦІТА більш фундаментальна, ніж ця. Насправді QUOTE є тривіальним оператором: її мета - запобігти взагалі що- небудь. Зокрема, це нічого не створює.

Що (QUOTE X) говорить - це, в основному, "нічого не роби, просто дай мені X". X не повинен бути списком, як у (QUOTE (ABC)) або символом, як у (QUOTE FOO). Це може бути будь-який об’єкт, що завгодно. Дійсно, результат оцінювання списку, який виробляється (СПИСОК «ЦІЛЬКИЙ ЯКИЙ-ОБ'ЄКТ»), завжди просто поверне НЕЧІЙ-ОБ'ЄКТ, яким би він не був.

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

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


11

Цитата заважає виконувати або оцінювати форму, перетворюючи її замість на дані. Взагалі ви можете виконати дані, потім зрівнявши їх.

цитата створює структури даних зі списку, наприклад, такі еквіваленти:

(quote a)
'a

Його також можна використовувати для створення списків (або дерев):

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

Вам, мабуть, найкраще отримати вступну книгу на ліпі, наприклад, Практичний звичайний Lisp (який можна читати в режимі он-лайн).


3

У Emacs Lisp:

Що можна процитувати?

Списки та символи.

Цитування числа оцінюється до самого числа: '5те саме, що 5.

Що відбувається, коли ви цитуєте списки?

Наприклад:

'(one two) оцінює до

(list 'one 'two) який оцінює до

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

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

Але що таке символ?

Наприклад, в мовах OO (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) дані, пов'язані з ним AND (водночас - для того ж символу) 2) код, пов'язаний з ним - залежно від контексту, або дані, або код викликається.

Наприклад, у Elisp:

(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 вище, цей addELisp-Symbol може бути записаний в Python якSymbol("add",(lambda x,y: x+y),2) .

Велике спасибі людям на IRC #emacs за пояснення символів та цитат.


2

Коли ми хочемо передавати сам аргумент замість передачі значення аргументу, тоді ми використовуємо цитату. Це здебільшого пов'язане з процедурою, що проходить під час використання списків, пар і атомів, яких немає в мові програмування на С (більшість людей починає програмувати за допомогою програмування на С, отже, ми плутаємося) Це код мови програмування схеми, який є діалектом 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 і передає значення це. Оскільки ми не надали йому жодної цінності


1

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

Так. Звичайні функції Lisp перетворюють свої аргументи у внутрішнє уявлення, оцінюють аргументи та застосовують функцію. Цитата перетворює свої аргументи у внутрішнє представлення і просто повертає це. Технічно правильно сказати, що цитата каже: "Не оцінюй", але коли я намагався зрозуміти, що це робило, мені було неприємно говорити, що вона не робить. Мій тостер також не оцінює функції Lisp; але це не так, як ви пояснюєте, що робить тостер.


1

Коротка відповідь:

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 Зворотне котирування

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

`(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)

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.