クラスA
には、次のコンパレータがあります。
class A
attr_accessor x
def my_comparator(a)
x**2 <=> (a.x)**2
end
end
このコンパレータを使用して、各アイテムがクラスAの配列を並べ替えたいと思います。
class B
def my_method
items.sort!(<how can I pass my_comparator here ?>)
end
end
my_comparator
をsort!
に渡すにはどうすればよいですか?
独自の<=>
を定義し、Comparableを含めます。これは 比較可能なドキュメント からです:
class SizeMatters
include Comparable
attr :str
def <=>(an_other)
str.size <=> an_other.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]
実際にComparableを含める必要はありませんが、<=>
を定義した後で追加すると、追加の機能を無料で利用できます。
それ以外の場合、オブジェクトがすでに<=>
を実装している場合は、ブロックで Enumerableのsort
を使用できます。
いくつかの異なる比較を使用する別の方法は、ラムダを使用することです。これは、新しい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]
これらは両方とも機能するはずです。
items.sort_by! { |a| (a.x)**2 }
items.sort! { |a1,a2| a1.my_comparator(a2) }
items.sort!(&:my_comparator)
これは内部で:my_comparator.to_proc
を呼び出し、ブロックを返します
proc {|x,y| x.my_comparator(y)}
したがって、この答えをベンアルパートの答えに減らします。
(しかし、これがクラスのnatural順序である場合は、代わりにブリキの木こりの答えを使用する必要があるというPhrogzの観察に同意します。)
これらのコンパレータを別の場所で再利用する場合は、毎回同じラムダ式を書き直すのではなく、クラスとして定義することをお勧めします。
これは、JavaのComparableインターフェースの実装に基づいています。
_module Comparator
def compare(a, b)
raise NotImplementedError, 'must implement this method'
end
def to_proc
->(a, b) { compare(a, b) }
end
end
class LengthComparator
include Comparator
def compare(a, b)
a.length <=> b.length
end
end
class ReverseLengthComparator < LengthComparator
def compare(a, b)
-super
end
end
_
比較ロジックは#compareメソッドで実装します。その後、次のようにこのクラスを使用できます:array.sort(&MyCustomComparator.new)
。それは本質的にラムダ式に要約されますが、私の意見ではより多くの再利用性をサポートします。