В чем разница между eq ?, eqv ?, equal ?, и = в схеме?

Интересно, какая разница между этими операциями. Я видел подобные вопросы в Stack Overflow, но они касаются Lisp, и нет сравнения между тремя из этих операторов. Поэтому, если это уже было задано, сообщите мне.

Я пишу различные типы команд в Схеме, и я получаю следующие результаты:

(eq? 5 5) -->#t (eq? 2.5 2.5) -->#f (equal? 2.5 2.5) --> #t (= 2.5 2.5) --> #t 

Может кто-нибудь объяснить, почему это так?

Я отвечу на этот вопрос постепенно. Начнем с предиката = эквивалентности. Предикат = используется для проверки того, равны ли два числа. Если вы предоставите ему что-нибудь еще, кроме числа, то это вызовет ошибку:

 (= 2 3) => #f (= 2.5 2.5) => #t (= '() '()) => error 

eq? предикат используется для проверки того, представляют ли его два параметра один и тот же объект в памяти. Например:

 (define x '(2 3)) (define y '(2 3)) (eq? xy) => #f (define yx) (eq? xy) => #t 

Однако обратите внимание, что в памяти есть только один пустой список '() (на самом деле пустой список не существует в памяти, но указатель на ячейку памяти 0 считается пустым списком). Следовательно, при сравнении пустых списков eq? всегда будет возвращать #t (потому что они представляют один и тот же объект в памяти):

 (define x '()) (define y '()) (eq? xy) => #t 

Теперь в зависимости от реализации eq? может или не может возвращать #t для примитивных значений, таких как числа, строки и т. д. Например:

 (eq? 2 2) => depends upon the implementation (eq? "a" "a") => depends upon the implementation 

Это где eqv? предикат входит в картину. eqv? точно такая же, как eq? предикат, за исключением того, что он всегда возвращает #t для тех же самых примитивных значений. Например:

 (eqv? 2 2) => #t (eqv? "a" "a") => depends upon the implementation 

Следовательно, eqv? является надмножеством eq? и для большинства случаев вы должны использовать eqv? вместо eq? ,

Наконец мы приходим к equal? сказуемое. equal? предикат точно такой же, как и eqv? предикат, за исключением того, что он также может использоваться для проверки того, имеют ли два списка, векторы и т. д. соответствующие элементы, которые удовлетворяют условию eqv? сказуемое. Например:

 (define x '(2 3)) (define y '(2 3)) (equal? xy) => #t (eqv? xy) => #f 

В целом:

  1. Используйте предикат = если вы хотите проверить, эквивалентны ли два числа.
  2. Использовать eqv? предикат, когда вы хотите проверить, эквивалентны ли два нечисловых значения.
  3. Использовать equal? предикат, когда вы хотите проверить, эквивалентны ли два списка, векторы и т. д.
  4. Не использовать eq? если вы точно не знаете, что делаете.

Есть две полные страницы в спецификации RNRS, связанные с eq?, eqv?, equal? and = eq?, eqv?, equal? and = . Вот спецификация проекта R7RS . Проверьте это!

Объяснение:

  • = сравнивает числа, 2.5 и 2.5 численно равны.
  • equal? для чисел сводится к = , 2.5 и 2.5 численно равны.
  • eq? сравнивает «указатели». Число 5 в вашей реализации схемы реализовано как «немедленное» (вероятное), таким образом, 5 и 5 идентичны. Число 2.5 может потребовать распределения «записи с плавающей запятой» в вашей реализации Схемы, два указателя не идентичны.

eq? это #t когда это тот же адрес / объект. Обычно можно ожидать #t для одного символа, boolean и object и #f для значений различного типа, с разными значениями или не с той же структурой. Схемы / Lisp-реализации имеют традицию встраивать тип в свои указатели и вставлять значения в том же пространстве, если достаточно места. Таким образом, некоторые указатели действительно не являются адресами, а значениями, такими как char R или Fixnum 10 . Это будут eq? поскольку «адрес» представляет собой встроенное значение типа +. Некоторые реализации также используют неизменяемые константы. (eq? ‘(1 2 3)’ (1 2 3)) может быть #f при интерпретации, но #t при компиляции, так как он может получить тот же адрес. (Как постоянный пул строк в Java). Из-за этого многие выражения, содержащие eq? не определены, таким образом, он оценивает значение #t или #f, зависит от реализации.

