Recursion 递归本身是一种特性吗?

Recursion 递归本身是一种特性吗?,recursion,Recursion,……还是只是一种练习 我问这个问题是因为我和教授的一个争论:我失去了递归调用函数的荣誉,因为我们没有在课堂上讨论递归,我的争论是我们通过学习return和方法来隐式地学习它 我在这里提问是因为我怀疑有人有一个明确的答案 例如,以下两种方法的区别是什么: public static void a() { return a(); } public static void b() { return a(); } 除了“a永远持续”(在实际的程序中,当提供无效输入时,

……还是只是一种练习

我问这个问题是因为我和教授的一个争论:我失去了递归调用函数的荣誉,因为我们没有在课堂上讨论递归,我的争论是我们通过学习
return
和方法来隐式地学习它

我在这里提问是因为我怀疑有人有一个明确的答案

例如,以下两种方法的区别是什么:

public static void a() {
    return a();
    }

public static void b() {
    return a();
    }
除了“
a
永远持续”(在实际的程序中,当提供无效输入时,它被正确地用于再次提示用户)之外,
a
b
之间有什么根本区别吗?对于未优化的编译器,如何以不同的方式处理它们


归根结底,这取决于我们是否通过学习从
b
返回a()来学习从
a
返回a()。是吗?

对于您提出的具体问题,有很多观点需要考虑,但我可以说的是,从学习语言的角度来看,递归本身并不是一种功能。如果你的教授真的用了一个他还没教过的“特征”来标记你的分数,那是错误的,但是就像我说的,这里还有其他观点要考虑,这实际上使教授在扣除分数时是正确的。
从你的问题中我可以推断,在输入失败的情况下使用递归函数请求输入不是一个好的实践,因为每个递归函数的调用都会被推到堆栈上。由于这种递归是由用户输入驱动的,因此可能有一个无限递归函数,从而导致堆栈溢出

您在问题中提到的这两个示例在功能上没有区别(但在其他方面有所不同)——在这两种情况下,返回地址和所有方法信息都被加载到堆栈中。在递归情况下,返回地址仅仅是方法调用后的行(当然,它不是您在代码本身中看到的,而是编译器创建的代码中看到的)。在Java、C和Python中,递归与迭代(通常)相比成本相当高,因为它需要分配新的堆栈框架。更不用说,如果输入多次无效,可能会出现堆栈溢出异常

我相信教授扣了分,因为递归被认为是一门独立的学科,没有编程经验的人不太可能想到递归。(当然这并不意味着他们不会,但不太可能)

我认为教授给你扣了分数是对的。您可以很容易地将验证部分采用另一种方法,并按如下方式使用:

public bool foo() 
{
  validInput = GetInput();
  while(!validInput)
  {
    MessageBox.Show("Wrong Input, please try again!");
    validInput = GetInput();
  }
  return hasWon(x, y, piece);
}

如果你所做的确实可以用这种方式解决,那么你所做的是一种不好的做法,应该避免。

根据我从你的问题中得出的结论,在输入失败的情况下使用递归函数请求输入不是一种好的做法。为什么?

因为每个递归函数调用都会被推送到堆栈上。由于这种递归是由用户输入驱动的,因此可能有一个无限递归函数,从而产生:-p


有一个非递归循环来完成这项工作是一种方法。

老师想知道你是否学习过。显然,你没有按照他教你的方式(好方法;迭代)解决问题,因此,你认为你没有。我完全支持创造性的解决方案,但在这种情况下,我必须同意您的老师的观点,原因不同:
如果用户多次提供无效输入(即按住enter键),您将出现堆栈溢出异常,您的解决方案将崩溃。此外,迭代解更有效,更易于维护。我想这就是你的老师应该给你的原因。

因为“我们没有在课堂上讨论递归”而扣分是很糟糕的。如果你学会了如何调用函数A,函数A调用函数B,函数C调用函数B,函数C返回到函数B,函数A返回到调用方,而老师没有明确告诉你这些函数必须是不同的函数(例如,在旧的FORTRAN版本中就是这样),那么没有理由A,B和C不能都是同一个函数


另一方面,我们必须查看实际代码,以确定在特定情况下使用递归是否真的是正确的做法。细节不多,但听起来确实不对

也许你的教授还没有教过,但听起来你已经准备好学习递归的优缺点了

递归的主要优点是,递归算法通常更容易、更快地编写

递归的主要缺点是递归算法可能导致堆栈溢出,因为每个递归级别都需要向堆栈中添加额外的堆栈帧


对于生产代码,如果扩展可以导致生产中的递归级别比程序员的单元测试中的递归级别多得多,那么缺点通常大于优点,并且在实际使用时经常避免递归代码。

回答您的特定问题:不,从学习语言的角度来看,递归不是一种特性。如果你的教授真的因为你使用了一个他还没有教过的“功能”而扣分,那就错了

从字里行间看,一种可能性是,通过使用递归,您避免了使用本应作为课程学习成果的功能。例如,可能您根本没有使用迭代,或者您只对循环使用
,而不是对
同时使用
。通常,作业的目的是测试你做某些事情的能力,如果你不去做,你的教授就不能给你分数
private int getInput() {
    int input;
    do {
        input = promptForInput();
    } while (!inputIsValid(input))
    return input;
}
private int getInput() {
    int input = promptForInput();
    if(inputIsValid(input)) {
        return input;
    }
    return getInput();
}
a();
move the address of label 1 to variable return_from_a
jump to label function_a
label 1
function a()
{
   var1 = 5;
   return;
}
label function_a
move 5 to variable var1
jump to the address stored in variable return_from_a
>>> map((lambda x: lambda f: x(lambda g: f(lambda v: g(g)(v))))(
...   lambda c: c(c))(lambda R: lambda n: 1 if n < 2 else n * R(n - 1)),
...   xrange(10))
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
factorial = lambda n: 1 if n < 2 else n * factorial(n-1)