2011-02-01 1 views
6

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

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

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

+0

Насколько я знаю, Ruby не распространяется '' === каждому элементу рекурсивно ... http://ideone.com/yMAlZ – Nakilon

+0

@ Nakilon Я протестировал, и он, похоже, не применяет его рекурсивно. Например, '['hello', 3]' не будет соответствовать '[String, 3]'. – ClosureCowboy

+0

aaaa ... извините, моя вина. Забытая разница между '==' и '==='. – Nakilon

ответ

2

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

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 
+0

+1 Это определенно чище, чем мой второй ответ, но не модифицирует встроенные классы, несколько обескураженные? Это неподдельный вопрос; Я новичок в Ruby. – ClosureCowboy

+0

Кроме того, почему вы делаете копию 'other'? Будет ли 'shift' влиять на массив, переданный как параметр в противном случае? Полагаю, я обновлю свой пример, чтобы это отразить (я уже украл «Array.all?», О котором я не знал ... – ClosureCowboy

+1

Я думаю, что это безопасно модифицирует Array таким образом, поскольку Array не реализует '==='. Вы попадаете в потенциально опасные воды, когда переопределяете базовый класс таким образом, чтобы он нарушал ожидаемое поведение. –

3

Вам необходимо использовать 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 
+0

Yikes. Спасибо, редактор! – ClosureCowboy

2

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

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 
+0

+1 Есть ли не отвратительный способ сделать эту работу с произвольным количеством параметров? На самом деле, идеи появляются в моем сознании, и они не связаны с изменением класса «Array» ... – ClosureCowboy

3

Поскольку ключевое слово Ruby's when поддерживает список значений, разделенных запятыми, вы можете использовать оператор 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 
+1

После повторного чтения этого вопроса я могу неправильно прочитать исходный вопрос - я думал, что сравнение было между одной переменной и массивом возможных совпадающих значений. – RyanV