Что такое ловить и бросать в Ruby? - PullRequest
31 голосов
/ 15 сентября 2010

В большинстве других языков операторы catch и throw делают то же, что и операторы begin, rescue и throw в Ruby. Я знаю, что вы можете сделать это с этими двумя утверждениями:

catch :done do
  puts "I'm done."
end

и

if some_condition
  throw :done
end

Но для чего это нужно? Может кто-нибудь дать мне пример того, для чего в Ruby используются операторы catch и throw?

Ответы [ 3 ]

34 голосов
/ 15 сентября 2010

Вы можете использовать это, чтобы вырваться из вложенных циклов.

INFINITY = 1.0 / 0.0
catch (:done) do
  1.upto(INFINITY) do |i|
    1.upto(INFINITY) do |j|
      if some_condition
        throw :done
      end
    end
  end
end

Если бы вы использовали оператор break выше, он бы вышел из внутреннего цикла. Но если вы хотите выйти из вложенного цикла, то этот улов / бросок был бы очень полезен. Я использовал это здесь , чтобы решить одну из проблем Эйлера.

21 голосов
/ 15 сентября 2010

Я долго искал хороший пример, пока не встретил Синатру. ИМХО, Синатра выставляет очень интересный пример использования для catch.

В Sinatra вы можете немедленно прекратить запрос в любое время, используя halt.

halt

Вы также можете указать статус при остановке ...

halt 410

Или тело ...

halt 'this will be the body'

Или оба ...

halt 401, 'go away!'

Метод остановки реализован с использованием throw .

def halt(*response)
  response = response.first if response.length == 1
  throw :halt, response
end

и , пойманные методом invoke.

Есть несколько вариантов использования :halt в Синатре. Вы можете прочитать исходный код для большего количества примеров.

1 голос
/ 03 декабря 2014

При написании рекурсивных алгоритмов, которые воздействуют на вложенные структуры данных с использованием рекурсивных функций, вы можете использовать throw аналогично тому, как вы использовали бы break или ранний return при написании итерационных алгоритмов, которые воздействуют на плоские данные с использованием for петли.

Например, предположим, что у вас есть список натуральных чисел, и вы хотите (по какой-то причине) написать функцию, которая будет возвращать true, если выполняется любое из следующих условий:

  • Сумма всех элементов в списке больше 100
  • Некоторый элемент в списке, если он равен 5

Скажем также, что вы всегда хотите выполнить эту проверку за один короткий проход по списку, вместо того, чтобы делать вызов reduce для получения суммы и отдельный вызов any? для поиска пятерок.

Вы, вероятно, написали бы некоторый код, похожий на этот (действительно, вы когда-нибудь написали код, подобный этому, на каком-то языке в какой-то момент вашей жизни):

def test(list)
  sum = 0
  for i in list
    sum += i
    if i == 5 || sum > 100
      return true
    end
  end
  return false
end

В большинстве языков не существует чистого эквивалента для взлома рекурсивного алгоритма, который использует рекурсивную функцию. В Ruby, правда, есть! Предположим, что вместо того, чтобы иметь список и хотеть проверить, содержат ли его элементы пять или сумму более 100, у вас есть дерево и вы хотите проверить, содержит ли его листьев пять или сумма более 100 при коротком замыкании и возврате, как только вы узнаете ответ.

Вы можете сделать это элегантно с помощью throw / catch, например:

def _test_recurse(sum_so_far, node)
  if node.is_a? InternalNode
    for child_node in node.children
      sum_so_far = _test_recurse(sum_so_far, child_node)
    end
    return sum_so_far
  else # node.is_a? Leaf
    sum_so_far += node.value
    if node.value == 5
      throw :passes_test
    elsif sum_so_far > 100
      throw :passes_test
    else
      return sum_so_far
    end
  end
end

def test(tree)            
  catch (:passes_test) do
    _test_recurse(0, tree)
    return false
  end
  return true
end

throw :passes_test здесь действует немного как break; это позволяет вам выпрыгнуть из всего стека вызовов ниже самого внешнего _test вызова. В других языках вы могли бы сделать это либо путем использования исключений для этой цели, либо с помощью некоторого кода возврата, чтобы сообщить рекурсивной функции о прекращении рекурсии, но это более просто и просто.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...