【问题标题】:Simple language identification using LINQ使用 LINQ 进行简单的语言识别
【发布时间】:2011-05-06 01:31:50
【问题描述】:

我是第一次尝试使用 LINQ,并决定尝试基本的人类语言识别。输入文本将针对该语言中最常见的 10,000 个单词中的HashSets 进行测试并获得分数。

我的问题是,有没有更好的 LINQ 查询方法?也许我不知道的另一种形式?它有效,但我相信这里的专家将能够提供更清洁的解决方案!

public PolyAnalyzer() {
    Dictionaries = new Dictionary<string, AbstractDictionary>();
    Dictionaries.Add("Bulgarian", new BulgarianDictionary());
    Dictionaries.Add("English", new EnglishDictionary());
    Dictionaries.Add("German", new GermanDictionary());
    Dictionaries.Values.Select(n => new Thread(() => n.LoadDictionaryAsync())).ToList().ForEach(n => n.Start());            
}  

public string getResults(string text) {
    int total = 0;
    return string.Join(" ",
        Dictionaries.Select(n => new {
            Language = n.Key,
            Score = new Regex(@"\W+").Split(text).AsQueryable().Select(m => n.Value.getScore(m)).Sum()
        }).
        Select(n => { total += n.Score; return n; }).
        ToList().AsQueryable(). // Force immediate evaluation
        Select(n =>
        "[" + n.Score * 100 / total + "% " + n.Language + "]").
        ToArray());
}

附:我知道这是一种非常简单的语言识别方法,我只是对 LINQ 方面感兴趣。

【问题讨论】:

  • 属于 codereview.SE,没有 SO。顺便说一句,字符级 n-gram 的语言检测往往更可靠。

标签: c# linq lambda functional-programming nlp


【解决方案1】:

我会这样重构它:

    public string GetResults(string text)
    {
        Regex wordRegex = new Regex(@"\W+");
        var scores = Dictionaries.Select(n => new
            {
                Language = n.Key,
                Score = wordRegex.Split(text)
                                 .Select(m => n.Value.getScore(m))
                                 .Sum()
            });

        int total = scores.Sum(n => n.Score);
        return string.Join(" ",scores.Select(n => "[" + n.Score * 100 / total + "% " + n.Language + "]");
    }

几点:

  1. AsQueryAble() 是不必要的 - 这都是 Linq to Objects,其中 是IEnumerable&lt;T&gt; - 足够好。

  2. 删除了一些ToList() - 也 不必要并避免急切加载 不需要时的结果。

  3. 虽然只有一个 LINQ 很好 查询 这不是比赛 - 目标 为了整体的可读性并考虑如何 您(和其他人)必须维护代码。我将您的查询分成三个更易读的 (imo) 部分。

  4. 避免副作用 可能 - 我删除了你的那个 到变量total - 它是 令人困惑 - LINQ 查询不应该 有副作用,因为两次运行相同的查询可能会产生不同的结果。在您的情况下,您可以在单独的 Linq 查询中计算总数。

  5. 不要在 Linq 中重新新建或重新计算变量 非必要时投影 - I 从 Linq 中删除了正则表达式 查询并初始化变量 一旦在外面 - 否则你就是 更新正则表达式实例N 次 而不是一次。根据查询的不同,这可能会对性能产生巨大影响。

【讨论】:

  • 确实我可以删除AsQueryables!感谢您的说明,我会记住的(尤其是关于副作用的部分)。
  • 在保留功能的同时接受代码的改进。感谢您的提示:)
【解决方案2】:

我认为您发布的代码非常混乱。我已经重写了它,我认为它给了你相同的结果(当然我无法测试它,实际上我认为你的代码有一些错误的部分)但现在应该更简洁了。如果这不正确,请告诉我。

public PolyAnalyzer()
{
    Dictionaries = new Dictionary<string, AbstractDictionary>();
    Dictionaries.Add("Bulgarian", new BulgarianDictionary());
    Dictionaries.Add("English", new EnglishDictionary());
    Dictionaries.Add("German", new GermanDictionary());

    //Tip: Use the Parallel library to to multi-core, multi-threaded work.
    Parallel.ForEach(Dictionaries.Values, d =>
    {
        d.LoadDictionaryAsync();
    });            
}  

public Dictionary<string, int> GetResults(string text)
{
    //1) Split the words.
    //2) Calculate the score per dictionary (per language).
    //3) Return the scores.
    string[] words = new Regex(@"\w+").Split().ToArray();
    Dictionary<string, int> scores = this.Dictionaries.Select(d => new
    {
        Language = d.Key,
        Score = words.Sum(w => d.Value.GetScore(w))
    }));

    return scores;
}

【讨论】:

  • 感谢words.Sum,这比我的解决方案要好得多!
  • 不客气。除此之外,您不应该在Select 内“做事”。你应该只在那里选择东西。 (例如,不要在那里计算总数。)我希望这个例子足够清楚并且对你有用。
  • 有没有其他方法可以为每个元素执行某些操作?
  • 当然:elements.ForEach(e =&gt; DoSomethingWith(e))
  • 不起作用...仅适用于 List,至少适用于 .NET 3.5(我只有 Visual Studio 2008),我相信它不会返回 e。跨度>
猜你喜欢
  • 2023-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-27
相关资源
最近更新 更多