Reverse-range alternatives?

Discussion in 'Ruby' started by Kenneth McDonald, Jun 4, 2007.

  1. Since a reverse range (eg. 4...1) is functionally almost the same as an
    empty range, is there an alternative in the standard library, where
    each would actually iterate over the elements from first to last, in
    this case 4, 3, 2?

    Thanks,
    Ken
    Kenneth McDonald, Jun 4, 2007
    #1
    1. Advertising

  2. On 04.06.2007 22:51, Kenneth McDonald wrote:
    > Since a reverse range (eg. 4...1) is functionally almost the same as an
    > empty range, is there an alternative in the standard library, where each
    > would actually iterate over the elements from first to last, in this
    > case 4, 3, 2?


    $ ruby -e '4.downto 1 do |i| p i end'
    4
    3
    2
    1

    robert@fussel ~
    $ ruby -e '4.step 1, -1 do |i| p i end'
    4
    3
    2
    1

    Kind regards

    robert
    Robert Klemme, Jun 4, 2007
    #2
    1. Advertising

  3. Kenneth McDonald

    fREW Guest

    On 6/4/07, Kenneth McDonald <> wrote:
    > Since a reverse range (eg. 4...1) is functionally almost the same as an
    > empty range, is there an alternative in the standard library, where
    > .each would actually iterate over the elements from first to last, in
    > this case 4, 3, 2?
    >
    > Thanks,
    > Ken
    >
    >


    You could do 4.downto(2) { |i| ... }
    --
    -fREW
    fREW, Jun 4, 2007
    #3
  4. fREW wrote:
    > On 6/4/07, Kenneth McDonald <> wrote:
    >> Since a reverse range (eg. 4...1) is functionally almost the same as an
    >> empty range, is there an alternative in the standard library, where
    >> .each would actually iterate over the elements from first to last, in
    >> this case 4, 3, 2?
    >>
    >> Thanks,
    >> Ken
    >>
    >>

    >
    > You could do 4.downto(2) { |i| ... }

    Oh, of course. I'm still not entirely used to thinking of numbers as
    having a bunch of their own methods. Thanks!

    Ken
    Kenneth McDonald, Jun 4, 2007
    #4
  5. Kenneth McDonald

    Peña, Botp Guest

    From: Kenneth McDonald [mailto:] :
    # Since a reverse range (eg. 4...1) is functionally almost the=20
    # same as an=20
    # empty range, is there an alternative in the standard library, where=20
    # .each would actually iterate over the elements from first to last, in=20
    # this case 4, 3, 2?

    assumming we're all talking about range.

    irb(main):014:0> x
    =3D> 1..4
    irb(main):015:0> x.class
    =3D> Range
    irb(main):016:0> x.last
    =3D> 4
    irb(main):017:0> x.first
    =3D> 1
    irb(main):018:0> x.last.downto x.first do |e|
    irb(main):019:1* p e
    irb(main):020:1> end
    4
    3
    2
    1
    =3D> 4


    but i really hope something of a bidirectional range, ie, (4..1).to_a =
    =3D=3D [4,3,2,1] and (4..1)=3D=3D(1..4).reverse, and then =
    (4..1).each{|x| p x =3D>4,3,2,1

    right now (4..1) is useless, but it does not _err..

    kind regards -botp
    Peña, Botp, Jun 5, 2007
    #5
  6. Kenneth McDonald wrote:
    > Since a reverse range (eg. 4...1) is functionally almost the same as an
    > empty range, is there an alternative in the standard library, where
    > .each would actually iterate over the elements from first to last, in
    > this case 4, 3, 2?


    I can't resist this one...

    class Range
    def reverse
    r = dup
    def r.each(&block)
    last.downto(first, &block)
    end
    r
    end
    end

    >> (1..9).to_a

    => [1, 2, 3, 4, 5, 6, 7, 8, 9]
    >> (1..9).reverse.to_a

    => [9, 8, 7, 6, 5, 4, 3, 2, 1]

    :-D

    Daniel
    Daniel DeLorme, Jun 5, 2007
    #6
  7. I'd thought of that, but it's simply too risky. Changing the behavior of
    something as fundamental as Range could really screw up if another
    required module counted on that behavior.

    Generally, I'll reopen a class to add methods to it, but not to change
    its behavior.

    Too bad, though, that the original Range type didn't have different
    semantics, if only to throw an exception when given an inverted range.


    Cheers,
    Ken

    Daniel DeLorme wrote:
    > Kenneth McDonald wrote:
    >> Since a reverse range (eg. 4...1) is functionally almost the same as
    >> an empty range, is there an alternative in the standard library,
    >> where .each would actually iterate over the elements from first to
    >> last, in this case 4, 3, 2?

    >
    > I can't resist this one...
    >
    > class Range
    > def reverse
    > r = dup
    > def r.each(&block)
    > last.downto(first, &block)
    > end
    > r
    > end
    > end
    >
    > >> (1..9).to_a

    > => [1, 2, 3, 4, 5, 6, 7, 8, 9]
    > >> (1..9).reverse.to_a

    > => [9, 8, 7, 6, 5, 4, 3, 2, 1]
    >
    > :-D
    >
    > Daniel
    >
    >
    Kenneth McDonald, Jun 5, 2007
    #7
  8. Kenneth McDonald

    Trans Guest

    On Jun 4, 4:51 pm, Kenneth McDonald <>
    wrote:
    > Since a reverse range (eg. 4...1) is functionally almost the same as an
    > empty range, is there an alternative in the standard library, where
    > .each would actually iterate over the elements from first to last, in
    > this case 4, 3, 2?


    Would be nice if Range supported this. It would mean working off a
    #pred, not just #succ. It's been a while since I've messed with it,
    but I'm pretty sure Facets' Interval class does this.

    For a lite solution however you might consider:

    (-4..-1).each { |i| i.abs }

    T.
    Trans, Jun 5, 2007
    #8
  9. First let me take the liberty of reversing the top posting:
    On 6/5/07, Kenneth McDonald <> wrote:
    > Daniel DeLorme wrote:
    > >
    > > I can't resist this one...
    > >
    > > class Range
    > > def reverse
    > > r = dup
    > > def r.each(&block)
    > > last.downto(first, &block)
    > > end
    > > r
    > > end
    > > end

    >
    > I'd thought of that, but it's simply too risky. Changing the behavior of
    > something as fundamental as Range could really screw up if another
    > required module counted on that behavior.
    >
    > Generally, I'll reopen a class to add methods to it, but not to change
    > its behavior.


    Actually if you look carefully that's what his code does. He added a
    method to range which returns a new instance of range with a singleton
    method which overrides each. Normal instances of range won't be
    affected.

    It's a nice usage of the nested method definitions we were discussing recently.

    Bravo Daniel.

    Of course the reversed range should probably also invariants like:

    (1..3).reverse.last == (1..3).reverse.to_a.last

    And methods like to_s and step should also do the right thing too.

    If you want to go that far it's probably better to have a ReverseRange
    class and have the Range#reverse return an instance of that.

    --
    Rick DeNatale

    My blog on Ruby
    http://talklikeaduck.denhaven2.com/
    Rick DeNatale, Jun 5, 2007
    #9
  10. Oops, my bad, I saw the class being reopened and jumped to a conclusion
    without even looking at the code. Thanks for pointing this out.

    But I still wish it were possible to write (3...1) and have it do
    something that (IMHO) would be a bit more useful than the current
    behavior. :) Oh well, too late now.

    K

    Rick DeNatale wrote:
    > First let me take the liberty of reversing the top posting:
    > On 6/5/07, Kenneth McDonald <> wrote:
    >> Daniel DeLorme wrote:
    >> >
    >> > I can't resist this one...
    >> >
    >> > class Range
    >> > def reverse
    >> > r = dup
    >> > def r.each(&block)
    >> > last.downto(first, &block)
    >> > end
    >> > r
    >> > end
    >> > end

    >>
    >> I'd thought of that, but it's simply too risky. Changing the behavior of
    >> something as fundamental as Range could really screw up if another
    >> required module counted on that behavior.
    >>
    >> Generally, I'll reopen a class to add methods to it, but not to change
    >> its behavior.

    >
    > Actually if you look carefully that's what his code does. He added a
    > method to range which returns a new instance of range with a singleton
    > method which overrides each. Normal instances of range won't be
    > affected.
    >
    > It's a nice usage of the nested method definitions we were discussing
    > recently.
    >
    > Bravo Daniel.
    >
    > Of course the reversed range should probably also invariants like:
    >
    > (1..3).reverse.last == (1..3).reverse.to_a.last
    >
    > And methods like to_s and step should also do the right thing too.
    >
    > If you want to go that far it's probably better to have a ReverseRange
    > class and have the Range#reverse return an instance of that.
    >
    Kenneth McDonald, Jun 5, 2007
    #10
  11. On 06.06.2007 00:40, Kenneth McDonald wrote:
    > Oops, my bad, I saw the class being reopened and jumped to a conclusion
    > without even looking at the code. Thanks for pointing this out.
    >
    > But I still wish it were possible to write (3...1) and have it do
    > something that (IMHO) would be a bit more useful than the current
    > behavior. :) Oh well, too late now.


    The real issue here is that there are at least two useful ways to deal
    with ranges where the second element lies before the first one:

    1. no iteration

    This is useful for scenarios where you somehow determine the end point
    and you want to iterate only if it is to the right of the starting
    point. For example

    def show_silly_example(s, start, char)
    (start .. s.index(char)).each do |i|
    puts s
    end
    end

    2. backwards iteration

    This is useful when you want to be able to do backward iteration.

    Given the fact that not foo all possible range endpoints there is a
    meaningful #pred method, I guess option 1 is actually a better choice:

    irb(main):012:0> "ab".succ
    => "ac"
    irb(main):013:0> "ab".pred
    NoMethodError: undefined method `pred' for "ab":String
    from (irb):13
    from :0
    irb(main):014:0>

    IMHO a ReverseRange class would be good, but at the moment I cannot
    think of a compelling syntax that would make creation as straightforward
    as for Range.

    Kind regards

    robert
    Robert Klemme, Jun 6, 2007
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. dogbite
    Replies:
    4
    Views:
    693
    osmium
    Oct 10, 2003
  2. Replies:
    46
    Views:
    970
    Antoon Pardon
    Jul 25, 2006
  3. Lambda
    Replies:
    2
    Views:
    398
    James Kanze
    Jul 16, 2008
  4. Tomoyuki Kosimizu

    Range does not take an Range object.

    Tomoyuki Kosimizu, Nov 25, 2003, in forum: Ruby
    Replies:
    3
    Views:
    151
    Tomoyuki Kosimizu
    Nov 27, 2003
  5. David Bird
    Replies:
    1
    Views:
    209
    Tiago Macedo
    Jun 23, 2008
Loading...

Share This Page