2013-06-27 4 views
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 

Каждые два элемента коллекции сделать один результат, который я хочу функцию WordsFromPascalCasedString уступить.

Мои вопросы: Как бы вы, в общем, имели дело с возвратом двух предметов одновременно. Мне любопытно, есть ли какие-либо интересные подходы без грубой силы.

ответ

5

Лично я бы с ответом Саймон Белэнджер «s в данном конкретном случае. Но в целом, для выбора последовательных пар, из 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); 
} 

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)); 
} 
+0

это чистый развлекательный ответ - 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

Смежные вопросы