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

时间:2018-05-17 22:43:34

标签: ruby tail-recursion first-class

将比较运算符作为参数传递给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

3 个答案:

答案 0 :(得分:4)

比较运算符只是Ruby中的方法,所以你可以这样做:

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

这意味着您可以像其他任何公共方法一样public_send

1.public_send(:<=, 2)

答案 1 :(得分:3)

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

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

答案 2 :(得分: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的符号进行基本比较或传递一个块确定排序对你意味着什么。

这不会在进程中创建任何中间Array,也不会在第一次失败时产生短路。

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

  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