Элегантный вариант синтаксического анализа текста LINQ для этого?

Я ищу элегантный способ анализа заголовков судебных дел, которые могут иметь псевдонимы, такие как «AKA» и «FKA». Мне нужно получить тип псевдонима, а также следующую подпись. Я переборщил с решением, но хотел бы посмотреть, какие еще есть варианты. Мне нравится Linq, и я попробовал Sprache, но не совсем понял.

Example caption:
JOHN SMITH AKA JOHN R SMITH FKA JOHNNY R SMITH  

Desired output: 
Alias Type Found: AKA   
Alias Caption Found: JOHN R SMITH   
Alias Type Found: FKA   
Alias Caption Found: JOHNNY R SMITH

Ниже приводится то, что я уже собрал в LinqPad.

void Main()
{
    var caption = "JOHN SMITH AKA JOHN R SMITH FKA JOHNNY R SMITH";
    caption.Split().ParseAliases( (t,c)=>{
        Console.WriteLine ("Alias Type Found: {0}",t);
        Console.WriteLine ("Alias Caption Found: {0}",c);
    });
}

public delegate void AliasRetrievedDelegate(string aliasType, string aliasCaption);

public static class ParserExtensions{
    private static IEnumerable<string> aliasTypes = new[]{"AKA","FKA"};

    public static void ParseAliases(this IEnumerable<string> tokens, 
        aliasRetrievedDelegate d, 
        int startIdx = 0){
                   // TODO

    }
}

person Ken    schedule 29.09.2012    source источник
comment
Звучит как работа для регулярных выражений, а не для LINQ.   -  person Matthew Strawbridge    schedule 30.09.2012
comment
Это была моя первая мысль, но я хотел бы более читаемое решение, что-то вроде мини-DSL, которое можно было бы легче расширить.   -  person Ken    schedule 30.09.2012


Ответы (1)


Это может быть не так элегантно, как хотелось бы, но это работает. Он группирует типы псевдонимов со списком следующих строк. Затем он объединяет строки, чтобы сформировать соответствующие псевдонимы.

public static class ParserExtensions
{
    private static IEnumerable<string> aliasTypes = new[]{"AKA","FKA"};

    public static void ParseAliases(this IEnumerable<string> tokens, 
        Action<string, string> d, 
        int startIdx = 0)
    {
        var aliases = tokens.Skip(startIdx)
                            .GroupMatchesWithTrailing(x => aliasTypes.Contains(x));
        foreach(var alias in aliases)
        {
            string aliasType = alias.Item1;
            string aliasName = string.Join(" ", alias.Item2.ToArray());
            d(alias.Type, alias.Name);
        }   
    }

Сложная часть — группировка типов псевдонимов с соответствующими именами. Этот метод довольно многословен, но итерирует source только один раз и может выполняться лениво. Есть более краткие решения, но они имеют компромиссы.

    private static IEnumerable<Tuple<T, List<T>>> GroupMatchesWithTrailing<T>(
        this IEnumerable<T> source,
        Func<T, bool> predicate)
    {
        var items = source.SkipWhile(x => predicate(x) == false);
        using (IEnumerator<T> iterator = items.GetEnumerator())
        {
            bool hasItems = iterator.MoveNext();
            while(hasItems)
            {
                T match = iterator.Current;
                List<T> trailing = new List<T>();
                hasItems = iterator.MoveNext();
                while(hasItems && predicate(iterator.Current) == false)
                {
                    trailing.Add(iterator.Current);
                    hasItems = iterator.MoveNext();
                }
                yield return Tuple.Create(match, trailing);
            }
        }
    }
}
person Risky Martin    schedule 30.09.2012
comment
Хорошо, никогда не замечал, что у Tuple есть фабричный метод. Не совсем то, на что я надеялся, но это работает, и сейчас я читаю предметно-ориентированные языки для будущего упрощения. - person Ken; 01.10.2012