将比较运算符作为参数传递给 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
顺序,传递类似于 reduce
或 inject
的符号进行基本比较或传递一个块以确定 sorted 对您意味着什么。
这不会在过程中产生任何中介 Array
s,并且在第一次失败时会短路。
这不再限制您进行基本操作,例如
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
将比较运算符作为参数传递给 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
顺序,传递类似于 reduce
或 inject
的符号进行基本比较或传递一个块以确定 sorted 对您意味着什么。
这不会在过程中产生任何中介 Array
s,并且在第一次失败时会短路。
这不再限制您进行基本操作,例如
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