Using $ 1, $ 2, etc. Global variables inside a method definition

Given the following two code snippets:

def hello(z) "hello".gsub(/(o)/, &z) end z = proc {|m| p $1} hello(z) # prints: nil 



 def hello z = proc {|m| p $1} "hello".gsub(/(o)/, &z) end hello # prints: "o" 

Why is the output from these two code snippets different? Is there a way to pass the gsub block from outside the method definition so that the variables $1 , $2 evaluated in the same way as if the block was specified inside the method definition?

+7
scope ruby pseudo-globals
Aug 31 '13 at 16:46
source share
3 answers

Why is the conclusion different?

The proc in ruby ​​has a lexical domain. This means that when it finds a variable that is not defined, it is resolved in the context when proc was defined , not called . This explains the behavior of your code.

You can see that the block is defined before regexp, and this can cause confusion. The problem is with the magic ruby ​​variable, and it works in a completely different way than other variables. Quoting @ JörgWMittag

This is pretty simple: the reason why $ SAFE does not behave as you would expect from a global variable, because it is not a global variable. This is a magical unicorn game.

There are quite a few such magical unicorns in Ruby, and they, unfortunately, are not very well documented (not documented at all), since developers of alternative Ruby implementations have found the hard way. These things all behave differently and (apparently) inconsistently, and pretty much the only thing they have in common is that they look like global variables, but they don't behave like them.

Some have a local area. Some of them have a local area. Some magically change if no one assigns them. Some have a magical meaning for the translator and change the behavior of the language. Some have other weird semantics attached to them.

If you really find how the $1 and $2 variables work, I assume that the only “documentation” you will find, rubyspec , is the ruby ​​specification, done in a difficult way by the Rubinus people. Have a good break, but be prepared for pain.




Is there a way to pass the gsub block from another context with the correct settings $ 1, $ 2?

You can achieve what you want with the following modification (but I'm sure you already know this)

 require 'pp' def hello(z) #z = proc {|m| pp $1} "hello".gsub(/(o)/, &z) end z = proc {|m| pp m} hello(z) 

I do not know how to change the scope of proc on the fly. But do you really want to do this?

+2
Aug 31 '13 at 17:01
source share

The two versions are different from each other, since the variable $1 is a local-local and local-method-method. In the first example, $1 exists only in a block outside the hello method. In the second example, $1 exists inside the hello method.

It is not possible to pass $ 1 to the gsub block outside the method definition.

Note that gsub passes the matching string to the block, so z = proc { |m| pp m } z = proc { |m| pp m } will only work as long as your regular expression contains only a complete match. Once your regular expression contains anything other than the link you need, you're out of luck.

For example, "hello".gsub(/l(o)/) { |m| m } "hello".gsub(/l(o)/) { |m| m } => hello , because the entire match string was passed to the block.

Whereas "hello".gsub(/l(o)/) { |m| $1 } "hello".gsub(/l(o)/) { |m| $1 } => helo , since the matched l discarded by the block, all we are interested in is the captured o .

My solution is to match regex, then pass the MatchData object to the block:

 require 'pp' def hello(z) string = "hello" regex = /(o)/ m = string.match(regex) string.gsub(regex, z.call(m)) end z = proc { |m| pp m[1] } pp hello(z) 
+1
Aug 31 '13 at 17:41
source share

Things like $1 , $2 act like LOCAL VARIABLES, despite its leading $ . You can try the code below to prove it:

 def foo /(hell)o/ =~ 'hello' $1 end def bar $1 end foo #=> "hell" bar #=> nil 

Your problem is that proc z is defined outside the hello method, so z accesses $1 in the main context, but gsub sets $1 in the context of the hello method.

+1
May 9 '14 at 7:58
source share



All Articles