Intervals in Ruby

Howdy,

I’m not a Ruby developer at all, I use Python, but this is not flame-
bait. I’m interested in how Ruby folks find using intervals.

In Python, we deal with integer ranges virtually exclusively with the
range() function. range() always results in a half-open interval:

range(5) => 0, 1, 2, 3, 4
range(1, 5) => 1, 2, 3, 4
range(4, -1, -1) => 4, 3, 2, 1, 0

The start argument is always included, the end argument is never
included, and there is an optional step size (defaults to 1).

I understand that in Ruby you have quite a few choices, some of which
are
half-open like Python, some of which are closed:

0…5 => 0, 1, 2, 3, 4, 5
0…5 => 0, 1, 2, 3, 4

5.downto(1) => 5, 4, 3, 2, 1
1.upto(5) => 1, 2, 3, 4, 5
5.times() => 0, 1, 2, 3, 4
5.step(11, 3) => 5, 8, 11

and the Range.new method.

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

How useful are the closed interval forms? Do you find yourself making
off-
by-one errors or needing to increment/decrement variables by one?

e.g. do you often need to write things like:

start.step(end + 1, increment){| i | block }
start.step(end - 1, increment){| i | block }

Writing in Python, I almost never need to “shift the fence-posts”, so to
speak. E.g. I virtually never need to write something like:

range(start, end+1)

to avoid an off-by-one error. When I used to program in Pascal (which
exclusively uses closed intervals) I used to need to do it all the time.
What’s the Ruby experience?

Thank you,

Hi Steven,

On 24-Oct-07, at 9:45 PM, Steven D’Aprano wrote:

Howdy,

I’m not a Ruby developer at all, I use Python, but this is not
flame-
bait. I’m interested in how Ruby folks find using intervals.

I can only speak for myself.

I understand that in Ruby you have quite a few choices, some of
which are
half-open like Python, some of which are closed:

0…5 => 0, 1, 2, 3, 4, 5
0…5 => 0, 1, 2, 3, 4

You need to write (0…5).to_a to get an array, though you don’t need
to convert it to an array for the usual kind of things like looping
and choosing elements of an array.

5.downto(1) => 5, 4, 3, 2, 1
1.upto(5) => 1, 2, 3, 4, 5
5.times() => 0, 1, 2, 3, 4
5.step(11, 3) => 5, 8, 11

and the Range.new method.

No arrays here either (if that matters to you)

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

I had a look through about 25,000 lines of Ruby I working with now.

I’ve never used …, only … – and then only for choosing parts of
an array.

In my installed gems (about 60k lines but there are multiple versions
of some libraries) this seems to happen in 6 files dealing with
bigdecimals, telnet, http, webrick (a web server), and in an xml
parser. The most interesting use is in a case statement handling HTTP
error codes.

How useful are the closed interval forms? Do you find yourself
making off-
by-one errors or needing to increment/decrement variables by one?

e.g. do you often need to write things like:

start.step(end + 1, increment){| i | block }
start.step(end - 1, increment){| i | block }

This does not appear in my code base. I habitually iterate over
collections and arrays. I can’t recall ever needing the a sequence of
integers (except in benchmarks and tests)

Step shows up in the total code base I’ve got (roughly 80k lines)
seven times, downto appears twice. Range.new shows up 4 times and in
all cases with true for the third argument (i.e. equivalent to …
rather than …)

Writing in Python, I almost never need to “shift the fence-posts”,
so to
speak. E.g. I virtually never need to write something like:

range(start, end+1)

I have had to do this once in the 25k lines: (3…(ARGV.length -
1)).each – which, now that you’ve reminded me is better written
(3…ARGV.length).each – I’ll change that now. Thanks.

to avoid an off-by-one error. When I used to program in Pascal (which
exclusively uses closed intervals) I used to need to do it all the
time.
What’s the Ruby experience?

In a few other languages this has definitely been something of an
issue. Definitely.

Basically, I don’t use that idiom, I iterate over collections. I
think other Ruby programmers don’t do that either.

Interesting. Thanks. Nice question.

Cheers,
Bob


Bob H. – tumblelog at http://
www.recursive.ca/so/
Recursive Design Inc. – weblog at http://www.recursive.ca/
hutch
http://www.recursive.ca/ – works on http://www.raconteur.info/
cms-for-static-content/home/

Steven D’Aprano wrote:

Howdy,

Hi

The start argument is always included, the end argument is never
included, and there is an optional step size (defaults to 1).

I understand that in Ruby you have quite a few choices, some of which are
half-open like Python, some of which are closed:

0…5 => 0, 1, 2, 3, 4, 5
0…5 => 0, 1, 2, 3, 4

