Рубиновые операторы с несколькими переменными - PullRequest
6 голосов
/ 01 февраля 2011

Ruby имеет довольно мощную конструкцию case..when..else для случаев, когда нужно сопоставить критерии с одной переменной. Каков «канонический» способ сопоставления критериев с несколькими переменными без простого вложения case операторов?

Обтекание нескольких переменных в массиве (например, [x, y]) и сопоставление с ним не эквивалентно, потому что Ruby не будет применять магический оператор case === к элементам массива; оператор применяется только к самому массиву.

Я собираюсь продолжить и ответить вики-сообществом ответом (побежденным) на этот вопрос.

Ответы [ 4 ]

4 голосов
/ 01 февраля 2011

Вам необходимо использовать if..elsif..else и убедиться, что переменные, с которыми вы хотите сопоставить, отображаются в правой части оператора === (что в сущности делает case).

Например, если вы хотите сопоставить x и y по некоторым критериям:

if (SomeType === x) && (1..10 === y)
  some_value
elsif (:some_symbol === x) && (11..20 === y)
  some_other_value
end
3 голосов
/ 03 мая 2012

Поскольку ключевое слово when в Ruby поддерживает список значений, разделенных запятыми, вы можете использовать оператор splat *.Это, конечно, предполагает, что вы имеете в виду набор дискретных значений, которые находятся в массиве или могут стать им.

Оператор splat преобразует список аргументов в массив, как это часто встречается в

def method_missing(method, *args, &block)

Менее известен тот факт, что он также выполняет обратную операцию - превращение массива в список аргументов.

Так что в этом случае вы можете сделать что-то вроде

passing_grades = ['b','c']
case grade
when 'a'
  puts 'great job!'
when *passing_grades
  puts 'you passed'
else
  puts 'you failed'
end
3 голосов
/ 01 февраля 2011

Это упрощенный способ добавления ===:

class Array
  def ===(other)
    return false if (other.size != self.size)

    other_dup = other.dup
    all? do |e|
      e === other_dup.shift
    end
  end
end

[
  ['foo', 3],
  %w[ foo bar ],
  %w[ one ],
  []
].each do |ary|

  ary_type = case ary
  when [String, Fixnum] then "[String, Fixnum]"
  when [String, String] then "[String, String]"
  when [String] then "[String]"
  else
    "no match"
  end

  puts ary_type

end

# >> [String, Fixnum]
# >> [String, String]
# >> [String]
# >> no match
2 голосов
/ 01 февраля 2011

Если этот шаблон достаточно распространен в вашем коде, чтобы оправдать экономическое выражение, вы можете сделать это самостоятельно:

class BiPartite
  attr_reader :x, :y

  def self.[](x, y)
    BiPartite.new(x, y)
  end

  def initialize(x, y)
    @x, @y = x, y
  end

  def ===(other)
    x === other.x && y === other.y
  end
end

....

case BiPartite[x, y]
when BiPartite[SomeType, 1..10]
  puts "some_value"
when BiPartite[:some_symbol, 11..20]
  puts "some_other_value"
end
...