JnB*_*ymn 1 ruby regex finite-automata callback
我有一个函数,它接受一个输入字符串,然后通过几个正则表达式运行字符串,直到找到匹配.找到匹配后,我返回一个输出,该输出是原始字符串和匹配的函数.所以在红宝石中:
str = "my very long original string ... millions of characters"
case str
when regex1
do_something1(str,$1)
when regex2
do_something2(str,$1)
when regex3
do_something3(str,$1)
...
when regex100000
do_something100000(str,$1)
else
do_something_else(str)
end
Run Code Online (Sandbox Code Playgroud)
现在,Ruby实际上是在优化这个开关循环,以便str只被遍历一次吗?假设它不是,那么使用嵌入了回调的一个很长的正则表达式可以更有效地执行此功能.像这样的东西:
/(?<callback:do_something1>regex1)|
(?<callback:do_something2>regex2)|
(?<callback:do_something3>regex3)|
...
(?<callback:do_something100000>regex100000)/
Run Code Online (Sandbox Code Playgroud)
是否有任何技术可以做到这一点?
如果是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
Run Code Online (Sandbox Code Playgroud)
我们将回到它是如何工作的.要使用它,您需要一个正则表达式列表以及应为每个表达式调用的函数名称.确保仅使用命名组:
PATTERNS_AND_FUNCTIONS = [
[/ABC(?<value>\d+)/, :foo],
[/DEF(?<value>\d+)/, :bar],
]
Run Code Online (Sandbox Code Playgroud)
和函数,包括在没有匹配时被调用的函数:
def foo(match)
p ["foo", match[:value]]
end
def bar(match)
p ["bar", match[:value]]
end
def default(match)
p ["default"]
end
Run Code Online (Sandbox Code Playgroud)
最后,这是它的使用方式. 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"]
Run Code Online (Sandbox Code Playgroud)
请参阅下面的折叠,了解让它发挥作用的技巧.
BigPatternMatcher#make_big_regex将所有正则表达式组合成一个,每个正则表达式括在括号中并由|例如
/(ABC(?<value>\\d+))|(DEF(?<value>\\d+))/
Run Code Online (Sandbox Code Playgroud)
但这还不够.当其中一个子表达式匹配时,我们需要某种方式来识别哪一个匹配,从而确定要调用哪个函数.为此,我们将使每个子表达式成为自己的命名组,其名称基于应该调用的函数:
/(?<func_foo>ABC(?<value>\\d+))|(?<func_bar>DEF(?<value>\\d+))/
Run Code Online (Sandbox Code Playgroud)
现在,让我们看看我们如何从匹配到调用函数.鉴于字符串:
"blah ABC1 blah"
Run Code Online (Sandbox Code Playgroud)
然后匹配组是:
#<MatchData "ABC1" func_foo:"ABC1" value:"1" func_bar:nil value:nil>
Run Code Online (Sandbox Code Playgroud)
要确定要调用哪个函数,我们只需要找到名称以"func_"开头并且具有非零值的匹配项."func_"之后的组名称部分命名要调用的函数.
在你的问题中根据简单的技术测量这种表现对于读者来说是一种练习.
| 归档时间: |
|
| 查看次数: |
352 次 |
| 最近记录: |