C# 将LINQ Select中的递归方法组转换为迭代方法
我有一门课看起来像这样:C# 将LINQ Select中的递归方法组转换为迭代方法,c#,linq,recursion,iteration,C#,Linq,Recursion,Iteration,我有一门课看起来像这样: public class SourceObject { public string Id { get; set; } public List<SourceObject> Children { get; set; } public SourceObject() { Children = new List<SourceObject>(); } } 当源对象图的大小不是太大或太深时,此方法可以
public class SourceObject
{
public string Id { get; set; }
public List<SourceObject> Children { get; set; }
public SourceObject()
{
Children = new List<SourceObject>();
}
}
当源对象图的大小不是太大或太深时,此方法可以正常工作,但是,当源对象图非常大时,此方法将引发StackOverflow异常
我已经设法创建了此函数的另一个版本,该版本删除了递归,并使用类似于中所述的技术将其替换为队列/堆栈。但是,我注意到队列/堆栈也会变得非常大,我不确定我的实现是否最有效
是否可以将递归函数转换为在源对象图上纯粹使用迭代的函数(即删除递归,理想情况下使用队列/堆栈)
是否可以将递归函数转换为纯粹使用迭代的函数
在源对象图上(即移除递归,理想情况下,使用队列/堆栈)
使用堆栈和队列可以实现用堆栈替换LINQ.Select的递归调用。
我使用一个元组来记住父节点的id
运行时-o(n)。
空间复杂度-o(一个级别中的节点数)
如果我们只使用一个队列-o(min(h*d,n)),我们可以改变空间复杂度。
h表示高度,b表示节点中的最大子节点数。
考虑这个代码:
public DestinationObject MapSourceToDestination(SourceObject root)
{
Stack<Tuple<DestinationObject,int>> stack = new Stack<Tuple<DestinationObject,int>>();
DestinationObject currentChild = new DestinationObject();
currentChild.Id = root.Id;
stack.Push(new Tuple<DestinationObject,int>(currentChild,root.Id));
while(stack.Count > 0)
{
Tuple<DestinationObject,int> currentTuple = stack.Pop();
current = currentTuple[0];
children = current.Children;
foreach (SourceObject sourceChild in root.Children)
{
currentChild = new DestinationObject();
currentChild.Id = currentTuple[1];
Children.Add(currentChild);
stack.Push(new Tuple<DestinationObject,int>(currentChild,sourceChild.Id));
}
}
}
public DestinationObject映射sourcetodestination(SourceObject root)
{
堆栈=新堆栈();
DestinationObject currentChild=新的DestinationObject();
currentChild.Id=根.Id;
Push(新元组(currentChild,root.Id));
而(stack.Count>0)
{
Tuple currentTuple=stack.Pop();
当前=当前元组[0];
children=当前的。children;
foreach(root.Children中的SourceObject sourceChild)
{
currentChild=新的DestinationObject();
currentChild.Id=currentTuple[1];
添加(currentChild);
Push(新元组(currentChild,sourceChild.Id));
}
}
}
我敢说您有相互冲突的需求,因此您的问题在于需求/设计,而不是代码?关于您在问题中提到的两点:
SourceObject
的子对象数是未知的。在这种情况下,堆栈溢出的可能性是不可避免的。当数据的大小未知,并且在运行时数据大于机器上的可用空间时,就会发生这种情况SourceObject
s存储在某些数据结构中,以便在继续处理时跟踪要访问的对象为了避免这种情况,要么增加计算机上的内存(即放大),要么增加为您完成工作的计算机数量,同时并行化您的算法(即缩小)。我不认为纯粹使用迭代的函数本身更好,但我会通过几个扩展来实现它
public static SourceObject GetAtList(this SourceObject s, List<int> cycleRef)
{
var ret = s;
for (int i = 0; i < cycleRef.Count; i++)
{
ret = ret.Children[cycleRef[i]];
}
return ret;
}
public static void SetAtList(this DestinationObject d, List<int> cycleRef, SourceObject s)
{
var ret = d;
for (int i = 0; i < cycleRef.Count - 1; i++)
{
ret = ret.Children[cycleRef[i]];
}
ret.Children.Add ( new DestinationObject() { Id = s.Id } );
}
publicstaticsourceobjectgetatlist(此sourceobjects,List cycleRef)
{
var-ret=s;
for(int i=0;i
和迭代器列表
public static DestinationObject MapSourceToDestinationIter(SourceObject source)
{
var result = new DestinationObject();
result.Id = source.Id;
if (source.Children.Count == 0)
{
return result;
}
List<int> cycleTot = new List<int>();
List<int> cycleRef = new List<int>();
cycleRef.Add(0);
cycleTot.Add(source.Children.Count-1);
do
{
var curr = source.GetAtList(cycleRef);
result.SetAtList(cycleRef, curr);
if (curr.Children.Count == 0)
{
cycleRef[cycleRef.Count - 1]++;
while (cycleRef[cycleRef.Count - 1]> cycleTot[cycleTot.Count-1])
{
cycleRef.RemoveAt(cycleRef.Count - 1);
cycleTot.RemoveAt(cycleTot.Count - 1);
if (cycleRef.Count == 0)
{
break;
}
cycleRef[cycleRef.Count - 1]++;
}
} else
{
cycleRef.Add(0);
cycleTot.Add(curr.Children.Count - 1);
}
} while (cycleTot.Count>0);
return result;
}
公共静态目标对象映射源ToDestinationItemer(源对象源)
{
var result=new DestinationObject();
result.Id=source.Id;
if(source.Children.Count==0)
{
返回结果;
}
List cycleTot=新列表();
List cycleRef=新列表();
循环ref.Add(0);
cycleTot.Add(source.Children.Count-1);
做
{
var curr=source.GetAtList(cycleRef);
结果.设置列表(cycleRef,curr);
if(curr.Children.Count==0)
{
cycleRef[cycleRef.Count-1]+;
while(cycleRef[cycleRef.Count-1]>cycleTot[cycleTot.Count-1])
{
cycleRef.RemoveAt(cycleRef.Count-1);
cycleTot.RemoveAt(cycleTot.Count-1);
如果(cycleRef.Count==0)
{
打破
}
cycleRef[cycleRef.Count-1]+;
}
}否则
{
循环ref.Add(0);
cycleTot.Add(curr.Children.Count-1);
}
}而(循环次数>0);
返回结果;
}
我不一定建议这样做,但它可能比Linq的替代方案更快
无论如何,明确使用
堆栈(如Ivan Stoev的例子)将是最佳解决方案 我仍然相信以树的最大深度为大小的堆栈是最佳的一般解决方案
但有趣的是,数据结构和具体过程包含了实现转换所需的所有信息,而无需仅基于Children.Count
。让我们看看我们需要什么:
(1) 是否有更多源子项要处理:source.children.Count!=target.Children.Count)
(2) 哪个是下一个要处理的源子级:source.Children[target.Children.Count]
(3) 当前正在处理的子进程是什么
public static DestinationObject MapSourceToDestinationIter(SourceObject source)
{
var result = new DestinationObject();
result.Id = source.Id;
if (source.Children.Count == 0)
{
return result;
}
List<int> cycleTot = new List<int>();
List<int> cycleRef = new List<int>();
cycleRef.Add(0);
cycleTot.Add(source.Children.Count-1);
do
{
var curr = source.GetAtList(cycleRef);
result.SetAtList(cycleRef, curr);
if (curr.Children.Count == 0)
{
cycleRef[cycleRef.Count - 1]++;
while (cycleRef[cycleRef.Count - 1]> cycleTot[cycleTot.Count-1])
{
cycleRef.RemoveAt(cycleRef.Count - 1);
cycleTot.RemoveAt(cycleTot.Count - 1);
if (cycleRef.Count == 0)
{
break;
}
cycleRef[cycleRef.Count - 1]++;
}
} else
{
cycleRef.Add(0);
cycleTot.Add(curr.Children.Count - 1);
}
} while (cycleTot.Count>0);
return result;
}
public static DestinationObject MapSourceToDestination(SourceObject source)
{
// Map everything except childen
Func<SourceObject, DestinationObject> primaryMap = s => new DestinationObject
{
Id = s.Id,
// ...
Children = new List<DestinationObject>(s.Children.Count) // Empty list with specified capacity
};
var target = primaryMap(source);
var currentSource = source;
var currentTarget = target;
int depth = 0;
while (true)
{
if (currentTarget.Children.Count != currentSource.Children.Count)
{
// Process next child
var sourceChild = currentSource.Children[currentTarget.Children.Count];
var targetChild = primaryMap(sourceChild);
currentTarget.Children.Add(targetChild);
if (sourceChild.Children.Count > 0)
{
// Move one level down
currentSource = sourceChild;
currentTarget = targetChild;
depth++;
}
}
else
{
// Move one level up
if (depth == 0) break;
depth--;
currentSource = source;
currentTarget = target;
for (int i = 0; i < depth; i++)
{
int index = currentTarget.Children.Count - 1;
currentSource = currentSource.Children[index];
currentTarget = currentTarget.Children[index];
}
}
}
return target;
}
currentSource = currentSource.Parent;
currentTarget = currentTarget.Parent;