2011-01-07 13 views
25

Clase A tiene la siguiente comparación:¿Cómo pasar un comparador personalizado para "ordenar"?

class A 
    attr_accessor x 

    def my_comparator(a) 
    x**2 <=> (a.x)**2 
    end 
end 

me gustaría utilizar este comparador para ordenar una matriz en donde cada elemento es de clase A:

class B 
    def my_method 
    items.sort!(<how can I pass my_comparator here ?>) 
    end 
end 

¿Cómo debería pasar my_comparator a sort!?

+1

Literalmente, se puede usar 'Items.Sort! {| x, y | x.my_comparator y} ', pero si este es el comportamiento de clasificación predeterminado para la clase, debería considerar algo así como lo que Tin Man tiene a continuación. – coreyward

Respuesta

33

definir sus propias <=>, e incluyen comparable. Esto es de la Comparable doc:

class SizeMatters 
    include Comparable 
    attr :str 
    def <=>(anOther) 
    str.size <=> anOther.str.size 
    end 
    def initialize(str) 
    @str = str 
    end 
    def inspect 
    @str 
    end 
end 

s1 = SizeMatters.new("Z") 
s2 = SizeMatters.new("YY") 
s3 = SizeMatters.new("XXX") 
s4 = SizeMatters.new("WWWW") 
s5 = SizeMatters.new("VVVVV") 

s1 < s2      #=> true 
s4.between?(s1, s3)   #=> false 
s4.between?(s3, s5)   #=> true 
[ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV] 

En realidad no tiene que incluir comparable, pero se obtiene una funcionalidad adicional gratis si lo hace después de haber definido <=>.

De lo contrario, puede usar Enumerable's sort con un bloque si sus objetos ya implementan <=>.

EDITAR: Otra forma de utilizar varias comparaciones diferentes es utilizar lambdas. Este sistema utiliza la nueva sintaxis de declaración 1.9.2:

ascending_sort = ->(a,b) { a <=> b } 
descending_sort = ->(a,b) { b <=> a } 

[1, 3, 2, 4].sort(& ascending_sort) # => [1, 2, 3, 4] 
[1, 3, 2, 4].sort(& descending_sort) # => [4, 3, 2, 1] 

foo = ascending_sort 
[1, 3, 2, 4].sort(& foo) # => [1, 2, 3, 4] 
+1

O, específico para esta pregunta: 'alias_method: <=>,: my_comparator' – Phrogz

+0

+1. Buena captura @Phrogz, aunque sería más Ruby-ish llamar al método '<=>' en primer lugar. –

+0

En mi caso, tengo varios comparadores, por lo que no quiero anular '<=>' con 'mi_comparador'. –

16

Ambos deben trabajar:

items.sort_by! { |a| (a.x)**2 } 
items.sort! { |a1,a2| a1.my_comparator(a2) } 
+0

Esto es bueno y correcto, pero la respuesta de @ theTinMan es mejor para las clases personalizadas. – Phrogz

+0

Esta fue la respuesta que estaba buscando, no esa mierda de OO allá arriba. –

5
items.sort!(&:my_comparator) 

Este llama a la :my_comparator.to_proc internamente, que devuelve un bloque

proc {|x,y| x.my_comparator(y)} 

reduciendo así la respuesta a la respuesta de Ben Alpert.

(Pero estoy de acuerdo con la observación de Phrogz que si este es el naturales para que la clase, entonces debe usar la respuesta del hombre de hojalata en su lugar.)

Cuestiones relacionadas