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…?
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).
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.
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
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.
I don’t understand. Neither of your #each methods takes arguments.
You have found the fix
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]
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.