В чем разница между 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
Может кто-нибудь объяснить, почему это так?
6 ответов
Я отвечу на этот вопрос постепенно. Давайте начнем с =
предикат эквивалентности. =
Предикат используется для проверки, равны ли два числа. Если вы предоставите ему что-нибудь еще, кроме числа, это вызовет ошибку:
(= 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
В общем:
- Использовать
=
Предикат, когда вы хотите проверить, эквивалентны ли два числа. - Использовать
eqv?
Предикат, когда вы хотите проверить, эквивалентны ли два нечисловых значения. - Использовать
equal?
Предикат, когда вы хотите проверить, эквивалентны ли два списка, вектора и т. д. - Не используйте
eq?
предикат, если вы не знаете точно, что вы делаете.
В спецификации RNRS есть две полные страницы, связанные с eq?, eqv?, equal? and =
, Вот черновик спецификации R7RS. Проверьте это!
Объяснение:
=
сравнивает числа, 2,5 и 2,5 численно равны.equal?
для чисел сводится к=
2,5 и 2,5 численно равны.eq?
сравнивает "указатели". Число 5 в вашей реализации Схемы реализовано как "немедленное" (вероятно), поэтому 5 и 5 идентичны. Число 2.5 может потребовать выделения "записи с плавающей запятой" в вашей реализации Схемы, эти два указателя не идентичны.
eq?
является #t
когда это тот же адрес / объект. Обычно можно ожидать #t для одного и того же символа, логического значения и объекта и #f для значений другого типа, с разными значениями или с разной структурой. Реализация Scheme/Lisp имеет традицию вставлять тип в свои указатели и вставлять значения в одном и том же месте, если места достаточно. Таким образом, некоторые указатели действительно являются не адресами, а значениями, такими как символ 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?
, но это работает только для строк. Это может быть более эффективным.
equal?
рекурсивно сравнивает два объекта (любого типа) на равенство.
Обратите внимание, что это может быть дорого для большой структуры данных, поскольку потенциально необходимо просмотреть весь список, строку, вектор и т. Д.
Если объект содержит только один элемент (например, число, символ и т. Д.), Это то же самое, что и
eqv?
,
eqv?
проверяет два объекта, чтобы определить, "оба ли они обычно рассматриваются как один и тот же объект".
eqv?
а такжеeq?
Это очень похожие операции, и различия между ними будут зависеть от реализации.
eq?
такой же как eqv?
но может быть в состоянии различить более тонкие различия и может быть реализовано более эффективно.
- Согласно спецификации, это может быть реализовано как быстрое и эффективное сравнение указателей, в отличие от более сложной операции для
eqv?
,
=
сравнивает числа для числового равенства.- Обратите внимание, что можно указать более двух номеров, например:
(= 1 1.0 1/1 2/2)
Вы не упоминаете реализацию схемы, но в Racket, eq?
возвращает true, только если аргументы ссылаются на один и тот же объект. Ваш второй пример дает #f, потому что система создает новое число с плавающей запятой для каждого аргумента; они не тот же объект.
equal?
а также =
проверяют на эквивалентность значений, но =
применимо только к номерам.
Если вы используете Racket, проверьте здесь для получения дополнительной информации. В противном случае, проверьте документацию вашей схемы реализации.
Думать о eq?
в качестве указателя равенства. Авторы отчета хотят, чтобы он был как можно более общим, поэтому они не говорят этого прямо, потому что он зависит от реализации и, скажем так, предпочтет реализации на основе указателей. Но они говорят
Обычно будет возможно реализовать эк? гораздо эффективнее, чем eqv?, например, в качестве простого сравнения указателей
Вот что я имею в виду. (eqv? 2 2)
гарантированно вернуть #t
но (eq? 2 2)
не указано Теперь представьте реализацию на основе указателя. В этом eq?
это просто сравнение указателей. поскольку (eq? 2 2)
не указано, это означает, что эта реализация может просто создавать новое представление объекта памяти для каждого нового числа, которое она считывает из исходного кода. eqv?
должен на самом деле проверить свои аргументы.
Ото (eq 'a 'a)
является #t
, Это означает, что такая реализация должна распознавать символы с повторяющимися именами и использовать один и тот же объект представления в памяти для всех них.
Предположим, что реализация не основана на указателе. Пока это придерживается Отчета, это не имеет значения. Авторы просто не хотят, чтобы их диктовали специфике реализаций разработчикам, поэтому они тщательно выбирают свою формулировку.
В любом случае, это мое предположение.
Очень грубо, eq?
равенство указателей, eqv?
осведомлен о (атомарных) ценностях, equal?
также осведомлен о структуре (проверяет свои аргументы рекурсивно, так что, наконец, (equal? '(a) '(a))
должен быть #t
), =
для чисел, string=?
для строк, а детали в отчете.
Помимо предыдущих ответов, я добавлю несколько комментариев.
Все эти предикаты хотят определить абстрактную функцию identity
для объекта, но в разных контекстах.
EQ?
зависит от реализации и не отвечает на вопрос are 2 objects the same?
только в ограниченном использовании. С точки зрения реализации, этот предикат просто сравнивает 2 числа (указатель на объекты), он не смотрит на содержимое объектов. Так, например, если ваша реализация не сохраняет строки внутри однозначно, но выделяет разную память для каждой строки, тогда(eq? "a" "a")
будет ложным.
EQV?
- это заглядывает внутрь объектов, но с ограниченным использованием. Это зависит от реализации, если он возвращает true для(eqv? (lambda(x) x) (lambda(x) x))
. Здесь представлена полная философия того, как определять этот предикат, поскольку в настоящее время мы знаем, что есть несколько быстрых методов для сравнения функциональности некоторых функций с ограниченным использованием. Ноeqv?
дает последовательный ответ для больших чисел, строк и т. д.
На практике некоторые из этих предикатов пытаются использовать абстрактное определение объекта (математически), в то время как другие используют представление объекта (как это реализовано на реальной машине). Математическое определение идентичности исходит от Лейбница и гласит:
X = Y iff for any P, P(X) = P(Y)
X, Y being objects and
P being any property associated with object X and Y.
В идеале это определение можно было бы реализовать на компьютере, но по причинам неопределенности и / или скорости оно не реализовано буквально. Вот почему есть много операторов, которые пытаются сосредоточить каждого на разных точках зрения на это определение.
Попробуйте представить себе абстрактное определение идентичности для продолжения. Даже если вы можете предоставить определение подмножества функций (сигма-рекурсивный класс функций), язык не требует, чтобы какой-либо предикат был истинным или ложным. Это сильно усложнило бы как определение языка, так и реализацию.
Контекст для других предикатов легче анализировать.