Florian Gross
10/5/2004 12:36:00 AM
Bill Atkins wrote:
> Currently, the following code
>
> a = [1, 2, 3, 4, 5]
> a[0, 3]
>
> returns
>
> [1, 2, 3]
>
> This is somewhat counter-intuitive. Since Ruby has a built-in range
> type, [] ought to take advantage of it. I propose that the []
> operators be redefined so that this behavior can only be achieved by
> explicitly providing a Range, e.g. a[0...3]. The original code would
> then work like #values_at and return [1, 3].
I've also thought about this circa one and a half year ago. (And I
didn't even realize that I'm using Ruby for that long a time already. I
still feel like I've only barely scratched the surface of what this
language is able to do and there's so many unexplored libraries,
concepts, mind sets that I haven't explored properly yet...)
That aside, my solution was to use ary[[0, 1..3, 4]] for this.
I coded up an implementation of it, but please note that this is quite
old code. I didn't know about values_at at the time I wrote it and I'm
not even sure if the code is correct in all cases. (Had I written this
now, I would want to have a handful of test cases, even though I might
hesitate to write them -- I really need to make this a habit.)
I have attached the code, because it is a quick way of testing this
alternate syntax from irb -- it might be especially interesting to see
what happens with other Enumerables in action -- it is hard to think
about such border cases without an implementation.
So, what do you think about using this syntax instead? It is slightly
more complex than the one you proposed of course, but it is also
consistent with ary[1..3] and could in theory be useful in more cases.
Regards,
Florian Gross
# this changes Array#[] so that it will also take arrays
# e. g.:
# ary = ["foo", "bar", "qux", "quz", "quv"]
# ary[[0..1, 0..2, 0..3]]
# => ["foo", "bar", "foo", "bar", "qux", "foo", "bar", "qux", "quz"]
# it also changes Array#[]=
# e. g.:
# ary = (100..110).to_a
# ary[[0..3, 6..9]] = (0..3).to_a + (6..9).to_a
# ary
# => [0, 1, 2, 3, 104, 105, 6, 7, 8, 9, 110]
class Array
alias :old_get :[]
def [](index, *more)
if !more.empty?
return old_get(index, *more)
# Ranges are likely handled more efficently internally therefore we ignore them
elsif index.respond_to?(:each) and !index.is_a? Range
result = []
index.each { |subindex|
element = self[subindex]
element = [element] unless element.is_a? Array
result += element
}
return result
else
return old_get(index)
end
end
alias :old_set :[]=
def []=(index, *more)
args = more.clone
to = more.pop
old_to = to.clone
raise ArgumentError unless to
if !more.empty?
return old_set(index, *args)
# Ranges are likely handled more efficently internally therefore we ignore them
elsif index.respond_to?(:each) and !index.is_a? Range
index.each { |subindex|
self[subindex] = to.slice!(0 ... subindex.length)
}
return old_to - to
else
return old_set(index, *args)
end
end
end