Coercion and ranges

P

Philipp Kern

Dear Ruby fellows,

what is the reason behind the fact that there is no generic coercion
system apart from the additional overhead?

Concretly I tried to add some ranges. Childish as I am I thought of a
straightforward way by implementing Range#+:

class Range
def +(other)
self.entries + other.entries
end
end

However, this does not work on multiple ranges as a temporary array is
constructed, which does not know of the ability to coerce ranges to arrays.

irb(main):001:0> (?A..?C) + (?a..?c)
=> [65, 66, 67, 97, 98, 99]
irb(main):002:0> (?A..?C) + (?a..?c) + (?0..?3)
TypeError: cannot convert Range into Array
from (irb):2:in `+'
from (irb):2

From my understanding the conversion should happen automatically when
coercion is properly implemented. Surely one could just add "to_a" to
all the ranges, but it certainly made me pondering about it.

Kind regards,
Philipp Kern
 
E

ES

Le 27/5/2005 said:
Dear Ruby fellows,

what is the reason behind the fact that there is no generic coercion
system apart from the additional overhead?

Concretly I tried to add some ranges. Childish as I am I thought of a
straightforward way by implementing Range#+:

class Range
def +(other)
self.entries + other.entries
end
end

However, this does not work on multiple ranges as a temporary array is
constructed, which does not know of the ability to coerce ranges to arrays.

irb(main):001:0> (?A..?C) + (?a..?c)
=3D> [65, 66, 67, 97, 98, 99]
irb(main):002:0> (?A..?C) + (?a..?c) + (?0..?3)
TypeError: cannot convert Range into Array
from (irb):2:in `+'
from (irb):2

From my understanding the conversion should happen automatically when
coercion is properly implemented. Surely one could just add "to_a" to
all the ranges, but it certainly made me pondering about it.

I think you probably would be able to create a properly working
addition operator, but you should consider the semantics, as well.
What logical range is represented by (?A..?C) + (?0..?3)? I believe
any range should be characterized by being able to move from start
to end by repeatedly calling #succ.

So you could presumably do something like

def +(other)
(self.first..other.last)
end
Kind regards,
Philipp Kern

E
 
J

Jim Weirich

Philipp Kern said:
From my understanding the conversion should happen automatically when
coercion is properly implemented. Surely one could just add "to_a" to
all the ranges, but it certainly made me pondering about it.

I find it a bit odd that adding two ranges results in an array, but
assuming that is /really/ what you want, you could indicate a Range's
willingness to be converted to an array by including a to_ary method.

class Range
def to_ary
to_a
end
end
(?A..?C) + (?a..?c) + (?x..?y)
=> [65, 66, 67, 97, 98, 99, 120, 121]

The difference between to_a and to_ary is that to_a is an explicit
conversion to an array. to_ary indicates that whenever the object is
found in the context where an array is desired, it can be treated as an
array by calling to_ary (an implicit conversion).
 
P

Philipp Kern

Jim said:
I find it a bit odd that adding two ranges results in an array, but
assuming that is /really/ what you want, you could indicate a Range's
willingness to be converted to an array by including a to_ary method.

Well if I see the range as an array of its elements (which you would get
when you iterate over it), then it's what one might want. Thank you,
that solved it. I thought about to_ary yesterday night, but I was not
aware that it would be called by Array#+.

Kind regards,
Philipp Kern
 
R

Robert Klemme

Philipp said:
Well if I see the range as an array of its elements (which you would
get when you iterate over it), then it's what one might want. Thank
you, that solved it. I thought about to_ary yesterday night, but I
was not aware that it would be called by Array#+.

Another probably more efficient variant would be to create a special class
that represents sums (= sequences) of Enumerables:

class EnumSequence
include Enumerable

def initialize(*enums) @enums = enums end

def each(&b)
@enums.each {|e| e.each(&b)}
self
end

def +(enum)
@enums << enum
self
end
end

class Range
def +(r) EnumSequence.new(self,r) end
end
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101, 23, 24, 25]

Kind regards

robert
 
P

Philipp Kern

Robert said:
Another probably more efficient variant would be to create a special class
that represents sums (= sequences) of Enumerables:

It's at least the more elegant solution, thank you Robert. :)

Kind regards,
Philipp Kern
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,781
Messages
2,569,615
Members
45,301
Latest member
BuyPureganics

Latest Threads

Top