Why is $1 in a grep() equal to nil?

class DataSource
    def get_cpu_info
        "cpu8001"
    end
    def get_cpu_price
        101
    end

    def get_mouse_info
        "mouse241"
    end
    def get_mouse_price
        40
    end
end

DataSource.new.methods.grep(/^get_(.*?)_info$/) do |meth|
    puts "---#{meth}---"
end

--output:--
---get_cpu_info---
---get_mouse_info---

class Computer
    def initialize(an_id, data_source)
        @id = an_id
        @ds = data_source

        @ds.methods.grep(/^get_(.+?)_info$/) do

            puts "-->#{$1}<---"

            Computer.send(:define_method, $1.to_sym) do
                puts "****" + $1 + "****" #***NIL NIL NIL NIL

                info = @ds.send("get_#{$1}_info".to_sym)
                price = @ds.send("get_#{$1}_price".to_sym)

                alert = ""
                if price > 100
                    alert = "*"
                end

                puts "#{alert} #{info} #{price}"
             end
        end
    end

end

comp1 = Computer.new(1, DataSource.new)
puts comp1.mouse

--output:--
Line 32:in `+': can't convert nil into String (TypeError)
  from t.rb:32:in `mouse'
  from t.rb:52

···

--
Posted via http://www.ruby-forum.com/.

If I add the line:

name = $1

before the line:

Computer.send(.....)

and replace all the $1's in the body of the define_method(), then the
code works like I want.

···

--
Posted via http://www.ruby-forum.com/.

I'm not sure of the specifics, but $1 doesn't persist outside of the
block created in the grep statement. When you call comp1.mouse, that's
no longer within that block -- the method was defined in it, but once
it was made a method it took on an existence of its own.

···

On Thu, Feb 24, 2011 at 2:59 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

class DataSource
def get_cpu_info
"cpu8001"
end
def get_cpu_price
101
end

def get_mouse_info
"mouse241"
end
def get_mouse_price
40
end
end

DataSource.new.methods.grep(/^get_(.*?)_info$/) do |meth|
puts "---#{meth}---"
end

--output:--
---get_cpu_info---
---get_mouse_info---

