Behavior of $* in String subclasses

J

Jamis Buck

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
 
N

Nikolai Weibull

Jamis said:
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.

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
 
N

nobu.nokada

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
 
J

Jamis Buck

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"

Hmmm. Either I'm misunderstanding you, or you misunderstood me. :)
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
 
N

nobu.nokada

Hi,

At Sun, 12 Jun 2005 13:09:28 +0900,
Jamis Buck wrote in [ruby-talk:145191]:
Hmmm. Either I'm misunderstanding you, or you misunderstood me. :)
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 $~.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,173
Messages
2,570,937
Members
47,481
Latest member
ElviraDoug

Latest Threads

Top