【问题标题】:Last iteration of recursive method check递归方法检查的最后一次迭代
【发布时间】:2011-10-20 12:31:31
【问题描述】:

我有一个递归方法,可以构建资源及其相关资源的树状结构。

对于我使用的每个资源,我将其添加到一个类成员列表中,我会在每次迭代时检查该列表,以确保我们不会在相互依赖的资源上无限循环。

每次第一次调用这个递归方法,都需要明确类成员列表。

目前我有一个单独的方法来执行此操作,我可以在调用递归方法之间调用它。

我想摆脱这个方法调用,每次都自动重置列表。

目前我可以看到解决此问题的两个选项:

  1. 测试调用方法是否与当前调用方法相同 执行方法,如果没有,重置列表
  2. 取而代之的是摆脱递归和排队项目,出队和 我们去排队。在方法调用结束时,我可以重置列表。

您将如何解决这个问题?你会采取什么方法?

这是我的代码目前的样子:

public class GetAllRelatedResourcesByParentGuidQuery : IGetAllRelatedResourcesByParentGuidQuery
    {
        private readonly IList<Guid> _itemsCheckedForRelations = new List<Guid>();

        public IEnumerable<IDependency> Invoke(Guid parentCiId, 
                                                  IResoucesByIdQuery getResources)
        {
            if (!_itemsCheckedForRelations.Contains(parentCiId))
            {
                var relatedResources = getResources.Invoke(parentCiId);

                _itemsCheckedForRelations.Add(parentCiId);

                if (relatedResources.Count() > 0)
                {
                    foreach (var relatedResource in relatedResources)
                    {
                        relatedResource.Resource.DependentResources = Invoke(
                                                         relatedResource.Resource.Id, 
                                                         getResources);

                        yield return relatedResource;
                    }
                }
            }
        }

        public void ResetCheckedItemsCollection()
        {
            _itemsCheckedForRelations.Clear();
        }
    }

【问题讨论】:

  • 如果您粘贴部分代码可能有助于理解您的推理。在您的场景中,我会说拥有 2 个重载方法是完美的:一个用于第一次调用,第二个是递归的,由第一个调用。
  • 一些代码会有所帮助。 AFAIK,如果没有返回语句和要作为参数传递的对象,递归是无效的,将对其执行操作,并且该对象必须随着每次递归而改变。
  • 谢谢大家。我已经在上面包含了我的代码。

标签: c# recursion queue


【解决方案1】:

我会创建一个 public 方法来执行创建,但不关心 recursive 方法,并将其作为参数。

public List<string> DoSomething(int input)
{
    List<string> results = new List<string>();
    DoSomethingImpl(input, results);
    return results;
}

private void DoSomethingImpl(int input, List<T> results)
{
    // For example...
    if (input == 0)
    {
        return results;
    }
    results.Add("Foo");
    DoSomethingImpl(input - 1, results);        
}

【讨论】:

  • 那不叫重载方法;)
  • 谢谢乔恩。那很完美。现在看来很明显了。
【解决方案2】:

只需像这样创建一个调用内部方法的方法:

public class GetAllRelatedResourcesByParentGuidQuery : IGetAllRelatedResourcesByParentGuidQuery 
{ 
        private readonly IList<Guid> _itemsCheckedForRelations = new List<Guid>(); 

        public IEnumerable<IDependency> Invoke(Guid parentCiId,  
                                                  IResoucesByIdQuery getResources) 
        {
            Reset();
            return InternalInvoke(parentCiID, getResources);
        }

        private IEnumerable<IDependency> InternalInvoke(Guid parentCiId,  
                                                  IResoucesByIdQuery getResources) 
        {
             //actual implementation, when going recursive, call this internal method
        }
}

【讨论】:

    【解决方案3】:

    在我看来,List 不应该是类成员,而是您调用的方法的参数...任何直接调用都发生在 null 的此参数(甚至可以是默认值!)。 .. 该方法在这种情况下分配List,在递归调用中它只是传递分配的List...

    【讨论】:

      【解决方案4】:

      您可以尝试使用递归 lambda 表达式:

      public class GetAllRelatedResourcesByParentGuidQuery :
                          IGetAllRelatedResourcesByParentGuidQuery
      {
          public IEnumerable<IDependency> Invoke(
                  Guid parentCiId,
                  IResoucesByIdQuery getResources)
          {
              var checkedItems = new List<Guid>();
              Func<Guid, IResoucesByIdQuery, IEnumerable<IDependency>> invoke = null;
              invoke = (pcid, gr) =>
              {
                  if (!checkedItems.Contains(pcid))
                  {
                      checkedItems.Add(pcid);
                      var drs = gr.Invoke(pcid).ToArray();
                      foreach (var relatedResource in drs)
                      {
                          relatedResource
                              .Resource
                              .DependentResources =
                                  invoke(relatedResource.Resource.Id, gr);
                      }
                      return drs;
                  }
                  return Enumerable.Empty<IDependency>();
              };
          }
      }
      

      通过这种方法,可以在没有任何特殊逻辑的情况下进行多个并发调用来清除列表。

      【讨论】:

        猜你喜欢
        • 2016-03-21
        • 2017-01-16
        • 1970-01-01
        • 1970-01-01
        • 2021-12-24
        • 2014-09-10
        • 2015-07-11
        • 2016-07-21
        • 1970-01-01
        相关资源
        最近更新 更多