Andrew Johnson wrote:
> William James wrote:
> > Andrew Johnson wrote:
> >> If I am reading your specs correctly:
> >>
> >>   /^(((?!\[joe\]).)*(\[joe\]((?!\[joe\]).)+\[\/joe\])){1,3}((?!\[joe\]).)*$/
> >
> > [
> >   "good [joe]  [/joe]  [joe] [/joe]",
> >   "bad [/joe] [joe] [/joe]",
> >   "bad [joe] [/joe] [/joe]"
> > ].each { |s|
> >   p s =~
> > /^(((?!\[joe\]).)*(\[joe\]((?!\[joe\]).)+\[\/joe\])){1,3}((?!\[joe\]).)*$/
> > }
>
> Quite right:
>
> [
>   "good [joe]  [/joe]  [joe] [/joe]",
>   "bad [/joe] [joe] [/joe]",
>   "bad [joe] [/joe] [/joe]"
> ].each { |s|
>   p s if s =~
> /^(((?!\[\/?joe\]).)*(\[joe\]((?!\[\/?joe\]).)+\[\/joe\])){1,3}((?!\[\/?joe\]).)*$/
> }
>
> cheers
> andrew

Yours is faster for very short strings; longer strings allow the array
method to pull ahead.

require 'benchmark'

$n = 10_000
$strings = [
  "good [joe] Wasn't that what [i]he[/i] was seeking? [/joe]
     [joe] Can't you [b]see[/b] that? [/joe]",
  "bad was Peck's boy [/joe] [joe] But he'll never know. [/joe]",
  "bad to the bone [joe] Or will he?! [/joe] mish mash mush
   Marching on Tom Tidler's ground fatigues me.  [/joe]",
  "bad: too many [joe] [/joe] [joe] [/joe] [joe] [/joe] [joe] [/joe]",
  "bad: too few"
]

def regexp
  $regexp_good = 0
  $n.times{ $strings.each { |s|
    $regexp_good += 1   if s =~
/\A(((?!\[\/?joe\]).)*(\[joe\]((?!\[\/?joe\]).)+\[\/joe\])){1,3}((?!\[\/?joe\]).)*\Z/m
  } }
end
def array
  $array_good = 0
  $n.times{ $strings.each { |s|
    ary = s.scan( %r{\[/?joe\]} )
    if [2,4,6].include?(ary.size)  and
      ary == ary.partition{|t| "[joe]"==t}.inject{|a,b| a.zip(b)}.
        flatten
      $array_good += 1
    end
  } }
end

Benchmark.bmbm do |x|
  x.report("regexp") { regexp }
  x.report("array")  { array  }
end

puts  ;  p $regexp_good, $array_good

Rehearsal ------------------------------------------
regexp   6.870000   0.000000   6.870000 (  7.391000)
array    2.653000   0.000000   2.653000 (  2.874000)
--------------------------------- total: 9.523000sec

             user     system      total        real
regexp   6.940000   0.000000   6.940000 (  7.441000)
array    2.634000   0.000000   2.634000 (  2.854000)

10000
10000