¿Por qué Ruby usa el rendimiento?

Soy nuevo en Ruby. He usado una gran cantidad de C # y JavaScript que permiten funciones de orden superior y normalmente los uso a diario.

Sin embargo, Ruby me parece un poco extraño. each función podría verse como:

 def each @items.each do |item| yield(item) end end items.each { |item| puts item } 

Sin embargo, Ruby también tiene algo de soporte para funciones de orden superior. Lo anterior podría reescribirse a algo como:

 def each(proc) @items.each do |item| proc.call item end end items.each -> (item) { puts item } # Or... items.each lambda { |item| puts item } 

O incluso:

 def each(&proc) @items.each do |item| proc.call item end end # No difference in syntax. items.each { |item| puts item } 

Que está más a la par con la mayoría de los otros idiomas, y es sólo unos pocos caracteres más. En lugar de pasar explícitamente en un bloque, todo parece utilizar el yield .

yield sí mismo parece loco, mágico y misterioso. Después de todo, va al origen de la llamada y toma un bloque inmediatamente después de la llamada. Esto parece extraño y poco natural, y no conozco ningún paralelo de esta característica en otro idioma.

Entonces, ¿cuál es el trato con el yield ?

El rendimiento pasa los objetos al bloque de un método

[El rendimiento es] yendo al origen de la llamada y tomando un bloque inmediatamente después de la llamada.

Realmente no. yield pasa un argumento a un bloque; no “agarra un bloque” ni hace nada con él. En otras palabras, esto:

 def foo; yield self; end foo { |x| x.inspect } # => "main" 

Aquí, el yield no hace nada más que pasar un argumento al bloque que se pasa al método foo . Cada método de Ruby admite un bloque opcional, excepto cuando un bloque es realmente obligatorio, por lo que la única “magia” es que el lenguaje permite que se pase un bloque, incluso cuando no se declara explícitamente como parte de la firma del método.

Otros ejemplos

Para ver esta firma implícita en acción, considera esto:

 def foo; puts block_given?; end foo { |x| x.inspect } 

que imprimirá “verdadero” y devolverá nil , que es el valor de retorno esperado del método de puts .

Por supuesto, sin yield el bloque no hace nada en absoluto. Por ejemplo:

 def foo; end foo { |x| x.inspect } # => nil 

El rendimiento es syntax de azúcar.

Este ejemplo de rendimiento:

 def do_something_for_each(array) array.each do |el| yield(el) end end 

Es solo syntax de azúcar para:

 def do_something_for_each(array, &block) array.each do |el| block.call(el) end end 

Elige la syntax que te gusta y corre con ella.

Una de las ventajas del yield es que también le permite usar el next (como continue ) y break . En otros idiomas, para el next , es posible que tenga que usar return , y para break , que tenga que (ab) usar excepciones. Podría decirse que es mejor tener soporte integrado para este tipo de operaciones.

En la mayoría de los casos, ejecuta el bloque allí mismo en el método, utilizando el rendimiento .

El bloque pasa directamente al método, y el método puede volver a llamar al bloque con la palabra clave de rendimiento .

 def a_method(a, b) a + yield(a, b) end a_method(1, 2) {|x, y| (x + y) * 3 } # => 10 

Cuando vuelve a llamar al bloque, puede proporcionar valores para sus argumentos, al igual que lo hace cuando llama a un método. Además, como un método, un bloque devuelve el resultado de la última línea de código que evalúa.