Propose Range.new(..., &succ)

Discussion in 'Ruby' started by Jeff Mitchell, Apr 24, 2004.

1. Jeff MitchellGuest

(5..8).map { |x|
# stuff
}.other_stuff

and at some point I wanted x to be in increments of 0.5. What to do?
Well I could be silly and define Float#succ. Short of that, I need to
change the code to either

accum = []
5.step(8, 0.5) { |x|
accum <<
# stuff
}
accum.other_stuff

or

((2*5)..(2*8)).map { |x|
x = x.to_f/2
# stuff
}.other_stuff

both of which seem unappealing compared to the original.

There are natural kinds of Ranges which, currently, which would
require a preposterous definition such as Float#succ or Integer#succ.
And we can't assume it's possible to create an array to use as the
elements of your Enumeration because such an array can be arbitrarily
large.

I propose giving Range.new an optional block which acts as succ
function:

Range.new(4, 16){|x| x + 4}.map{|x| x}
# => [4, 8, 12, 16]

Range.new(3.1, 6.2){|x| x + 0.3}.map{|x| x}
# => [3.1, 3.4, 3.7, 4.0, 4.3, 4.6, 4.9, 5.2, 5.5, 5.8, 6.1]

Range.new("a", "r"){|x| x.succ.succ}.map{|x| x}
# => ["a", "c", "e", "g", "i", "k", "m", "o", "q"]

Such a block naturally must assume <=> since it's possible to miss the
endpoint.

Here is a ruby implementation. (Note #member and #step need to be
defined as well.)

class Range
alias_method rig_init, :initialize
def initialize(first, last, exclude_end = false, &succ_block)
orig_init(first, last, exclude_end)
@succ_block = nil
@succ_block = succ_block if block_given?
end

alias_method rig_each, :each
def each(&block)
if @succ_block.nil?
orig_each(&block)
else
cur = self.first
while true
comp = (cur <=> self.last)
if comp > 0 or (comp == 0 and self.exclude_end?)
break
else
block.call(cur)
cur = @succ_block.call(cur)
end
end
self
end
end
end

__________________________________
Do you Yahoo!?
Yahoo! Photos: High-quality 4x6 digital prints for 25¢
http://photos.yahoo.com/ph/print_splash

Jeff Mitchell, Apr 24, 2004

2. Kristof BastiaensenGuest

On Sun, 25 Apr 2004 02:20:03 +0900, Jeff Mitchell wrote:

> The other day I had
>
> (5..8).map { |x|
> # stuff
> }.other_stuff
>
> and at some point I wanted x to be in increments of 0.5. What to do?
> Well I could be silly and define Float#succ. Short of that, I need to
> change the code to either
>
> accum = []
> 5.step(8, 0.5) { |x|
> accum <<
> # stuff
> }
> accum.other_stuff
>
> ...

Hi,
here is an alternative, using the incredible enum_for
(doesn't work with ruby-1.6).

require "enumerator"
5.enum_forstep, 8, 0.5).map { |x| #stuff }.other_stuff

Kristof Bastiaensen, Apr 25, 2004

3. Tim SutherlandGuest

In article <>, Jeff Mitchell
wrote:
>
>(5..8).map { |x|
> # stuff
>}.other_stuff
>
>and at some point I wanted x to be in increments of 0.5. What to do?
>Well I could be silly and define Float#succ. Short of that, I need to
>change the code to either
>
>accum = []
>5.step(8, 0.5) { |x|
> accum <<
> # stuff
>}
>accum.other_stuff

[...]

As an alternative,

(0..10.0).step(0.5) { |x|
# stuff
}

Note that at least one of the ends of the range must be a Float (rather than
an Integer), otherwise you will get a "ArgumentError: step can't be 0"
exception.

--
Tim Sutherland <>
2004 SDKACM President
Software Developers' Klub - the University of Auckland ACM Student Chapter
http://www.sdkacm.com/

Tim Sutherland, Apr 26, 2004