class Computer
def initialize(an_id, data_source)
@id = an_id
@ds = data_source

   @ds\.methods\.grep\(/^get\_\(\.\+?\)\_info$/\) do

       puts &quot;\-\-&gt;\#\{$1\}&lt;\-\-\-&quot;

       Computer\.send\(:define\_method, $1\.to\_sym\) do
           puts &quot;\*\*\*\*&quot; \+ $1 \+ &quot;\*\*\*\*&quot;   \#\*\*\*NIL NIL NIL NIL

           info = @ds\.send\(&quot;get\_\#\{$1\}\_info&quot;\.to\_sym\)
           price = @ds\.send\(&quot;get\_\#\{$1\}\_price&quot;\.to\_sym\)

           alert = &quot;&quot;
           if price &gt; 100
               alert = &quot;\*&quot;
           end

           puts &quot;\#\{alert\} \#\{info\} \#\{price\}&quot;
        end
   end

end

end

comp1 = Computer.new(1, DataSource.new)
puts comp1.mouse

--output:--
Line 32:in `+': can't convert nil into String (TypeError)
from t.rb:32:in `mouse'
from t.rb:52

class Computer
def initialize(an_id, data_source)
@id = an_id
@ds = data_source

   @ds\.methods\.grep\(/^get\_\(\.\+?\)\_info$/\) do

       puts &quot;\-\-&gt;\#\{$1\}&lt;\-\-\-&quot;

       Computer\.send\(:define\_method, $1\.to\_sym\) do

inside this block, your code references $1 itself.
but what you really want is that $1 be evaluated first before the
block generation.

           puts &quot;\*\*\*\*&quot; \+ $1 \+ &quot;\*\*\*\*&quot;   \#\*\*\*NIL NIL NIL NIL

           info = @ds\.send\(&quot;get\_\#\{$1\}\_info&quot;\.to\_sym\)
           price = @ds\.send\(&quot;get\_\#\{$1\}\_price&quot;\.to\_sym\)

           alert = &quot;&quot;
           if price &gt; 100
               alert = &quot;\*&quot;
           end

           puts &quot;\#\{alert\} \#\{info\} \#\{price\}&quot;
        end
   end

end

try this (brute force) way just to get the idea,

class Computer
   def initialize(an_id, data_source)
       @id = an_id
       @ds = data_source

       @ds.methods.grep(/^get_(.+?)_info$/) do

          puts "-->#{$1}<---"
          eval <<-EVILHERE
           Computer.send(:define_method, $1.to_sym) do
               #puts "****" + ($1) + "****" #***NIL NIL NIL NIL

               info = @ds.send("get_#{$1}_info".to_sym)
               price = @ds.send("get_#{$1}_price".to_sym)

               alert = ""
               if price > 100
                   alert = "*"
               end

               puts "\#{alert} \#{info} \#{price}"
            end
          EVILHERE
       end
   end
end

best regards -botp

···

On Fri, Feb 25, 2011 at 4:59 AM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

An online copy of Programming Ruby all the match variables are local to
the current scope:

http://webcache.googleusercontent.com/search?q=cache:TH-QZKH6TfsJ:phrogz.net/programmingruby/language.html+scope+of+ruby+match+variables&cd=3&hl=en&ct=clnk&gl=us&client=firefox-a&source=www.google.com

Here is a simpler example showing that trait of $1:

def meth_a
  "a" =~ /(.)/
   puts $1
end

meth_a

puts "-->#{$1}<---"

--output:--
a
---><---

So, Eric Christoperson was right: $1 is a local variable.

···

--
Posted via http://www.ruby-forum.com/.

$1, $2 etc. look like global variables but they are in fact local to
the scope where they are set.

Aren't local variables also local to the scope where they are set? In
other words, what defines a scope in ruby? More specifically, how do
you know the extent of $1's scope?

In my last example, the each block seems to define the scope of the
'name' variable. Why doesn't the each block also define the scope of
the $1 variable--after all the match occurred inside the each block?

Thanks.

···

--
Posted via http://www.ruby-forum.com/\.

Eric Christopherson wrote in post #983739:

end
---get_cpu_info---
     puts "-->#{$1}<---"
       end
comp1 = Computer.new(1, DataSource.new)
puts comp1.mouse

--output:--
Line 32:in `+': can't convert nil into String (TypeError)
from t.rb:32:in `mouse'
from t.rb:52

I'm not sure of the specifics, but $1 doesn't persist outside of the
block created in the grep statement. When you call comp1.mouse, that's
no longer within that block -- the method was defined in it, but once
it was made a method it took on an existence of its own.

$1 is a *global* variable, so saying it doesn't persist outside of a
block doesn't make any sense.

I think what is happening is that the body of the define_method() call
forms a closure around the variable $1. Unfortunately, the problem with
global variables is that other parts of the code can change their value.
In this instance, I think a subsequent unsuccessful pattern match
assigns nil to $1. Here is an example of that:

arr = ["hello"]

arr.each do |x|
    x =~ /h(.)ll/
    puts $1
end

puts $1

"hello" =~ /xxx/

puts $1

--output:--
e
e
nil

There are a lot other methods in the DataSource class that are inherited
by all classes, and the last one in the list must not be one of the
methods I defined, so the pattern match fails against that method name,
and $1 gets set to nil. Subsequently, when I call the mouse() method,
it reads the current value of $1, which is nil.

Assigning $1 to a local variable, like 'name', means that each method
created by define_method() gets its own distinct 'name' variable in the
method body.

···

On Thu, Feb 24, 2011 at 2:59 PM, 7stud -- <bbxx789_05ss@yahoo.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

7stud -- wrote in post #983980:

An online copy of Programming Ruby says all the match variables are
local to
the current scope:

http://webcache.googleusercontent.com/search?q=cache:TH-QZKH6TfsJ:phrogz.net/programmingruby/language.html+scope+of+ruby+match+variables&cd=3&hl=en&ct=clnk&gl=us&client=firefox-a&source=www.google.com

Here is a simpler example showing that trait of $1:

def meth_a
  "a" =~ /(.)/
   puts $1
end

meth_a

puts "-->#{$1}<---"

--output:--
a
---><---

So, Eric Christopherson was right: $1 is a local variable.

But then how do you explain the problem with my code? It appears that
'name' is more local than $1. It seems that $1 persists from each
grep() loop to the next, like a global variable, but it's value is only
visible inside the block.

···

--
Posted via http://www.ruby-forum.com/\.

Oh, I think you have it exactly. I was thinking that maybe the
numbered globals weren't truly global, since I read on
http://www.rubyist.net/~slagell/ruby/globalvars.html that $_ and $~
aren't actually global.

···

On Thu, Feb 24, 2011 at 8:04 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

Eric Christopherson wrote in post #983739:

On Thu, Feb 24, 2011 at 2:59 PM, 7stud -- <bbxx789_05ss@yahoo.com> >> wrote:

end
---get_cpu_info---
puts "-->#{$1}<---"
end
comp1 = Computer.new(1, DataSource.new)
puts comp1.mouse

--output:--
Line 32:in `+': can't convert nil into String (TypeError)
from t.rb:32:in `mouse'
from t.rb:52

I'm not sure of the specifics, but $1 doesn't persist outside of the
block created in the grep statement. When you call comp1.mouse, that's
no longer within that block -- the method was defined in it, but once
it was made a method it took on an existence of its own.

$1 is a *global* variable, so saying it doesn't persist outside of a
block doesn't make any sense.

I think what is happening is that the body of the define_method() call
forms a closure around the variable $1.

It isn't actually global. I don't know the specifics, but I used to worry
about that too, and found out later it was not necessary.

def meth_a
  "a" =~ /(.)/
  puts "in a, $1 = #{$1.inspect}"
end

def meth_b
  "b" =~ /(.)/
  puts "in b, $1 = #{$1.inspect}"
  meth_a
  puts "in b, $1 = #{$1.inspect}"
end

meth_b

# >> in b, $1 = "b"
# >> in a, $1 = "a"
# >> in b, $1 = "b"

···

On Thu, Feb 24, 2011 at 8:04 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

$1 is a *global* variable, so saying it doesn't persist outside of a
block doesn't make any sense.

Josh Cheek wrote in post #983791:

$1 is a *global* variable, so saying it doesn't persist outside of a
block doesn't make any sense.

It isn't actually global. I don't know the specifics, but I used to
worry
about that too, and found out later it was not necessary.

def meth_a
  "a" =~ /(.)/
  puts "in a, $1 = #{$1.inspect}"
end

def meth_b
  "b" =~ /(.)/
  puts "in b, $1 = #{$1.inspect}"
  meth_a
  puts "in b, $1 = #{$1.inspect}"
end

meth_b

# >> in b, $1 = "b"
# >> in a, $1 = "a"
# >> in b, $1 = "b"

Uh oh. Someone is going to have to explain that to me. $1 does not act
like a regular global variable:

def meth_a
  $global = 10
end

def meth_b
  $global = 5
  puts $global
  meth_a()
  puts $global
end

meth_b

--output:--
5
10

···

On Thu, Feb 24, 2011 at 8:04 PM, 7stud -- <bbxx789_05ss@yahoo.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

Josh Cheek wrote in post #983791:

$1 is a *global* variable, so saying it doesn't persist outside of a
block doesn't make any sense.

It isn't actually global. I don't know the specifics, but I used to
worry
about that too, and found out later it was not necessary.

[...]

Uh oh. Someone is going to have to explain that to me. $1 does not act
like a regular global variable:

This is getting more confusing for me. I altered your original script by adding:

['foo'].grep(/f(.)o/)

right before your puts "-->#{$1}<---" (at the end of the definition of
initialize). Obviously that grep was successful; but surprisingly, the
value of $1 remains 'o' even outside of the invocation of initialize:

grep_test.rb:30:in `block (2 levels) in initialize': undefined method
`get_o_info' for #<DataSource:0x82ea38> (NoMethodError)
  from grep_test.rb:49:in `<main>'

The whole script:

# From question on ruby-talk 2/24/2011

class DataSource
  def get_cpu_info
    "cpu8001"
  end
  def get_cpu_price
    101
  end

  def get_mouse_info
    "mouse241"
  end
  def get_mouse_price
    40
  end
end

class Computer
  def initialize(an_id, data_source)
    @id = an_id
    @ds = data_source

    @ds.methods.grep(/^get_(.+?)_info$/) do
      puts "-->#{$1}<---"

      Computer.send(:define_method, $1.to_sym) do
        puts "****" + $1 + "****" #***NIL NIL NIL NIL

        info = @ds.send("get_#{$1}_info".to_sym)
        price = @ds.send("get_#{$1}_price".to_sym)

        alert = ""
        if price > 100
          alert = "*"
        end

        puts "#{alert} #{info} #{price}"
      end
      puts "-->#{$1}<---"
    end
    ['foo'].grep(/f(.)o/)
    puts "-->#{$1}<---"
  end
end

comp1 = Computer.new(1, DataSource.new)
puts "$1: #$1"
puts comp1.mouse

···

On Fri, Feb 25, 2011 at 2:53 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

On Thu, Feb 24, 2011 at 8:04 PM, 7stud -- <bbxx789_05ss@yahoo.com> >> wrote:

$1, $2 etc. look like global variables but they are in fact local to
the scope where they are set. This automatically also means that they
are thread local. Josh's example demonstrates that nicely.

The reason is to simplify applications which use multiple regular
expression matches - nested and concurrently.

Kind regards

robert

···

On Fri, Feb 25, 2011 at 9:53 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

Josh Cheek wrote in post #983791:

On Thu, Feb 24, 2011 at 8:04 PM, 7stud -- <bbxx789_05ss@yahoo.com> >> wrote:

$1 is a *global* variable, so saying it doesn't persist outside of a
block doesn't make any sense.

It isn't actually global. I don't know the specifics, but I used to
worry
about that too, and found out later it was not necessary.

def meth_a
"a" =~ /(.)/
puts "in a, $1 = #{$1.inspect}"
end

def meth_b
"b" =~ /(.)/
puts "in b, $1 = #{$1.inspect}"
meth_a
puts "in b, $1 = #{$1.inspect}"
end

meth_b

# >> in b, $1 = "b"
# >> in a, $1 = "a"
# >> in b, $1 = "b"

Uh oh. Someone is going to have to explain that to me. $1 does not act
like a regular global variable:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Eric Christopherson wrote in post #984033:

worry
about that too, and found out later it was not necessary.

[...]

Uh oh. Someone is going to have to explain that to me. $1 does not act
like a regular global variable:

This is getting more confusing for me.

I think your source of confusion is how closures work. I believe the
following brief example demonstrates what you are seeing in my larger
script:

def do_stuff
  x = 'hello'

  p = Proc.new {puts x}

  x = 'goodbye'

  return p
end

my_callable = do_stuff
my_callable.call

--output:--
goodbye

The block {puts x} closes over the *variable* x--not the value 'hello'.
At the end of the def, x is still in scope and it is changed to
'goodbye'. Then when the def ends, x goes out of scope, so x can no
longer be changed. That means that inside the block {puts x}, the value
of x is 'goodbye'.

···

On Fri, Feb 25, 2011 at 2:53 PM, 7stud -- <bbxx789_05ss@yahoo.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote in post #984624:

worry
meth_a

Uh oh. Someone is going to have to explain that to me. $1 does not act
like a regular global variable:

$1, $2 etc. look like global variables but they are in fact local to
the scope where they are set.

Thanks for the response Robert.

As far as I can tell, there is a difference between $1 and a local
variable. Take a look at this example:

def test
   procs =

   %w[a b c].each do |letter|
     letter =~ /(.)/ #sets value of $1
     name = letter #sets value of 'name'

     my_proc = Proc.new do
       puts "name = #{name}"
       puts "$1 = #{$1}"
     end

     procs << my_proc
  end

  puts "$1 = #{$1}"
  #puts "name = #{name}" #error: undefined variable or method 'name'

  return procs
end

arr = test

arr.each do |a_proc|
a_proc.call
end

--output:--
$1 = c
name = a
$1 = c
name = b
$1 = c
name = c
$1 = c

The output shows that in all the procs, the value of $1 is the value
produced by the last regex match. Yet a new name variable is created
every time through the each loop, and each proc closes over a different
name variable. So there is a difference between a local variable like
name and $1.

···

On Fri, Feb 25, 2011 at 9:53 PM, 7stud -- <bbxx789_05ss@yahoo.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote in post #984624:

worry
meth_a

Uh oh. Someone is going to have to explain that to me. $1 does not act
like a regular global variable:

$1, $2 etc. look like global variables but they are in fact local to
the scope where they are set.

Thanks for the response Robert.

As far as I can tell, there is a difference between $1 and a local
variable.

Yes, of course.

The output shows that in all the procs, the value of $1 is the value
produced by the last regex match. Yet a new name variable is created
every time through the each loop, and each proc closes over a different
name variable. So there is a difference between a local variable like
name and $1.

This is true but I never claimed differently. I wrote "they are local
to the scope..." and not "they are local variables". :slight_smile:

Cheers

robert

···

On Tue, Mar 1, 2011 at 8:53 PM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

On Fri, Feb 25, 2011 at 9:53 PM, 7stud -- <bbxx789_05ss@yahoo.com> >> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/