eqv? #t для тех же вещей, что и eq? , Это также #t, если это число или символ, и это значение одно и то же , даже когда данные слишком велики, чтобы вписаться в указатель. Таким образом, для тех, делает ли дополнительная работа проверка этого типа одним из поддерживаемых, что оба являются одним и тем же типом, а целевые объекты имеют одинаковое значение.

equal? # t для тех же вещей, что и eqv? и если это сложный тип, такой как пара, вектор, строка и bytevector, он рекурсивно делает equal? с частями. На практике он вернет #t, если оба объекта будут выглядеть одинаково . До R6RS небезопасно использовать equal? на круговых структурах.

= как eqv? но он работает только для числовых типов . Это может быть более эффективным.

string=? как equal? , но он работает только для строк. Это может быть более эффективным.

equal? рекурсивно сравнивает два объекта (любого типа) для равенства.

  • Обратите внимание, что это может быть дорогостоящим для большой структуры данных, так как потенциально должен быть пройден весь список, строка, вектор и т. Д.

  • Если объект содержит только один элемент (EG: число, символ и т. Д.), Это то же самое, что и eqv? ,


eqv? проверяет два объекта, чтобы определить, являются ли оба они «обычно считаются одним и тем же объектом».

  • eqv? и eq? являются очень похожими операциями, и различия между ними будут несколько конкретными.

eq? то же самое, что и eqv? но могут быть способны различать более тонкие различия и могут быть реализованы более эффективно.

  • Согласно спецификации, это может быть реализовано как быстрое и эффективное сравнение указателей, в отличие от более сложной операции для eqv? ,

= сравнивает числа для численного равенства.

  • Обратите внимание, что может быть предоставлено более двух чисел, например: (= 1 1.0 1/1 2/2)

Вы не упомянули о реализации схемы, но в Racket, eq? возвращает true только в том случае, если аргументы ссылаются на один и тот же объект. Второй пример дает #f, потому что система создает новый номер с плавающей запятой для каждого аргумента; это не тот же объект.

equal? и = проверяют эквивалентность значений, но = применимо только к числам.

Если вы используете Racket, проверьте здесь дополнительную информацию. В противном случае проверьте документацию по реализации вашей схемы.

Подумайте об eq? как равенство указателя. Авторы отчета хотят, чтобы он был настолько общим, насколько это возможно, поэтому они не говорят об этом напрямую, потому что он зависит от реализации и, если сказать, будет способствовать реализации на основе указателей. Но они говорят

Как правило, можно реализовать eq? гораздо эффективнее, чем eqv ?, например, как простое сравнение указателей

Вот что я имею в виду. (eqv? 2 2) гарантированно возвращает #t но (eq? 2 2) не указывается. Теперь представьте себе реализацию на основе указателей. В нем eq? это просто сравнение указателей. Поскольку (eq? 2 2) не указывается, это означает, что эта реализация может просто создать новое представление объекта памяти каждого нового числа, которое он читает из исходного кода. eqv? должен фактически проверить свои аргументы.

OTOH (eq 'a 'a)#t . Это означает, что такая реализация должна распознавать символы с повторяющимися именами и использовать один и тот же объект представления в памяти для всех из них.

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

В любом случае, это моя догадка.

Так очень грубо, eq? является равенством указателя, eqv? является (атомным) знанием, знающим, equal? также проверяет структуру (проверяет его аргументы рекурсивно, так что, наконец, (equal? '(a) '(a)) требуется, чтобы быть #t ), = для чисел, string=? для строк, а детали указаны в отчете.

Давайте будем гением компьютера.