Closures and for loops in Ruby

前端 未结 2 1907
伪装坚强ぢ
伪装坚强ぢ 2021-01-13 07:15

I\'m kind of new to Ruby and some of the closure logic has me a confused. Consider this code:

array = []
for i in (1..5)
  array << lambda {i}
end
arra         


        
相关标签:
2条回答
  • 2021-01-13 07:34

    What version of Ruby are you running this on? 1.8 does not have block scope for local variables, so j is still hanging around (and equal to 5) even after the end of the for.

    0 讨论(0)
  • 2021-01-13 07:55

    Okay, this is getting ridiculous. Every time I try to answer a question about how for loops work in Ruby, I get it wrong.

    The reason for this is, of course, that I don't use for loops in Ruby, neither does anybody else, so it really doesn't matter to me :-)

    Anyway, to settle the matter once and for all, I went straight to the ultimate source, the December 1, 2009 preliminary Draft of the IPA Ruby Language Specification (destined to become the ISO Ruby Language Specification):

    §11.4.1.2.3 The for expression

    Syntax

    • for-expression for for-variable in expression do-clause end
    • for-variable left-hand-side | multiple-left-hand-side

    The expression of a for-expression shall not be a jump-expression.

    Semantics

    A for-expression is evaluated as follows:

    1. Evaluate the expression. Let O be the resulting value.
    2. Let E be the primary-method-invocation of the form primary-expression [no line-terminator here].each do | block-formal-argument-list | block-body end, where the value of the primary-expression is O,the block-formal-argument-list is the for-variable, the block-body is the compound-statement of the do-clause.

      Evaluate E, but skip Step c of §11.2.2.

    3. The value of the for-expression is the resulting value of the invocation.

    Okay, so basically this means that

    for for_variable in expression
      do_clause
    end
    

    gets translated to

    O = expression
    O.each do |for_variable|
      do_clause
    end
    

    Or, in your case:

    for i in 1..5
      puts j if i > 1 #undefined local variable or method `j' (NameError)
      j = i
    end
    

    gets translated to

    (1..5).each do |i|
      puts j if i > 1 #no excpetion here, works just fine ??!!??
      j = i
    end
    

    Aha! But we forgot something! There's this ominous "skip Step c of §11.2.2." thing! So, what does it say?

    • Push an empty set of local variable bindings onto ⟦local-variable-bindings⟧.

    Note that Step b

    • Set the execution context to Eb.

    is not skipped.

    So, as far as I can see, a for loop gets its own execution context, which starts out as a copy of the current execution context, but it does not get its own set of local variable bindings. IOW: it gets its own dynamic execution context, but not its own lexical scope.

    I must admit, I'm still not sure I fully understand it, but it doesn't get any more precise than this.

    0 讨论(0)
提交回复
热议问题