Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Intervals in Ruby

0 views
Skip to first unread message

Steven D'Aprano

unread,
Oct 24, 2007, 9:43:33 PM10/24/07
to
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

Bob Hutchison

unread,
Oct 24, 2007, 10:49:15 PM10/24/07
to
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.recursive.ca/
hutch
http://www.recursive.ca/ -- works on http://www.raconteur.info/
cms-for-static-content/home/


Justin Collins

unread,
Oct 24, 2007, 10:53:26 PM10/24/07
to
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

unread,
Oct 24, 2007, 10:58:34 PM10/24/07
to
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

unread,
Oct 24, 2007, 11:09:25 PM10/24/07
to
> 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

Giles Bowkett

unread,
Oct 25, 2007, 1:43:57 AM10/25/07
to
> > 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.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

Giles Bowkett

unread,
Oct 25, 2007, 1:45:34 AM10/25/07
to
hey, cool. Ruby has "for x in y" too. I had forgotten about that.

Robert Klemme

unread,
Oct 25, 2007, 2:35:46 AM10/25/07
to

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

unread,
Oct 25, 2007, 2:40:00 AM10/25/07
to
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

unread,
Oct 25, 2007, 2:55:54 AM10/25/07
to
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

--

Just Another Victim of the Ambient Morality

unread,
Oct 25, 2007, 3:26:48 AM10/25/07
to

"Steven D'Aprano" <st...@REMOVE-THIS-cybersource.com.au> wrote in message
news:13hvt65...@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...


Just Another Victim of the Ambient Morality

unread,
Oct 25, 2007, 3:27:48 AM10/25/07
to

"Andreas Haller" <andrea...@gmail.com> wrote in message
news:0C1522C0-3313-4FE0...@gmail.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?
>
> 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?


David A. Black

unread,
Oct 25, 2007, 5:46:10 AM10/25/07
to
Hi --

On Thu, 25 Oct 2007, 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?

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

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

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

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

--
Upcoming training by David A. Black/Ruby Power and Light, LLC:
* Advancing With Rails, Edison, NJ, November 6-9
* Advancing With Rails, Berlin, Germany, November 19-22
* Intro to Rails, London, UK, December 3-6 (by Skills Matter)
See http://www.rubypal.com for details!

Steven D'Aprano

unread,
Oct 25, 2007, 5:56:59 AM10/25/07
to
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

James Edward Gray II

unread,
Oct 25, 2007, 8:02:35 AM10/25/07
to
On Oct 25, 2007, at 1:40 AM, Robert Klemme wrote:

> 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

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


John Joyce

unread,
Oct 25, 2007, 10:38:05 AM10/25/07
to

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

Giles Bowkett

unread,
Oct 25, 2007, 1:18:10 PM10/25/07
to
> 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."

Joel VanderWerf

unread,
Oct 25, 2007, 1:28:54 PM10/25/07
to

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

puts ("a".."j").to_a

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

James Edward Gray II

unread,
Oct 25, 2007, 1:36:59 PM10/25/07
to

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

Devi Web Development

unread,
Oct 25, 2007, 4:10:15 PM10/25/07
to
On 10/25/07, Just Another Victim of the Ambient Morality 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.


-------------------------------------------
Daniel Brumbaugh Keeney
Devi Web Development
Devi.We...@gMail.com
-------------------------------------------

ara.t.howard

unread,
Oct 25, 2007, 4:48:05 PM10/25/07
to

On Oct 25, 2007, at 4:00 AM, Steven D'Aprano wrote:

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

yes. the most obvious one is needing adjacent entries and/or self-
modification. something along the lines of

pixels.size.times do |i|
pixel = pixels[i]
left = pixels[i - 1]
right = pixels[i + 1]

if left.nil? and condition(right) or
condition(left) and condition(right) or
condition(left) and right.nil?

pixel = transformation left, pixel, right
end
end

granted, i personally would create my own neighborhood iterator for
such a case - but then sometimes you need to compare adjacent
neighborhoods...

i would go so far as to say *any* interesting algorithm can require
random access into a data structure that's also being modified. in
those cases iterating by size is a smart alternative since very few
data structures in ruby support modification + traversal.

another good use case is

min = [a.size, b.size].min

min.times do |i|
something_with a[i] and b[i]
end

which is to say iterating datasets in parallel.

yes there is zip - fantastic if you are into duplicating both
datasets in memory *and* are iterating over n=2 data structures but
otherwise useless.

that said - i'll take #each or #map any day.

cheers.

a @ http://codeforpeople.com/
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama


ara.t.howard

unread,
Oct 25, 2007, 4:53:34 PM10/25/07
to

i use both frequently. '...' is always leads to shorter code when
complex iterations are done and, of course, lead to fewer off by one
errors since ruby arrays are zero index based.

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

no. but i've fixed alot of code that has.

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

in my experience open intervals, in c, fortran, c++ and idl often
lead to off by one errors but, that being said, i think it's less in
fortran because it uses '1' based arrays. also, anyone whose has
coded C has probably found 'off, length' to be less error prone and
open intervals lend themselves more easily to that notion.

