2010-09-15 5 views
30

En la mayoría de los otros lenguajes, las declaraciones catch y throw hacen lo que hacen las declaraciones begin, rescue y raise en Ruby. Sé que el que puede hacer esto con estas dos afirmaciones:¿Para qué se utilizan la captura y el lanzamiento en Ruby?

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

y

if some_condition 
    throw :done 
end 

Pero lo que es útil para este? ¿Alguien puede por favor darme un ejemplo de para qué se usan las declaraciones catch and throw en Ruby?

+1

Posible duplicado de: http://stackoverflow.com/questions/51021/what-is-the-differance-between-raising-exceptions-vs-throwing-exceptions-in-ruby – Shadwell

Respuesta

32

Puede usar esto para salir de los bucles anidados.

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 

Si ha utilizado una declaración de interrupción anterior, se habría salido del ciclo interno. Pero si quieres salir del ciclo anidado, entonces este catch/throw sería realmente útil. Lo he usado here para resolver uno de los problemas de Euler.

21

He estado buscando un buen ejemplo por un tiempo, hasta que conocí a Sinatra. En mi humilde opinión, Sinatra expone un uso de ejemplo muy interesante para catch.

En Sinatra puede immediately terminate a request en cualquier momento usando halt.

halt 

También puede especificar el estado cuando está deteniendo ...

halt 410 

O el cuerpo ...

halt 'this will be the body' 

O las dos cosas ...

halt 401, 'go away!' 

El método de detención es implemented using throw.

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

y caught by el método invoke.

Hay varios usos de :halt en Sinatra. Puedes leer el código fuente para más ejemplos.

+1

preguntando, ¿cómo es esto mejor? que usar excepciones? – jsz

+2

Lanzar es una forma más elegante de usar un sistema de excepción como flujo de control. –

+2

@jsz para uno, es mucho más rápido: el marco de pila no tiene que transportarse a lo largo del "símbolo lanzado", y no se crea ningún objeto. Control de flujo no lineal ligero. – mezis

1

Al escribir algoritmos recursivos que actúan sobre las estructuras de datos anidadas utilizando las funciones recursivas, puede utilizar throw de manera similar a como se puede utilizar un break o principios return al escribir algoritmos iterativos que actúan sobre los datos planas usando for bucles.

Por ejemplo, suponga que tiene una lista de números enteros positivos y que desea (por alguna razón) para escribir una función que devolverá verdadero si se cumple alguna de las siguientes condiciones:

  • La suma de todos los elementos de la lista es mayor que 100
  • Algunos elementos de la lista si es igual a 5

Digamos también que siempre se quiere realizar esta comprobación en una sola pasada circuito a corto, sobre la lista, en lugar de hacer un reduce c todos para obtener la suma y una llamada por separado any? para buscar cinco.

lo que probablemente escribir algo de código un poco como esto (de hecho, es probable que tenga código escrito como este en algún lenguaje en algún momento de su vida):

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

En la mayoría de los idiomas, no hay equivalente limpio para romper un algoritmo recursivo que usa una función recursiva. ¡En Ruby, sin embargo, hay! Suponga que, en lugar de tener una lista y querer comprobar si sus elementos contienen un cinco o una suma de más de 100, tiene un árbol y quiere comprobar si su contiene contiene un cinco o suma de más de 100, mientras que corto -circuitar y regresar tan pronto como sepa la respuesta.

Usted puede hacer esto con elegancia con throw/catch, así:

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 

El throw :passes_test aquí actúa un poco como un break; te permite saltar de toda la pila de llamadas debajo de la llamada más externa _test. En otros idiomas, puede hacer esto al abusar de las excepciones para este fin o al usar un código de retorno para indicar a la función recursiva que detenga el recursivo, pero esto es más directo y más simple.

Cuestiones relacionadas