如果是 Ruby 1.9,并且如果您在正则表达式中使用命名组,那么您可以将所有正则表达式组合成一个,但需要一点技巧。这是完成繁重工作的课程:
class BigPatternMatcher
def initialize(patterns_and_functions, no_match_function)
@regex = make_big_regex(patterns_and_functions)
@no_match_function = no_match_function
end
def match(s, context)
match = @regex.match(s)
context.send(function_name(match), match)
end
private
FUNC_GROUP_PREFIX = "func_"
FUNC_GROUP_REGEX = /^#{FUNC_GROUP_PREFIX}(.*)$/
def function_name(match)
if match
match.names.grep(FUNC_GROUP_REGEX).find do |name|
match[name]
end[FUNC_GROUP_REGEX, 1]
else
@no_match_function
end
end
def make_big_regex(patterns_and_functions)
patterns = patterns_and_functions.map do |pattern, function|
/(?<#{FUNC_GROUP_PREFIX}#{function}>#{pattern.source})/
end
Regexp.union(patterns)
end
end
我们将回到它是如何工作的。要使用它,您需要一个正则表达式列表以及应该为每个表达式调用的函数的名称。确保仅使用命名组:
PATTERNS_AND_FUNCTIONS = [
[/ABC(?<value>\d+)/, :foo],
[/DEF(?<value>\d+)/, :bar],
]
还有这些函数,包括在没有匹配项时调用的函数:
def foo(match)
p ["foo", match[:value]]
end
def bar(match)
p ["bar", match[:value]]
end
def default(match)
p ["default"]
end
最后,这是它的使用方式。 BigPatternMatcher#match
接受要匹配的字符串,以及调用函数的对象:
matcher = BigPatternMatcher.new(PATTERNS_AND_FUNCTIONS, :default)
matcher.match('blah ABC1 blah', self) # => ["foo", "1"
matcher.match('blah DEF2 blah', self) # => ["bar", "2"]
matcher.match('blah blah', self) # => ["default"]
请参阅折叠下方,了解使其起作用的技巧。
BigPatternMatcher#make_big_regex
将所有正则表达式组合为一个,每个都用括号括起来并用 分隔|
,例如
/(ABC(?<value>\\d+))|(DEF(?<value>\\d+))/
但这还不够。我们需要某种方式,当其中一个子表达式匹配时,识别哪个匹配,从而调用哪个函数。为此,我们将为每个子表达式创建自己的命名组,其名称基于应调用的函数:
/(?<func_foo>ABC(?<value>\\d+))|(?<func_bar>DEF(?<value>\\d+))/
现在,让我们看看我们如何从匹配到调用函数。给定字符串:
"blah ABC1 blah"
那么匹配组是:
#<MatchData "ABC1" func_foo:"ABC1" value:"1" func_bar:nil value:nil>
要确定调用哪个函数,我们只需要找到名称以“func_”开头且具有非零值的匹配项。“func_”之后的组名部分命名要调用的函数。
用你的问题中的简单技术来衡量它的性能对读者来说是一个练习。