[RCR?] Enumerable#each with arguments

I’ve been using REXML lately and wishing I could do something like this:

elt = … # get a REXML::Element somehow
elt_parts = elt.elements.collect(‘part’) # XPath query

The #collect call fails because it requires 0 arguments. But each can be
called with an argument, so the following does what I want:

elt_parts = []
elt.elements.each(‘part’) { |x| elt_parts << x }

Or I could use #select, but then it’s extra work to get the XPath
functionality.

This leads to a general suggestion: instance methods in Enumerable
should pass on their arguments to each. To be more specific, here’s an
example that doesn’t work now:

class Foo
include Enumerable

 def initialize; @stuff = [1,2,3]; end

 def each(n=nil)
   if n
     @stuff.each {|x| yield x if x < n}
   else
     @stuff.each {|x| yield x}
   end
 end

end

foo = Foo.new
foo.each(2) {|x| p x} # ==> 1

#p foo.collect(2) # ==> should be [1], but wrong number of args

At this point #collect can’t be called with arguments. But it’s easy to fix:

class Foo
def collect(*args)
rslt = []
each(*args) {|x| rslt << x}
rslt
end
end

p foo.collect(2) # ==> [1]

I could always redefine collect and friends in Enumerable, but why not
add this behavior in enum.c? Maybe there’s a performance cost…?

   elt_parts = elt.elements.collect('part') # XPath query

bdbxml has the same problem : see how it try to fix it.

The #collect call fails because it requires 0 arguments. But each can be
called with an argument, so the following does what I want:

What are the arguments given to #each ?

Guy Decoux

Hi,

···

In message “[RCR?] Enumerable#each with arguments” on 03/05/11, Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

#p foo.collect(2) # ==> should be [1], but wrong number of args

Hmm, but some of enumerable methods take its own argument, for example
“grep”. In addition, class dependent additional argument breaks
polymorphism. There must be better idea (e.g. external iterator object).

						matz.

ts wrote:

“J” == Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

elt_parts = elt.elements.collect(‘part’) # XPath query

bdbxml has the same problem : see how it try to fix it.

I don’t understand. Neither of your #each methods takes arguments.

The #collect call fails because it requires 0 arguments. But each can be
called with an argument, so the following does what I want:

What are the arguments given to #each ?

In the case of instances of REXML::Elements#each, an optional XPath string.

Quoteing matz@ruby-lang.org, on Mon, May 12, 2003 at 12:14:46PM +0900:

Hmm, but some of enumerable methods take its own argument, for example
“grep”. In addition, class dependent additional argument breaks
polymorphism. There must be better idea (e.g. external iterator object).

Yes, but we like using each() rather than an external iterator!

I had a similar problem with vCards. each() took an argument, but that
argument wasn’t available to all the Enumerable methods, so I had to
build versions of them that did take arguments. It would be nice to
have a better way, and passing unused args down into each seems kindof
attractive, though I’ve no idea what other problems it will cause.

Cheers,
Sam

Hi,

Quoteing matz@ruby-lang.org, on Mon, May 12, 2003 at 12:14:46PM +0900:

Hmm, but some of enumerable methods take its own argument, for example
“grep”. In addition, class dependent additional argument breaks
polymorphism. There must be better idea (e.g. external iterator object).

Yes, but we like using each() rather than an external iterator!

Yes, but it is easy like this:

Enumerable extension.

module Enumerable
class Enumerator
include Enumerable
def initialize(obj, args)
@obj = obj
@args = args
end
def each(&block)
@obj.each(
@args, &block)
end
end
def each_with(*args)
Enumerator.new(self, *args)
end
end

sample class

class Foo
include Enumerable
def each(n=100)
for i in 0…n
yield i
end
end
end

f = Foo.new
f.each(5){|i| p i}
f.each_with(5).collect{|i| i*3}

						matz.
···

In message “Re: [RCR?] Enumerable#each with arguments” on 03/05/13, Sam Roberts sroberts@uniserve.com writes:

I don't understand. Neither of your #each methods takes arguments.

You have found the fix :slight_smile:

In the case of instances of REXML::Elements#each, an optional XPath string.

and if someone want to call it with 2, 3, ... arguments ?

For me, your problem seems specific to a class (REXML::Elements)

Guy Decoux

ts wrote:

“J” == Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

I don’t understand. Neither of your #each methods takes arguments.

You have found the fix :slight_smile:

In the case of instances of REXML::Elements#each, an optional XPath string.

and if someone want to call it with 2, 3, … arguments ?

Just pass them all on, from #collect to #each. We assume that the
arguments tell #each something about how to filter or otherwise process
the elements before yielding them.

For me, your problem seems specific to a class (REXML::Elements)

Yes. I guess it would be more rubylike to always use select for this
sort of thing. But in the case that someone writes a library where #each
takes arguments, this proposal would propagate that functionality to the
other Enumerable methods.

ts wrote:

“J” == Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

I don’t understand. Neither of your #each methods takes arguments.

You have found the fix :slight_smile:

In the case of instances of REXML::Elements#each, an optional XPath string.

and if someone want to call it with 2, 3, … arguments ?

For me, your problem seems specific to a class (REXML::Elements)

There’s another example, and it’s in Ruby itself: ObjectSpace. It’s
always puzzled me that ObjectSpace doesn’t inherit from Enumerable, so
you can’t collect, select, etc. So why not do this:

class << ObjectSpace
include Enumerable
alias each each_object
end

module Enumerable
def collect(*args, &block)
rslt =
if block
each(*args) {|x| rslt << yield(x)}
else
each(*args) {|x| rslt << x}
end
rslt
end
end

p ObjectSpace.collect(Class) {|c|c.name}.sort[0…9]

The last line is a little bit easier than what works now:

tmp =
ObjectSpace.each_object(Class) {|c| tmp << c.name}
p tmp.sort[0…9]

Which one is more ruby-like?

Hi,

···

In message “Re: [RCR?] Enumerable#each with arguments” on 03/05/12, Joel VanderWerf vjoel@PATH.Berkeley.EDU writes:

There’s another example, and it’s in Ruby itself: ObjectSpace. It’s
always puzzled me that ObjectSpace doesn’t inherit from Enumerable, so
you can’t collect, select, etc. So why not do this:

It’s because I don’t want ObjectSpace to become useful too much. It’s
sort of back door, which should not be used too often.

						matz.