What is catch and throw used for in Ruby?

前端 未结 3 1387
囚心锁ツ
囚心锁ツ 2020-12-25 12:19

In most other languages, the catch and throw statements do what the begin, rescue, and raise statements do in Ruby. I know the you can do this with these two statements:

相关标签:
3条回答
  • 2020-12-25 12:57

    You can use this to break out of nested loops.

    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
    

    If you had used a break statement above, it would have broken out of the inner loop. But if you want to break out of the nested loop, then this catch/throw would be really helpful. I have used it here to solve one of the Euler problems.

    0 讨论(0)
  • 2020-12-25 13:02

    When writing recursive algorithms that act on nested data structures using recursive functions, you can use throw similarly to how you would use a break or early return when writing iterative algorithms that act on flat data using for loops.

    For example, suppose that you have a list of positive integers and you want (for some reason) to write a function that will return true if either of the following conditions are met:

    • The sum of all elements in the list is greater than 100
    • Some element in the list if equal to 5

    Let's say also that you always want to perform this check in a single, short-circuiting pass over the list, rather than doing a reduce call to get the sum and a separate any? call to look for fives.

    You'd probably write some code a bit like this (indeed, you probably HAVE written code like this in some language at some point in your life):

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

    In most languages, there is no clean equivalent for breaking out of a recursive algorithm that uses a recursive function. In Ruby, though, there is! Suppose that, instead of having a list and wanting to check if its elements contain a five or sum to over 100, you have a tree and want to check if its leaves contain a five or sum to over 100, while short-circuiting and returning as soon as you know the answer.

    You can do this elegantly with throw/catch, like this:

    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
    

    The throw :passes_test here acts a bit like a break; it lets you jump out of your whole call stack below the outermost _test call. In other languages, you could do this either by abusing exceptions for this purpose or by using some return code to tell the recursive function to stop recursing, but this is more direct and simpler.

    0 讨论(0)
  • 2020-12-25 13:22

    I have been looking for a good example for a while, until I met Sinatra. IMHO, Sinatra exposes a very interesting example usage for catch.

    In Sinatra you can immediately terminate a request at any time using halt.

    halt
    

    You can also specify the status when halting...

    halt 410
    

    Or the body...

    halt 'this will be the body'
    

    Or both...

    halt 401, 'go away!'
    

    The halt method is implemented using throw.

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

    and caught by the invoke method.

    There are several uses of :halt in Sinatra. You can read the source code for more examples.

    0 讨论(0)
提交回复
热议问题