pass for define argument ruby block

ruby - for - Bloques y rendimientos en rubí.



ruby pass block (10)

A veces uso "rendimiento" como este:

def add_to_http "http://#{yield}" end puts add_to_http { "www.example.com" } puts add_to_http { "www.victim.com"}

Estoy tratando de entender los bloques y el yield y cómo funcionan en Ruby.

¿Cómo se usa el yield ? Muchas de las aplicaciones de Rails que he examinado usan el yield de una manera extraña.

¿Puede alguien explicarme o enseñarme dónde ir para entenderlos?


En Ruby, los métodos pueden verificar si fueron llamados de tal manera que se proporcionó un bloque además de los argumentos normales. Típicamente esto se hace usando el block_given? método, pero también puede referirse al bloque como un proceso explícito prefijando un signo ( & ) antes del nombre del argumento final.

Si se invoca un método con un bloque, entonces el método puede yield control (llamar al bloque) con algunos argumentos, si es necesario. Considere este método de ejemplo que demuestra:

def foo(x) puts "OK: called as foo(#{x.inspect})" yield("A gift from foo!") if block_given? end foo(10) # OK: called as foo(10) foo(123) {|y| puts "BLOCK: #{y} How nice =)"} # OK: called as foo(123) # BLOCK: A gift from foo! How nice =)

O, usando la sintaxis de argumento de bloque especial:

def bar(x, &block) puts "OK: called as bar(#{x.inspect})" block.call("A gift from bar!") if block end bar(10) # OK: called as bar(10) bar(123) {|y| puts "BLOCK: #{y} How nice =)"} # OK: called as bar(123) # BLOCK: A gift from bar! How nice =)


En Ruby, un bloque es básicamente una parte del código que se puede pasar y ejecutar por cualquier método. Los bloques siempre se usan con métodos, que generalmente les suministran datos (como argumentos).

Los bloques se utilizan ampliamente en gemas de Ruby (incluidos los rieles) y en código de Ruby bien escrito. No son objetos, por lo tanto no pueden ser asignados a variables.

Sintaxis basica

Un bloque es una pieza de código entre {} o do..end. Por convención, la sintaxis de corchetes se debe usar para bloques de una sola línea y la sintaxis de do..end se debe usar para bloques de múltiples líneas.

