From: Jamis Buck Date: 2004-10-18T03:12:03+09:00 Subject: Re: [QUIZ] Regexp.build() (#4) --------------080605000108030807000709 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit So, according to my calculations, 48+ hours have elapsed. Thus, here's my solution to Regexp.build(). I assumed the following: 1) leading zeros are accepted (ie, "0004" =~ Regexp.build(4) matches) 2) nothing is captured (besides the match itself) 3) "2004" !~ Regexp.build(4) - Jamis Ruby Quiz wrote: > The three rules of Ruby Quiz: > > 1. Please do not post any solutions or spoiler discussion for this quiz until > 48 hours have passed from the time on this message. > > 2. Support Ruby Quiz by submitting ideas as often as you can: > > http://www.grayproductions.net/ruby_quiz/ > > 3. Enjoy! > > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= > > 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. > > Here are some examples of possible usage: > > lucky = Regexp.build( 3, 7 ) > "7" =~ lucky # => true > "13" =~ lucky # => false > "3" =~ lucky # => true > > month = Regexp.build( 1..12 ) > "0" =~ month # => false > "1" =~ month # => true > "12" =~ month # => true > day = Regexp.build( 1..31 ) > "6" =~ day # => true > "16" =~ day # => true > "Tues" =~ day # => false > year = Regexp.build( 98, 99, 2000..2005 ) > "04" =~ year # => false > "2004" =~ year # => true > "99" =~ year # => true > > num = Regexp.build( 0..1_000_000 ) > "-1" =~ num # => false > > Some issues you may want to consider while building you're library: > > * How should leading zeros be handled? > > Match the hour from a clock formatted in military time (0 to 23). Hours 0 > through 9 may or may not have a single leading zero. > > * Should anything be captured by the returned Regexp? > > * How should anchoring work? > > "2004" =~ Regexp.build( 4 ) # => ??? > > . > -- Jamis Buck jgb3@email.byu.edu http://www.jamisbuck.org/jamis --------------080605000108030807000709 Content-Type: text/plain; name="regexp.rb" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="regexp.rb" class Regexp class NumericRegexpBuilder def initialize @patterns = [] end def add_pattern( pattern ) @patterns << pattern end alias :<< :add_pattern def to_regexp Regexp.new( "(?:^|[^-])\\b0*(?:" + @patterns.map{|p| "(?:#{p})"}.join( "|" ) + ")\\b" ) end end def self.build( *parms ) raise ArgumentError, "expected at least one parameter" if parms.empty? builder = NumericRegexpBuilder.new parms.each do |parm| case parm when Numeric builder << parm when Range parm.each { |i| builder << i } else raise ArgumentError, "unsupported parm type #{parm.class} (#{parm.inspect})" end end return builder.to_regexp end end if $0 == __FILE__ require 'test/unit' class TC_Regexp < Test::Unit::TestCase def test_build_none assert_raise( ArgumentError ) do Regexp.build end end def test_build_one_integer re = Regexp.build( 5 ) assert_match re, "5" assert_match re, "!5!" assert_match re, "!00005," assert_no_match re, "15" assert_no_match re, "52" end def test_build_multiple_integers re = Regexp.build( 5, 7, 15 ) assert_match re, "5" assert_match re, "!5!" assert_match re, "!00005," assert_match re, "015" assert_match re, "007" assert_no_match re, "52" assert_no_match re, "57" assert_no_match re, "070" end def test_build_one_range re = Regexp.build( 0..100 ) assert_match re, "000" assert_match re, "052" assert_match re, "15,32" assert_match re, "100" assert_no_match re, "777" assert_no_match re, "101" end def test_build_multiple_ranges re = Regexp.build( 0..10, 20...35, 71..77 ) assert_match re, "000" assert_match re, "34" assert_match re, "000072" assert_no_match re, "11" assert_no_match re, "35" end def test_mix_and_match re = Regexp.build( 0, 5, 10..15, 17, 21, 31...35, 70...100 ) assert_match re, "0" assert_match re, "005" assert_match re, "012" assert_no_match re, "22" assert_no_match re, "35" assert_no_match re, "100" end def test_negative re = Regexp.build( 0..5 ) assert_no_match re, "-1" re = Regexp.build( -5..5 ) assert_no_match re, "-1" end end end --------------080605000108030807000709--