2013-06-27 90 views
4

我正在使用Regex.Split来编写一个PascalCaseParser,我希望一次从一个集合中选择两个项目。如何一次选择2个项目?

此示例代码演示。

void Main() 
{ 
    string pascalCasedString = "JustLikeYouAndMe"; 
    var words = WordsFromPascalCasedString(pascalCasedString); 
    words.Dump(); 
} 

IEnumerable<string> WordsFromPascalCasedString(string pascalCasedString) 
{ 
    var rx = new Regex("([A-Z])"); 
    return rx.Split(pascalCasedString) 
      .Where(c => !string.IsNullOrEmpty(c)) 
      // how to select 2 elements at a time? 
      ; 
} 

以上代码的结果是:

IEnumerable<String> (10 items) 
J 
ust 
L 
ike 
Y 
ou 
A 
nd 
M 
e 

收集的每两个元素构成一个结果,我想要的功能WordsFromPascalCasedString屈服。

我的问题是:您一般会如何处理一次返回两个项目的要求。我很好奇,如果有任何有趣的非蛮力方法。

回答

5

就个人而言,我会用西蒙贝朗格的答案去在这种特定的情况下。但在一般情况下,选择连续的对,从IEnumerable,你会使用这样的:

IEnumerable<Tuple<string, string>> WordsFromPascalCasedString(string pascalCasedString) 
{ 
    var rx = new Regex("([A-Z])"); 
    var array = rx.Split(pascalCasedString) 
        .Where(c => !string.IsNullOrEmpty(c)) 
        .ToArray(); 
    var items = Enumerable.Range(0, array.Length/2) 
          .Select(i => Tuple.Create(array[i * 2], array[i * 2 + 1]); 
} 

还是这个,这需要更多的努力,但它的可重复使用的,更高效:

IEnumerable<Tuple<T, T>> Pairs<T>(IEnumerable<T> input) 
{ 
    var array = new T[2]; 
    int i = 0; 
    foreach(var x in input) 
    { 
     array[i] = x; 
     i = (i + 1) % 2; 
     if (i == 0) 
     { 
      yield return Tuple.Create(array[0], array[1]); 
     } 
    } 
} 


IEnumerable<Tuple<string, string>> WordsFromPascalCasedString(string pascalCasedString) 
{ 
    var rx = new Regex("([A-Z])"); 
    var output = rx.Split(pascalCasedString) 
        .Where(c => !string.IsNullOrEmpty(c)); 
    var items = Pairs(output); 
} 

它可以容易地扩展到的n组:

IEnumerable<IEnumerable<T>> Batches<T>(IEnumerable<T> input, int n) 
{ 
    var array = new T[n]; 
    int i = 0; 
    foreach(var x in input) 
    { 
     array[i] = x; 
     i = (i + 1) % n; 
     if (i == 0) 
     { 
      yield return array.ToArray(); 
     } 
    } 

    if (i != 0) 
    { 
     yield return array.Take(i); 
    } 
} 

类似的方法在MoreLINQ存在。

5

正则表达式应该是([A-Z][a-z]*)。如果您还想包含数字,请调整最后一部分。如果您至少需要一个小写字母元素,请使用+而不是*

编辑至于实际的问题,您需要实现并迭代for循环以获得更好的性能(通过列表一次)。在您的具体问题,你可以使用Regex.Matches

var result = Regex.Matches("([A-Z][a-z]*)([A-Z][a-z]*)?", "AbCdEfGhIj") 
        .OfType<Match>() 
        .Where(m => m.Success) 
        .Select(m => Tuple.Create(m.Groups[1].Value, m.Groups[2].Value)); 
+0

+1解决我的实际问题(感谢)...我还是会感兴趣,如果我问的问题得到一个答案,但... –

+0

什么其他问题吗? –

+0

是的,我真的没有试图找出解析器,我真的很想看到其他人想要使用linq样式选择,一次处理2个项目...但我再次感谢帮助 –

2

最简单的就是编写简单返回对的函数。

喜欢的东西:

IEnumerable<Tuple<T,T>> Pairs<T>(IEnumerable<T> items) 
{ 
    T first = default(T); 
    bool hasFirst = false; 
    foreach(T item in items) 
    { 
     if (hasFirst) 
      yield return Tuple.Create(first, item); 
     else 
      first = item; 
     hasFirst = !hasFirst; 
    } 
} 

Aggregate可能只有一个行的办法。由于在路上创建了大量垃圾,这纯粹是娱乐代码,但没有使用可变对象。

IEnumerable<Tuple<T,T>> Pairs<T>(IEnumerable<T> collection) 
{ 
    return collection 
    .Aggregate(
     Tuple.Create(false, default(T), Enumerable.Empty<Tuple<T,T>>()), 
     (accumulate, item)=> !accumulate.Item1 ? 
     Tuple.Create(true, item, accumulate.Item3) : 
      Tuple.Create(false, default(T), 
       accumulate.Item3.Concat(
       Enumerable.Repeat(Tuple.Create(accumulate.Item2, item), 1))), 
     accumulate => accumulate.Item3); 
} 

奇数和偶数元素(index %2 ==/!= 0)的Zip是2线的方法。请注意,迭代源集合两次。

IEnumerable<Tuple<T,T>> Pairs<T>(IEnumerable<T> collection) 
{ 
    return collection 
    .Where((item, index)=>index %2 == 0) 
    .Zip(collection.Where((item, index)=>index %2 != 0), 
    (first,second)=> Tuple.Create(first,second)); 
} 
+0

This是纯粹的娱乐回答 - pswg(+1)已经涵盖了创建配对的更好的版本。 –

2

这只是分享,我抛出了我得到的灵感来自其他答案后的解决方案。它并不比其他人好......

void Main() 
{ 
    string pascalCasedString = "JustLikeYouAndMe"; 
    var words = WordsFromPascalCasedString(pascalCasedString); 
    words.Dump(); 
} 

IEnumerable<string> WordsFromPascalCasedString(string pascalCasedString) 
{ 
    var rx = new Regex("([A-Z])"); 
    return rx.Split(pascalCasedString) 
      .Where(c => !string.IsNullOrEmpty(c)) 
      .InPieces(2) 
      .Select(c => c.ElementAt(0) + c.ElementAt(1)); 
} 

static class Ext 
{ 
    public static IEnumerable<IEnumerable<T>> InPieces<T>(this IEnumerable<T> seq, int len) 
    { 
     if(!seq.Any()) 
      yield break; 

     yield return seq.Take(len); 

     foreach (var element in InPieces(seq.Skip(len), len)) 
      yield return element; 
    } 
} 
+0

+1我见过很多'配料'技术,但这是我见过的第一个递归的技术。它可能不是最有效的,但它绝对是有创意的。 –

+0

+1,将其保存以供将来使用。 – vittore

相关问题