2011-02-06 26 views
33

¿Cuál es la mejor explicación para los bloques de Ruby que puedes compartir?¿La mejor explicación de los bloques Ruby?

¿Código de uso y escritura que puede tomar un bloque?

+1

¿Está buscando una introducción al concepto de bloques, o una referencia exhaustiva sobre ellos? – Phrogz

+16

¿O simplemente está tratando de obtener reputación repitiendo preguntas que no necesita las respuestas, no tiene la intención de aceptar, y no tiene la intención ni siquiera de participar en la discusión? Veremos si respondes. – Phrogz

+0

Este es un hilo útil: http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ – Lucio

Respuesta

6

El libro "Programming Ruby" tiene un gran explanation of blocks and using them.

En 1.9+, la lista de parámetros en un bloque pasado se hicieron más sofisticados, lo que permite a las variables locales pueden definir:

do |a,b;c,d| 
    some_stuff 
end 

;c,d declarar dos nuevas variables locales dentro del bloque, que no reciben los valores de la declaración de llamada-rutina yield. Ruby 1.9+ garantiza que, si las variables existieron fuera del bloque, no serán pisoteadas por las mismas variables dentro del bloque. Este es un nuevo comportamiento; 1.8 pisaría ellos.

def blah 
    yield 1,2,3,4 
end 

c = 'foo' 
d = 'bar' 

blah { |a, *b; c,d| 
    c = 'hello' 
    d = 'world' 
    puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}", "d: #{d}" 
} 

puts c, d 
# >> a: 1 
# >> b: 2,3,4 
# >> c: hello 
# >> d: world 
# >> foo 
# >> bar 

También está el "splat" operador *, que trabaja en la lista de parámetros:

do |a,*b| 
    some_stuff 
end 

asignaría el primero de varios valores a, y todo lo demás sería capturado "a" en "b", que se trataría como una matriz. El * podría estar en la variable a:

do |*a,b| 
    some_stuff 
end 

capturaría todos pasaron en las variables, excepto la última, que se pasó a b. Y, de manera similar a los dos anteriores:

do |a,*b,c| 
    some_stuff 
end 

asignaría el primer valor a a, el último valor de c y todos los valores intermedios/alguna a b.

Creo que es bastante poderoso y resbaladizo.

Por ejemplo:

def blah 
    yield 1,2,3,4 
end 

blah { |a, *b| puts "a: #{a}", "b: #{b.join(',')}" } 
# >> a: 1 
# >> b: 2,3,4 

blah { |*a, b| puts "a: #{a.join(',')}", "b: #{b}" } 
# >> a: 1,2,3 
# >> b: 4 

blah { |a, *b, c| puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}" } 
# >> a: 1 
# >> b: 2,3 
# >> c: 4 
19

De Why's (poignant) guide to ruby:

Cualquier código rodeado por llaves es un bloque.

2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } es un ejemplo.

Con bloques, puede agrupar un conjunto de instrucciones para que puedan pasar alrededor de su programa. Las llaves dan la apariencia de pinzas de cangrejo que han robado el código y lo mantienen unido. Cuando vea estas dos pinzas, recuerde que el código interno ha sido presionado en una sola unidad.Es como uno de esas pequeñas cajas de Hello Kitty que venden en el centro comercial que está rellena con diminutos lápices y papel microscópica, todos embutida en un caja transparente brillante que puede ser ocultado en su palma para encubiertas estacionarios operaciones. Excepto que los bloques no requieren tanto entrecerrar los ojos. Las llaves rizadas también se pueden cambiar por palabras do y end, lo que es bueno si su bloque es más largo que una línea.

loop do 
    print "Much better."  
    print "Ah. More space!" 
    print "My back was killin' me in those crab pincers." 
end 

argumentos de bloque son un conjunto de variables rodeadas de tubo caracteres y separados por comas .

|x|, |x,y|, and |up, down, all_around| are examples. 

argumentos de bloque se utilizan al comienzo de un bloque.

{ |x,y| x + y } 

En el ejemplo anterior, | x, y | son los argumentos. Después de los argumentos, nosotros tenemos un poco de código. La expresión x + y agrega los dos argumentos juntos. I me gusta pensar que los caracteres de tubería representan un túnel. Le dan a la apariencia de una rampa que las variables se están deslizando hacia abajo. (Una x va águila desplegada, mientras que perfectamente n cruza sus piernas.) Este conducto actúa como un pasadizo entre los bloques y el mundo a su alrededor. Las variables son pasadas a través de este conducto (o túnel) en el bloque.

+16

