Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Regex global variables are not being set

I came across something that seems unusual and I was wondering if anyone could explain why.

1.8.7 :001 > some_str = "Hello World"
 => "Hello World" 
1.8.7 :002 > some_str.try(:match, /^(\w*)/)
 => #<MatchData "Hello" 1:"Hello"> 
1.8.7 :003 > $1
 => nil 
1.8.7 :004 > some_str.match(/^(\w*)/)
 => #<MatchData "Hello" 1:"Hello"> 
1.8.7 :005 > $1
 => "Hello" 

I'm not sure why the global variable $1 is not being set the first time, but is set the second. Any insights?

like image 646
Kyle d'Oliveira Avatar asked Jul 11 '12 21:07

Kyle d'Oliveira


People also ask

Why is it that all variables are not declared as global?

Global variables can be altered by any part of the code, making it difficult to remember or reason about every possible use. A global variable can have no access control. It can not be limited to some parts of the program. Using global variables causes very tight coupling of code.

Why it is not optimal to make global variables in JavaScript?

Avoid global variables or minimize the usage of global variables in JavaScript. This is because global variables are easily overwritten by other scripts. Global Variables are not bad and not even a security concern, but it shouldn't overwrite values of another variable.

Why global variables are not supported in Java?

Global variables are not technically allowed in Java. A global variable is one declared at the start of the code and is accessible to all parts of the program. Since Java is object-oriented, everything is part of a class. ... A static variable can be declared, which can be available to all instances of a class.

How do you not use global variables?

The simplest way to avoid globals all together is to simply pass your variables using function arguments. As you can see, the $productData array from the controller (via HTTP request) goes through different layer: The controller receives the HTTP request. The parameters are passed to the model.


2 Answers

Let me show you how try is implemented. If you want to see it yourself, then take a look at the activesupport source. It's defined in /lib/active_support/core_ext/object/try.rb

class Object
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      public_send(*a, &b)
    end
  end
end

What this basically does, is just sending the method name and the complete arguments to the Object. public_send is the same as send, but can only be used to call public methods.

So I rewrote this, to debug your issue:

class Object
  def try(*a)
    result = public_send(*a)
    puts $1.inspect
    result
  end
end

string = "Hello"
string.try(:match, /^(\w*)/)
puts $1.inspect

This outputs

"Hello"
nil

So the great question arises: Is this a bug in the ruby interpreter?. Maybe. At least it's not documented in any official source. I found a reference that tells the following (See Global variables.)

[...], $_ and $~ have local scope. Their names suggest they should be global, but they are much more useful this way, and there are historical reasons for using these names.

So it seems like $1 is not a global variable as well, even though it is reported by the Kernel as a global variable:

1.9.3-p194 :001 > global_variables
 => [:$;, :$-F, :$@, :$!, :$SAFE, :$~, :$&, :$`, :$', :$+, :$=, :$KCODE, :$-K,
     :$,, :$/, :$-0, :$\, :$_, :$stdin, :$stdout, :$stderr, :$>, :$<, :$.,
     :$FILENAME, :$-i, :$*, :$?, :$$, :$:, :$-I, :$LOAD_PATH, :$",
     :$LOADED_FEATURES, :$VERBOSE, :$-v, :$-w, :$-W, :$DEBUG, :$-d, :$0,
     :$PROGRAM_NAME, :$-p, :$-l, :$-a, :$binding, :$1, :$2, :$3, :$4, :$5, :$6,
     :$7, :$8, :$9] 

To make sure, I forwarded this incosistency to the Ruby Bug Tracker. See Ruby Bug #6723

like image 195
iblue Avatar answered Sep 23 '22 04:09

iblue


try is defined as

def try(method, *args, &block)
  send(method, *args, &block)
end

except of course on nil where it just returns nil. Why does this matter? Because the regexp globals aren't real globals: they're maintained on a per method and per thread basis (it's easy enough to see this by perusing the ruby source). When you call match via try the globals are set in the scope for try but in the next case they are set at the top level. It's easy to verify this

def do_match string, regexp
  string =~ regexp
  $1
end
do_match "Hello World", /^(\w*)/ #=> returns 'Hello'
$1 #=> returns nil 
like image 29
Frederick Cheung Avatar answered Sep 22 '22 04:09

Frederick Cheung