尾递归,如何消除return语句?

时间:2019-01-05 00:28:17

标签: c# recursion tail-recursion

这是一个尾部递归C#程序,该程序将1到10的平方相加。除AddSquares2()中的最后一个以外,它都有效。它不起作用,因为C#似乎在返回值的方法中的每个路径中都要求有return语句。但是,我不需要像这种尾部递归方法中的return语句。查看评论。

这仅仅是C#的特质吗?还是有办法告诉编译器我知道我在做什么,并且我不希望每个路径都返回一个值?

这是我的程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Recursive
{
    class Program
    {
        static void Main(string[] args)
        {
            int start = 1;
            int end = 10;
            int sum = 0;
            AddSquares(start, end, sum);
            sum = AddSquares2(start, end, sum);
        }

        private static int AddSquares2(int start, int end, int sum)
        {
            Console.WriteLine($"AddSquares2({start}, {end}, {sum})");
            if (start > end)
                return (sum);
            else
                //should not have to return anything here
                return(AddSquares2(start + 1, end, sum + start * start));
        }

        private static void AddSquares(int start, int end, int sum)
        {
            Console.WriteLine($"AddSquares({start}, {end}, {sum})");
            if (start > end)
                Console.WriteLine($"  The total sum is {sum}");
            else
                AddSquares(start + 1, end, sum + start * start);
        }
    }
}

以下是输出:

 AddSquares(1, 10, 0)
    AddSquares(2, 10, 1)
    AddSquares(3, 10, 5)
    AddSquares(4, 10, 14)
    AddSquares(5, 10, 30)
    AddSquares(6, 10, 55)
    AddSquares(7, 10, 91)
    AddSquares(8, 10, 140)
    AddSquares(9, 10, 204)
    AddSquares(10, 10, 285)
    AddSquares(11, 10, 385)
      The total sum is 385
    AddSquares2(1, 10, 0)
    AddSquares2(2, 10, 1)
    AddSquares2(3, 10, 5)
    AddSquares2(4, 10, 14)
    AddSquares2(5, 10, 30)
    AddSquares2(6, 10, 55)
    AddSquares2(7, 10, 91)
    AddSquares2(8, 10, 140)
    AddSquares2(9, 10, 204)
    AddSquares2(10, 10, 285)
    AddSquares2(11, 10, 385)
      The total sum is 385

3 个答案:

答案 0 :(得分:1)

简而言之,C#中的方法必须在所有代码执行路径上都有一个return,您可能不喜欢它,但是确实如此。在数学中,所有函数都具有相同的结果,这就是函数的作用。

现在要说的是,结果可以是不确定的,Infinity,0,null,或者在C#的返回类型为非空int的情况下,结果可以为0。签名。

我们假装一秒钟怎么样,就可以在其他地方消失代码执行。如果调用方法依赖于结果(通常是这样),并且即使该方法以您提出的方式结束,那么调用者会怎么想呢?那么它需要能够处理是否缺少值。

如果您真的需要知道自己的别的没有值,请使您的签名int?(可空)并返回null,以便调用者知道该怎么做。如果只是求和的数字不存在,则将其设为0,则无害

答案 1 :(得分:1)

不使用返回符“逃脱”的唯一方法是抛出错误。在您遇到的情况下这样做是没有意义的。

您还可以通过不提供代码路径来避免使用return(在这种情况下,没有else子句)

但是,您做对了,因为您返回了在其他情况下递归调用AddSquares2的结果。如果您不这样做,那么AddSquares2的代码将永远不会递归并且无法正常工作(因此,您DID毕竟必须返回一些东西!啊!)。

递归方法必须在其内部具有一个点,它们在不调用自身的情况下返回(以阻止递归永远消失),并且还必须在其上具有一个点(以使递归进行一段时间)

可能是一项学术练习,但也许值得一提的是,递归是循环的一种形式,它(ab)使用调用堆栈来提供循环结构。递归方法可以(而且应该恕我直言)使用常规循环完成

答案 2 :(得分:1)

该问题的断言是正确的,即它不需要“返回”,但这取决于您对“返回”的含义。从低层次上讲,如果执行了 if 尾调用,则此时无需执行“ ret”指令(某种标准堆栈清除类型的指令),因为尾调用将使实际执行的行为等效于一个简单的循环。

但是,C#与金属的距离并不那么近。在编写C#时,我们指定 intent ,然后让编译器(和/或JITter)负责优化,例如尾部调用,方法内联等。 C#return关键字的存在不一定表示或强制进行某些堆栈清除。相反,它指示程序员的意图

如果我们允许您省略“返回”,则会使编译器理解您的意图的工作变得更加困难。程序员是否意识到会发生的优化,并且在概念上打算使该方法“返回”,还是他的意思是明确不对结果做任何事情。 C#假定后者。

现在,如果您担心不能遗漏返回的事实意味着无法执行尾声,请不必担心。编译器和JITter会自动处理这些优化。他们会以最优化的方式采用您指定的意图实现。而且,与程序员相比,他们几乎总是在决定什么类型和何时应用这些优化方面做得更好。