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

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

or

((25)…(28)).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 :orig_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 :orig_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¢

Oops, I meant re-definition of Integer#succ :slight_smile:

···

— Jeff Mitchell quixoticsycophant@yahoo.com wrote:

There are natural kinds of Ranges which, currently, which would
require a preposterous definition such as Float#succ or Integer#succ.


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

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

require “enumerator”
5.enum_for(:step, 8, 0.5).map { |x| #stuff }.other_stuff

···

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

Thanks, I never knew about that. Just another one of those neat
features in the standard library which is pushed into obscurity
due to having no documentation.

···

— Kristof Bastiaensen kristof@vleeuwen.org wrote:

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

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

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

require “enumerator”
5.enum_for(:step, 8, 0.5).map { |x| #stuff }.other_stuff


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