2011-10-25 20 views
14

¿Cuál es una forma garantizada de comparar dos objetos por su identidad en Ruby? Dadas dos variables, quiero volver verdadero si las variables apuntan exactamente al mismo objeto en la memoria.Forma garantizada de comparar objetos por identidad en Ruby

Para la mayoría de los objetos de Ruby, el método equal? compara por la identidad:

f = g = Object.new 
p f.equal? g # => true 

Sin embargo, esto no funciona para todos los objetos. Por ejemplo:

class F 
    def ==(obj) false end 
    def ===(obj) false end 
    def eql?(obj) false end 
    def equal?(obj) false end 
    def object_id; self end 
end 

f = g = F.new 
p f == g  # => false 
p f === g  # => false 
p f.eql? g  # => false 
p f.equal? g # => false 
p f.object_id == g.object_id # => false 

¿Qué es una forma infalible/garantizada de comparar dos objetos de identidad que no puede ser derrotado?

Esta es una cuestión puramente intelectual. La respuesta a cualquier pregunta que comience con "por qué" probablemente será "porque tengo curiosidad".

+0

¿Cuál es la palabra para cosas en informática y matemáticas como F? Hay alguna palabra para eso. Quiero decir "diabólico", pero menos malvado. –

+0

Creo que la palabra es "incorrecta". Como en, no es algo que debas hacer. – Chuck

+1

Quiero usar una palabra que comienza con "F" ...No hay garantías a menos que quieras escribir una extensión C, se supone que debes ser amable, pero no hay nada que impida que alguien sea un sociópata antisocial. –

Respuesta

12

se pudiera tomar una versión no unida de Object#object_id, vincularlo al objeto en cuestión, y ver lo que dice. Teniendo en cuenta su clase F con una adición:

class F 
    # ... 
    def inspect; 'pancakes!' end # Just so we can tell what we have later. 
end 

continuación:

>> f = F.new 
>> f.object_id 
=> pancakes! 
>> unbound_object_id = Object.instance_method(:object_id) 
>> unbound_object_id.bind(f).call 
=> 2153000340 
>> ObjectSpace._id2ref(2153000340).inspect 
=> "pancakes!" 

Por supuesto, si alguien abre Objeto y reemplaza object_id entonces estás de suerte pero esto va a ser la última de su problemas si alguien hace eso. Si se puede agarrar su unbound_object_idUnboundMethod antes de cargar cualquier otra cosa, entonces no importará si alguien cambia Object#object_id como su unbound_object_id seguirá siendo la correcta originales.

Así que esta rotonda truco que da una fiable object_id para cualquier objeto (con las salvedades anteriores). Ahora puede tomar y comparar los identificadores de objetos para obtener una comparación confiable.

+0

¡Genial, funciona! Me gustaría contribuir con una función que utiliza esta técnica para comparar dos variables por identidad: 'def compare_by_identity (x, y); id = Object.instance_method: object_id; id.bind (x) .call == id.bind (y) .call; end' –

3

La respuesta por mu es demasiado corto es grande, pero no hay otra manera de hacerlo utilizando una característica especial de la clase Hash:

def compare_by_identity(x, y) 
    h = {}.compare_by_identity 
    h[x] = 1 
    h[y] = 2 
    h.keys.size == 1 
end 

Se añadió la función compare_by_identity en Ruby 1.9. 2, por lo que esta función no funcionará en versiones anteriores. Creo que mu es demasiado corta La respuesta de es mejor.

4

Uso BasicObject#equal?. De acuerdo con Ruby documentation:

A diferencia de ==, el igual? El método nunca debe ser anulado por subclases: se usa para determinar la identidad del objeto (es decir, a.equal? ​​(b) si f a es el mismo objeto que b).

Necesidad de garantía más fuerte? AFAIK no puede recibirlo, BaseObject#object_id, ObjectSpace._id2ref, Hash#compare_by_identity pueden ser anulados o revisados ​​también (al menos esta es mi creencia personal).

+0

Puede proporcionar su propia implementación 'def equal? ​​(other) false end' si tiene una actitud suficientemente odiosa. –

Cuestiones relacionadas