Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/ant/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将LINQ Select中的递归方法组转换为迭代方法_C#_Linq_Recursion_Iteration - Fatal编程技术网

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;