I nearly always use 0…5
Sometimes 0…5 in special cases.

5.downto(1) => 5, 4, 3, 2, 1
1.upto(5) => 1, 2, 3, 4, 5
5.times() => 0, 1, 2, 3, 4
5.step(11, 3) => 5, 8, 11

Rarely do I use these. And even then it’s only 5.times do … end

and the Range.new method.

Never.

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse

because you can never remember which is which?

Maybe it’s just me, but I nearly always use … so it’s easy to remember
that … is the “special” case for me.

How useful are the closed interval forms? Do you find yourself making off-
by-one errors or needing to increment/decrement variables by one?

e.g. do you often need to write things like:

start.step(end + 1, increment){| i | block }
start.step(end - 1, increment){| i | block }

Never.

Thank you,

Maybe I’m unusual (most of my code is for my own pet projects) but
nearly all my iteration is done with arrays and hashes or objects that
act like them, so my most commonly used loop construct is ‘each’. After
that it’s probably ‘until’, then ‘while’, and ‘times’ last.

That’s been my experience.

-Justin

On Oct 24, 8:45 pm, Steven D’Aprano <st…@REMOVE-THIS-
cybersource.com.au> wrote:

and the Range.new method.

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

I can only speak from my own experience.

I came to Ruby from Perl, so I knew … and … backwards and forwards.
There might have been a bit of a stumbling block when I was learning
Perl, but it’s been so long that it all seems natural to me.

And speaking of Perl and “natural”, much of that has to do with the
language’s creator/designer being a linguist (or at least people like
to say so). When I came to Ruby, it felt like Matz took up that cause
and ran with it. For many problems, there are many, many solutions.
You choose which one works for you. Methods like Integer#times,
Integer#downto, Integer#upto, Numeric#step all have their uses.
Choosing the right one feels like choosing the right word to construct
a sentence.

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

Hi
i think it makes perfect sense to have those two options and with
such a similar syntax. I had to think a second everytime to remember
which is which, but i got used to it now. (Although it does not make
much sense to me that three dots mean “less” than two dots.)

[…]. range() always results in a half-open interval:
range(1, 5) => 1, 2, 3, 4
I had to look up the terms “half-open” and “half-closed”. http://
en.wikipedia.org/wiki/Interval_(mathematics) says that an half-open
interval (2,4] excludes 2, but includes 4. So in Python that would be
half-closed intervals by default (right?), ikewise in Ruby.

I did not know upto() and downto() Uups.
Together with n.times they are just nice to have and you can use the
one message that would be the most expressive, depending what you
want to write.

What i find strage about ranges is that:
(0…3).to_a # => [0, 1, 2, 3]
but
(3…0).to_a # => []

Regarding the “off-by-one” question, i dont really know.

andreas

Am 25.10.2007 um 03:45 schrieb Steven D’Aprano:

hey, cool. Ruby has “for x in y” too. I had forgotten about that.


Giles B.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

I had a look through about 25,000 lines of Ruby I working with now.

I’ve never used …, only … – and then only for choosing parts of
an array.

I used … last week a couple times, for what that’s worth.

In my installed gems (about 60k lines but there are multiple versions
of some libraries) this seems to happen in 6 files dealing with
bigdecimals, telnet, http, webrick (a web server), and in an xml
parser. The most interesting use is in a case statement handling HTTP
error codes.

e.g. do you often need to write things like:

start.step(end + 1, increment){| i | block }
start.step(end - 1, increment){| i | block }

This does not appear in my code base. I habitually iterate over
collections and arrays. I can’t recall ever needing the a sequence of
integers (except in benchmarks and tests)

Honestly, I think I might have seen #step before once, in a book.
I’ve definitely never seen it in production code that I can recall,
and I’ve definitely never used it myself. The idiom of iterating over
collections became very natural to me very quickly and I think it’s
likely that this is the case for other Ruby programmers in general.

I don’t know if this is an important part of programming in Python or
not. I’ve played with Python but everything like that, I think I did
with “for x in y” loops. (those were cool, by the way.) I did do a few
“for x in range(y)” loops, so I could see how it would matter.

in Ruby it’s nearly always

collection.each {|foo| bar}

Even when you’re dealing with a subset of the collection, or dealing
with the collection in subsets of X number, it’s more like

collection.each { |foo| in_groups_of(bar) { baz }}

There’s more than one way to do it, like in Perl, but in practice
people seem to gravitate to this particular way, to the exclusion of
other ways.

(By the way the syntax is pseudocode.)


Giles B.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

On 25.10.2007 03:43, Steven D’Aprano wrote:

