您将如何重构此方法以将 ABC 大小减小到小于 15?


require 'active_support'
require 'active_support/core_ext'
def subtract_two_ranges(range_a, range_b)
return [range_a] unless range_a.overlaps?(range_b)
[
((range_a.begin..(range_b.begin - 1)) if range_a.begin < range_b.begin),
(((range_b.end + 1)..range_a.end)     if range_b.end   < range_a.end)
].compact
end

ABC大小通常表示您的方法太复杂,难以阅读,并且可能破坏了SRP。

  1. 它应该是范围的方法。
  2. 将数组中的表达式移动到其自己的私有方法。
  3. 使用优化来避免猴子修补基类。

试试这样。

require 'active_support'
require 'active_support/core_ext'
class Range
def -(other)
return [self] unless overlaps?(other)
[substract_lower_bound(other), substract_upper_bound(other)].compact
end
private
def substract_lower_bound(other)
first..(other.begin - 1) if first < other.first
end
def substract_upper_bound(other)
(other.last + 1)..last if other.last < last
end
end

我在下面介绍了两种方法。两者都是纯红宝石,并在任意有限范围内运行。1.第一个是效率更高,但更复杂。

高效但复杂

法典

def subtract_range(r1, r2)
return [r1] if r2.end   < r1.begin  || r2.begin > r1.end
return []   if r2.begin <= r1.begin && r2.end  >= r1.end
if r2.begin <= r1.begin
r2.end < r1.begin ? [r1] : [r2.end.succ..r1.end]
else # r1.begin < r2.begin <= r1.end
e = pred(r1.begin, r2.begin)
r2.end >= r1.end ? [(r1.begin)..e] :
[r1.begin..e, (r2.end.succ)..r1.end]
end
end
def pred(start_at, e)
(return e-1) if e.kind_of? Numeric
loop do
break start_at if start_at.succ == e
start_at = start_at.succ
end
end

范围的所有元素(无论它们的类如何)都有一个方法succ(例如,String#succ),但通常没有类似的前置方法。数字类是例外:n的前身是n-1。对于非数字元素,必须从某个已知的较小值开始计算前置值,并依次应用succ直到找到前置值。

例子

subtract_range 15..25, 20..30  #=> [15..19]
subtract_range 15..25, 10..20  #=> [21..25]
subtract_range 15..25, 17..23  #=> [15..16, 24..25]
subtract_range 15..25,  5..10  #=> [15..25]
subtract_range 15..25, 30..35  #=> [15..25]
subtract_range 15..25, 25..30  #=> [15..24]
subtract_range 15..25, 16..30  #=> [15..15]
subtract_range 15..25, 10..30  #=> []
subtract_range 'd'..'j', 'g'..'m'  #=> ["d".."f"]
subtract_range 'd'..'j', 'f'..'h'  #=> ["d".."e", "i".."j"]

效率较低,但更简单

法典

def subtract_range(r1, r2)
arr = r1.to_a - r2.to_a
return [] if arr.empty?
return [arr.first..arr.first] if arr.size == 1
arr.slice_when { |m,n| m.succ < n }.
map { |a| a.size == 1 ? a.first..a.first : a.first..a[-1] }
end

例子

此方法为上面给出的示例生成相同的返回值。

解释

假设(从最后一个示例)

r1 = 'd'..'j'
r2 = 'f'..'h'

然后

arr = r1.to_a - r2.to_a
#=> ["d", "e", "f", "g", "h", "i", "j"] - ["f", "g", "h"] 
#=> ["d", "e", "i", "j"]
arr.empty?
#=> false (so do not return [])
arr.size == 1
#=> false (so do not return [arr.first..arr.first])
enum = arr.slice_when { |m,n| m.succ < n }
#=> #<Enumerator: #<Enumerator::Generator:0x00000001d2c228>:each>

我们可以将enum转换为数组以查看将传递给map的元素

enum.to_a
#=> [["d", "e"], ["i", "j"]]

最后

enum.map { |a| a.size == 1 ? a.first..a.first : a.first..a[-1] }
#=> ["d".."e", "i".."j"]

12.0..6.0 - 4.0..8.0 #=> 2.0...4.0,不是问题(注意三个点)。但是,2.0..4.0- 1.0..3.0is a problem, as the resulting range,3.0.。4.0(不包括3.0)不能表示为Range实例。

相关内容

  • 没有找到相关文章

最新更新