{ # This is a single line block } do # This is a multi-line block end

Cualquier método puede recibir un bloque como argumento implícito. Un bloque es ejecutado por la declaración de rendimiento dentro de un método. La sintaxis básica es:

def meditate print "Today we will practice zazen" yield # This indicates the method is expecting a block end # We are passing a block as an argument to the meditate method meditate { print " for 40 minutes." } Output: Today we will practice zazen for 40 minutes.

Cuando se alcanza la declaración de rendimiento, el método de meditación cede el control al bloque, se ejecuta el código dentro del bloque y el control se devuelve al método, que reanuda la ejecución inmediatamente después de la declaración de rendimiento.

Cuando un método contiene una declaración de rendimiento, espera recibir un bloqueo en el momento de la llamada. Si no se proporciona un bloque, se lanzará una excepción una vez que se alcance la declaración de rendimiento. Podemos hacer que el bloque sea opcional y evitar que se genere una excepción:

def meditate puts "Today we will practice zazen." yield if block_given? end meditate Output: Today we will practice zazen.

No es posible pasar varios bloques a un método. Cada método puede recibir solo un bloque.

Ver más en: http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html


Es muy posible que alguien proporcione una respuesta verdaderamente detallada aquí, pero siempre he encontrado que esta publicación de Robert Sosinski es una gran explicación de las sutilezas entre los bloques, procs y lambdas.

Debo añadir que creo que la publicación a la que me estoy vinculando es específica de ruby ​​1.8. Algunas cosas han cambiado en ruby ​​1.9, como las variables de bloque que son locales al bloque. En 1.8, obtendrías algo como lo siguiente:

>> a = "Hello" => "Hello" >> 1.times { |a| a = "Goodbye" } => 1 >> a => "Goodbye"

Mientras que 1.9 te daría:

>> a = "Hello" => "Hello" >> 1.times { |a| a = "Goodbye" } => 1 >> a => "Hello"

No tengo 1.9 en esta máquina, por lo que lo anterior puede tener un error.


Hay dos puntos que quiero hacer sobre el rendimiento aquí. Primero, aunque muchas respuestas aquí hablan de diferentes formas de pasar un bloque a un método que usa el rendimiento, también hablemos del flujo de control. Esto es especialmente relevante ya que puedes producir MÚLTIPLES veces para un bloque. Echemos un vistazo a un ejemplo:

class Fruit attr_accessor :kinds def initialize @kinds = %w(orange apple pear banana) end def each puts ''inside each'' 3.times { yield (@kinds.tap {|kinds| puts "selecting from #{kinds}"} ).sample } end end f = Fruit.new f.each do |kind| puts ''inside block'' end => inside each => selecting from ["orange", "apple", "pear", "banana"] => inside block => selecting from ["orange", "apple", "pear", "banana"] => inside block => selecting from ["orange", "apple", "pear", "banana"] => inside block

Cuando se invoca cada método, se ejecuta línea por línea. Ahora, cuando lleguemos al bloque de 3 veces, este bloque se invocará 3 veces. Cada vez que invoca el rendimiento. Ese rendimiento está vinculado al bloque asociado con el método que llamó a cada método. Es importante notar que cada vez que se invoca el rendimiento, devuelve el control al bloque de cada método en el código del cliente. Una vez que el bloque termina de ejecutarse, vuelve al bloque 3.times. Y esto sucede 3 veces. De modo que ese bloque en el código del cliente se invoca en 3 ocasiones diferentes, ya que el rendimiento se llama explícitamente 3 veces por separado.

Mi segundo punto es sobre la enumeración y el rendimiento. Enum_for crea una instancia de la clase Enumerator y este objeto Enumerator también responde al rendimiento.

class Fruit def initialize @kinds = %w(orange apple) end def kinds yield @kinds.shift yield @kinds.shift end end f = Fruit.new enum = f.to_enum(:kinds) enum.next => "orange" enum.next => "apple"

Por lo tanto, tenga en cuenta que cada vez que invocamos tipos con el iterador externo, invocará el rendimiento solo una vez. La próxima vez que lo llamemos, invocará el siguiente rendimiento y así sucesivamente.

Hay un dato interesante con respecto a enum_for. La documentación en línea establece lo siguiente:

enum_for(method = :each, *args) → enum Creates a new Enumerator which will enumerate by calling method on obj, passing args if any. str = "xyz" enum = str.enum_for(:each_byte) enum.each { |b| puts b } # => 120 # => 121 # => 122

Si no especifica un símbolo como argumento para enum_for, ruby ​​conectará el enumerador a cada método del receptor. Algunas clases no tienen un método de cada uno, como la clase String.

str = "I like fruit" enum = str.to_enum enum.next => NoMethodError: undefined method `each'' for "I like fruit":String

Por lo tanto, en el caso de algunos objetos invocados con enum_for, debe ser explícito en cuanto a cuál será su método de enumeración.


He encontrado este artículo para ser muy útil. En particular, el siguiente ejemplo:

#!/usr/bin/ruby def test yield 5 puts "You are in the method test" yield 100 end test {|i| puts "You are in the block #{i}"} test do |i| puts "You are in the block #{i}" end

Lo que debería dar el siguiente resultado:

You are in the block 5 You are in the method test You are in the block 100 You are in the block 5 You are in the method test You are in the block 100

Así que, esencialmente, cada vez que se realiza una llamada para yield ruby ejecutará el código en el bloque do o dentro de {} . Si se proporciona un parámetro para el yield , se proporcionará como un parámetro para el bloque do .

Para mí, esta fue la primera vez que entendí realmente lo que do bloques do . Es básicamente una forma para que la función dé acceso a las estructuras de datos internas, ya sea para la iteración o para la configuración de la función.

Así que cuando en los carriles escribes lo siguiente:

respond_to do |format| format.html { render template: "my/view", layout: ''my_layout'' } end

Esto ejecutará la función respond_to que produce el bloque do con el parámetro de format (interno). Luego llama a la función .html en esta variable interna que a su vez produce el bloque de código para ejecutar el comando de render . Tenga en cuenta que .html solo producirá si es el formato de archivo solicitado. (tecnicidad: estas funciones realmente usan block.call no se puede ver desde la source pero la funcionalidad es esencialmente la misma, vea esta pregunta para una discusión). Esto proporciona una manera para que la función realice una inicialización y luego reciba información de El código de llamada y luego continuar el procesamiento si es necesario.

O dicho de otra manera, es similar a una función que toma una función anónima como argumento y luego la llama en javascript.


Los rendimientos, en pocas palabras, permiten que el método que creas tome y llame a los bloques. La palabra clave de rendimiento específicamente es el lugar donde se realizarán las "cosas" en el bloque.


Quería agregar una especie de por qué harías las cosas de esa manera a las ya excelentes respuestas.

No tengo idea de de qué idioma proviene, pero suponiendo que es un lenguaje estático, este tipo de cosas le resultarán familiares. Así es como lees un archivo en java.

public class FileInput { public static void main(String[] args) { File file = new File("C://MyFile.txt"); FileInputStream fis = null; BufferedInputStream bis = null; DataInputStream dis = null; try { fis = new FileInputStream(file); // Here BufferedInputStream is added for fast reading. bis = new BufferedInputStream(fis); dis = new DataInputStream(bis); // dis.available() returns 0 if the file does not have more lines. while (dis.available() != 0) { // this statement reads the line from the file and print it to // the console. System.out.println(dis.readLine()); } // dispose all the resources after using them. fis.close(); bis.close(); dis.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }

Ignorando todo el tema del encadenamiento de la corriente, la idea es esta

  1. Inicializar el recurso que necesita ser limpiado
  2. usar recurso
  3. asegúrate de limpiarlo

Así es como lo haces en rubí.

File.open("readfile.rb", "r") do |infile| while (line = infile.gets) puts "#{counter}: #{line}" counter = counter + 1 end end

Muy diferente. Rompiendo éste

  1. decirle a la clase de archivo cómo inicializar el recurso
  2. decirle a la clase de archivo qué hacer con él
  3. ríete de los chicos de java que todavía están escribiendo ;-)

Aquí, en lugar de manejar los pasos uno y dos, básicamente lo delegas en otra clase. Como puede ver, eso reduce drásticamente la cantidad de código que tiene que escribir, lo que facilita la lectura y reduce las posibilidades de que se produzcan pérdidas de memoria o bloqueos de archivos.

Ahora, no es que no puedas hacer algo similar en Java, de hecho, la gente lo ha estado haciendo durante décadas. Se llama el patrón de Strategy . La diferencia es que sin bloques, para algo simple como el ejemplo de archivo, la estrategia se vuelve excesiva debido a la cantidad de clases y métodos que necesita escribir. Con los bloques, es una forma tan simple y elegante de hacerlo, que no tiene ningún sentido NO estructurar su código de esa manera.

Esta no es la única forma en que se usan los bloques, pero las otras (como el patrón de Builder, que se puede ver en el formulario de api en los rieles) son lo suficientemente similares como para que sea obvio lo que está pasando una vez que encabeza la cabeza. Cuando ve bloques, generalmente es seguro asumir que la llamada al método es lo que quiere hacer, y el bloque describe cómo quiere hacerlo.


Sí, es un poco desconcertante al principio.

En Ruby, los métodos pueden recibir un bloque de código para realizar segmentos de código arbitrarios.

Cuando un método espera un bloque, lo invoca llamando a la función de yield .

Esto es muy útil, por ejemplo, para iterar sobre una lista o para proporcionar un algoritmo personalizado.

Tomemos el siguiente ejemplo:

do_with_name una clase de Person inicializada con un nombre y proporcionaré un método do_with_name que, cuando se invoca, solo pasará el atributo de name al bloque recibido.

class Person def initialize( name ) @name = name end def do_with_name yield( @name ) end end

Esto nos permitiría llamar a ese método y pasar un bloque de código arbitrario.

Por ejemplo, para imprimir el nombre que haríamos:

person = Person.new("Oscar") #invoking the method passing a block person.do_with_name do |name| puts "Hey, his name is #{name}" end

Imprimiría

Hey, his name is Oscar

Observe que el bloque recibe, como parámetro, una variable llamada name (NB puede llamar a esta variable como quiera, pero tiene sentido llamarlo name ). Cuando el código invoca el yield , llena este parámetro con el valor de @name .

yield( @name )

Podríamos proporcionar otro bloque para realizar una acción diferente. Por ejemplo, invierta el nombre:

#variable to hold the name reversed reversed_name = "" #invoke the method passing a different block person.do_with_name do |name| reversed_name = name.reverse end puts reversed_name => "racsO"

Usamos exactamente el mismo método ( do_with_name ), es solo un bloque diferente.

Este ejemplo es trivial. Usos más interesantes son filtrar todos los elementos en una matriz:

days = ["monday", "tuesday", "wednesday", "thursday", "friday"] # select those which start with ''t'' days.select do | item | item.match /^t/ end => ["tuesday", "thursday"]

O, también podemos proporcionar un algoritmo de orden personalizado, por ejemplo, basado en el tamaño de la cadena:

days.sort do |x,y| x.size <=> y.size end => ["monday", "friday", "tuesday", "thursday", "wednesday"]

Espero que esto te ayude a entenderlo mejor.

Por cierto, si el bloque es opcional, deberías llamarlo así:

yield(value) if block_given?

Si no es opcional, solo invocarlo.


El rendimiento se puede utilizar como bloque sin nombre para devolver un valor en el método. Considere el siguiente código:

Def Up(anarg) yield(anarg) end

Puede crear un método "Up" al cual se le asigna un argumento. Ahora puede asignar este argumento al rendimiento que llamará y ejecutará un bloque asociado. Puede asignar el bloque después de la lista de parámetros.

Up("Here is a string"){|x| x.reverse!; puts(x)}

Cuando las llamadas del método Up producen, con un argumento, se pasa a la variable de bloque para procesar la solicitud.