Me pregunto cuál es la diferencia entre esas operaciones en Scheme. He visto preguntas similares en Stack Overflow, pero son sobre Lisp y no hay una comparación entre tres de esos operadores.
Estoy escribiendo los diferentes tipos de comandos en Scheme y obtengo los siguientes resultados:
(eq? 5 5) -->#t
(eq? 2.5 2.5) -->#f
(equal? 2.5 2.5) --> #t
(= 2.5 2.5) --> #t
¿Por qué es este el caso?
functional-programming
scheme
yrazlik
fuente
fuente
eqv?
, que significa algo diferente deeq?
oequal?
Respuestas:
Responderé esta pregunta de forma incremental. Comencemos con el
=
predicado de equivalencia. El=
predicado se usa para verificar si dos números son iguales. Si le proporciona algo más que un número, generará un error:(= 2 3) => #f (= 2.5 2.5) => #t (= '() '()) => error
El
eq?
predicado se usa para verificar si sus dos parámetros representan el mismo objeto en la memoria. Por ejemplo:(define x '(2 3)) (define y '(2 3)) (eq? x y) => #f (define y x) (eq? x y) => #t
Sin embargo, tenga en cuenta que solo hay una lista vacía
'()
en la memoria (en realidad, la lista vacía no existe en la memoria, pero un puntero a la ubicación de la memoria0
se considera la lista vacía). Por lo tanto, cuando se comparan listas vacíaseq?
, siempre se devolverá#t
(porque representan el mismo objeto en la memoria):(define x '()) (define y '()) (eq? x y) => #t
Ahora, dependiendo de la implementación,
eq?
puede o no regresar#t
para valores primitivos como números, cadenas, etc. Por ejemplo:(eq? 2 2) => depends upon the implementation (eq? "a" "a") => depends upon the implementation
Aquí es donde
eqv?
entra en escena el predicado. Eleqv?
es exactamente igual que eleq?
predicado, excepto que siempre regresará#t
para los mismos valores primitivos. Por ejemplo:(eqv? 2 2) => #t (eqv? "a" "a") => depends upon the implementation
Por
eqv?
lo tanto, es un superconjunto deeq?
y, en la mayoría de los casos, debe usar eneqv?
lugar deeq?
.Finalmente llegamos al
equal?
predicado. Elequal?
predicado es exactamente el mismo que eleqv?
predicado, excepto que también se puede usar para probar si dos listas, vectores, etc. tienen elementos correspondientes que satisfacen eleqv?
predicado. Por ejemplo:(define x '(2 3)) (define y '(2 3)) (equal? x y) => #t (eqv? x y) => #f
En general:
=
predicado cuando desee probar si dos números son equivalentes.eqv?
predicado cuando desee probar si dos valores no numéricos son equivalentes.equal?
predicado cuando desee probar si dos listas, vectores, etc. son equivalentes.eq?
predicado a menos que sepa exactamente lo que está haciendo.fuente
(eqv? "a" "a") ==> unspecified
. Tendrá que usarequal?
o (posiblemente más optimizado)string=?
(eq? '(1) '(1))
está especificado , por lo que es posible que su(define x '(1 2))
ilustración no funcione.(eq? 'foo 'foo) -> #t
,(eq? 'foo 'bar)
-> falso`. Leí esto aquí y aquíHay dos páginas completas en la especificación RnRS relacionadas con
eq?, eqv?, equal? and =
. Aquí está el borrador de la especificación R7RS . ¡Echale un vistazo!Explicación:
=
compara números, 2.5 y 2.5 son numéricamente iguales.equal?
porque los números se reducen a=
, 2.5 y 2.5 son numéricamente iguales.eq?
compara 'punteros'. El número 5, en la implementación de su esquema, se implementa como un 'inmediato' (probable), por lo tanto, 5 y 5 son idénticos. El número 2.5 puede requerir una asignación de un 'registro de punto flotante' en su implementación de Scheme, los dos punteros no son idénticos.fuente
eq?
es#t
cuando es la misma dirección / objeto. Normalmente, uno podría esperar #t para el mismo símbolo, booleano y objeto y #f para valores que son de tipo diferente, con valores diferentes, o que no tienen la misma estructura Las implementaciones de Scheme / Lisp tienen una tradición de incrustar el tipo en sus punteros y de incrustar valores en el mismo espacio si hay suficiente espacio. Por lo tanto, algunos punteros en realidad no son direcciones sino valores, como charR
o Fixnum10
. Esto seráeq?
debido a que la "dirección" es un tipo + valor incrustado. Algunas implementaciones también reutilizan constantes inmutables. (eq? '(1 2 3)' (1 2 3)) podría ser #f cuando se interpreta pero #t cuando se compila ya que podría obtener la misma dirección. (Como el grupo de cadenas constante en Java). Debido a esto, muchas expresiones que involucraneq?
no están especificados, por lo tanto, si se evalúa como #t o #f depende de la implementación.eqv?
son #t por lo mismo queeq?
. También es #t si es un número o carácter y su valor es el mismo , incluso cuando los datos son demasiado grandes para caber en un puntero. Por lo tanto, para aquelloseqv?
, el trabajo adicional de verificar que el tipo sea uno de los admitidos, que ambos sean del mismo tipo y que los objetos de destino tengan el mismo valor de datos.equal?
es #t para las mismas cosas queeqv?
y si es un tipo compuesto como par, vector, cadena y bytevector, lo hace de forma recursivaequal?
con las partes. En la práctica, devolverá #t si los dos objetos se ven iguales . Antes de R6RS, no es seguro usarloequal?
en estructuras circulares.=
es comoeqv?
pero solo funciona para tipos numéricos . Podría ser más eficiente.string=?
es comoequal?
, pero solo funciona para cadenas. Podría ser más eficiente.fuente
equal?
compara recursivamente dos objetos (de cualquier tipo) para la igualdad.Tenga en cuenta que esto podría resultar caro para una estructura de datos grande, ya que potencialmente se debe atravesar toda la lista, cadena, vector, etc.
Si el objeto solo contiene un solo elemento (EG: número, carácter, etc.), este es el mismo que
eqv?
.eqv?
prueba dos objetos para determinar si ambos "normalmente se consideran el mismo objeto".eqv?
yeq?
son operaciones muy similares, y las diferencias entre ellas van a ser algo específicas de implementación.eq?
es igual que,eqv?
pero puede discernir distinciones más finas y puede implementarse de manera más eficiente.eqv?
.=
compara números para la igualdad numérica.(= 1 1.0 1/1 2/2)
fuente
eq?
era la igualdad real del puntero (noeqv?
). Es "el más fino o el más exigente". Por ejemplo,(eqv? 2 2)
se garantiza que lo sea#t
, pero no(eq? 2 2)
está "especificado". Es decir, depende de si una implementación crea un nuevo objeto de memoria real para cada número recién leído o si puede reutilizar uno creado previamente.eq?
yeqv?
son más sutiles que las otras operaciones.No mencionas la implementación de un esquema, pero en Racket,
eq?
solo devuelve verdadero si los argumentos se refieren al mismo objeto. Su segundo ejemplo produce #f porque el sistema está creando un nuevo número de punto flotante para cada argumento; no son el mismo objeto.equal?
y=
están verificando la equivalencia de valores, pero=
solo es aplicable a números.Si está utilizando Racket, consulte aquí para obtener más información. De lo contrario, consulte la documentación de la implementación de su esquema.
fuente
Piense
eq?
en la igualdad de punteros. Los autores del Informe quieren que sea lo más general posible, por lo que no dicen esto directamente porque depende de la implementación, y decirlo favorecería las implementaciones basadas en punteros. Pero ellos dicenEsto es lo que quiero decir.
(eqv? 2 2)
está garantizado para regresar#t
pero(eq? 2 2)
no está especificado. Ahora imagina una implementación basada en punteros. En éleq?
es solo una comparación de punteros. Dado(eq? 2 2)
que no está especificado, significa que esta implementación es gratuita para crear una nueva representación de objeto de memoria de cada nuevo número que lee del código fuente.eqv?
realmente debe inspeccionar sus argumentos.OTOH lo
(eq 'a 'a)
es#t
. Esto significa que dicha aplicación debe reconocer símbolos con nombres duplicados y utilizar el mismo un objeto de representación en la memoria de todos ellos.Suponga que una implementación no está basada en punteros. Mientras se adhiera al Informe, no importa. Los autores simplemente no quieren que se les vea dictando los detalles de las implementaciones a los implementadores, por lo que eligen su redacción con cuidado.
Esta es mi suposición de todos modos.
Entonces, de manera muy aproximada, ¿
eq?
es la igualdad de punteros,eqv?
es (atómico-) consciente de los valores,equal?
también es consciente de la estructura (verifica sus argumentos de forma recursiva, por lo que finalmente(equal? '(a) '(a))
se requiere que sea#t
),=
es para números,string=?
es para cadenas, y los detalles son en el Informe.fuente
Aparte de las respuestas anteriores, agregaré algunos comentarios.
Todos estos predicados quieren definir la función abstracta de
identity
para un objeto, pero en diferentes contextos.EQ?
depende de la implementación y no responde a la preguntaare 2 objects the same?
solo con un uso limitado. Desde el punto de vista de la implementación, este predicado solo compara 2 números (puntero a objetos), no mira el contenido de los objetos. Entonces, por ejemplo, si su implementación no mantiene las cadenas dentro de forma única, sino que asigna memoria diferente para cada cadena, entonces(eq? "a" "a")
será falso.EQV?
- esto mira dentro de los objetos, pero con uso limitado. Depende de la implementación si devuelve verdadero para(eqv? (lambda(x) x) (lambda(x) x))
. Aquí es una filosofía completa cómo definir este predicado, ya que sabemos hoy en día que existen algunos métodos rápidos para comparar la funcionalidad de algunas funciones, con uso limitado. Peroeqv?
proporciona una respuesta coherente para números grandes, cadenas, etc.Prácticamente, algunos de estos predicados intentan usar la definición abstracta de un objeto (matemáticamente), mientras que otros usan la representación de un objeto (cómo se implementa en una máquina real). La definición matemática de identidad proviene de Leibniz y dice:
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.
Lo ideal sería poder implementar esta misma definición en la computadora, pero por razones de indecidibilidad y / o velocidad no se implementa literalmente. Es por eso que hay muchos operadores que intentan que cada uno se enfoque en diferentes puntos de vista en torno a esta definición.
Intente imaginar la definición abstracta de una identidad para una continuación. Incluso si puede proporcionar una definición de un subconjunto de funciones ( clase de funciones sigma-recursiva ), el lenguaje no impone ningún predicado para que sea verdadero o falso. Complicaría mucho tanto la definición del lenguaje como mucho más la implementación.
El contexto de los otros predicados es más fácil de analizar.
fuente