【问题标题】:How to pass a custom comparator to "sort"?如何将自定义比较器传递给“排序”?
【发布时间】:2011-06-05 01:34:18
【问题描述】:

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!

【问题讨论】:

  • 从字面上看,您可以使用items.sort! { |x,y| x.my_comparator y },但如果这是该课程的默认排序行为,您应该考虑类似于下面的 Tin Man 的内容。

标签: ruby sorting comparator


【解决方案1】:

定义您自己的 &lt;=&gt;,并包含 Comparable。这是来自Comparable doc

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,但如果您在定义 &lt;=&gt; 之后这样做,您将免费获得额外的功能。

否则,如果您的对象已经实现了&lt;=&gt;,您可以将Enumerable's sort 与块一起使用。

使用几种不同比较的另一种方法是使用 lambda。这使用了新的 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]

【讨论】:

  • 或者,针对这个问题:alias_method :&lt;=&gt;, :my_comparator
  • +1。 @Phrogz 很好,尽管首先调用方法 &lt;=&gt; 会更符合 Ruby 风格。
  • 就我而言,我有几个比较器,所以我不想用my_comparator 覆盖&lt;=&gt;
  • 我添加了一些使用 lambdas 的示例,可让您轻松交换比较。
【解决方案2】:

这两个都应该工作:

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

【讨论】:

  • 这很好而且正确,但@theTinMan 的回答更适合自定义类。
  • 这是我正在寻找的答案,而不是那里的 OO 废话。
【解决方案3】:
items.sort!(&:my_comparator)

这会在内部调用:my_comparator.to_proc,它会返回一个块

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

因此将此答案简化为 Ben Alpert 的答案。

(但我同意 Phrogz 的观察,如果这是课程的自然顺序,那么您应该改用 Tin Man 的答案。)

【讨论】:

    【解决方案4】:

    如果你想在不同的地方复用这些比较器,最好将它们定义为一个类,而不是每次都重写同一个 lambda 表达式。

    这是基于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(&amp;MyCustomComparator.new)。它本质上归结为一个 lambda 表达式,但在我看来支持更多的可重用性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-27
      • 2013-11-04
      • 2020-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多