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-

···

--
Posted via http://www.ruby-forum.com/.

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 are rather limits of a "static" interval. So there's 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

···

--
Posted via http://www.ruby-forum.com/.

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:

···

--
Posted via http://www.ruby-forum.com/.

Roger and Jan,

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

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 are rather limits of a "static" interval. So there's 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

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 Class: Range (Ruby 1.9.3) 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 Goldberg-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 Shnelvar

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 Shnelvar <ralphs@dos32.com>:

Roger and Jan,

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

> 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 are rather limits of a "static" interval. So there's 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

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 Class: Range (Ruby 1.9.3) 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 Goldberg-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 Shnelvar

--
-- Matma Rex

snt frum my awesum ansroid... txt mite b rong

···

On Apr 13, 2012 5:04 PM, "Ralph Shnelvar" <ralphs@dos32.com> wrote:

Roger and Jan,

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

> 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 are rather limits of a "static" interval. So there's 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

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

...

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

···

On Apr 13, 2012 5:04 PM, "Ralph Shnelvar" <ralphs@dos32.com> wrote

Bartosz,

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

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?

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 Shnelvar
<ralphs@dos32.com> napisał:

Bartosz,

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

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

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 Rex

Bartosz,

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

W dniu 14 kwietnia 2012 02:32 użytkownik Ralph Shnelvar
<ralphs@dos32.com> napisał:

Bartosz,

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

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

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"

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.