将比较运算符作为参数传递给 Ruby 中的方法

Passing a comparison operator as argument to a method in Ruby

将比较运算符作为参数传递给 Ruby 中的方法的最佳方法是什么?我想创建一个通用的排序?数组识别器,并将“<=”或“>=”传递给此方法。

到目前为止我有这个代码:

class Array
  def sorted?
    return 1 if sorted_increasing?
    return -1 if sorted_decreasing?
    0
  end

  def sorted_increasing?
    return true if length < 2
    self[0] <= self[1] && self.drop(1).sorted_increasing?
  end

  def sorted_decreasing?
    return true if length < 2
    self[0] >= self[1] && self.drop(1).sorted_decreasing?
  end
end

– 似乎用 sorted_generic?(comparison_operator) 方法代替 sorted_increasing 会更好?和 sorted_decreasing?.

更新:感谢您的回复,我的解决方案如下:

class Array
  def sorted?
    return 1 if sorted_generic?(:<=)
    return -1 if sorted_generic?(:>=)
    0
  end

  def sorted_generic?(comparison)
    return true if length < 2
    self[0].send(comparison, self[1]) &&
        self.drop(1).sorted_generic?(comparison)
  end
end

比较运算符只是 Ruby 中的方法,因此您可以:

1 <= 2 # is the same as
1.<=(2)

这意味着您可以 public_send 它们就像任何其他 public 方法一样:

1.public_send(:<=, 2)

虽然不是对您问题的直接回答,但我建议采用不同的方法。

class Array
  def sorted?(direction)
    sorted = self.sort
    case direction
    when :increasing
      self == sorted ? 1 : 0
    when :decreasing 
      self == sorted.reverse ? -1 : 0
    else
      # <raise exception>
    end
  end
end

[1,2,3,4].sorted?(:increasing) #=>  1
[4,3,2,1].sorted?(:increasing) #=>  0
[1,3,2,4].sorted?(:increasing) #=>  0
[1,1,1,1].sorted?(:increasing) #=>  1
[1,2,3,4].sorted?(:decreasing) #=>  0
[4,3,2,1].sorted?(:decreasing) #=> -1
[1,3,2,4].sorted?(:decreasing) #=>  0
[1,1,1,1].sorted?(:decreasing) #=> -1

另一种方法是使用Enumerable#each_cons

class Array
  def sorted?(op)
    each_cons(2).all? { |e,f| e.public_send(op, f) } ? (op == :<= ? 1 : -1) : 0
  end
end

[1,2,3,4].sorted?(:<=) #=>  1
[4,3,2,1].sorted?(:<=) #=>  0
[1,3,2,4].sorted?(:<=) #=>  0
[1,1,1,1].sorted?(:<=) #=>  1
[1,2,3,4].sorted?(:>=) #=>  0
[4,3,2,1].sorted?(:>=) #=> -1
[1,3,2,4].sorted?(:>=) #=>  0
[1,1,1,1].sorted?(:>=) #=> -1

正如@CarySwoveland 提到的: 您也可以只使用 Enumerable 方法 #each_cons#all?

不过,我会更进一步,以允许默认和自定义 sorted? 机制。

class Array 
   def sorted?(sym=:<=,&block)
     block ||= ->(a,b){ a.public_send(sym,b) } 
     each_cons(2).all?(&block)
   end
end

这允许您灵活地调用 sorted? 并使用预期的 increasing 顺序,传递类似于 reduceinject 的符号进行基本比较或传递一个块以确定 sorted 对您意味着什么。

这不会在过程中产生任何中介 Arrays,并且在第一次失败时会短路。

这不再限制您进行基本操作,例如

  array_of_hashes = [{n:9,a:1},{n:14,a:-3},{n:11, a:0}]
  array_of_hashes.sorted?
  #=> false
  array_of_hashes.sorted? do |a,b| 
    a.values.reduce(:+) <= b.values.reduce(:+)
  end
  #=> true 

此外,您可以添加一些快捷方式,例如

class Array 
   def sorted_increasing? 
     sorted?(:<=)
   end 
   def sorted_decreasing? 
     sorted?(:>=)
   end
end

这样

a = [1,2,3,4] 
a.sorted? == a.sorted_increasing?
#=> true