rails objetos metodos manejo lista importar for clases bucle bloques ruby closures

objetos - proc ruby



ruby: ¿puede un bloque afectar variables locales en un método? (4)

Estoy aprendiendo ruby ​​y tratando de entender el alcance del código ejecutado en bloques. Por ejemplo, quiero poder crear un bloque que afecte el método al que está conectado, así:

def test(&block) block.call() if block_given? puts "in test, foo is #{foo}" puts "in test, bar is #{bar}" end test() { foo="this is foo" bar="this is bar" }

En este caso, no quiero tener que modificar el bloque en absoluto; quiero poder escribirlo usando referencias de variables simples y sin parámetros. Solo haciendo cambios al método de ''prueba'' en el ejemplo anterior , ¿es posible acceder a las variables definidas en el bloque?

De nuevo, el objetivo es dejar el bloque sin modificar, pero poder acceder a las variables creadas desde ''prueba'' después de que se ejecute el bloque.


def test(&block) foo = yield puts "in test, foo is #{foo}" end test { "this is foo" }

impresiones in test, foo is this is foo

El valor del rendimiento es el valor del bloque.

También puede pasar parámetros para ceder, a los que luego se puede acceder mediante el bloque usando | param, otro | al comienzo del bloque.

Además, mira los procs.

foo = "this is foo" p = Proc.new { "foo is #{foo}" } p.call

Imprime "foo is this is foo"

def test(p) p.call end test p

Imprime "foo is this is foo"

def test2(p) foo = "monkey" p.call end test2 p

Imprime "foo is this is foo"


No, un bloque no puede afectar las variables locales en el lugar donde se llama.

Los bloques en Ruby son cierres , lo que significa que capturan el alcance a su alrededor cuando se crean. Las variables que son visibles cuando crea el bloque son las que ve. Si hubiera un foo y una bar en la parte superior de tu código, fuera de cualquier método, ese bloque los cambiaría cuando se llamara.


Puedes hacer lo que quieras siendo un poco más detallado:

class Test def foo(t) @foo = t end def bar(t) @bar = t end def test(&block) self.instance_eval &block if block_given? puts "in test, foo is #{@foo}" puts "in test, bar is #{@bar}" end end Test.new.test() { foo "this is foo" bar "this is bar" }

Puede crear métodos como attr_accessor que generarán un setter apropiado (los métodos foo y bar ).


En primer lugar, block.call() se realiza con yield , y no necesita el parámetro &block esa manera.

Normalmente no puede hacer lo que quiere, los bloques están vinculados cuando se crean, y dentro del bloque puede ver las variables locales definidas en ese momento; La forma más fácil de hacer lo que desea, que no es cómo usará los bloques normalmente, es esta:

def test() foo = yield if block_given? puts "in test, foo is #{foo}" end test() { foo="this is foo" }

Pero eso es solo un efecto secundario porque foo es "devuelto" por el bloque. Si en cambio haces esto:

def test() foo = yield if block_given? puts "in test, foo is #{foo}" end test() { foo="this is foo" "ha ha, no foo for you" }

Notarás que hace algo diferente.

Aquí hay más magia:

def test(&block) foo = eval "foo", block.binding puts foo block.call foo = eval "foo", block.binding puts foo end foo = "before test" test() { foo = "after test" "ha ha, no foo for you" }

Eso funcionaría, pero se rompe si eliminas foo = "before test" porque foo convierte en una variable local en el bloque y no existe en el enlace.

Resumen: no se puede acceder a las variables locales desde un bloque, solo los locales donde se definió el bloque y el valor de retorno del bloque.

Incluso esto no funcionará:

def test(&block) eval "foo = ''go fish''", block.binding block.call bar = eval "foo", block.binding puts bar end

porque el foo en el enlace es diferente del local en el bloque (no lo sabía, gracias).