如何使用 c# 查找递归函数采用的路径
本文关键字:路径 递归函数 何使用 查找 | 更新日期: 2023-09-27 17:56:29
我有一个对象,它有一个属性,允许我创建同一对象(嵌套对象)的另一个实例。我需要搜索一个列表并找出嵌套中对象的第一个出现。
一旦找到一个,我想找出找到它的确切路径。
我有以下课程
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; }
}