Передача оператора сравнения в качестве аргумента методу в Ruby - PullRequest
0 голосов
/ 18 мая 2018

Как лучше всего передать оператор сравнения в качестве аргумента методу в 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? (сравнение_operator) вместо sorted_increasing?и sorted_decreasing?.

Upd: Спасибо за ваши ответы, мое решение выглядит следующим образом:

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 голосов
/ 18 мая 2018

Хотя это и не прямой ответ на ваш вопрос, я предлагаю другой подход.

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
0 голосов
/ 18 мая 2018

Как упомянул @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 для базовых сравнений, или передавать блок, чтобы определить, что отсортированозначит для вас.

Это не создает никаких промежуточных 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
0 голосов
/ 18 мая 2018

Операторы сравнения - это просто методы в Ruby, поэтому вы можете сделать:

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

, что означает, что вы можете public_send использовать их так же, как любой другой открытый метод:

1.public_send(:<=, 2)
...