2009-12-19 1 views
38

Per una semplice struct simile classe:Qual è il modo giusto per realizzare l'uguaglianza in rubino

class Tiger 
    attr_accessor :name, :num_stripes 
end 

qual è il modo corretto per implementare l'uguaglianza correttamente, per garantire che ==, ===, eql?, ecc lavoro, e in modo che le istanze della classe di gioco ben in set, hash, ecc

EDIT

Inoltre, che cosa è un bel modo per implementare l'uguaglianza quando si desidera confrontare base su uno stato che non è esposto al di fuori della classe? Per esempio:

class Lady 
    attr_accessor :name 

    def initialize(age) 
    @age = age 
    end 
end 

qui Vorrei che il mio metodo di uguaglianza di prendere @age in considerazione, ma la signora non esporre la sua età ai clienti. Dovrei usare instance_variable_get in questa situazione?

+0

[Questo è un interessante resoconto a confronto i pro ei contro di definizione dell'uguaglianza oggetto] (http://www.skorks.com/2009/09/ruby-ugality-and-object-comparison /) – ennuikiller

risposta

61

Per semplificare gli operatori di confronto per oggetti con più di una variabile di stato, creare un metodo che restituisce tutto lo stato dell'oggetto come una matrice. Poi basta confrontare i due stati:

class Thing 

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

    def ==(o) 
    o.class == self.class && o.state == state 
    end 

    protected 

    def state 
    [@a, @b, @c] 
    end 

end 

p Thing.new(1, 2, 3) == Thing.new(1, 2, 3) # => true 
p Thing.new(1, 2, 3) == Thing.new(1, 2, 4) # => false 

Inoltre, se si desidera che le istanze della classe a essere utilizzabile come chiave hash, quindi aggiungere:

alias_method :eql?, :== 

    def hash 
    state.hash 
    end 

questi devono essere pubbliche.

+2

Mi piace molto questo trucco di confrontare gli oggetti usando delegando il confronto allo stato array. –

1

In genere con l'operatore ==.

+0

Grazie. Tuttavia, penso che se definisci solo ==, le istanze della classe non si comportano come previsto in hash e insiemi. –

+0

Inoltre, I/think/that == non dovrebbe controllare l'uguaglianza di tipo (come sta facendo l'esempio). Questo è ciò che eql? dovrebbe fare. Potrebbe essere sbagliato su questo. –

+0

Il comportamento varia solo se lo fai variare, Pete. Ultimo ho controllato 'true == true' (e' 1 + 1 == 2') restituisce ancora 'true' ... –

12

testare tutte le variabili di istanza uguaglianza in una sola volta:

def ==(other) 
    other.class == self.class && other.state == self.state 
end 

def state 
    self.instance_variables.map { |variable| self.instance_variable_get variable } 
end 
+3

Questo è davvero il modo migliore perché fa molta strada per assicurarti di non lasciare casualmente nessuna variabile. Ora, potrebbero anche esserci alcune conseguenze indesiderate dell'uso automatico di ogni variabile di istanza, ma non sono sicuro e non lo saprò finché non mi sorprenderà un giorno. – WattsInABox

+0

È vero, puoi aggiungere parametri facoltativi, come "solo" o "tranne" per quei casi particolari. – jvenezia