[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Intervals in Ruby

Steven D'Aprano

10/25/2007 1:44:00 AM

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,




--
Steven
41 Answers

Bob Hutchison

10/25/2007 2:49:00 AM

0

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.

>
> 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

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

>
>
>
> Thank you,
>
>
>
>
> --
> Steven
>

----
Bob Hutchison -- tumblelog at http://
www.recursive.ca/so/
Recursive Design Inc. -- weblog at http://www.rec...
hutch
http://www.rec... -- works on http://www.racon...
cms-for-static-content/home/




Justin Collins

10/25/2007 2:53:00 AM

0

Steven D'Aprano wrote:
> Howdy,
>

Hi
> 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
>
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.

> 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,
>

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

Yossef Mendelssohn

10/25/2007 2:59:00 AM

0

On Oct 24, 8:45 pm, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> 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:
>
> 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?


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.

--
-yossef


Andreas Haller

10/25/2007 3:09:00 AM

0

> 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:

> 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,
>
>
>
>
> --
> Steven
>


Giles Bowkett

10/25/2007 5:44:00 AM

0

> > 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 Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...
Tumblelog: http://giles.t...

Giles Bowkett

10/25/2007 5:46:00 AM

0

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

--
Giles Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...
Tumblelog: http://giles.t...

Robert Klemme

10/25/2007 6:36:00 AM

0

On 25.10.2007 03:43, 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.
>
> 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?

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

Michael Ulm

10/25/2007 6:40:00 AM

0

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.
>
> 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).

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



Gordon Beaton

10/25/2007 6:56:00 AM

0

On Thu, 25 Oct 2007 15:40:00 +0900, Michael Ulm 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

--

Randy R

10/25/2007 7:27:00 AM

0


"Steven D'Aprano" <steve@REMOVE-THIS-cybersource.com.au> wrote in message
news:13hvt65e4d1vd0b@corp.supernews.com...
> Howdy,
>
> 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?

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...