Behavior of $* in String subclasses

Here's an interesting snafu I ran into today:

   class Substring < String
     def sub!(pat, r=nil, &b)
       super(pat, r, &b)
       # --------------------
       p $1 # -> "ll"
       # --------------------
     end
   end

   s = Substring.new("hello")
   s.sub!(/(ll)/, "r")

   # --------------------
   p $1 # -> nil!!!
   # --------------------

The captured subgroup in the regexp is correctly assigned to $1 when examined inside the overridden #sub! method, but when checked after the invocation of the overridden method, the value of $1 is nil.

Why?

- Jamis

Jamis Buck wrote:

The captured subgroup in the regexp is correctly assigned to $1 when
examined inside the overridden #sub! method, but when checked after
the invocation of the overridden method, the value of $1 is nil.

A related example of something resembling something I was trying once:

def a
  /abc/.match("abc")
end

def b
  p $~[0]
end

a; b
        => NoMethodError: undefined method `[]' for nil:NilClass

My conclusion was that $~ and its relatives weren’t as global as their
insignia might suggest.

Why?

I was wondering the same thing but figured that it was desired behavior
as I found no previous discussion of it on the mailing list,
        nikolai

···

--
Nikolai Weibull: now available free of charge at http://bitwi.se/!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

Hi,

At Sun, 12 Jun 2005 04:29:27 +0900,
Jamis Buck wrote in [ruby-talk:145177]:

   class Substring < String
     def sub!(pat, r=nil, &b)
       super(pat, r, &b)

         m = eval("proc{$~}", b).call # get caller's MatchData

       # --------------------
       p $1 # -> "ll"
       # --------------------

         p m[1] # -> "ll"

···

     end
   end

--
Nobu Nakada

Hmmm. Either I'm misunderstanding you, or you misunderstood me. :slight_smile: This still doesn't allow the caller of Substring#sub! to access the captured subgroups via the $digit variables.

However, further hunting has uncovered (among other information) http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/79303, which seems to indicate that there is no way to do this in Ruby. Thus, I've changed my approach. I was basically trying to discover whether a String has been modified and was inserting code to capture the self-modifying methods and set a flag. Instead, I've changed it to do the following:

   class Substring < String
     def initialize(str="")
       super(str)
       reset_dirty!
     end

     def dirty?
       @original_contents != self
     end

     def reset_dirty!
       @original_contents = dup
     end
   end

I don't like having to do #dup, but it's not that big of a deal because Ruby does copy-on-write anyway and the string's contents won't be copied until the string is actually modified (right?). Anyway, this seems to work, and still allows sub! to work right with captured subgroups. Calling #dirty? is more expensive than I would like, but it won't be called very frequently.

- Jamis

···

On Jun 11, 2005, at 8:22 PM, nobu.nokada@softhome.net wrote:

Hi,

At Sun, 12 Jun 2005 04:29:27 +0900,
Jamis Buck wrote in [ruby-talk:145177]:

   class Substring < String
     def sub!(pat, r=nil, &b)
       super(pat, r, &b)

         m = eval("proc{$~}", b).call # get caller's MatchData

       # --------------------
       p $1 # -> "ll"
       # --------------------

         p m[1] # -> "ll"

     end
   end

Hi,

At Sun, 12 Jun 2005 13:09:28 +0900,
Jamis Buck wrote in [ruby-talk:145191]:

>> class Substring < String
>> def sub!(pat, r=nil, &b)
>> super(pat, r, &b)
>>

            $~ = eval("proc{$~}", b).call

>
>> # --------------------
>> p $1 # -> "ll"
>> # --------------------
>>
>> end
>> end
>>

Hmmm. Either I'm misunderstanding you, or you misunderstood me. :slight_smile:
This still doesn't allow the caller of Substring#sub! to access the
captured subgroups via the $digit variables.

$digit variables are just wrappers of $~.

···

--
Nobu Nakada