Подтвердить что ты не робот

В чем разница между цитатой и списком?

Я знаю, что вы можете использовать ' (aka quote) для создания списка, и я использую его все время, например:

> (car '(1 2 3))
1

Но он не всегда работает, как ожидал Id. Например, я попытался создать список функций, вроде этого, но он не работал:

> (define math-fns '(+ - * /))
> (map (lambda (fn) (fn 1)) math-fns)
application: not a procedure;
  expected a procedure that can be applied to arguments
  given: '+

Когда я использую list, он работает:

> (define math-fns (list + - * /))
> (map (lambda (fn) (fn 1)) math-fns)
'(1 -1 1 1)

Почему? Я думал, что ' является просто удобной стенографией, так почему же поведение отличается?

4b9b3361

Ответ 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.