2009-01-07 14 views
10

Estoy aprendiendo ruby ​​y tratando de entender el alcance del código ejecutado en bloques. Por ejemplo, quiero ser capaz de crear un bloque que afecta el método que se une a, así:ruby: ¿puede un bloque afectar variables locales en un método?

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 escribir usando referencias de variables simples y sin parámetros. Solo al realizar cambios en el método de "prueba" en el ejemplo anterior, ¿es posible acceder a las variables definidas en el bloque?

Una vez más, 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.

Respuesta

10

En primer lugar, se realiza con block.call()yield, y que no es necesario el parámetro &block de 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 quiera, que no cómo va a utilizar bloques normalmente es, es la siguiente:

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

test() { 
    foo="this is foo" 
} 

Pero eso es sólo un efecto secundario debido foo se "devolvió" por el bloque. Si en cambio hace 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á 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 haría tipo de trabajo, pero se rompe si se quita foo = "before test" porque foo se convierte en una variable local en el bloque y no existe en la unión.

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 la unión es diferente del local en el bloque (yo no lo sabía, gracias).

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

test { "this is foo" } 

grabados in test, foo is this is foo

El valor de rendimiento es el valor del bloque.

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

Además, consulte 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 

Prints "foo is this is foo"

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

test2 p 

Prints "foo is this is foo"

+0

Esto es engañoso, no se está accediendo a los locales en el bloque como la pregunta dice, sólo el valor de retorno del bloque. –

3

No, un bloque no pueden afectar a 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 se crea el bloque son los que ve. Si hubiera un foo y bar en la parte superior de su código, fuera de cualquier método, que bloquean sería cambiar los cuando se llamaba.

2

Puede hacer lo que quiera por ser 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 colocador apropriate (los foo y bar métodos).

Cuestiones relacionadas