4

我正在使用 PascalCaseParser 编写一个Regex.Split,我开始希望一次从一个集合中选择两个项目。

此示例代码演示。

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产生的结果。

我的问题是:一般而言,您将如何处理一次退回两件物品的要求。我很好奇是否有任何有趣的非暴力方法。

4

4 回答 4

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));
于 2013-06-27T03:04:30.573 回答
5

就个人而言,在这种特殊情况下,我会选择Simon Belanger的答案。但一般来说,要从 中选择连续的对,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.

于 2013-06-27T03:15:05.040 回答
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); 
}

Zip奇数和偶数元素 ( index %2 ==/!= 0) 是 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));
}
于 2013-06-27T03:16:38.483 回答
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;
    }
}
于 2013-06-27T03:46:49.153 回答