range(4, -1, -1) => 4, 3, 2, 1, 0

range(start, end+1)

to avoid an off-by-one error. When I used to program in Pascal (which
exclusively uses closed intervals) I used to need to do it all the time.
What’s the Ruby experience?

I rarely use #downto, #upto and #step. I frequently use #times and
ranges - usually the “…” version but sometimes also the other one. I
never use Range.new.

Both range versions come in handy, for example, if you need to iterate
through an array using an index counter then the triple dot is
convenient because you do not need to to math:

for i in 0…ar.size
puts ar[i]
end

#times is an alternative here

ar.size.times do |i|
puts ar[i]
end

I think Justing pinpointed the major reason why these integer iterating
constructs are less use in Ruby: collections are typically traversed via
their #each method.

Kind regards

robert

Steven D’Aprano wrote:

range(4, -1, -1) => 4, 3, 2, 1, 0

The start argument is always included, the end argument is never
included, and there is an optional step size (defaults to 1).

If there is only one kind of range to be supported (which I understand
to be in sync with the Python philosophy), I agree that half-open
intervals are the way to go.

I understand that in Ruby you have quite a few choices, some of which are
half-open like Python, some of which are closed:

0…5 => 0, 1, 2, 3, 4, 5
0…5 => 0, 1, 2, 3, 4

The two different ranges are nice-to-have, because sometimes the closed
interval is the right thing to use. I’d have exchanged the syntax
'though.
Having said that, I have never had a problem distinguishing the two.

5.downto(1) => 5, 4, 3, 2, 1
1.upto(5) => 1, 2, 3, 4, 5
5.times() => 0, 1, 2, 3, 4
5.step(11, 3) => 5, 8, 11
–snip–

This is not really a range, but the Ruby Way of looping; it is what made
me
fall in love with Ruby in the first place. For me, it is one the most
beautiful aspects of any programming language that I know.

Best regards,

Michael

On Thu, 25 Oct 2007 15:40:00 +0900, Michael U. wrote:

If there is only one kind of range to be supported (which I
understand to be in sync with the Python philosophy), I agree that
half-open intervals are the way to go.

For iterating, I agree.

But for case expressions, I prefer the interval to be inclusive.
Example from the pickaxe book:

kind = case year
when 1850…1889 then “Blues”
when 1890…1909 then “Ragtime”
when 1910…1929 then “New Orleans Jazz”
when 1930…1939 then “Swing”
when 1940…1950 then “Bebop”
else “Jazz”
end

/gordon

“Andreas Haller” [email protected] wrote in message
news:[email protected]

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

Hi
i think it makes perfect sense to have those two options and with such a
similar syntax. I had to think a second everytime to remember which is
which, but i got used to it now. (Although it does not make much sense to
me that three dots mean “less” than two dots.)

I think of it as the shorter keyword being the one more often used. 

As
I’m sure you know, half open intervals are used far more often than
closed
intervals…

It's a little scary that they're so similar but do different things 

in
that it may be easy to mistake one for the other. It would be an
annoying
typo bug to track down…

[…]. range() always results in a half-open interval:
range(1, 5) => 1, 2, 3, 4
I had to look up the terms “half-open” and “half-closed”. http://
en.wikipedia.org/wiki/Interval_(mathematics) says that an half-open
interval (2,4] excludes 2, but includes 4. So in Python that would be
half-closed intervals by default (right?), ikewise in Ruby.

I don't think that it would be half open, by default, in Python. 

More
like it will always be half open in Python.

What i find strage about ranges is that:
(0…3).to_a # => [0, 1, 2, 3]
but
(3…0).to_a # => []

I find this strange, as well...
Aside from breaking many lines of code, how bad would it be to 

change
it?

Hi –

On Thu, 25 Oct 2007, Steven D’Aprano wrote:

range(4, -1, -1) => 4, 3, 2, 1, 0

5.downto(1) => 5, 4, 3, 2, 1
1.upto(5) => 1, 2, 3, 4, 5
5.times() => 0, 1, 2, 3, 4
5.step(11, 3) => 5, 8, 11

and the Range.new method.

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

They’re both useful. Don’t forget that a range is not really a
collection (though some ranges can masquerade as arrays) as a kind of
boolean inclusion tester. The exclusive version (…) is like having <
as well as <= as a comparison operator – very handy :slight_smile:

As for the various methods above, I use times quite a bit, downto and
upto and step not all that often (but I wouldn’t be surprised if there
are cases where I might have used them but didn’t think of it).

range(start, end+1)

to avoid an off-by-one error. When I used to program in Pascal (which
exclusively uses closed intervals) I used to need to do it all the time.
What’s the Ruby experience?

