Hay muchas cosas que son buenas acerca de los bloques. El discurso del ascensor: Bloques nos dejó pasar alrededor de acciones de la misma manera que normalmente pasamos alrededor de datos.
El nivel más obvio es que le permiten abstraer las cosas en funciones que de otro modo no serían posibles.Por ejemplo, veamos un caso común donde se tiene una lista de las cosas y que desea filtrar para incluir sólo los elementos que coinciden con algún criterio:
int list[50] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
int evenNumbers[50] = {0};
int copyIndex = 0;
for (int i = 0; i < 50; i++) {
if (list[i] % 2 == 0) {
evenNumbers[copyIndex++] = list[i];
}
}
Así es como se escribe que en Ruby:
list = 1..50
listCopy = list.select {|n| n.even?}
Todo el trabajo común se mueve fuera de su código y en un método con un nombre significativo. No nos importa copiar el arreglo, examinar los índices y todo eso, solo queremos una lista filtrada. Y eso es lo que nos da select
. El bloque nos permite pasar nuestra lógica personalizada a este método estándar.
Pero los iteradores no son el único lugar donde este "hole in the middle pattern" es útil. Por ejemplo, si pasa un bloque al File.open
, abrirá el archivo, ejecutará el bloque con el archivo y luego cerrará el archivo por usted.
Otra cosa que los bloques nos dan es una forma realmente poderosa de devoluciones de llamadas. Por ejemplo, sin bloques, que podría tener que hacer algo como esto (en función de cómo funcionan realmente los diálogos en Objective-C cacao):
class Controller
def delete_button_clicked(item)
item.add_red_highlight
context = {:item => item}
dialog = Dialog.new("Are you sure you want to delete #{item}?")
dialog.ok_callback = :delete_OK
dialog.ok_receiver = self
dialog.cancel_callback = :cancel_delete
dialog.cancel_receiver = self
dialog.context = context
dialog.ask_for_confirmation
end
def delete_OK(sender)
delete(sender.context[:item])
sender.dismiss
end
def cancel_delete(sender)
sender.context[:item].remove_red_highlight
sender.dismiss
end
end
Yowza. Con bloques, podríamos hacer esto en su lugar (en base a un patrón común que se utiliza en muchas bibliotecas de Ruby):
class Controller
def delete_button_clicked(item)
item.add_red_highlight
Dialog.ask_for_confirmation("Are you sure you want to delete #{item}?") do |response|
response.ok { delete item }
response.cancel { item.remove_red_highlight }
end
end
end
Eso es en realidad dos niveles de bloques - el bloque do...end
y los dos bloques al estilo {}
. Pero se lee bastante natural, ¿no? Esto funciona porque un bloque captura el contexto en el que se creó, por lo que no es necesario pasar por self
y item
.
En cuanto a los Procs, solo son un contenedor de objetos para bloques. No mucho para ellos.
Busca más información sobre Closures, te ayudará a explicar cómo funcionan los bloques y procesos y para qué sirven. – Doon