C # Linq / Lambda выражение: как выбрать целое число из строки? - PullRequest
4 голосов
/ 02 февраля 2010

Я думаю, что лучший способ объяснить мой вопрос - это короткий (универсальный) пример кода linq-to-objects:

IEnumerable<string> ReadLines(string filename)
{
    string line;
    using (var rdr = new StreamReader(filename))
        while ( (line = rdr.ReadLine()) != null)
           yield return line;
}

IEnumerable<int> XValuesFromFile(string filename)
{
    return ReadLines(filename)
               .Select(l => l.Substring(3,3))
               .Where(l => int.TryParse(l))
               .Select(i => int.Parse(i));
}

Обратите внимание, что этот код анализирует целое число дважды.Я знаю, что упускаю очевидный простой способ безопасного устранения одного из этих вызовов (а именно потому, что я делал это раньше).Я просто не могу найти это прямо сейчас.Как я могу это сделать?

Ответы [ 3 ]

9 голосов
/ 02 февраля 2010

Как насчет:

int? TryParse(string s)
{
    int i;
    return int.TryParse(s, out i) ? (int?)i : (int?)null;
}
IEnumerable<int> XValuesFromFile(string filename)
{
    return from line in ReadLines(filename)
           let start = line.Substring(3,3)
           let parsed = TryParse(start)
           where parsed != null
           select parsed.GetValueOrDefault();
}

Вы можете объединить вторую / третью строки, если хотите:

    return from line in ReadLines(filename)
           let parsed = TryParse(line.Substring(3,3))

Выбор GetValueOrDefault заключается в том, что при этом пропускается проверка проверки, которую выполняет приведение (int) или .Value - т.е. это (очень-чуть) быстрее (и мы уже убедились, что это не так). null).

3 голосов
/ 02 февраля 2010

Я думаю, я пойду с чем-то вроде этого:

IEnumerable<O> Reduce<I,O>(this IEnumerable<I> source, Func<I,Tuple<bool, O>> transform )
{
    foreach (var item in source)
    {
       try
       {
          Result<O> r = transform(item);
          if (r.success) yield return r.value;
       }
       catch {}
    }
}

ReadLines().Reduce(l => { var i; new Tuple<bool, int>(int.TryParse(l.Substring(3,3),i), i)} );

Мне это не очень нравится, так как я уже записал , что не люблю использовать таким образом кортежи . К сожалению, я не вижу много альтернатив помимо злоупотребления исключениями или ограничения их ссылочными типами (где null определяется как неудачное преобразование), ни один из которых не намного лучше.

3 голосов
/ 02 февраля 2010

Это не совсем красиво, но вы можете сделать:

return ReadLines(filename)
    .Select(l =>
                {
                    string tmp = l.Substring(3, 3);
                    int result;
                    bool success = int.TryParse(tmp, out result);
                    return new
                               {
                                   Success = success,
                                   Value = result
                               };
                })
    .Where(i => i.Success)
    .Select(i => i.Value);

Конечно, это в основном просто толкает работу в лямбду, но она дает правильные ответы с помощью одного разбора (но с дополнительным выделением памяти).

...