Scopes and blocks

2019-05-31 21:00发布

In ruby, module, class, and def keywords define a new scope. I'm confused as to why local variables defined in a block do not exist outside of the block. Is a block argument another scope gate? For example:

(1..2).each { |n| numer = 'czesc' }
numer # => NameError: undefined local variable or method `czesc' for main:Object

Or more simply:

def kon; end
kon { kot = 3 }
kot # => NameError: undefined local variable or method `kot' for main:Object

I thought, maybe it's not persisted because it's defined in the method's arguments, but the following works for the normal arguments:

def pokaz(cos)
  p cos
end
pokaz(co = "to")
co # => "to"

标签: ruby scope
2条回答
老娘就宠你
2楼-- · 2019-05-31 21:35

In ruby, module, class, and def keywords define a new scope.

There are six scope constructs in Ruby: module bodies, class bodies, method bodies and script bodies create new scopes, block bodies and "stabby lambda" literal bodies create new nested scopes.

I'm confused as to why local variables defined in a block do not exist outside of the block.

Because a block body has its own lexical scope. The scope is nested, which means that it has access to local variables from the outer scope, but variables from an inner scope never leak into the outer scope.

查看更多
你好瞎i
3楼-- · 2019-05-31 21:38

You might think about codeblocks as of lazy instances of Proc class.

Your second example with kon/kot does actually not what you were expecting from it. You supply a codeblock to a function kon. This codeblock is not evaluated unless requested. In your snippet it is never evaluated. Look:

▶ def kon; end
#⇒ :kon
▶ kon { puts 'I am here' }
#⇒ nil

You passed a codeblock. Fine. Now kon should invoke it, if needed:

▶ def kon; yield ; end
#⇒ :kon
▶ kon { puts 'I am here' }
# I am here
#⇒ nil

When you passed a codeblock to, say, each method of Enumerator instance:

(1..2).each { |n| numer = 'czesc' }

The codeblock is being evaluated in context of this Enumerator. The default receiver, self, is still the main thread, though. That makes codeblocks to act mostly like closures (they have an access to the caller bindings):

▶ kot = 10  
#⇒ 10
▶ 5.times do |i|  
▷   kot = i  
▷ end      
▶ puts kot
#⇒ 4

Hope it sheds some light.

查看更多
登录 后发表回答