Понимание режима данных в Лиспе, почему эти выражения не совпадают?
В настоящее время я читаю "Земля Лисп". В одном из последних примеров кода автор дал:
> (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 *)is
NIL`.
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
Есть и другие способы сравнения объектов (eq
eql
equal
equalp
=
). Лучше читать документацию и играть с REPL
чтобы увидеть разницу.