2011-03-19 4 views
7

루프 :어떻게이 루프를 빠르게 할 수 있습니까? 한 번에 여러 용어를 대체하는 수업이 있습니까?

var pattern = _dict[key]; 
string before; 
do 
{ 
    before = pattern; 
    foreach (var pair in _dict) 
     if (key != pair.Key) 
      pattern = pattern.Replace(string.Concat("{", pair.Key, "}"), string.Concat("(", pair.Value, ")")); 
} while (pattern != before); 
return pattern; 

그냥 않는 키의 무리에서 찾기 및 바꾸기를 반복했다. 사전은 단지 <string,string>입니다.

이 두 가지 개선 사항을 확인할 수 있습니다.

  1. 언제든지 pattern.Replace 문자열의 처음부터 다시 검색합니다. 첫 번째 {을 눌렀을 때 (아마도 바이너리 검색을 사용하여) 일치하는 키 목록을 살펴본 다음 해당 키를 바꾸는 것이 더 좋을 것입니다.
  2. pattern != before 비트는 반복하는 동안 바뀐 것이 있는지 확인하는 방법입니다. pattern.Replace 함수가 실제로 얼마나 많은 대체물이 반환되었거나 실제로 대체 된 경우 대체물이 필요하지 않을 것입니다.

그러나 ... 나는 모든 것을하기 위해 큰 불쾌한 것을 쓰고 싶지 않습니다. 이는 매우 일반적인 시나리오 여야합니다. 존재하는 솔루션이 있습니까? 이었다는으로 서


전체 클래스

감사 ChrisWue.

class FlexDict : IEnumerable<KeyValuePair<string,string>> 
{ 
    private Dictionary<string, string> _dict = new Dictionary<string, string>(); 
    private static readonly Regex _re = new Regex(@"{([_a-z][_a-z0-9-]*)}", RegexOptions.Compiled | RegexOptions.IgnoreCase); 

    public void Add(string key, string pattern) 
    { 
     _dict[key] = pattern; 
    } 

    public string Expand(string pattern) 
    { 
     pattern = _re.Replace(pattern, match => 
      { 
       string key = match.Groups[1].Value; 

       if (_dict.ContainsKey(key)) 
        return "(" + Expand(_dict[key]) + ")"; 

       return match.Value; 
      }); 

     return pattern; 
    } 

    public string this[string key] 
    { 
     get { return Expand(_dict[key]); } 
    } 

    public IEnumerator<KeyValuePair<string, string>> GetEnumerator() 
    { 
     foreach (var p in _dict) 
      yield return new KeyValuePair<string,string>(p.Key, this[p.Key]); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

서로를 연장 할 수 복잡한 정규 표현식을 구축 예를 들어 사용

class Program 
{ 
    static void Main(string[] args) 
    { 
     var flex = new FlexDict 
      { 
       {"h", @"[0-9a-f]"}, 
       {"nonascii", @"[\200-\377]"}, 
       {"unicode", @"\\{h}{1,6}(\r\n|[ \t\r\n\f])?"}, 
       {"escape", @"{unicode}|\\[^\r\n\f0-9a-f]"}, 
       {"nmstart", @"[_a-z]|{nonascii}|{escape}"}, 
       {"nmchar", @"[_a-z0-9-]|{nonascii}|{escape}"}, 
       {"string1", @"""([^\n\r\f\\""]|\\{nl}|{escape})*"""}, 
       {"string2", @"'([^\n\r\f\\']|\\{nl}|{escape})*'"}, 
       {"badstring1", @"""([^\n\r\f\\""]|\\{nl}|{escape})*\\?"}, 
       {"badstring2", @"'([^\n\r\f\\']|\\{nl}|{escape})*\\?"}, 
       {"badcomment1", @"/\*[^*]*\*+([^/*][^*]*\*+)*"}, 
       {"badcomment2", @"/\*[^*]*(\*+[^/*][^*]*)*"}, 
       {"baduri1", @"url\({w}([!#$%&*-\[\]-~]|{nonascii}|{escape})*{w}"}, 
       {"baduri2", @"url\({w}{string}{w}"}, 
       {"baduri3", @"url\({w}{badstring}"}, 
       {"comment", @"/\*[^*]*\*+([^/*][^*]*\*+)*/"}, 
       {"ident", @"-?{nmstart}{nmchar}*"}, 
       {"name", @"{nmchar}+"}, 
       {"num", @"[0-9]+|[0-9]*\.[0-9]+"}, 
       {"string", @"{string1}|{string2}"}, 
       {"badstring", @"{badstring1}|{badstring2}"}, 
       {"badcomment", @"{badcomment1}|{badcomment2}"}, 
       {"baduri", @"{baduri1}|{baduri2}|{baduri3}"}, 
       {"url", @"([!#$%&*-~]|{nonascii}|{escape})*"}, 
       {"s", @"[ \t\r\n\f]+"}, 
       {"w", @"{s}?"}, 
       {"nl", @"\n|\r\n|\r|\f"}, 

       {"A", @"a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?"}, 
       {"C", @"c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?"}, 
       {"D", @"d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?"}, 
       {"E", @"e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?"}, 
       {"G", @"g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g"}, 
       {"H", @"h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h"}, 
       {"I", @"i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i"}, 
       {"K", @"k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k"}, 
       {"L", @"l|\\0{0,4}(4c|6c)(\r\n|[ \t\r\n\f])?|\\l"}, 
       {"M", @"m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m"}, 
       {"N", @"n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n"}, 
       {"O", @"o|\\0{0,4}(4f|6f)(\r\n|[ \t\r\n\f])?|\\o"}, 
       {"P", @"p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p"}, 
       {"R", @"r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r"}, 
       {"S", @"s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s"}, 
       {"T", @"t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t"}, 
       {"U", @"u|\\0{0,4}(55|75)(\r\n|[ \t\r\n\f])?|\\u"}, 
       {"X", @"x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x"}, 
       {"Z", @"z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z"}, 
       {"Z", @"z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z"}, 

       {"CDO", @"<!--"}, 
       {"CDC", @"-->"}, 
       {"INCLUDES", @"~="}, 
       {"DASHMATCH", @"\|="}, 
       {"STRING", @"{string}"}, 
       {"BAD_STRING", @"{badstring}"}, 
       {"IDENT", @"{ident}"}, 
       {"HASH", @"#{name}"}, 
       {"IMPORT_SYM", @"@{I}{M}{P}{O}{R}{T}"}, 
       {"PAGE_SYM", @"@{P}{A}{G}{E}"}, 
       {"MEDIA_SYM", @"@{M}{E}{D}{I}{A}"}, 
       {"CHARSET_SYM", @"@charset\b"}, 
       {"IMPORTANT_SYM", @"!({w}|{comment})*{I}{M}{P}{O}{R}{T}{A}{N}{T}"}, 
       {"EMS", @"{num}{E}{M}"}, 
       {"EXS", @"{num}{E}{X}"}, 
       {"LENGTH", @"{num}({P}{X}|{C}{M}|{M}{M}|{I}{N}|{P}{T}|{P}{C})"}, 
       {"ANGLE", @"{num}({D}{E}{G}|{R}{A}{D}|{G}{R}{A}{D})"}, 
       {"TIME", @"{num}({M}{S}|{S})"}, 
       {"PERCENTAGE", @"{num}%"}, 
       {"NUMBER", @"{num}"}, 
       {"URI", @"{U}{R}{L}\({w}{string}{w}\)|{U}{R}{L}\({w}{url}{w}\)"}, 
       {"BAD_URI", @"{baduri}"}, 
       {"FUNCTION", @"{ident}\("}, 
      }; 

     var testStrings = new[] { @"""str""", @"'str'", "5", "5.", "5.0", "a", "alpha", "url(hello)", 
      "url(\"hello\")", "url(\"blah)", @"\g", @"/*comment*/", @"/**/", @"<!--", @"-->", @"~=", 
      "|=", @"#hash", "@import", "@page", "@media", "@charset", "!/*iehack*/important"}; 

     foreach (var pair in flex) 
     { 
      Console.WriteLine("{0}\n\t{1}\n", pair.Key, pair.Value); 
     } 

     var sw = Stopwatch.StartNew(); 
     foreach (var str in testStrings) 
     { 
      Console.WriteLine("{0} matches: ", str); 
      foreach (var pair in flex) 
      { 
       if (Regex.IsMatch(str, "^(" + pair.Value + ")$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)) 
        Console.WriteLine(" {0}", pair.Key); 
      } 
     } 
     Console.WriteLine("\nRan in {0} ms", sw.ElapsedMilliseconds); 
     Console.ReadLine(); 
    } 
} 

목적

. 즉, the css spec을 구현하려고합니다.

답변

9

가 나는 foo이 사전의 핵심 인 것을 일어나는 경우 {foo}을 대체하는 MatchEvaluator을 사용하여 다음 정규 표현식을 사용하여 {foo}의 발생을 보면 더 빨리, 그리고 것이라고 생각합니다.

나는 여기에 현재 비주얼 스튜디오를 가지고,하지만 난이 코드 예제와 기능이 동일한 것 같다 : 당신이 할 일이 필요한 이유 루프 동안

var pattern = _dict[key]; 
bool isChanged = false; 

do 
{ 
    isChanged = false; 

    pattern = Regex.Replace(pattern, "{([^}]+)}", match => { 
     string matchKey = match.Groups[1].Value; 

     if (matchKey != key && _dict.ContainsKey(matchKey)) 
     { 
      isChanged = true; 
      return "(" + _dict[matchKey] + ")"; 
     } 

     return match.Value; 
    }); 
} while (isChanged); 

내가/당신을 요청할 수 있습니까? 사전에있는 키의 값을 다시 바꿔야 할 {placeholders}을 포함 할 수 있습니까? 키 "A""Blahblah {B}"이 포함되어 있고 키 "B""Blahblah {A}"이 포함되어있는 무한 루프에 갇히지 않았는지 확인할 수 있습니까?

편집 : 더 개선 사항은 다음과 같습니다 미리 컴파일 된 정규식을 사용하여

  • .
  • 루프 대신 재귀를 사용합니다 (ChrisWue's 주석 참조).
  • _dict.TryGetValue()을 사용하면 Guffa's 코드와 같습니다.

O (n) 알고리즘으로 끝날 것입니다. 여기서 n은 출력 크기이므로 이보다 더 잘할 수 없습니다.

+0

예, 값에 다른 {자리 표시 자}가 있습니다. 아니요, 저는 무한 루프에 갇히지 않을 것이라고 확신 할 수 없습니다 ...'key! = pair.Key'는 그것을 방지하기로되어 있었지만 (어쨌든 w를 바꾸는 것), 여러분이 지적했듯이 다른 방법으로 발생할 수 있습니다. 나는 너무 걱정하지 않는다 ... 무한 루프가 만들어지면, 그것은 빨리 분명해진다. – mpen

+2

추가 개선 사항은 루프 대신 재귀 적으로 만드는 것일 수 있습니다. 위의 대체 코드가 Replace (s, re, dict)라는 함수라고 가정하면 'return'("+ Replace (_dict [matchKey], re, _dict) +") "" '중첩의 양과 길이에 따라 소스 문자열 중 더 많은 것을 향상시킬 수 있습니다. 무한 루프가 아닌 stackoverflow에서 실행되는 "이점"이 있습니다. 또한 정규 표현식을 미리 컴파일하는 것이 도움이 될 수 있습니다. – ChrisWue

+0

@ChrisWue : 나는 그것을 재귀 적으로 만들 수 있다고 생각하지 않습니다 (업데이트 된 질문 참조). 내가 설정 한 방식대로, 특정 키에 대한 패턴을 반환하지만, 입력 문자열을 받아들이지 않습니다 ... 실제로 .. 몇 가지 수정을 통해이를 수행 할 수 있습니다. Q를 다시 업데이트 중입니다. – mpen

-1

PLINQ를 사용할 수 있습니까?

var keys = dict.KeyCollection.Where(k => k != key); 
bool replacementMade = keys.Any(); 

foreach(var k in keys.AsParallel(),() => {replacement code}) 
+4

병렬 작업은 문자열 작업에 적합하지 않습니다. – gor

1

당신은 다음과 같은 알고리즘을 구현할 수 있습니다 :의 라인을 따라

뭔가

  1. 검색 소스 문자열에서 {에 대한
  2. {
  3. 찾기 매칭을 모두 StringBuilder하는 개까지
  4. 복사 모든 } (검색은 마지막 좋아하는 위치에서 수행)
  5. 은 사전에 키 {} 사이의 값을 비교는 문자열 빌더 ( + Value + )
  6. 소스 문자열에서
  7. 그렇지 사본
  8. 만약 소스 문자열의 끝에 복사와 일치하는 경우
    • 도달하지 못했습니다. 1 단계로 이동하십시오.
2

정규식을 사용하여 일치하는 항목을 찾을 수 있습니다. 그런 다음 목록으로 사용하지 않고 사전을 빠르게 검색 할 수도 있습니다.

var pattern = _dict[key]; 
bool replaced = false; 
do { 
    pattern = Regex.Replace(pattern, @"\{([^\}]+)\}", m => { 
    string k = m.Groups[1].Value; 
    string value; 
    if (k != key && _dict.TryGetValue(k, out value) { 
     replaced = true; 
     return "(" + value + ")"; 
    } else { 
     return "{" + k + "}"; 
    } 
    }); 
} while (replaced); 
return pattern; 
+0

우리 둘 다 거의 동일한 코드를 작성하는 방법을 멋지게 꾸몄습니다 :-). BTW, 루프의 시작 부분에서'replaced'를'false'로 재설정하는 것을 잊었습니다. –

관련 문제