It all depends where the endpoints are coming from. If I can control
the end one, then I don’t have to change it. If I don’t, then I do.
I don’t think it’s any different from any other such situation –
e.g.:

items.each_with_index do |item,index|
puts “Item #{index + 1}: #{item.description}”
end

It’s more about the problem space than the Ruby methods you’re using.

David

On Thu, 25 Oct 2007 01:43:33 +0000, Steven D’Aprano wrote:

I understand that in Ruby you have quite a few choices, some of which
are half-open like Python, some of which are closed:

My question is: how useful are all these different mechanisms? Do you
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

How useful are the closed interval forms? Do you find yourself making
off- by-one errors or needing to increment/decrement variables by one?

Thank you for everyone who wrote back, all good food for thought.

Are there many (any?) good examples of where you need to iterate over an
integer count, rather than directly over a data structure?

“Steven D’Aprano” [email protected] wrote in
message
news:[email protected]

Writing in Python, I almost never need to “shift the fence-posts”, so to
speak. E.g. I virtually never need to write something like:

range(start, end+1)

to avoid an off-by-one error. When I used to program in Pascal (which
exclusively uses closed intervals) I used to need to do it all the time.
What’s the Ruby experience?

They're obviously not used a lot but they can be useful.
They're really for times when you know the start and end points of

indexing, rather than the start and length of indexing. My girlfriend
in
highschool (who was wonderfully nerdy) was well aware of off-by-one
errors
while not being a computer programmer (she was into math and physics and
went into engineering) because of problems like asking how many days
someone
has stayed somewhere if they arrived on the 5’th and left on the 16’th.
The
naive answer, of course, is 16 - 5 but, upon closer inspection, it would
actually be 16 - 5 + 1. Apparently, this pattern has happened to her
more
than once 'cause it came up (somehow) and she brought alot of attention
to
this fact, so much so that I still remember this and am relaying it to
all
of you…
So, in Python, if you wanted to access the fifth to sixteenth
element in
an array, you’d need to do something like this:

for i in range(5, 16 + 1):
array[i] # do something with this…

Some people might be tempted to just stick 17 in there but I 

wouldn’t…

On Oct 25, 2007, at 5:00 AM, Steven D’Aprano wrote:

off- by-one errors or needing to increment/decrement variables by


Steven

If there is one thing Ruby gets right over every other language it’s
iteration/looping mechanisms.
they look strange at first, but use them a bit and the feel natural

On Oct 25, 2007, at 1:40 AM, Robert K. wrote:

ar.size.times do |i|
puts ar[i]
end

letters = (0…9).map { |i| (?a + i).chr }
=> [“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “j”]

letters.each_index do |i|
?> puts letters[i]

end
a
b
c
d
e
f
g
h
i
j
=> [“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “j”]

James Edward G. II

Are there many (any?) good examples of where you need to iterate over an
integer count, rather than directly over a data structure?

Fizzbuzz. Anything where the problem space involves integers. Ruby
collection iterating becomes very normal to you when you use it. Any
problem space which doesn’t involve integers and does involve
collections you’ll probably address with collections.

Also, strictly speaking, when you iterate over a collection, you’re
interacting with the collection in an OO manner, while using integer
indices to access their contents sequentially probably violates the
Law of Demeter or something. I want to avoid flame wars, especially
since if there was a flame war about that, it’d be Ruby & Smalltalk
vs. the universe, but the point seems valid. Getting into
implementation instead of just telling the object “do this, I don’t
care how.”


Giles B.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

On Oct 25, 2007, at 12:28 PM, Joel VanderWerf wrote:

#times is an alternative here
b
What was that example meant to show? Must be something other than
this:

puts (“a”…“j”).to_a

Yeah, that was dumb of me. That wasn’t the point though. I was
showing each_index() which was recreated above in two different ways.

James Edward G. II

James Edward G. II wrote:

#times is an alternative here
a
b
c
d
e
f
g
h
i
j
=> [“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “j”]

What was that example meant to show? Must be something other than this:

puts (“a”…“j”).to_a

On 10/25/07, Just Another Victim of the Ambient M. wrote:

What i find strage about ranges is that:
(0…3).to_a # => [0, 1, 2, 3]
but
(3…0).to_a # => []

I find this strange, as well...
Aside from breaking many lines of code, how bad would it be to change

it?

Ranges are based upon comparisons of what is less than something else.
In fact, if I recall correctly, to generate an array (Range#to_a)
calls next on the start of the range until it reaches the end of the
range. With your range (3…0) it would go [3,4,5…Infinity], so ruby
opts to produce an empty array.