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 關注
- 130 瀏覽
添加回答
舉報