Reverse-range alternatives?

  • Thread starter Kenneth McDonald
  • Start date
K

Kenneth McDonald

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
 
R

Robert Klemme

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
 
F

fREW

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| ... }
 
K

Kenneth McDonald

fREW said:
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
 
P

Peña, Botp

From: Kenneth McDonald [mailto:[email protected]] :
# 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
 
D

Daniel DeLorme

Kenneth said:
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
 
K

Kenneth McDonald

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 said:
Kenneth said:
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
 
T

Trans

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

Rick DeNatale

First let me take the liberty of reversing the top posting:
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.
 
K

Kenneth McDonald

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
 
R

Robert Klemme

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top