Unable to do "down" range?

Hello.
Since this is possible
range = 40…10

I would have expected this to work:
range.each{|n| puts n} # it doesn’t

Shouldn’t that work?
-roger-

Hi,

No, this doesn’t work, because 40…10 is an empty range. A range
consists of all objects between the left limit and the right limit.

We’ve already discussed this topic here (I cannot find it right now). My
personal opinion is that many people are confusing ranges with
sequences, because the “…” syntax looks like you’re enumerating
numbers:

4…10 => 4, 5, 6, …, 10

But a range isn’t a sequence. You’re not saying “count from … to …”.
The numbers you give are rather limits of a “static” interval. So there
isn’t a real direction implied, the elements are simply put out in the
most obvious way: from low to high.

I think Integer#downto is what you’re looking for:

40.downto 10 do |i|
puts i
end

Roger and Jan,

Friday, April 13, 2012, 1:23:54 PM, you wrote:

JE> Hi,

JE> No, this doesn’t work, because 40…10 is an empty range. A range
JE> consists of all objects between the left limit and the right limit.

JE> We’ve already discussed this topic here (I cannot find it right
now). My
JE> personal opinion is that many people are confusing ranges with
JE> sequences, because the “…” syntax looks like you’re enumerating
JE> numbers:

4…10 =>> 4, 5, 6, …, 10

JE> But a range isn’t a sequence. You’re not saying “count from … to
…”.
JE> The numbers are rather limits of a “static” interval. So there’s
isn’t a
JE> real direction implied, the elements are simply put out in the most
JE> obvious way: from low to high.

JE> I think Integer#downto is what you’re looking for:

JE> 40.downto 10 do |i|
JE> puts i
JE> end

I have thought about this problem and I think I may make my contribution
to the Ruby language an extension that would allow negative ranges as
you describe.

There are problems, though.

How many pieces of code would break if
range = 40…10
range.each{|n| puts n} # it doesn’t
actually did what you want?

Probably not many.

The documentation in http://www.ruby-doc.org/core-1.9.3/Range.html says
each {| i | block } → rng click to toggle source
each → an_enumerator

Iterates over the elements rng, passing each in turn to the block. You
can only iterate if the start object of the range supports the succ
method (which means that you can’t iterate over ranges of
Float objects).

To do what Roger wants, we would need to define a prev function (similar
to the succ function) on Fixnums.

3.succ # 4
4,prev # Undefined!

Fortunately, this is easy to do in Ruby

class Fixnum
def prev
self - 1
end
end

4.prev # 3

So, should we clutter Ruby with a collection of prev functions (because
we’d also need to do it for all the things that succ is defined on)?
Possibly break existing code?

I’d vote yes.

There would be at least two advantages to having a well-defined way of
dealing with negative ranges beyond the fact that we’d have the
relatively minor advantage of having a negative range.

As I understand the code right now,

(1…10_000_000).last(5) # [9999996, 9999997, 9999998, 9999999,
10000000]

will create a temporary array of ten million elements and then peel off
the last five array elements.

Now from my perspective, having to allocate that relatively huge
temporary array is awful; truly Rube G.-esque. Implementing a
prev function that would operate on the “end object” (in this instance,
10_000_000) the way that succ works on the “start object” would
eliminate the need for that temporary array. If we had that in the
implementation of Ruby, then we could get access to the last five
elements more-or-less directly.

Ralph S.

Apart from the problems Bartosz mentioned, I still don’t see the
advantage of “down ranges” over using iterators or enumerators. I mean,
isn’t that exactly what iterators/enumerators are for? You can use them
to count up, count down, count with a different step length etc.

Sure, they are a bit cumbersome, because they don’t have their own
literal. But instead of overloading the Range class more and more, I’d
rather introduce a new class for tuples/sequences together with new
literals:

up_seq = <1, …, 10>
down_seq = <10, …, 1>
step_seq = <2, 4, …, 10>

(Much like Haskell’s list notation)

Maybe we’ll even have list comprehensions one day. :expressionless:

Now what should string[1…-2] do in your version? And would the end
result with at least 3 different behaviors for ranges make any sense?

Ruby ranges are currently not sequences and not really intervals
either, but a horrible mixture of the two, and they can’t be “fixed”
without breaking, like, all existing code.

2012/4/13, Ralph S. [email protected]:

snt frum my awesum ansroid… txt mite b rong

On Apr 13, 2012 5:04 PM, “Ralph S.” [email protected] wrote

As I understand the code right now,

(1…10_000_000).last(5) # [9999996, 9999997, 9999998, 9999999, 10000000]

will create a temporary array of ten million elements and then peel off
the last five array elements.

Now from my perspective, having to allocate that relatively huge
temporary array is awful; truly Rube G.-esque.

This can be done with lazy evaluation… take a look at haskell, though
in
some ways it is the arch-nemesis of ruby, it is a very cool language
with
some awesome features! And porting some of those features (like lazy
eval)
would be pretty cool imho…

hex

snt frum my awesum ansroid… txt mite b rong

Bartosz,

Saturday, April 14, 2012, 2:31:51 AM, you wrote:

BD> W dniu 14 kwietnia 2012 02:32 użytkownik Ralph S.
BD> [email protected] napisał:

Bartosz,

Friday, April 13, 2012, 3:55:46 PM, you wrote:

BD> Now what should string[1…-2] do in your version? And would the end
BD> result with at least 3 different behaviors for ranges make any sense?

I’m not sure what string[1…2] is supposed to do. What do you mean it to do?

BD> string[1…-2], with a minus. It currently returns first to
BD> second-to-last letters of the string. As in:

irb(main):001:0>> string = ‘abcdefg’
=>> “abcdefg”
irb(main):002:0>> string[1…-2]
=>> “bcdef”

That’s cool. I recently saw this usage of a range as a subscript at a
DeRailed meeting.

To answer your question about “Now what should string[1…-2] do in your
version?”, it would do the exact same thing. No changes to the
semantics of ranges used as subscripts.

The original poster wanted a change to the behavior of “each”

range = 40…10
range.each{|n| puts n} # it doesn’t

I think the OP’s request is a reasonable one.

Bartosz,

Friday, April 13, 2012, 3:55:46 PM, you wrote:

BD> Now what should string[1…-2] do in your version? And would the end
BD> result with at least 3 different behaviors for ranges make any
sense?

I’m not sure what string[1…2] is supposed to do. What do you mean it
to do?

W dniu 14 kwietnia 2012 02:32 użytkownik Ralph S.
[email protected] napisał:

Bartosz,

Friday, April 13, 2012, 3:55:46 PM, you wrote:

BD> Now what should string[1…-2] do in your version? And would the end
BD> result with at least 3 different behaviors for ranges make any sense?

I’m not sure what string[1…2] is supposed to do. What do you mean it to do?

string[1…-2], with a minus. It currently returns first to
second-to-last letters of the string. As in:

irb(main):001:0> string = ‘abcdefg’
=> “abcdefg”
irb(main):002:0> string[1…-2]
=> “bcdef”

– Matma R.