Calling to_enum on a MatchData object

Hi, I have the following snippet of code:

def x
File.open(‘screen.css’) do |f|
while line = f.gets
file = line.match(/"(\w*.css)"/)
puts file.class #MatchData
puts file.methods #to_enum defined
e = file.to_enum
puts e.class #Enumerator
puts e.methods #each method defined
e.each do |entry|
puts entry
end
end
end
end

The result of the regex operation gets stored in “file” as a MatchData
object. Then I convert it to an Enumerator object, which by all accounts
has an each method defined. Yet when I try to “e.each” here’s what I
get:

undefined method `each’ for #<MatchData ““reset.css”” 1:“reset.css”>
(NoMethodError).

Then I check the docs for the MatchData class:
http://www.ruby-doc.org/core/classes/MatchData.html

and find that no, “to_enum” isn’t defined. But then I don’t understand
why
file.methods lists it, and why no error is generated when I call
file.to_enum.

Help really appreciated.

to_enum() is defined in Object, and all classes inherit from Object, so
the MatchData class does have a to_enum() method. However, you need to
hook up an enumerator’s each() method to an iteration
method of another object
. The iteration method you choose is specified
as an argument in the call to to_enum(). If you don’t specify an
iteration method, the default is each():

======
class Object

obj.to_enum(method = :each, *args)

But the MatchData class does not define an each() method–hence your
error.

Also, your regex is faulty: a . matches anything not just a period. Try
this instead:

line = ‘hello “world.css” goodbye’

md = line.match(/" (\w* [.] css) "/xms)

By the way, one way to get a handle on which instance methods a class
defines itself and which are inherited is like this:

not_inherited = false
puts MatchData.instance_methods(not_inherited).sort

#‘false’ means don’t display inherited methods

–output:–

[]
begin
captures
end
eql?
hash
inspect
length
names
offset
post_match
pre_match
regexp
size
string
to_a
to_s
values_at

Then to find to_enum() you need to look in parent classes and any
modules that MatchData includes. It would be nice if the docs specified
any parent class to make the hierarchy easy to follow.

Thank you for your explanations, I can see now that :each is an
inherited method and it had to be ferreted out of the MatchData set of
methods.

So in order to use an enumerator, an object itself has to define an
(:each) method and then an enumerator just augments that. I agree that
enum_for(:some_method) is better syntax. This is what I’ve done just to
experiment:

def x
File.open(‘screen.css’) do |f|
while line = f.gets
file = line.match(/" (\w* [.] css) “/xms)
puts file.class
puts file.respond_to?(:each) #false
file = line.match(/” (\w* [.] css) "/xms).captures
puts file.class #Array
puts file.class.instance_methods(false).sort #each is defined

  e = file.enum_for(:each) #just the default
  e.each do |entry|  #now the enumerator works
    puts entry
  end

end

end
end

Regarding the regexp: yeah - I was using the universal matcher instead
of the dot character by mistake. It worked to fetch the filenames but it
was a false positive of course.

“It would be nice if the docs specified any parent class to make the
hierarchy easy to follow.”

That would be helpful, definitely.

Cheers, Vahagn

You also might want to use the synonym enum_for() instead of to_enum(),
so that it is readily apparent that you are supposed to supply an
argument (“enum_for what?”). Sometimes to_enum() will read better in
the code.

Vahagn H. wrote in post #991914:

Thank you for your explanations, I can see now that :each is an
inherited method and it had to be ferreted out of the MatchData set of
methods.

I’m not sure what you are referring to because each() is neither defined
nor inherited in the MatchData class. Check the output of:

md = /h/.match(‘hello’)
puts md.methods.sort

And Enumerator defines its own each() method.

So in order to use an enumerator, an object itself has to define an
(:each) method and then an enumerator just augments that.

No, each() is not a requirement. An enumerator can be hooked up to any
“iteration method” for an object. An “iteration method” is any method
that calls yield(). Here is an example:

class Dog
def bark
yield ‘woof’
yield ‘whooooo’
yield ‘ruuuff’
end
end

dog = Dog.new
e = dog.enum_for(:bark)

results = e.select{|sound| sound[0] == ‘w’}
p results

–output:—
[“woof”, “whooooo”]

results = e.map{|sound| “#{sound.capitalize}” << ‘!’ }
p results

–output:–
[“Woof!”, “Whooooo!”, “Ruuuff!”]

if e.include?(‘woof’)
puts ‘yes’
end

–output:–
yes

You may be confusing Enumer-ator with something you read about
Enumer-able. If you want to make the Enumer-able methods available to
your class, e.g. group_by(), each_slice(), select(), map(), include?(),
etc., then your class has to:

  1. Define an each() method, and
  2. include Enumerable

Now the confusing part: the Enumer-ator class defines a few of its own
methods, but it also includes Enumer-able, so an enumerator object has
both the Enumerator and Enumerable methods available to it.

An enumerator can also be hooked up to a block that you define:

e = Enumerator.new do |y|
10.upto(14) do |num|
y << num
end
end

results = e.map{|x| x + 5}
p results

–output:–
[15, 16, 17, 18, 19]

That ‘y’ thing is a ruby object into which you insert(<<) values, and it
knows how to produce the values on command when the enumerator asks for
them.

Here’s how you can use an enumerator to create an infinite array–from
which you can print out finite chunks:

e = Enumerator.new do |y|
(0…9).cycle do |num|
y << num
end
end

p e.take(15)

–output:–
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4]

On Apr 9, 2011, at 8:03 AM, Vahagn H. wrote:

 e = file.enum_for(:each) #just the default
 e.each do |entry|  #now the enumerator works
   puts entry
 end

end
end
end

You are doing lots of unnecessary work there. Since MatchData#captures
returns an array and Array already defines each, you don’t need to
create
any enumerator at all to iterate through the #captures array:

end
end
end

Gary W.

Gary W. wrote in post #991944:

On Apr 9, 2011, at 8:03 AM, Vahagn H. wrote:

 e = file.enum_for(:each) #just the default
 e.each do |entry|  #now the enumerator works
   puts entry
 end

end
end
end

You are doing lots of unnecessary work there. Since MatchData#captures
returns an array and Array already defines each, you don’t need to
create
any enumerator at all to iterate through the #captures array:

As the op said:

This is what I’ve done just to
experiment:

Hi,

I’m not sure what you are referring to because each() is neither defined
nor inherited in the MatchData class. Check the output of:

md = /h/.match(‘hello’)
puts md.methods.sort

And Enumerator defines its own each() method.

Sorry - I meant to say “enum_for / to_enum” was inherited and had to be
ferreted out. The reason why I made this mistake is because of this:

o = Object.new
e = o.enum_for #No argument given, so :each is implied. No errors!
e.next #NoMethodError: undefined method `each’ for #Object:0x12ed00
o.respond_to?(:each) #false. Aha!

