0

I get the following different results:

Test 1:

def myblock
  x = 1
  y = 1
  yield(x)
  [x, y]
end

myblock do |x|
 y = 10
end
# => [1,1]

Test 2:

x = 1
y = 1

1.upto(2) do |x|
  y = 20
end
[x,y]
# => [1,20]

Variables created outside the block are available in the block. Why are they so?

2
  • 1
    “Why are they so?”—because blocks in ruby are closures. Commented Jul 26, 2017 at 5:30
  • but why the 2rd block can change 'y'? Commented Jul 26, 2017 at 6:18

3 Answers 3

2

That is how the scope of a local variable is defined. A local variable can percolate into a block, but not into a method definition.

Sign up to request clarification or add additional context in comments.

1 Comment

Technically there is another issue; in the first snippet there are no local variables crossing boundaries of the method definition.
1
def myblock
  x = 1
  y = 1
  z = yield(x)
  puts "y=#{y}, z=#{z}"
  [x, y]
end

myblock do |x|
 y = 10
end
y=1, z=10
  #=> [1, 1]

This shows that three things are going on here:

  • The y in the block is unrelated to the y in myblock. That's because the scope of the y in the block is confined to the block. Ruby can only get her hands on what is returned by the block, not on what's going on inside of it.
  • The block does indeed return the value of y in the block, but only because y = 10 was the last statement executed in the block. The execution of that statement returns 10, so 10 is returned by the block. Had the block contained y = 10; 7, 7 would have been returned.
  • The value of yield(x) is not captured by the variable y in myblock, so it has no effect.1.

1 It's a little-known fact that Ruby actually shoots such values into outer space.

3 Comments

that makes sense for test1, but how about test2?it seems "Ruby knows what's going on inside the block", So y is changed to 20. am I right? thankyou
In Test 2, it is because y is defined before the block that y in the block is the same y. If you remove y = 1 and you run the code (try it) you
It is because y is initialized before the block that it is the same y inside the block. Remove y = 1 and [x,y] will raise an exception: "Name error: undefined local variable 'y'". It has to be that way for the block to be able to use previously-defined variables in the block calculations. Here y = 1.upto(2) { |x| 20 } would work (but it doesn't make much sense).
0

After more testing and searching:

For Test1, x and y is method level local variable and they don't share the scope with block. y in the block is a new local variable(block level).

For Test2, x and y is class level local variable and they share the scope with block, y in the block is still the outside y(class level).

It's all because I messed up the scope, thanks all.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.