4

私は PascalCaseParser を使用して書いていましたRegex.Splitが、コレクションから一度に 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 

コレクションの 2 つの要素ごとに、関数に生成させたい 1 つの結果が作成WordsFromPascalCasedStringされます。

私の質問は次のとおりです。一般的に、一度に 2 つのアイテムを返品するという要件にどのように対処しますか。興味深い非力ずくのアプローチがあるかどうか、私は興味があります。

4

4 に答える 4

5

正規表現は([A-Z][a-z]*). 数字も含めたい場合は、最後の部分を調整します。大文字の区切り文字の後に少なくとも 1 つの小文字の要素が必要な場合は、+代わりに使用します。*

編集for実際の質問に関しては、パフォーマンスを向上させるためにループで具体化して反復する必要があります(リストを1回渡します)。特定の問題では、単に使用できます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

これは共有するだけです。他の回答に触発されて思いついたソリューションを投げています。他のものよりも優れていません...

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 に答える
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おそらく1行のアプローチのみです。これは途中で大量のガベージが作成されるため純粋にエンターテイメント コードですが、変更可能なオブジェクトは使用されません。

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 行のアプローチです。ソース コレクションを 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 に答える