My confusion with this stems from line 2 above - that you can define a
method using a non-existent method as default argument and not get any
error message as a consequence of that. But hey - “Ruby is dynamic and
so is human nature”, so I think I am going to like this behavior anyway.

You may be confusing Enumer-ator with something you read about
Enumer-able.

Exactly! I was mixing them up with regards to “each” and “yield”. “The
Well-Grounded Rubyist” is a great book though.

Thanks again for your help - your examples are excellent.

7stud – wrote in post #991942:

Here’s how you can use an enumerator to create an infinite array–from
which you can print out finite chunks:

e = Enumerator.new do |y|
(0…9).cycle do |num|
y << num
end
end

p e.take(15)

–output:–
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4]

I just tried out (0…9).cycle {|n| puts n} in an irb session and had to
Ctrl+C it to stop the loop. However when wrapped in a Enumerator like
your example above, just an Enumerator::Generator object is returned.
What happens to the infinite loop - does it still crunch the numbers out
in the background or does it “stop” at some point? I looked at puts
e.methods.sort
but they aren’t very revealing of what’s going on with the loop…

You are doing lots of unnecessary work there. Since MatchData#captures
returns an array and Array already defines each, you don’t need to
create
any enumerator at all to iterate through the #captures array:

As the op said:

This is what I’ve done just to
experiment:

Exactly - that was just to get proof of Enumerator working. In my
method, I just use :each of an Array object:

def x
File.open(‘screen.css’) do |f|
while line = f.gets
file = line.match(/" (\w* [.] css) "/xms).captures
file.each do |entry|
puts entry
y(entry)
end
end
end
end

On Wed, Apr 13, 2011 at 1:25 PM, Vahagn H. [email protected]
wrote:

e.methods.sort
but they aren’t very revealing of what’s going on with the loop…

The logic is a bit tricky. :slight_smile: The Enumerator::Generator is just
something which can enumerate something. There is no infinite loop
until you start iterating that instance via #each or any other method
defined in Enumerable. Actually the loop is “interrupted” every time
“y << num” is invoked.

Demonstration without infinite loop:

irb(main):003:0> e = Enumerator.new {|y| puts “+”; 5.times {|i| puts
“-#{i}”; y << i}}
=> #<Enumerator: #Enumerator::Generator:0x1093d1a8:each>
irb(main):004:0> e.each {|a| puts “*#{a}”}
+
-0
*0
-1
*1
-2
*2
-3
*3
-4
*4
=> 5

You can see how the block from line 4 and the last block in line 3 are
called interleavingly. Here’s another way to look at it:

irb(main):013:0> e = Enumerator.new {|y| puts “+”, y.class; y << 0}
=> #<Enumerator: #Enumerator::Generator:0x1097b340:each>
irb(main):014:0> e.each { puts caller(0) }
+
Enumerator::Yielder
(irb):14:in block in irb_binding' (irb):13:in<<’
(irb):13:in block in irb_binding' (irb):14:ineach’
(irb):14:in each' (irb):14:inirb_binding’
/opt/lib/ruby/1.9.1/irb/workspace.rb:80:in eval' ... /opt/bin/irb19:12:in
=> #Enumerator::Yielder:0x10978f14

Actually the block of #each is invoked through Enumerator::Yielder#<<.

Kind regards

robert

On Apr 13, 2011, at 7:28 AM, Vahagn H. wrote:

Exactly - that was just to get proof of Enumerator working. In my
method, I just use :each of an Array object:

OK. I guess I didn’t read closely enough.

Gary W.

Posted by Robert K. (robert_k78) on 2011-04-13 13:45

The logic is a bit tricky. :slight_smile: The Enumerator::Generator is just
something which can enumerate something. There is no infinite loop
until you start iterating that instance via #each or any other method
defined in Enumerable. Actually the loop is “interrupted” every time
“y << num” is invoked.

OK. So I guess this syntax:

e = Enumerator.new do |y|
(0…9).cycle do |num|
y << num
end
end

Is more declarative in that it just declares an enumerator without
setting off the infinite loop inside. To execute the loop one needs
something like:

e.take(15)

And then there is no infinite loop either, as the amount of iterations
is scoped by the argument to take().

Great examples in your post, I appreciate it!

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs