2012-07-04 2 views
1

나는 입력 문자열을 취하여 일치하는 문자열을 찾을 때까지 여러 정규식을 통해 문자열을 실행하는 기능을 가지고 있습니다. 일치가 발견되면 원래 문자열과 일치의 함수 인 출력을 반환합니다. 그래서 루비 : 그 한 번만 통과됩니다 STR 있도록일치를 기반으로 콜백을 사용한 정규식

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 

을 이제 루비는 실제로이 스위치 루프를 최적화한다? 가정하지 않는다면,이 기능은 콜백이 내장 된 하나의 큰 긴 정규 표현식으로 훨씬 더 효율적으로 수행 될 수 있습니다. 다음과 같은 내용이 있습니다 :

/(?<callback:do_something1>regex1)| 
(?<callback:do_something2>regex2)| 
(?<callback:do_something3>regex3)| 
    ... 
(?<callback:do_something100000>regex100000)/ 

어떤 기술이 있습니까?

답변

6

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 

어떻게 돌아가는지 돌아 보겠습니다. 이를 사용하려면 정규식 목록과 각 함수에 대해 호출해야하는 함수 이름이 필요합니다. 마지막으로, 여기가 사용되는 방법,

def foo(match) 
    p ["foo", match[:value]] 
end 

def bar(match) 
    p ["bar", match[:value]] 
end 

def default(match) 
    p ["default"] 
end 

을 그리고 : 일치가 없을 때 호출되는 것을 포함하여,

PATTERNS_AND_FUNCTIONS = [ 
    [/ABC(?<value>\d+)/, :foo], 
    [/DEF(?<value>\d+)/, :bar], 
] 

그리고 기능을 : 단지라는 이름의 그룹을 사용해야합니다. 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_"어느로 시작하는 이름을 가진 일치를 찾을 수있다 0이 아닌 값을가집니다. "func_"다음에 나오는 그룹 이름의 일부는 호출 할 함수의 이름을 지정합니다. 귀하의 질문에 간단한 기술에 대한이의 성능을 측정


독자들에게 운동이다.

+0

어 ... 이제는 매끄러운 코덱입니다. 방금 그걸 생각해 냈어? – JnBrymn

+1

@ 존, 감사합니다. 그렇습니다. 이전에 정규 표현식을 결합했지만 Ruby 1.8에서는 명명 된 그룹이 없으므로 매우 추했습니다. 그건 그렇고, 나는'BigPatternMatcher'를 편집하여 불일치 처리 버그를 수정하고 죽은 코드를 제거했습니다. 당신이 그것을 사용하기로 결정했다면, 당신은 그것을 다시 잡고 싶어 할 것입니다. –

+2

제안 된 (사소한) 리펙토링 : 문자열을'|'와 결합하고'Regexp.new'로 전달하는 대신'Regexp.union'을 사용하십시오. 추가 제안 :이 코드에서 GitHub Gist를 만들면 앞으로 커뮤니티에서 사용할 수있게 될 것입니다. (사람들이 코드를 포크하고 수정 사항을 공유 할 수 있습니다 ...). –

관련 문제