"Cualquier código rodeado de llaves es un bloque" a menos que es un ** hash **. – Meltemi

+1

No explica qué devuelven estos ejemplos. No lo entiendo –

+0

¡Por favor se mi tutor! Gracias por explicarlo de una manera tan directa y clara. – Benjamints

3

Los bloques son una forma de agrupar código en Ruby. Hay dos formas de escribir bloques. Uno está usando la instrucción do..end y el otro está rodeando el código con llaves: {}. Los bloques se consideran objetos en el lenguaje de programación Ruby y, de forma predeterminada, todas las funciones aceptan un argumento de bloque implícito.

Éstos son dos ejemplos de bloques que hacen lo mismo:

 
2.times { puts 'hi' } 
2.times do 
    puts 'hi' 
end 

bloques pueden recibir listas de argumentos separados por comas dentro de las barras verticales ||. Por ejemplo:

 
[1,2].map{ |n| n+2 } # [3, 4] 

bloques (en rubí 1.9.2) puede tener explícitamente variables locales:

 
x = 'hello' 
2.times do |;x| 
    x = 'world' 
    puts x 
end 

=> world 
=> world 

Las variables locales pueden ser combinados con parámetros:

 
[1,2].map{ |n;x| n+2 } 

Todas las funciones pueden recibir un argumento de bloque predeterminado:

 
def twice 
    yield 
    yield 
end 

twice { puts 'hello' } 
=> hello 
=> hello 

¿Cuál es la diferencia entre do..end y {} bloques? Por convención {} los bloques están en una sola línea y ...los bloques finales abarcan varias líneas, ya que cada uno de ellos es más fácil de leer de esta manera. La principal diferencia tiene que ver con preferencia sin embargo:

 
array = [1,2] 

puts array.map{ |n| n*10 } # puts (array.map{ |n| n*10 }) 
=> 10 
=> 20 

puts array.map do |n| n*10 end # (puts array.map) do |n| n*10 end 
=> <Enumerator:0x00000100862670> 
2

