2010-07-14 8 views
1

Si je crée deux instances String avec le même contenu séparément, elles sont identiques. Ce n'est pas le cas avec les classes personnalisées par défaut (voir l'exemple ci-dessous).Unicité des instances Ruby

Si j'ai ma propre classe (Test ci-dessous) et j'ai une variable (@v ci-dessous) qui est unique, c'est à dire. deux instances Test avec le même @v doivent être traitées comme identiques, alors comment pourrais-je dire à Ruby que c'est le cas?

Considérons cet exemple:

class Test 
    def initialize(v) 
    @v = v 
    end 
end 

a = {Test.new('a') => 1, Test.new('b') => 2} 

a.delete(Test.new('a')) 

p a 
# # Desired output: 
# => {#<Test:0x100124ef8 @v="b">=>2} 
+0

Deux instances de chaîne avec le même contenu ne sont pas identiques. '" string ".equal? ​​(" string ")' retourne 'false' –

+0

Ahh, mon erreur. J'ai supposé qu'ils étaient parce que 'a = {'chaîne' => 'une chaîne'}; a.delete ('string') 'fonctionne! –

+0

@JP: Je pense que les hachages ont un cas particulier s'ils utilisent une chaîne comme clé. –

Répondre

1

J'ai trouvé une autre façon d'y parvenir, en gardant la trace de toutes les instances de test, je peux retourner en interne l'instance premade plutôt que de faire une nouvelle et dire Ruby ils sont équivalents:

class Test 
    def self.new(v) 
    begin 
     return @@instances[v] if @@instances[v] 
    rescue 
    end 

    new_test = self.allocate 
    new_test.instance_variable_set(:@v,v) 
    (@@instances ||= {})[v] = new_test 
    end 
end 

maintenant Test.new('a') == Test.new('a')etTest.new('a') === Test.new('a') :)

1

Vous devez définir une méthode == qui définit ce que signifie l'égalité pour votre classe. Dans ce cas, vous voulez:

class Test 
    def initialize(v) 
    @v = v 
    end 
    def ==(other) 
    @v == other.instance_variable_get(:@v) 
    end 
end 
+0

Fantastique, merci! cela fonctionnera certainement, mais si je change un attribut sur 'a1 = Test.new ('a') 'sera-t-il également apparent sur' a2 = Test.new ('a') '? Je veux vraiment qu'ils * soient * le même objet, pas seulement l'équivalent. –

+0

Oups, j'ai parlé trop rapidement, en ajoutant que la méthode '==' à mon exemple ci-dessus ne donne pas réellement la sortie désirée - désolé! –

1

Vous utilisez des objets de la classe Test comme clés pour le hachage. Pour que pour fonctionner correctement (et par conséquent a.delete), vous devez définir deux méthodes à l'intérieur Test: Test#hash et Test#eql?

De: http://ruby-doc.org/core/classes/Hash.html

Hash utilise key.eql? pour tester les clés pour l'égalité . Si vous devez utiliser les instances de vos propres classes en tant que clés dans un Hash, , il est recommandé de définir les deux l'eql? et méthodes de hachage. La méthode hash doit avoir la propriété que a.eql? (B) implique a.hash == b.hash.

1

la plupart du temps, un objet, vous devez être comparable et/ou hashable est composé de variables membres qui sont soit des primitives (entiers, chaînes, etc.) ou sont eux-mêmes comparables/lavables. Dans ces cas, ce module:

module Hashable 

    include Comparable 

    def ==(other) 
    other.is_a?(self.class) && other.send(:parts) == parts 
    end 
    alias_method :eql?, :== 

    def hash 
    parts.hash 
    end 

end 

peut simplement être inclus dans votre classe pour prendre soin de tous les busywork. Tout ce que vous avez à faire est de définir une « parties » méthode qui renvoie toutes les valeurs qui composent l'état de l'objet:

class Foo 

    include Hashable 

    def initialize(a, b) 
    @a = a 
    @b = b 
    end 

    private 

    def parts 
    [@a, @b] 
    end 

end 

objets construits de cette façon sont comparables (ils ont <, < =, ==,> = ,>,! = et equ?) et ils peuvent être des clés de hachage.