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

J

Jeff Mitchell

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

((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 :eek: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 :eek: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
 
K

Kristof Bastiaensen

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_for:)step, 8, 0.5).map { |x| #stuff }.other_stuff
 
T

Tim Sutherland

Jeff Mitchell said:
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
[...]

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.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top