it-swarm.com.ru

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

Интересно, в чем разница между этими операциями. Я видел подобные вопросы в Stack Overflow, но они касаются LISP, и между этими тремя операторами нет сравнения. Так что, если об этом уже спрашивали, пожалуйста, дайте мне знать.

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

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

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

64
yrazlik

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

(= 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?, если точно не знаете, что делаете.
124
Aadit M Shah

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

Объяснение:

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

eq? - это #t, когда это тот же адрес/объект. Обычно можно ожидать #t для одного и того же символа, логического значения и объекта и #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?, и если это составной тип, такой как pair, vector, string и bytevector, он рекурсивно выполняет equal? с частями. На практике он вернет #t, если два объекта выглядят одинаково. До R6RS небезопасно использовать equal? на круговых структурах.

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

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

7
Sylwester

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

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

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

4
Alan Gilbert

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

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

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

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

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

В любом случае, это мое предположение.

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

3
Will Ness