繁体   English   中英

如何使用C#查找递归函数采用的路径?

[英]How to find the path that a recursive function took using c#?

我有一个对象,该对象的属性允许我创建同一对象(嵌套对象)的另一个实例。我需要搜索列表,找出对象在嵌套中的首次出现。

一旦找到一个,我想找出找到它的确切路径。

我有以下课程

public class ReportRelationMapping : IReportRelationMapping
{
    string Name { get; set; }

    IReportRelation LocalRelation { get; set; }

    IReportRelation ForeignRelation { get; set; }

    IReportRelationMapping RelatedThrough { get; set; } // This creates and instance of itself creating a chain
}

假设我有上述课程的以下列表

List<IReportRelationMapping> myList = new List<ReportRelationMapping> 
{
    new ReportRelationMapping 
    {
        Name = "A",
        LocalRelation = ..., 
        ForeignRelation = ...
        RelatedThrough = new ReportRelationMapping
        {
            Name = "B",
            LocalRelation = ..., 
            ForeignRelation = ...
            RelatedThrough = new ReportRelationMapping
            {
                Name = "C",
                LocalRelation = ..., 
                ForeignRelation = ...
                RelatedThrough = new ReportRelationMapping
                {
                    Name = "D",
                    LocalRelation = ..., 
                    ForeignRelation = ...
                    RelatedThrough = new ReportRelationMapping
                }
            }
        }
    }
    ,new ReportRelationMapping 
    {
        Name = "E",
        LocalRelation = ..., 
        ForeignRelation = ...
        RelatedThrough = new ReportRelationMapping
        {
            Name = "F",
            LocalRelation = ..., 
            ForeignRelation = ...

        }
    }
    ,new ReportRelationMapping 
    {
        Name = "G",
        LocalRelation = ..., 
        ForeignRelation = ...
    }
}

我需要找出查找第一个“ C”所用的确切路径。 我需要获得以下清单

List<ReportRelationMapping> pathToTarget = new List<ReportRelationMapping> 
{
    new ReportRelationMapping 
    {
        Name = "A",
        LocalRelation = ..., 
        ForeignRelation = ...
    }
    ,new ReportRelationMapping 
    {
        Name = "B",
        LocalRelation = ..., 
        ForeignRelation = ...
    }
    ,new ReportRelationMapping 
    {
        Name = "C",
        LocalRelation = ..., 
        ForeignRelation = ...
    }
}

我编写了一个递归方法,该方法可以正确找到“ C”,但不能捕获正确使用的路径。 这是我所做的

private void GetAvailableRelation(List<IReportRelationMapping> relationsMappings, string belongsTo, ref List<IReportRelationMapping> pathToTarget)
{

    var requiredRelation = relationsMappings.Where(x => x.LocalRelation.TableAlias == belongsTo || x.ForeignRelation.TableAlias == belongsTo).FirstOrDefault();

    if (requiredRelation == null)
    {
        //At this point we know there is no match on the top level, lets check the nested level
        var relatedRelations = new List<IReportRelationMapping>();

        foreach (var relationsMapping in relationsMappings)
        {
            if (relationsMapping.RelatedThrough != null)
            {
                relatedRelations.Add(relationsMapping.RelatedThrough);
            }
        }

        if (relatedRelations.Any())
        {
            GetAvailableRelation(relatedRelations, belongsTo, ref pathToTarget);
        }
        else
        {
            // Since we reached the last node and count not find a matching, reset the pathToTarget list
            pathToTarget.Clear();
        }
    }

    if (requiredRelation != null)
    {
        //Check if relation exists before adding it.
        pathToTarget.Add(requiredRelation);
    }

}


The problem seems to be that after I call the method `GetAvailableRelation` recursively, the value of `requiredRelation` will become `null` when the last line come there is nothing to add to my `pathToTarget`. 

问题:如何正确生成pathToTarget列表?

简短的答案是:不要变异。

