Intervals in Ruby

On Oct 24, 2007, at 7:45 PM, Steven D’Aprano wrote:

range(1, 5) => 1, 2, 3, 4
0…5 => 0, 1, 2, 3, 4, 5
find that having two operators … and … is a blessing, or a curse
because you can never remember which is which?

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.

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.

cheers.

a @ http://codeforpeople.com/

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/

ara.t.howard wrote:

 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.

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

mortee

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.

cheers.

a @ http://codeforpeople.com/

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

cheers.

a @ http://codeforpeople.com/

ara.t.howard wrote:

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

ara.t.howard wrote:

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