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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

(define x '())
(define y '())
(eq? x y)      => #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? x y)      => #t
(eqv? x y)        => #f

В целом:

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

Ответ 2

Есть две полные страницы в спецификации 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 может потребовать распределения "записи с плавающей запятой" в вашей реализации Схемы, два указателя не идентичны.

Ответ 3

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, если это число или символ, и это значение является тем же самым, даже если данные слишком велики, чтобы вписаться в указатель. Таким образом, для тех eqv? дополнительная работа по проверке этого типа является одной из поддерживаемых, что оба являются одним и тем же типом, и целевые объекты имеют одинаковое значение данных.

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

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

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

Ответ 4

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

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

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


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

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

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

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


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

Ответ 5

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

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

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

Ответ 6

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

Как правило, можно реализовать 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)) требуется be #t), = - для чисел, string=? - для строк, а детали указаны в отчете.