您正在传递对变量的引用,该变量本身就是对可变列表的引用,然后对其进行突变。 这是造成递归代码混乱的秘诀。 (您从不对引用进行变异; 如果您从不对变量进行变异, 为什么要通过ref传递变量?我想您可能对C#中的引用语义有根本的误解。)

相反,原因如下。 从基础开始:

  • 我的函数有输入和输出
  • 输出仅取决于输入
  • 输入和输出永不突变

好吧,现在想想:既然您已选择遵循这些规则,那么输入和输出必须是什么? 输入是某些东西的不可变序列。 输出是某些东西的不可变序列。 大。

由于这是一种递归方法,因此我们知道以下其他事实:

  • 在一个简单的基本情况下,我们将检测到该解决方案不重要,然后返回该重要解决方案。
  • 在递归的情况下,我们将问题分成较小的问题,然后递归求解,然后将递归解决方案组合为较大问题的解决方案。

现在您可以回答以下问题:

  • 我的方法的参数类型应该是什么?
  • 我的方法的返回类型应该是什么?
  • 小事是什么?
  • 如何将一个不重要的案例分解为一个或更多个简单的问题?
  • 如何将这些问题的解决方案合并为更大问题的解决方案?

让我们画出一些答案:

  • 该方法采用一个不变的项目序列和一个搜索项目。
  • 该方法返回一个不变的项目序列,即路径。
  • 有两种琐碎的情况。 情况一是:当前项目序列包含搜索项目。 简单的解决方案是包含匹配项的路径。 情况二是:当前项目序列为空。 简单的解决方案是路径为空。
  • 递归的情况是:找到一个更简单的问题并加以解决; 这给了一条路。 如果路径为空,则返回空路径,因为这意味着未找到该项目。 否则,将当前项目放在路径之前,然后将其返回。

现在可以编写您的方法了吗?

最好的解决方案是使用一个临时对象模拟堆栈来递归代码,当您找到对象时,该堆栈将包含指向它的确切路径。

我认为我通过尝试遵循埃里克·利珀特(Eric Lippert)正确答案中列出的规则解决了这个问题。

这是似乎起作用的功能。

    private List<IReportRelationMapping> GetAvailableRelation(List<IReportRelationMapping> relationsMappings, string belongsTo)
    {
        List<IReportRelationMapping> pathToTarget = new List<IReportRelationMapping>();

        var requiredRelation = relationsMappings.Where(x => x.LocalRelation.TableAlias == belongsTo || x.ForeignRelation.TableAlias == belongsTo).FirstOrDefault();

        if (requiredRelation != null)
        {
            //Handle the top level

            pathToTarget.Add(requiredRelation);

        } else {
            //At this point we know there is no match on the top level, lets check the nested level

            var relatedRelations = new List<IReportRelationMapping>();

            foreach (var relationsMapping in relationsMappings)
            {
                if (relationsMapping.RelatedThrough != null)
                {
                    //Add the path in between previous and next
                    pathToTarget.Add(relationsMapping);

                    foreach (var subRelation in relationsMapping.RelatedThrough)
                    {
                        relatedRelations.Add(subRelation);
                    }

                }
            }

            if (relatedRelations.Any())
            {
                //Now we know there is at least one more nested level that we need to check
                var subPathsToTarget = GetAvailableRelation(relatedRelations, belongsTo);

              if (subPathsToTarget.Any())
                {
                    //prepend the current items to the path
                    pathToTarget = pathToTarget.Concat(subPathsToTarget).ToList();
                }
                else
                {
                    //At this point we know we reach the final node and the item was not found.
                    pathToTarget.Clear();
                }

            }

        }

        return pathToTarget;

    }

更新

现在我的对象接受了这样的RelatedThrough列表

public class ReportRelationMapping : IReportRelationMapping
{
    public string Name { get; set; }

    public SqlJoinStatement JoinType { get; set; }

    public IReportRelation LocalRelation { get; set; }

    public IReportRelation ForeignRelation { get; set; }

    public List<IReportRelationMapping> RelatedThrough { get; set; }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM