I have a class where I want 'each' to yield another custom object, so I wrote this:
class Fq_price_set < Array
...
def [](i)
# instead of returning an array, it returns an Fq_price_rec based on the array at i
Fq_price_rec.new(super(i))
end
def each
c = 0
until c == size
yield self.[](c)
c += 1
end
end
...
end
This works: when I do
my_price_set.each {|rec| puts rec.class}
it shows Fq_price_rec. Similarly,
my_price_set.each {|rec| puts rec.mymethod}
outputs the proper value for that method call.
But when I use select, e.g.,
my_price_set.select {|rec| rec.mymethod == 1}
I get an error msg, "undefined method" 'mymethod' for Array:... So rec (in 'select') is not an Fq_price_rec, it's an array (of which Fq_price_rec is a subclass). I (obviously mistakenly) thou开发者_如何学运维ght that overriding 'each' would mean that the iterating methods like 'select' would use it, i.e., the subclass's version of 'each'. Is the simple answer that I must also override 'select', or is there a more elegant solution.
Yes, I'm pretty new at Ruby.
TIA
Why not get rid of inheriting from Array, and just have include Enumerable
?
class Fq_price_set
include Enumerable
def initialize(actual_array)
@actual_array = actual_array
end
# Make [] and each refer to values from @actual_array
end
Subclassing Array like that doesn't work very well. The reason is that a lot of Array is implemented inside the Ruby interpreter in C; the Array implementation makes certain assumptions about how Array behaves in order to avoid the cost of an extra round trip from the C implementation, into Ruby-land, and back down to C.
In particular, the implementation of select
looks like this:
static VALUE
rb_ary_select(VALUE ary)
{
VALUE result;
long i;
RETURN_ENUMERATOR(ary, 0, 0);
result = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) {
rb_ary_push(result, rb_ary_elt(ary, i));
}
}
return result;
}
The part that is giving you grief is this:
RARRAY_PTR(ary)[i]
Ruby is directly accessing the internal C array without going through your version of the []
operator.
So you should listen to Mr. Grimm:
- Include Enumerable.
- Add any extra methods and operators that you need to quack like an Array.
精彩评论