递归代码很难理解 - 需要简化

时间:2017-02-08 15:44:31

标签: c# recursion

我有一个实体,我可以拥有InvoiceLine,然后您可以无限次地信用该发票行。但只有主InvoiceLine才能引用原始实体。

我使用递归来获取原始实体,但代码不是那么可读

private static PermanentPlacement PopulatePermanentPlacement(InvoiceLine invoiceLine)
        {
            PermanentPlacement permanentPlacement;
            var creditReissue = invoiceLine.CreditReissue;
            do
            {
                permanentPlacement = creditReissue.InvoiceLine.PermanentPlacement;
                if (permanentPlacement == null)
                {
                    creditReissue = creditReissue.InvoiceLine.CreditReissue;
                }
            } while(permanentPlacement == null);

            return permanentPlacement;
        }

我有什么方法可以让它更具可读性和简化性?

3 个答案:

答案 0 :(得分:5)

我认为RenéVogt的答案显示了简化此代码的最佳方法。但还有其他人。例如,考虑将循环移动到辅助函数:

static IEnumerable<Reissue> CreditReissues(Reissue original)
{
  var current = original;
  while(true) 
  {
    yield return current;
    current = current.InvoiceLine.CreditReissue;
  } 
}

现在你不需要再次编写一个循环来使用无限的信用重发序列:

private static PermanentPlacement PopulatePermanentPlacement(
  InvoiceLine invoiceLine)
{
  return CreditReissues(invoiceLine.CreditReissue)
    .Select(cr => cr.InvoiceLine.PermanentPlacement)
    .First(pp => pp != null);
}

即:获取无限的信用重发序列,将其转换为永久放置的无限序列,并在序列中返回第一个非空信用序列。

注意如何通过将循环更改为序列,我们现在可以在语句和变量的级别上描述我们想要在序列级而不是上执行的操作

顺便说一句,你说 - 两次 - 你在原始代码中使用递归,但你不是。递归解决方案看起来像这样:

private static PermanentPlacement PopulatePermanentPlacement(
  InvoiceLine invoiceLine)
{
  return invoiceLine.PermanentPlacement ??
         PopulatePermanentPlacement(
           invoiceLine.CreditReissue.InvoiceLine);
} 

你不应该为可能无界的循环使用递归解决方案,因为C#不能保证是尾递归,因此可能会破坏堆栈。

答案 1 :(得分:2)

我会将代码缩短为:

private static PermanentPlacement PopulatePermanentPlacement(InvoiceLine invoiceLine)
{
    var creditReissue = invoiceLine.CreditReissue;
    while(creditReissue.InvoiceLine.PermanentPlacement == null)
        creditReissue = creditReissue.InvoiceLine.CreditReissue;

    return creditReissue.InvoiceLine.PermanentPlacement;
}

这与您的代码相同,只是它访问最后PermantPlacement InvoiceLine的额外时间。因此,如果此属性getter不仅仅返回一个值,则此修改可能无效。

答案 2 :(得分:-2)

我的代码看起来非常清晰易读。 RenéVogt的答案就是你可以在没有改变逻辑的情况下尽可能短的答案,但我有一种直觉,你真正想要的可能就是这样:

private static PermanentPlacement PopulatePermanentPlacement(InvoiceLine invoiceLine)
{
    var creditReissue = invoiceLine.CreditReissue;
    while (creditReissue.InvoiceLine.CreditReissue != null)
    {
        // Get the last credit reissue
        creditReissue = creditReissue.InvoiceLine.CreditReissue;
    }
    return creditReissue.InvoiceLine.PermanentPlacement;    
}

它确实改变了一点逻辑,请确认它是等效的。