2 回答
TA贡献1811条经验 获得超6个赞
你的问题中有一些误解,这很棒,因为现在你有机会了解事实而不是神话。
首先,您正在实现的方法通常称为CartesianProduct
,而不是GetAllPossibleCombos
,因此请考虑重命名它。
也许我没有正确理解这一点
你没有正确理解它。
这不是在 RAM 中构建整个组合列表吗?
不。 查询构建器构建查询,而不是执行查询的结果。 当你执行 a 时SelectMany
,你得到的是一个将在将来进行选择的对象。您不会得到该选择的结果。
如果有大量项目,该方法可能会导致计算机内存不足。
今天是停止将内存和 RAM 视为同一事物的好日子。当进程耗尽内存时,它不会耗尽 RAM。它耗尽了地址空间(不是 RAM)。考虑内存的更好方法是:内存是磁盘上的页面文件,而 RAM 是特殊的硬件,可以使页面文件更快。当 RAM 耗尽时,计算机的运行速度可能会慢得令人无法接受,但在地址空间耗尽之前,内存不会耗尽。请记住,进程内存是虚拟化的。
现在,可能存在执行此代码效率低下的情况,因为枚举查询耗尽了堆栈。在某些情况下,执行可能会变得低效,因为您将 n 个项目向上移动到堆栈 n 深。我建议您对代码进行更深入的分析,看看是否是这种情况,然后进行报告。
有没有办法重写方法以在每个组合上使用收益返回,而不是返回整个组合集?
SelectMany
是作为循环yield return
中的a 实现的foreach
,因此您已经yield return
在每个组合上将其实现为 a ;你刚刚隐藏了yield return
对 的调用SelectMany
。
也就是说,SelectMany<A, B, C>(IE<A> items, Func<A, IE<B>> f, Func<A, B, C> g)
实现如下:
foreach(A a in items) foreach(B b in f(a)) yield return g(a, b);
所以你已经在 中完成了yield return
。
如果你想编写一个直接执行 a的方法yield return
,那就有点困难了;最简单的方法是在每个子序列上形成一个枚举器数组,然后从每个Current
枚举器创建一个向量,yield return
即向量,然后将正确的迭代器前进一步。继续这样做,直到不再有正确的迭代器可以前进。
正如您可能从描述中看出的那样,簿记变得混乱。这是可行的,但编写起来并不是很令人愉快的代码。不过不妨尝试一下!该解决方案的好处是,您可以保证获得良好的性能,因为您不消耗任何堆栈。
TA贡献1851条经验 获得超4个赞
SelectMany和其他 Linq 方法返回一个IEnumerable,仅在枚举集合时才延迟计算。ToList()这可以采用orToArray()调用或在循环中对其进行迭代的形式foreach。当您在调试器中看到消息警告扩展集合将枚举可枚举对象时,这就是它警告您的行为。该集合尚未枚举 - Linq 查询仅构建一个调用链,告诉它如何枚举数据。
因此,您对 RAM 使用情况的担忧不一定准确(取决于启动的具体类型IEnumerable)。即使您调用ToList()orToArray()并将对该集合的引用存储在变量中,如果集合元素是引用类型,那么它也不会是副本。
在您的示例中,yield return如果您想延迟构建元素集合而不将其存储在单独的集合中(例如返回列表或数组,这需要额外的复制),则可以为您提供方便。我认为它不适用于您想要做的事情,因为SelectMany已经有这种行为。
如果您想尝试一下,Linq 可以轻松生成大型列表Enumerable.Repeat
// Define a collection with 10000000 items (items not created yet)
var manyItems = Enumerable.Repeat(123, 10000000);
// Enumerate the enumerable via ToList: creates the int 10000000 times
var manyItemsConcrete = manyItems.ToList();
// same deal with reference types
var manyReferenceTypes = Enumerable.Repeate(new object(), 10000000);
var manyReferenceTypesConcrete = manyReferenceTypes.ToList();
// This list already exists in RAM taking up space
var list = new List<object> { new object(), new object() /* ... x10000000 */ }
// This defines a transform on list, but doesn't take up RAM
var enumerable = list.Select(x => x.ToString());
// Now, there are two lists taking up RAM
var newList = enumerable.ToList();
- 2 回答
- 0 关注
- 100 浏览
添加回答
举报