Ответ 1
TL; DR: они разные; используйте list
, когда сомневаетесь.
Эмпирическое правило: используйте list
, когда вы хотите, чтобы аргументы были оценены; quote
"распределяет" по своим аргументам, поэтому '(+ 1 2)
похож на (list '+ '1 '2)
. Youll в конечном итоге с символом в вашем списке, а не функцией.
Углубленный взгляд на list
и quote
В схеме и Racket, quote
и list
- совершенно разные вещи, но поскольку оба они могут использоваться для составления списков, путаница является общей и понятной. Между ними существует невероятно важная разница: list
- простая старая функция, а quote
(даже без специального синтаксиса '
) - это специальная форма , То есть list
может быть реализована в простой схеме, но quote
не может быть.
Функция list
Функция list
на самом деле намного проще, так что давайте начнем там. Это функция, которая принимает любое количество аргументов и собирает аргументы в список.
> (list 1 2 3)
(1 2 3)
Этот вышеприведенный пример может ввести в заблуждение, потому что результат печатается как quote
способное s-выражение, а его значение true, в этом случае два синтаксиса эквивалентны. Но если мы немного усложнимся, вы увидите, что это другое:
> (list 1 (+ 1 1) (+ 1 1 1))
(1 2 3)
> '(1 (+ 1 1) (+ 1 1 1))
(1 (+ 1 1) (+ 1 1 1))
Что происходит в примере quote
? Хорошо, хорошо обсудите это в какой-то момент, но сначала взгляните на list
. Его просто обычная функция, поэтому она соответствует стандартной семантике оценки схемы: она оценивает каждый из своих аргументов, прежде чем они будут переданы функции. Это означает, что выражения типа (+ 1 1)
будут сведены к 2
, прежде чем они будут собраны в список.
Это поведение также отображается при подаче переменных в функцию списка:
> (define x 42)
> (list x)
(42)
> '(x)
(x)
С list
объект x
получает оценку перед тем, как перейти к list
. С quote
все сложнее.
Наконец, поскольку list
- это просто функция, его можно использовать точно так же, как и любую другую функцию, в том числе в порядке более высокого порядка. Например, он может быть передан функции map
, и он будет работать соответствующим образом:
> (map list '(1 2 3) '(4 5 6))
((1 4) (2 5) (3 6))
Форма quote
Цитата, в отличие от list
, является особой частью Lisps. Форма quote
является особенной, потому что она получает специальную аббревиатуру читателя, '
, но ее особенность даже без этого. В отличие от list
, quote
не является функцией, поэтому ему не нужно вести себя как одно - у него есть свои собственные правила.
Краткое описание исходного кода Lisp
В Lisp, из которых Scheme и Racket являются производными, весь код фактически состоит из обычных структур данных. Например, рассмотрим следующее выражение:
(+ 1 2)
Это выражение фактически является списком и имеет три элемента:
- символ
+
- номер
1
- число
2
Все эти значения являются нормальными значениями, которые могут быть созданы программистом. Его действительно легко создать значение 1
, потому что оно оценивает себя: просто введите 1
. Но символы и списки сложнее: по умолчанию символ в исходном коде выполняет переменный поиск! То есть символы не самооцениваются:
> 1
1
> a
a: undefined
cannot reference undefined identifier
Как оказалось, символы в основном являются строками, и на самом деле мы можем конвертировать между ними:
> (string->symbol "a")
a
Списки делают даже больше, чем символы, потому что по умолчанию список в исходном коде вызывает функцию! Выполнение (+ 1 2)
смотрит на первый элемент в списке, символ +
, просматривает связанную с ним функцию и вызывает его с остальными элементами в списке.
Иногда, однако, вы можете отключить это "особое" поведение. Вы можете просто получить список или получить символ без его оценки. Для этого вы можете использовать quote
.
Значение цитаты
Имея это в виду, довольно очевидно, что делает quote
: он просто "отключает" особое поведение оценки для выражения, которое он обертывает. Например, рассмотрим quote
символ:
> (quote a)
a
Аналогично рассмотрим quote
список:
> (quote (a b c))
(a b c)
Независимо от того, что вы даете quote
, он всегда будет всегда выталкивать его обратно. Не больше, не меньше. Это означает, что если вы дадите ему список, ни один из подвыражений не будет оценен - не ожидайте их! Если вам нужна оценка любого типа, используйте list
.
Теперь можно спросить: что произойдет, если вы quote
что-то другое, кроме символа или списка? Ну, ответ... ничего! Вы просто вернетесь.
> (quote 1)
1
> (quote "abcd")
"abcd"
Это имеет смысл, так как quote
все еще просто выплевывает именно то, что вы ему даете. Вот почему "литералы", такие как числа и строки, иногда называются "self-quoting" в языке Lisp.
Еще одно: что произойдет, если вы quote
выражение, содержащее quote
? То есть, что, если вы "double quote
"?
> (quote (quote 3))
'3
Что там произошло? Ну, помните, что '
на самом деле просто прямая аббревиатура для quote
, поэтому ничего особенного не произошло вообще! Фактически, если ваша схема имеет способ отключить сокращения при печати, она будет выглядеть так:
> (quote (quote 3))
(quote 3)
Не обманывайте себя тем, что quote
является особенным: точно так же, как (quote (+ 1))
, результат здесь - просто простой старый список. Фактически, мы можем получить первый элемент из списка: можете ли вы догадаться, что это будет?
> (car (quote (quote 3)))
quote
Если вы догадались 3
, вы ошибаетесь. Помните, что quote
отключает всю оценку, а выражение, содержащее символ quote
, по-прежнему остается простым списком. Играйте с этим в REPL, пока вам не станет удобно.
> (quote (quote (quote 3)))
''3
(quote (1 2 (quote 3)))
(1 2 '3)
Котировка невероятно проста, но она может оторваться как очень сложная из-за того, как она пытается игнорировать наше понимание традиционной модели оценки. На самом деле, это путает из-за того, насколько это просто: нет особых случаев, нет правил. Он просто возвращает именно то, что вы даете ему, точно как указано (отсюда и название "цитата" ).
Приложение A: Квазикратация
Итак, если котировка полностью отключает оценку, для чего это полезно? Ну, кроме составления списков строк, символов или цифр, которые известны заранее, не так много. К счастью, концепция квазиоборота дает возможность вырваться из цитаты и вернуться к обычной оценке.
Основы супер простые: вместо quote
используйте quasiquote
. Обычно это работает точно как quote
любым способом:
> (quasiquote 3)
3
> (quasiquote x)
x
> (quasiquote ((a b) (c d)))
((a b) (c d))
Что делает особенность quasiquote
особенностью, которая распознает специальный символ, unquote
. Везде, где unquote
появляется в списке, он заменяется произвольным выражением, которое он содержит:
> (quasiquote (1 2 (+ 1 2)))
(1 2 (+ 1 2))
> (quasiquote (1 2 (unquote (+ 1 2))))
(1 2 3)
Это позволяет использовать quasiquote
для создания шаблонов сортами, у которых есть "дыры", которые нужно заполнить с помощью unquote
. Это означает, что можно фактически включить значения переменных внутри кавычек:
> (define x 42)
> (quasiquote (x is: (unquote x)))
(x is: 42)
Конечно, использование quasiquote
и unquote
довольно многословно, поэтому у них есть собственные аббревиатуры, как и '
. В частности, quasiquote
имеет значение `
(обратная сторона), а unquote
- ,
(запятая). С этими сокращениями приведенный выше пример гораздо приятнее.
> `(x is: ,x)
(x is: 42)
Один окончательный пункт: квазикоттинг действительно может быть реализован в Racket с использованием довольно волосатого макроса, и это так. Он расширяется до значений list
, cons
и, конечно, quote
.
Приложение B: Внедрение list
и quote
в схеме
Реализация list
очень просто из-за того, как работает синтаксис "rest argument". Это все, что вам нужно:
(define (list . args)
args)
Вот оно!
Напротив, quote
намного сложнее - на самом деле, его невозможно! Казалось бы, это вполне осуществимо, поскольку идея отключения оценки звучит так же, как макросы. Тем не менее, наивная попытка выявляет проблемы:
(define fake-quote
(syntax-rules ()
((_ arg) arg)))
Мы просто берем arg
и выплевываем его обратно... но это не работает. Почему нет? Ну, результат нашего макроса будет оценен, поэтому все напрасно. Мы могли бы расшириться до типа quote
, расширившись до (list ...)
и рекурсивного цитирования элементов, например:
(define impostor-quote
(syntax-rules ()
((_ (a . b)) (cons (impostor-quote a) (impostor-quote b)))
((_ (e ...)) (list (impostor-quote e) ...))
((_ x) x)))
К сожалению, хотя без процедурных макросов мы не можем обрабатывать символы без quote
. Мы могли бы приблизиться, используя syntax-case
, но даже тогда мы будем эмулировать поведение quote
s, не реплицируя его.
Приложение C: Соглашения о печати в стойке
При попытке примеров в этом ответе в Racket вы обнаружите, что они не печатаются, как можно было бы ожидать. Часто они могут печатать с ведущим '
, например, в этом примере:
> (list 1 2 3)
'(1 2 3)
Это связано с тем, что Racket по умолчанию печатает результаты как выражения, когда это возможно. То есть, вы должны иметь возможность вводить результат в REPL и получать то же значение обратно. Я лично считаю это поведение приятным, но при попытке понять котировку это может сбивать с толку, поэтому, если вы хотите отключить его, вызовите (print-as-expression #f)
или измените стиль печати на "запись" в меню языка DrRacket.