mortee

unread,
Oct 25, 2007, 5:58:28 PM10/25/07
to
ara.t.howard wrote:
> yes. the most obvious one is needing adjacent entries and/or
> self-modification. something along the lines of

>
> pixels.size.times do |i|
> pixel = pixels[i]
> left = pixels[i - 1]
> right = pixels[i + 1]
>
> if left.nil? and condition(right) or
> condition(left) and condition(right) or
> condition(left) and right.nil?
>
> pixel = transformation left, pixel, right
> end
> end

What about #each_with_index for this case? Using pixels.size.times and
accessing each element by the yielded integer indexes explicitely
assumes that the index space is continuous - however, the same algorithm
using #each_with_index would only assume that each item yielded by it
has two adjacent neighbors.

> min = [a.size, b.size].min
>
> min.times do |i|
> something_with a[i] and b[i]
> end
>
> which is to say iterating datasets in parallel.
>
> yes there is zip - fantastic if you are into duplicating both datasets
> in memory *and* are iterating over n=2 data structures but otherwise
> useless.

Hmmm, are you sure that zip duplicates all the involved arrays even if
passed a block?

mortee


ara.t.howard

unread,
Oct 25, 2007, 7:11:20 PM10/25/07
to

On Oct 25, 2007, at 3:58 PM, mortee wrote:

> What about #each_with_index for this case? Using pixels.size.times and
> accessing each element by the yielded integer indexes explicitely
> assumes that the index space is continuous - however, the same
> algorithm
> using #each_with_index would only assume that each item yielded by it
> has two adjacent neighbors.

well, frankly, that example is made up but i have tons of image
processing code that does that in spirit - the point is that using
#each_with_index is going to give you errors: you cannot iterate
using map/each/each_with_index *while* also modifying a datastructure
- try this:

cfp:~ > ruby -e' (a=[42]).each_with_index{ a << 42 } '
...

it will never exit.

i certainly abstract that kind of code to yield the pixel and it's
neighbors (it doesn't always have them near the edges of an image)
but abstracting it will require both iteration by size *and* using
random access unless the algorithm is read only. you can see http://
kogs-www.informatik.uni-hamburg.de/~koethe/vigra/ for example of how
neighborhood iterators can generally be abstracted - my point is just
that the this *type* of problem - iterating while modifying and
looking at neighbors - requires an external iterator if one is using
the 'obvious' solution.

ara.t.howard

unread,
Oct 25, 2007, 7:23:44 PM10/25/07
to

On Oct 25, 2007, at 3:58 PM, mortee wrote:

>> yes there is zip - fantastic if you are into duplicating both
>> datasets
>> in memory *and* are iterating over n=2 data structures but otherwise
>> useless.
>
> Hmmm, are you sure that zip duplicates all the involved arrays even if
> passed a block?

pretty sure:

cfp:~ > ruby -e' p ObjectSpace.each_object(Array){}; p [4].zip([2])
{ break ObjectSpace.each_object(Array){} } '
6
9

6 to start, create 2, zip makes one more = 9

mortee

unread,
Oct 25, 2007, 9:49:22 PM10/25/07
to
ara.t.howard wrote:
> well, frankly, that example is made up but i have tons of image
> processing code that does that in spirit - the point is that using
> #each_with_index is going to give you errors: you cannot iterate using
> map/each/each_with_index *while* also modifying a datastructure - try this:
>
> cfp:~ > ruby -e' (a=[42]).each_with_index{ a << 42 } '
> ...
>
> it will never exit.
>
> i certainly abstract that kind of code to yield the pixel and it's
> neighbors (it doesn't always have them near the edges of an image) but
> abstracting it will require both iteration by size *and* using random
> access unless the algorithm is read only. you can see
> http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/ for example of

> how neighborhood iterators can generally be abstracted - my point is
> just that the this *type* of problem - iterating while modifying and
> looking at neighbors - requires an external iterator if one is using the
> 'obvious' solution.

Fair enough. However, the code in question hasn't altered the array of
pixels itself, just the pixel values. It's true that you have to pay
more attention when adding and/or deleting items while iterating.

mortee


mortee

unread,
Oct 25, 2007, 9:55:07 PM10/25/07
to
ara.t.howard wrote:
>
> On Oct 25, 2007, at 3:58 PM, mortee wrote:
>> Hmmm, are you sure that zip duplicates all the involved arrays even if
>> passed a block?
>
> pretty sure:
>
> cfp:~ > ruby -e' p ObjectSpace.each_object(Array){}; p [4].zip([2]){
> break ObjectSpace.each_object(Array){} } '
> 6
> 9
>
> 6 to start, create 2, zip makes one more = 9

It's sure to create arrays. Those ones that hold the corresponding
values from the original arrays, and only for the duration of the
current iteration. But if you zip two 100-element arrays, it won't
duplicate either simultaneously, it'll just create two-element arrays to
be passed to the block temporarily.

Is that so bad?

mortee


0 new messages