> There's been some discussion on Ruby Talk lately about Range.member? which tests
> if a given element (often a number) is a member of the set the Range object
> iterates over.  Obviously, this kind of test is useful in many aspects of
> programming, but let's approach this problem from a different angle.
> 
> This week's quiz is to build a library that adds a class method called build()
> to Regexp.  build() should accept a variable number of arguments which can
> include integers and ranges of integers.  Have build() return a Regexp object
> that will match only integers in the set of passed arguments.

Well, I came up with two solutions... But I wasn't able to complete
both of them. Here's the easier one; It splats ranges into arrays,
flattens the whole mess, makes sure they are all integers, uniqs them,
then joins them with pipes and embeds them in a regular expression. It
allows an arbitrary number of leading zeros, and anchors to the start
and end of the line. I didn't really spend any time on it, so I didn't
bother with negative values; I'm not sure what they would do.

def Regexp.build(*args)
  # splat ranges into arrays of numbers, convert to integers,
  # remove duplicates, and sort the list
  numbers = args.map{|n| [*n] }.flatten.map{|n| n.to_i }
  # create a range from the list of numbers
  /^0*(?:#{numbers.uniq.join("|")})$/
end

My other solution was a lot better; I just got too frustrated trying
to make it. I kept thinking I had it working, then a border case would
pop up that didn't work properly. So, I'll submit the idea for
inspection, maybe someone will pick it up and run with it.

Here's the basic algorithm:

1. break the range up into regexp friendly sections, like this:
  (23..1024) =>
    23..29,
    30..99,
    100..999,
    1000..1019,
    1020..1024

2. convert each range into a string regexp:
  23..29 => "2[3-9]"
  30..99 => "[3-9]\\d"
  100..999 => "[1-9]\\d\\d"
  1000..1019 => "10[01]\\d"
  1020..1024 => "102[0-4]"

3. join them all together
  /^0*(?:2[3-9]|[3-9]\d|[1-9]\d\d|10[01]\d|102[0-4])$/

That gets the most compact regexp for each range. But I've been
beating my head against the wall getting it programmed. I'll keep
thinking about it, but...

Anyone?

cheers,
Mark