【发布时间】:2010-11-07 12:14:14
【问题描述】:
有没有办法限制要考虑的关键字的数量?例如,我只想计算前 1000 个单词的文本。 Linq 中有一个“Take”方法,但它有不同的用途——所有的单词都会被计算出来,并且会返回 N 条记录。正确的选择是什么?
【问题讨论】:
-
Take()是一个惰性函数!它不会使所有单词都被计算。例如,请参阅ideone.com/WwDwg。
有没有办法限制要考虑的关键字的数量?例如,我只想计算前 1000 个单词的文本。 Linq 中有一个“Take”方法,但它有不同的用途——所有的单词都会被计算出来,并且会返回 N 条记录。正确的选择是什么?
【问题讨论】:
Take() 是一个惰性函数!它不会使所有单词都被计算。例如,请参阅ideone.com/WwDwg。
只需提早申请Take - 直接在调用Split 之后:
var results = src.Split()
.Take(1000)
.GroupBy(...) // etc
【讨论】:
好吧,严格来说,LINQ 不一定会读取所有内容; Take 将尽快停止。问题是,在相关问题中,您查看 Count,并且很难在不消耗所有数据的情况下获得 Count。同样,string.Split 将查看所有内容。
但是,如果您编写了一个惰性非缓冲拆分函数(使用 yield return)并且您想要前 1000 个唯一词,那么
var words = LazySplit(text).Distinct().Take(1000);
会工作
【讨论】:
Enumerable.Take 实际上会输出结果;它不会完全缓冲它的源,然后只返回第一个 N。不过,看看你原来的解决方案,问题是你想要做Take 的输入是String.Split。不幸的是,这种方法不使用任何类型的延迟执行。它急切地创建所有“拆分”的数组,然后返回它。
因此,从某些文本中获取流式单词序列的技术类似于:
var words = src.StreamingSplit() // you'll have to implement that
.Take(1000);
但是,我确实注意到您的其余查询是:
...
.GroupBy(str => str) // group words by the value
.Select(g => new
{
str = g.Key, // the value
count = g.Count() // the count of that value
});
请注意,GroupBy 是一个缓冲操作 - 您可以预期,来自其来源的所有 1,000 个单词最终都将存储在组输出过程中的某个位置。
在我看来,选项是:
src.Split().Take(1000) 很好。缺点是浪费时间(在不再需要后继续拆分)和浪费空间(将所有单词存储在数组中,即使只需要前 1,000 个单词)。但是,查询的 rest 不会对超出必要的单词进行操作。 src.StreamingSplit().Take(1000) 或等效项。在这种情况下,在找到 1,000 个单词后,不会处理任何原始文本。 请注意,在这两种情况下,这 1000 个词他们自己最终会被 GroupBy 子句缓冲。
【讨论】: