Why no decreasing enumerations?

I’m curious about enumerations.

I can write concisely:

(0 … 9).to_a

which gives me an array of 10 elements.

However, if I write:

(9 … 0).to_a

I get an empty list.

The “solution” is to write:

(0 … 9).to_a.reverse

But that’s a bit cumbersome if I want to count between two end points
and I’m not sure of the order ahead of time…

a = rand(20)
b = rand(20)
([a,b].min … [a,b].max).to_a.???

It’s tempting to do something with sort here but that’s obviously
inefficient (better to loop).

It would be much simpler to be able to specify
(a … b)

Hi,

I think you misunderstand the purpose of ranges. They aren’t meant to be
ordered lists but rather some kind of abstract interval (like in
mathematics). You can check if a specific object is in the range, but
you may not even be able to enumerate all the elements of the range.

For example, try
(1.0 … 5.0).each {|el| puts el}
This raises a TypeError: “can’t iterate from Float”

Of course, you can iterate over some ranges (if the endpoints have a
succ method). But then the elements will simply be put out in the most
obvious way: from low to high. The range itself doesn’t imply a certain
order.

If you actually want to count up/down from some integer to another, you
should use an appropriate data structure like an enumerator:

a, b = rand(20), rand(20)
list = if a <= b
a.upto b
else
a.downto b
end

On Sunday, March 18, 2012 1:21:21 PM UTC-4, Jan E. wrote:

Hi,

I think you misunderstand the purpose of ranges. They aren’t meant to be
ordered lists but rather some kind of abstract interval (like in
mathematics). You can check if a specific object is in the range, but
you may not even be able to enumerate all the elements of the range.

Ranges server dual purpose, so I don’t think that’s the reason. I think
there is no other reason then no one has implemented support for
descending
ranges. It does require a new method #pred (opposite of #succ) though.

Of course, then again matz can have some peculiar objections to things
some
times, so I could be wrong.

Well, I’m glad for his objections. I don’t think we need another PHP
with all the data structures mixed together. :wink:

Back on topic: I think it’s actually a problem of mistakable syntax. It
looks like a short notation of a sequence. Like you would write

2,4,…,10

in mathematics. In fact, Haskell has this kind of list notation:

[1…5] – short for [1,2,3,4,5]

Many Ruby beginners try to write this down in Ruby, too. But in Ruby,
the dot syntax describes a range, and a range isn’t a sequence.

2012/3/18 Intransition [email protected]:

That is incorrect. Range is really more a sequence than it is an interval.

Range is both a sequence and an interval, and this is deeply wrong.

Consider:

irb(main):008:0> r = ‘a’…‘aa’
=> “a”…“aa”
irb(main):009:0> r.to_a
=> [“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “j”, “k”, “l”, “m”,
“n”, “o”, “p”, “q”, “r”, “s”, “t”, “u”, “v”, “w”, "
x", “y”, “z”, “aa”]
irb(main):010:0> r.include? ‘c’
=> true
irb(main):011:0> r.cover? ‘c’
=> false

It’s false. I don’t know about you, but this seems quite
counter-intuitive. #to_a and #include? treat range as a sequence,
#cover? as an interval. But it’s just the beginning:

irb(main):012:0> r = ‘b’…‘aa’
=> “b”…“aa”
irb(main):013:0> r.to_a
=> []
irb(main):014:0> r.cover? ‘c’
=> false
irb(main):015:0> r.include? ‘c’
=> false

Explain these three to me, would you. I think this was some time ago
reported to Ruby’s bug tracker, the conclusion was that Range has a
“feature” that if start compares as bigger than end, it won’t bother
generating the sequence - this obviously fails for many non-numeric
ranges, as the one above. I don’t think anything was done about it,
though.

– Matma R.

On Sunday, March 18, 2012 3:17:55 PM UTC-4, Jan E. wrote:

Well, I’m glad for his objections. I don’t think we need another PHP
with all the data structures mixed together. :wink:

I didn’t say he did, not that “who knows”.

the dot syntax describes a range, and a range isn’t a sequence.

That is incorrect. Range is really more a sequence than it is an
interval.

(1..3).each{ |i| p i }
1
2
3

(1..3).to_a  #=> [1,2,3]

('a'..'c').to_a  #=> ['a', 'b', 'c']

[*1..3]  #=> [1,2,3]

Just look at how all the methods work:

Only cover? really expressly implies use as an interval.

On Mon, Mar 19, 2012 at 05:18:53AM +0900, Bartosz Dziewoński wrote:

2012/3/18 Intransition [email protected]:

That is incorrect. Range is really more a sequence than it is an interval.

Range is both a sequence and an interval, and this is deeply wrong.

I dunno . . . I think it’s a duck.

On Mon, Mar 19, 2012 at 02:52:01AM +0900, Intransition wrote:

On Sunday, March 18, 2012 1:21:21 PM UTC-4, Jan E. wrote:

I think you misunderstand the purpose of ranges. They aren’t meant to be
ordered lists but rather some kind of abstract interval (like in
mathematics). You can check if a specific object is in the range, but
you may not even be able to enumerate all the elements of the range.

Ranges server dual purpose, so I don’t think that’s the reason. I think
there is no other reason then no one has implemented support for descending
ranges. It does require a new method #pred (opposite of #succ) though.

Shouldn’t that be #prec (precede/succeed, or precessive/successive)
rather than #pred?

On Sunday, March 18, 2012 5:15:51 PM UTC-4, Chad P. wrote:

Shouldn’t that be #prec (precede/succeed, or precessive/successive)
rather than #pred?

I always thought of it as “predecessor/successor”.

Very interesting stuff… I’m new to Ruby so I’m still finding my way
around, especially with features which don’t obviously map to Perl.

Anyway, the upto and downto integer methods do work better than
solutions I had identified.

Using the enumeration form instead of the array:
a = rand(20)
b = rand(20)
((a > b) ? a.downto(b) : a.upto(b)).each do |i|
puts i
end

Hardly seems to live up to the Ruby standard for doing common tasks
concisely… but not TOO bad.

If you actually have to do this task all the time, you could define your
own Integer#to method:

class Integer

def to limit, &block
if self <= limit
upto limit, &block
else
downto limit, &block
end
end

end

10.to 1 do |i|
puts i
end

I wonder why that isn’t already in Ruby. :wink:

On Mar 18, 2012, at 22:09 , Jan E. wrote:

end

10.to 1 do |i|
puts i
end

I wonder why that isn’t already in Ruby. :wink:

Have you proposed it to ruby-core@?

On Mon, Mar 19, 2012 at 12:16:40PM +0900, Intransition wrote:

On Sunday, March 18, 2012 5:15:51 PM UTC-4, Chad P. wrote:

Shouldn’t that be #prec (precede/succeed, or precessive/successive)
rather than #pred?

I always thought of it as “predecessor/successor”.

To me, that implies that there is only one successor, but I don’t know
what the person who named the method thinks about it.

On 3/19/12 7:28 AM, Ryan D. wrote:

end

Have you proposed it to ruby-core@?

This is a feature I’ve implemented several times myself as well and
would seem to be useful to have in core. Ryan, what is the recommended
way of making these proposals? Just post a message to the ruby-core
mailing list? Open a ticket? Do we still have some sort of RCRchive?

It’s been a while since I’ve asked for something in Ruby :slight_smile:

-greg

On Mon, Mar 19, 2012 at 02:09:08PM +0900, Jan E. wrote:

10.to 1 do |i|
puts i
end

I wonder why that isn’t already in Ruby. :wink:

Is this in Facets?