Как бы вы изменили этот метод, чтобы уменьшить размер ABC до 15? - PullRequest
0 голосов
/ 05 мая 2018
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

Ответы [ 2 ]

0 голосов
/ 05 мая 2018

Я представил два метода ниже. Оба являются чисто Ruby и работают на произвольных конечных диапазонах. 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"]

1 2.0..6.0 - 4.0..8.0 #=> 2.0...4.0, не проблема (обратите внимание на три точки). Однако 2.0..4.0 - 1.0..3.0 is a problem, as the resulting range, 3.0 .. 4.0, исключая 3.0, нельзя представить как экземпляр Range.

0 голосов
/ 05 мая 2018

Размер ABC обычно указывает на то, что ваш метод слишком сложен, труден для чтения и, вероятно, нарушает SRP.

  1. Это должен быть метод Range.
  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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...