web-dev-qa-db-ja.com

カスタムコンパレータを「ソート」に渡す方法は?

クラス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_comparatorsort!に渡すにはどうすればよいですか?

26
Misha Moroshko

独自の<=>を定義し、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]
36
the Tin Man

これらは両方とも機能するはずです。

items.sort_by! { |a| (a.x)**2 }
items.sort! { |a1,a2| a1.my_comparator(a2) }
20
Sophie Alpert
items.sort!(&:my_comparator)

これは内部で:my_comparator.to_procを呼び出し、ブロックを返します

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

したがって、この答えをベンアルパートの答えに減らします。

(しかし、これがクラスのnatural順序である場合は、代わりにブリキの木こりの答えを使用する必要があるというPhrogzの観察に同意します。)

5
Ken Bloom

これらのコンパレータを別の場所で再利用する場合は、毎回同じラムダ式を書き直すのではなく、クラスとして定義することをお勧めします。

これは、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)。それは本質的にラムダ式に要約されますが、私の意見ではより多くの再利用性をサポートします。

0