Понимание режима данных в Лиспе, почему эти выражения не совпадают?

В настоящее время я читаю "Земля Лисп". В одном из последних примеров кода автор дал:

> (eq 'fooo 'FoOo)
T

доказать, что символы не чувствительны к регистру. Через несколько страниц официально введен режим данных.

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

> (eq 'Eq 'EQ)
T

Отлично. Это сработало, как и ожидалось. Но что, если я помещу это в список в режиме данных? Имейте в виду, я просто экспериментирую с чем-то новым для меня.

> (eq '(Eq) '(EQ))
NIL
> (eq '('Eq) '('EQ))
NIL

Эмм. Хорошо? Это почему? Я ожидал бы, что если бы я поместил один и тот же символ в два списка, эти списки считались бы равными.

Теперь вопрос: означает ли это, что сравнивается не содержимое списков, а сами списки? Что мне не хватает?

4 ответа

Решение

Символы чувствительны к регистру

> (eq (print (intern "foo")) (print (intern "FOO")))
|foo|   ; printed 
FOO     ; printed
==> NIL ; returned

По умолчанию читатель конвертирует регистр

(eq (read-from-string "foo") (read-from-string "FOO"))
==> T

Тем не менее, вы можете сделать так, чтобы читатель сохранял регистр:

(let ((*readtable* (copy-readtable)))
  (setf (readtable-case *readtable*) :preserve)
  (eq (read-from-string "foo") (read-from-string "FOO")))
==> NIL

Пожалуйста, посмотрите на

EQ сравнивает для идентификатора указателя

Common Lisp предоставляет 4 предиката равенства, вы должны использовать правильный для ваших нужд:

(equal '(Eq) '(EQ))
==> T
(equal '('Eq) '('EQ))
==> T

eq сравнивает для вещей, чтобы быть точно таким же, думаю, равенство указателей. Это как == в Java даже логически эквивалентные данные будут ложными.

Решение здесь заключается в использовании equal который просто делает "интеллектуальную" вещь и сравнивает элементы списков

> (equal '(A) '(a))
T

Кроме того, символы чувствительны к регистру. Но по умолчанию читатель (то, что превращает код в AST) по умолчанию не учитывает регистр. Вы можете заметить это различие с такой функцией, как intern,

Чтобы немного усложнить вещи.

Допустим, у нас есть следующее в файле. Затем мы компилируем и загружаем этот файл.

(defparameter *a* '(1))
(defparameter *b* '(1))

Теперь посчитаем:

(eq *a* *b*)

Не определено, если это NIL или же T, Компилятор Common Lisp может обнаружить, что списки equal и установите обе переменные в один и тот же список - то есть EQ тогда и будет правдой. На самом деле есть компиляторы Common Lisp, которые делают это. Выполнение двух DEFPARAMETER формы в * REPLone would usually expect that the Lisp system does not detect that and that(eq a * b *)isNIL`.

EQUAL сравнивает структуру и (eq *a* *b*) для нашего примера всегда T,

Лисп символы чувствительны к регистру. И даже читатель LISP чувствителен к регистру. Единственный момент заключается в том, что, когда читатель читает символ, он обычно делает его в верхнем регистре. Самый простой способ сказать читателю, что важен случай, - это поместить его между вертикальными линиями.

> '|asd|def|ghj|
|asdDEFght|
> '|asd|
|asd|
> '|ASD|
ASD
> 'ASD
ASD
> (eq 'asd '|ASD|)
t
> (eq 'asd '|aSd|)
nil

eq проверка предикатов, что аргументы - это одни и те же объекты (аналогично сравнению указателей с переменными в C)

> (defparameter *x* 1)
> (defparameter *y* 1)
> (eq *x* *y*)
nil

Поэтому, когда вы пишете '(asd) в REPL список с одним элементом создан. И когда вы пишете его во второй раз, создается другой список, и эти списки на самом деле являются разными объектами.

> (defparameter *list1* '('qwe))
> (defparameter *list2* '('qwe))
> (eq *list1* list2*) ;this are 2 different objects
nil
> (setf (first *list1* 'def))
> *list1* ;this list has changed
(DEF)
> *list2* ;and this did not
(QWE)

> (setf *list1* *list2*) ;now they are just different names for one object
> *list1*
(QWE)
> (eq *list1* *list2*)
t

Есть и другие способы сравнения объектов (eqeqlequalequalp=). Лучше читать документацию и играть с REPL чтобы увидеть разницу.

Другие вопросы по тегам