懒洋洋地建立一个可枚举的,中间有中断
lazily building up an enumerable with breaks in between
我有一个对象集合 IEnumerable<object> obs
。
我有另一个对象集合 IEnumerable<object> data
.
对于 obs
中的每个 ob
,我需要在 data
中找到与 属性 中具有相同值的第一个项目 ob
.例如,我可以在 data
中查找与 ob
具有相同 ToString()
值的第一个项目。当找到 属性 值匹配的第一个项目时,我对找到的数据项目做一些事情,然后检查 obs
中的下一个 ob
。如果找到 none,我会抛出一个错误。
这是一个天真的方法:
foreach (object ob in obs)
{
foreach (object dataOb in data)
if (ob.ToString() == dataOb.ToString())
{
... // do something with dataOb
goto ContinueOuter;
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
缺点是我每次都计算dataOb.ToString()
,这是不必要的。
我可以缓存它:
IDictionary<object, string> dataToDataStr = new Dictionary<object, string>();
foreach (object dataObj in data) // collect all ToString values in advance
dataToDataStr.Add(dataObj, dataObj.ToString());
foreach (object ob in obs)
{
foreach (object dataOb in dataToDataStr.Keys)
if (ob.ToString() == dataToDataStr[dataOb])
{
... // do something with dataOb
goto ContinueOuter;
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
缺点是我会计算所有 ToString()
值,即使这可能不是必需的。我可能会在数据收集的前半部分找到所有匹配的数据对象。
如何懒惰地构建 dataToDataStr
字典(或任何其他可让我检索对象及其唯一计算的 ToString 值的可枚举数据结构)?
这是我想到的代码(混合了伪代码):
IDictionary<object, string> dataToDataStr = new Dictionary<object, string>();
object lastProcessedDataOb = null;
foreach (object ob in obs)
{
foreach (object dataOb in dataToDataStr.Keys)
if (ob.ToString() == dataToDataStr[dataOb])
{
... // do something with dataOb
goto ContinueOuter;
}
foreach (object dataOb in data STARTING AFTER lastProcessedDataOb)
// if lastProcessedDataOb == null, start with the first entry of data
{
dataToDataStr.Add(dataOb, dataOb.ToString();
lastProcessedDataOb = dataOb;
if (ob.ToString() == dataToDataStr[dataOb])
{
... // do something with dataOb
goto ContinueOuter;
}
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
我知道如果 data
是一个 LinkedList
或任何具有索引访问的集合(那么我可以将链表节点或索引存储为 lastProcessedDataOb
),但是它不是 - 它是 IEnumerable
。也许这里可以用yield return
?
完全忘记了 LINQ 使用惰性求值...
这应该有效(我使用 C# 7 的新值元组表示法):
IEnumerable<(object, string)> objStrPairs = data.Select(o => (o, o.ToString()));
foreach (object ob in obs)
{
foreach ((object, string) dataPair in objStrPairs)
if (ob.ToString() == objStrPairs.Item2)
{
... // do something with objStrPairs.Item1
goto ContinueOuter;
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
如果您的集合非常大并且您真的不想为 data
的每个项目评估 ToString
,您可以使用以下方法:
- 创建已计算项目的缓存
- 如果找到某个项目,我会缓存 - 太好了,我们有匹配项。
否则 - 通过遍历 data
集合继续填充缓存,直到找到匹配项。这可以通过手动控制 **Enumerator**
数据收集(而不是使用 foreach
)有效地完成。
IEnumerable<object> obs;
IEnumerable<object> data;
Dictionary<string, object> dataCache = new Dictionary<string, object>();
var dataIterator = data.GetEnumerator();
foreach (var ob in obs)
{
var obText = ob.ToString();
object matchingDataItem = null;
if (!dataCache.TryGetValue(obText, out matchingDataItem))
{
while (dataIterator.MoveNext())
{
var currentData = dataIterator.Current;
var currentDataText = currentData.ToString();
if (!dataCache.ContainsKey(currentDataText)) // Handle the case when data collection contains duplicates
{
dataCache.Add(currentDataText, currentData);
if (currentDataText == obText)
{
matchingDataItem = currentData;
break;
}
}
}
}
if (matchingDataItem != null)
{
Console.WriteLine("Matching item found for " + obText);
}
else
{
throw new Exception("No matching data found.");
}
}
通过这种方式,您可以保证仅在找到所有 obs
项时迭代 data
集合,并且您不会评估 ToString
每个项目不止一次.
PS: 我希望 "ToString" 只是一个例子,你在那里有一些复杂的计算,值得这么复杂......
我有一个对象集合 IEnumerable<object> obs
。
我有另一个对象集合 IEnumerable<object> data
.
对于 obs
中的每个 ob
,我需要在 data
中找到与 属性 中具有相同值的第一个项目 ob
.例如,我可以在 data
中查找与 ob
具有相同 ToString()
值的第一个项目。当找到 属性 值匹配的第一个项目时,我对找到的数据项目做一些事情,然后检查 obs
中的下一个 ob
。如果找到 none,我会抛出一个错误。
这是一个天真的方法:
foreach (object ob in obs)
{
foreach (object dataOb in data)
if (ob.ToString() == dataOb.ToString())
{
... // do something with dataOb
goto ContinueOuter;
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
缺点是我每次都计算dataOb.ToString()
,这是不必要的。
我可以缓存它:
IDictionary<object, string> dataToDataStr = new Dictionary<object, string>();
foreach (object dataObj in data) // collect all ToString values in advance
dataToDataStr.Add(dataObj, dataObj.ToString());
foreach (object ob in obs)
{
foreach (object dataOb in dataToDataStr.Keys)
if (ob.ToString() == dataToDataStr[dataOb])
{
... // do something with dataOb
goto ContinueOuter;
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
缺点是我会计算所有 ToString()
值,即使这可能不是必需的。我可能会在数据收集的前半部分找到所有匹配的数据对象。
如何懒惰地构建 dataToDataStr
字典(或任何其他可让我检索对象及其唯一计算的 ToString 值的可枚举数据结构)?
这是我想到的代码(混合了伪代码):
IDictionary<object, string> dataToDataStr = new Dictionary<object, string>();
object lastProcessedDataOb = null;
foreach (object ob in obs)
{
foreach (object dataOb in dataToDataStr.Keys)
if (ob.ToString() == dataToDataStr[dataOb])
{
... // do something with dataOb
goto ContinueOuter;
}
foreach (object dataOb in data STARTING AFTER lastProcessedDataOb)
// if lastProcessedDataOb == null, start with the first entry of data
{
dataToDataStr.Add(dataOb, dataOb.ToString();
lastProcessedDataOb = dataOb;
if (ob.ToString() == dataToDataStr[dataOb])
{
... // do something with dataOb
goto ContinueOuter;
}
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
我知道如果 data
是一个 LinkedList
或任何具有索引访问的集合(那么我可以将链表节点或索引存储为 lastProcessedDataOb
),但是它不是 - 它是 IEnumerable
。也许这里可以用yield return
?
完全忘记了 LINQ 使用惰性求值... 这应该有效(我使用 C# 7 的新值元组表示法):
IEnumerable<(object, string)> objStrPairs = data.Select(o => (o, o.ToString()));
foreach (object ob in obs)
{
foreach ((object, string) dataPair in objStrPairs)
if (ob.ToString() == objStrPairs.Item2)
{
... // do something with objStrPairs.Item1
goto ContinueOuter;
}
throw new Exception("No matching data found.");
ContinueOuter: ;
}
如果您的集合非常大并且您真的不想为 data
的每个项目评估 ToString
,您可以使用以下方法:
- 创建已计算项目的缓存
- 如果找到某个项目,我会缓存 - 太好了,我们有匹配项。
否则 - 通过遍历
data
集合继续填充缓存,直到找到匹配项。这可以通过手动控制**Enumerator**
数据收集(而不是使用foreach
)有效地完成。IEnumerable<object> obs; IEnumerable<object> data; Dictionary<string, object> dataCache = new Dictionary<string, object>(); var dataIterator = data.GetEnumerator(); foreach (var ob in obs) { var obText = ob.ToString(); object matchingDataItem = null; if (!dataCache.TryGetValue(obText, out matchingDataItem)) { while (dataIterator.MoveNext()) { var currentData = dataIterator.Current; var currentDataText = currentData.ToString(); if (!dataCache.ContainsKey(currentDataText)) // Handle the case when data collection contains duplicates { dataCache.Add(currentDataText, currentData); if (currentDataText == obText) { matchingDataItem = currentData; break; } } } } if (matchingDataItem != null) { Console.WriteLine("Matching item found for " + obText); } else { throw new Exception("No matching data found."); } }
通过这种方式,您可以保证仅在找到所有 obs
项时迭代 data
集合,并且您不会评估 ToString
每个项目不止一次.
PS: 我希望 "ToString" 只是一个例子,你在那里有一些复杂的计算,值得这么复杂......