2011-07-07 8 views
5

No estoy haciendo sentido de la siguiente comportamiento (véase también in this SO thread):Ceder en un bloque anónimo

def def_test 
    puts 'def_test.in' 
    yield if block_given? 
    puts 'def_test.out' 
end 

def_test do 
    puts 'def_test ok' 
end 

block_test = proc do |&block| 
    puts 'block_test.in' 
    block.call if block 
    puts 'block_test.out' 
end 

block_test.call do 
    puts 'block_test' 
end 

proc_test = proc do 
    puts 'proc_test.in' 
    yield if block_given? 
    puts 'proc_test.out' 
end 

proc_test.call do 
    puts 'proc_test ok' 
end 

Salida:

def_test.in 
def_test ok 
def_test.out 
block_test.in 
block_test ok 
block_test.out 
proc_test.in 
proc_test.out 

no me importa tener que recurrir a declarar explícitamente el & variable de bloque y llamarlo directamente, pero lo ideal sería que me gustaría tener algún sentido de por qué termino necesitando.

+0

Relacionados: http://stackoverflow.com/questions/6628936/extinguendo-una-clase-método-en-un-módulo –

Respuesta

4

El lambda es un cierre y que parece ser la captura de la cuadra de su ámbito de aplicación externa block_given? y. Este comportamiento tiene sentido ya que el bloque es, más o menos, un argumento implícito para el método externo; incluso se puede capturar el bloque en un argumento con nombre si lo desea:

def def_test(&block) 
    frobnicate &block 
end 

Así que el bloque es parte de la lista de argumentos, incluso cuando no se nombra.

Considérese este simple trozo de código:

def f 
    lambda do 
     puts "\tbefore block" 
     yield if block_given? 
     puts "\tafter block" 
    end 
end 

puts 'Calling f w/o block' 
x = f; x.call 
puts 

puts 'Calling f w/ block' 
x = f { puts "\t\tf-block" }; x.call 
puts 

puts 'Calling f w/o block but x with block' 
x = f; x.call { puts "\t\tx-block" } 
puts 

puts 'Calling f w/ block and x with block' 
x = f { puts "\t\tf-block" }; x.call { puts "\t\tx-block" } 

Esto produce lo siguiente para mí con 1.9.2:

Calling f w/o block 
    before block 
    after block 

Calling f w/ block 
    before block 
     f-block 
    after block 

Calling f w/o block but x with block 
    before block 
    after block 

Calling f w/ block and x with block 
    before block 
     f-block 
    after block 

Además, Proc#call (También conocido como proc ===) no lo hace tomar un bloque:

prc === obj → result_of_proc
Invoca el bloque, con obj como parámetro del bloque. Es para permitir que un objeto de proceso sea un objetivo de cláusula when en el enunciado de caso.

comparar la primera línea con la documentación para Enumerable#chunk (por ejemplo):

enum.chunk {| elt | ...} → an_enumerator

El {...} indica que chunk se documenta a tomar un bloque, la falta de dicha notación para Proc#call indica que Proc#call hace falta ser un bloque.

Esto no es exactamente una respuesta autorizada, pero tal vez aclara un poco las cosas.

+0

Yo diría que es mucho más autoritario de lo que piensas: dos horas de búsqueda en Google no habían arrojado nada más al grano. ¡Gracias en nombre de los próximos nuevos rubyistas que se encuentran con este hilo! –

+0

@Denis: Lo llamaría autoritario si pudiera señalar alguna documentación oficial que dijera "lambdas capture blocks junto con todo lo demás", supongo que la documentación 'ProC# call' combinada con el argumento" hidden '& block' parameter" está muy cerca de eso. Buscar en Google las cosas comunes de Ruby hace que ceda (ja, ja) la frustración, pero está mejorando a medida que ruby-doc.org y apidock.com se elevan en la clasificación. –

5

block_given? considera def alcance, no lambda alcance:

def test 
    l = lambda do 
    yield if block_given? 
    end 
    l.call 
end 

test { puts "In block" } 
+0

Disculpa, pero esta no es la respuesta que esperaba. Mi pregunta es por qué 'l.call {pone" In block "}' work, en lugar de 'test {puts" In block "}'. Si edita su función para llamar a 'l.call & Proc.nuevo' mi impresión inicial fue que funcionaría (ya que lo hará con una función propiamente definida); pero no es así, y me gustaría entender por qué. –

+0

@Denis Depende de lo que quiere decir "trabajo". 'yield' y' block_given? 'siempre funcionarán en el ámbito' def', el alcance 'lambda' es transparente para ambos. Pasar bloque a 'lambda' no lo cambia. Puede usar el bloque pasado a 'lambda' por el parámetro' lambda do | & f | ', pero no por' yield' o 'block_given?'. 'lambda' no es lo mismo ad' def'. –

+0

Sí, finalmente "marcó", pero solo lo hizo después de leer la respuesta de mu. Veo retrospectivamente de qué manera su respuesta es válida. Es solo que, para el recién llegado de rubí como yo, la respuesta más detallada de mu hace que esté más limpio de lo que está sucediendo. –

Cuestiones relacionadas