Los bloques son literales ligeros para los procedimientos de primera clase anónimos con algunas limitaciones molestas. Funcionan de la misma manera en Ruby a medida que trabajan en casi todos los otros lenguajes de programación, módulo de las limitaciones antes mencionadas, que son:

  • bloques sólo pueden aparecer en las listas de argumentos
  • como máximo un bloque puede aparecer
      en una lista de argumentos (y debe ser el último argumento)
  • +0

    Buena respuesta, pero la relación con los objetos Proc parece esencial, ¿no? – maerics

    +0

    @maerics ¿Esencial para un recurso exhaustivo en Blocks? Sí. Esencial para una explicación de los bloques (que interpreto como una introducción a ellos para el novato)? Definitivamente no, IMO. – Phrogz

    +0

    Gracias. La tuya es la única respuesta que me ayudó a entender por qué '{puts" hello "}' no funciona. No permitido en absoluto? Eso es raro. –

    27

    ofrezco mi propia explicación de this answer, ligeramente modificado:

    "Bloques" en Ruby no son los mismos que los términos generales de programación " bloque de código "o r "bloque de código".

    Imagina por un momento que la siguiente (no válido) código Ruby efectivamente trabajadas:

    def add10(n) 
        puts "#{n} + 10 = #{n+10}" 
    end 
    
    def do_something_with_digits(method) 
        1.upto(9) do |i| 
        method(i) 
        end 
    end 
    
    do_something_with_digits(add10) 
    #=> "1 + 10 = 11" 
    #=> "2 + 10 = 12" 
    ... 
    #=> "9 + 10 = 19" 
    

    Mientras que el código no es válido, su intención de pasar algo de código a un método y tener ese método ejecuta el código-es posible en Ruby en una variedad de formas. Una de esas formas es "Bloques".

    Un bloque en Ruby es muy, muy parecido a un método: puede tomar algunos argumentos y ejecutar código para esos. Cada vez que vea foo{ |x,y,z| ... } o foo do |x,y,z| ... end, esos son bloques que toman tres parámetros y ejecutan el ... en ellos. (incluso puede ver que el método anterior upto se está pasando un bloque.)

    Debido bloques son una parte especial de la sintaxis de Ruby, se permite que todos los métodos para pasar de un bloque. Si el método usa o no, el bloque depende del método. Por ejemplo:

    def say_hi(name) 
        puts "Hi, #{name}!" 
    end 
    
    say_hi("Mom") do 
        puts "YOU SUCK!" 
    end 
    #=> Hi, Mom! 
    

    El método anterior se pasa a un bloque que está listo para emitir un insulto, pero dado que el método nunca llama el bloque, sólo se imprime el mensaje. Así es como llamamos a la cuadra de un método:

    def say_hi(name) 
        puts "Hi, #{name}!" 
        if block_given? 
        yield(name) 
        end 
    end 
    
    say_hi("Mridang") do |str| 
        puts "Your name has #{str.length} letters." 
    end 
    #=> Hi, Mridang! 
    #=> Your name has 7 letters. 
    

    Utilizamos block_given? para ver si es o no un bloque se pasa a lo largo o no. En este caso, pasamos una discusión al bloque; depende de su método decidir qué pasar al bloque. Por ejemplo:

    def say_hi(name) 
        puts "Hi, #{name}!" 
        yield(name, name.reverse) if block_given? 
    end 
    
    say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" } 
    #=> Hi, Mridang! 
    #=> Is your name Mridang or gnadirM? 
    

    Es sólo una convención (y una buena, y uno desea apoyar) para algunas clases pasen a la instancia recién creada al bloque.

    Esta no es una respuesta exhaustiva, ya que no cubre la captura de bloques como argumentos, cómo manejan arity, no-splatting en parámetros de bloque, etc. pero tiene la intención de servir como una introducción de Blocks-Are-Lambdas.

    26

    Los bloques de Ruby son una forma de crear Proc objects que representan el código que puede ser utilizado por otro código.Los objetos Proc son instrucciones entre llaves {} (o do...end frases para bloques de líneas múltiples, que tienen menor prioridad que las llaves) que opcionalmente pueden tomar argumentos y valores devueltos (por ejemplo, {|x,y| x+y}). Procs son first-class objects y se pueden construir de forma explícita o alcanzan implícitamente como método de pseudo-argumentos:

    1. Construcción como un objeto Proc (o usando el lambda palabra clave):

      add1 = Proc.new {|x| x+1} # Returns its argument plus one. 
      add1.call(1) # => 2 
      
    2. pasa como un pseudo método argumento, ya sea explícitamente utilizando el operador de azúcar de sintaxis especial & último argumento o implícitamente usando un par block_given?/yield:

      def twice_do(&proc) # "proc" is the block given to a call of this method. 
          2.times { proc.call() } if proc 
      end 
      twice_do { puts "OK" } # Prints "OK" twice on separate lines. 
      
      def thrice_do() # if a block is given it can be called with "yield". 
          3.times { yield } if block_given? 
      end 
      thrice_do { puts "OK" } # Prints "OK" thrice on separate lines. 
      

    El segundo formulario se utiliza generalmente para Visitor patterns; los datos se pueden pasar a los argumentos de bloque especiales como argumentos a los métodos call o yield.

    +4

    Las llaves tienen una alta precedencia; 'do' tiene una baja precedencia. Si la invocación del método tiene parámetros que no están entre paréntesis, la forma de llave de un bloque se vinculará al último parámetro, no a la invocación general. El formulario 'do' se vinculará a la invocación. – Green

    +0

    ¿Por qué el voto a favor? – maerics

    +2

    Inglés, por favor! ...... "Los bloques de Ruby son literales de sintaxis para objetos de Proc ..." - si la gente no sabe qué es un bloque, supongo que no sabrá qué significa "literales de sintaxis para objetos de Proc" . intenta explicar como si los lectores tuvieran 5 años. – BKSpurgeon

    6

    para nadie viene a esta pregunta de un fondo de C# (o más lenguajes en realidad), esto podría ayudar:

    Rubí bloques son como expresiones lambda y métodos anónimos en C#. Son lo que C# llama a los delegados (y Ruby llama a los Procs), lo que quiere decir que son esencialmente funciones que se pueden pasar como valores. Tanto en Ruby como en C#, también pueden comportarse como cierres.

    Ruby: { |x| x + 1 }

    C#: x => x + 1

    Ruby: { |name| puts "Hello there #{name}" }

    C#: name => { Console.WriteLine("Hello there {0}", name); }

    Tanto C# y Ruby ofrecer formas alternativas de escribir el ejemplo anterior.

    Ruby:

    do |name| 
        puts "Hello there #{name}" 
    end 
    

    C#:

    delegate(string name) 
    { 
        Console.WriteLine("Hello there {0}", name); 
    } 
    

    En tanto Ruby y C#, varias instrucciones están permitidos, En Ruby, la segunda sintaxis anterior se requiere para esto.

    Estos conceptos están disponibles en muchos otros idiomas que han sido influenciados por las ideas detrás de la programación funcional.

    Cuestiones relacionadas