关于 Java 新手的递归
Posted
技术标签:
【中文标题】关于 Java 新手的递归【英文标题】:About Recursion for Newbies in Java 【发布时间】:2017-03-11 21:20:36 【问题描述】:所以,我有这段代码,这正是我解决给我的一个练习的方法,其中包括创建一个接收数字的递归函数,然后给你 1 的总和,所有数字之间,和你的号码。我知道我让它听起来很混乱,但这里有一个例子:
如果我插入数字 5,那么返回的值必须是 15,因为:1+2+3+4+5 = 15。
public class Exercise
public static void main(String[] args)
int returnedValue = addNumbers(6);
System.out.print(returnedValue);
public static int addNumbers(int value)
if (value == 1) return value;
return value = value + addNumbers(value-1);
从技术上讲,我的代码工作得很好,但我仍然不明白为什么 Eclipse 让我写两个返回,这就是我想知道的。
有没有办法只能写一次“return”?
【问题讨论】:
Eclipse 让我写了两个返回:这是什么意思? Eclipse 到底说了什么?关于什么代码?上面的代码有什么问题,为什么你认为你应该只有一个回报?代码会是什么样子?为什么不直接使用return value + addNumbers(value-1);
而不是return value = value + addNumbers(value-1);
?
【参考方案1】:
递归函数始终至少有 2 条路径,正常的将递归的路径和“结束”路径仅返回(通常是一个常量)。
但是,您可以这样做:
public static int addNumbers(int value)
if (value != 1)
value = value + addNumbers(value-1);
return value;
但我不能说我认为它更好(有些人对修改参数感到恼火,就像他们在多次返回时一样)。当然,您可以创建一个新变量并将其设置为一个值或另一个值,但是有人会因为您使用了太多代码行和一个不必要的变量而感到不安。欢迎编程 :) 您的原始代码可能与您可能得到的一样好。
至于为什么“Eclipse”对你这样做,它实际上是 Java——Java 比大多数语言更好地确保你没有尽快做明显错误的事情(在这种情况下,当你打字而不是等你编译)。它检测到你的 if 的一个分支返回了一个值,而另一个没有 - 这显然是错误的。
Java 还非常明确地强制您使用“return”语句,而另一种语言可能会让您少花钱。在 Groovy 中,您会很想消除返回并编写如下内容:
def addNumbers(value)value + (value-1?0:addNumbers(value-1))
只是为了好玩,但我当然不会称这更具可读性! Java 只是认为,在大多数情况下,强制你变得明确会更好。
【讨论】:
我推荐> 1
而不是!= 1
,以免用户输入0或负数...
@Pjs 实际上,我在写作时就想到了
【参考方案2】:
如果我错了,请随时纠正我,但我认为没有办法消除其中一个返回,除非您决定将变量放在方法之外或将方法更改为递归。
在java中,一个返回值的方法,必须在某个时刻返回一个值,不管它里面的代码做什么。 eclipse 要求您添加第二个返回的原因是,第一个返回仅在您的 if 语句评估为 true 时运行。如果您没有第二次返回,并且 if 语句最终不为真,java 将无法离开该方法,并且不知道该做什么,因此,eclipse 将要求您添加一个返回if 语句之后的语句。
这些类型的错误称为检查错误或编译时错误。这意味着 eclipse 从字面上看无法将您的代码转换为可运行的文件,因为它不知道如何;存在语法错误,或者您缺少返回等。
【讨论】:
我会说你很明显错了。请参阅其他答案中的反例。 正如 pjs 在他/她自己的回答中指出的那样,您只需一次返回即可编写它。 @pjs 是啊……没想到。【参考方案3】:就像阿西莫夫的机器人一样,所有递归算法都必须遵守三个重要定律:
-
递归算法必须有一个基本情况。
递归算法必须改变它的状态并朝着基本情况前进。
递归算法必须递归调用自身。
您的if (value == 1) return value;
return 语句是基本情况。那是递归(调用自身)停止的时候。当发生函数调用时,编译器会将当前状态推送到堆栈然后进行调用。因此,当该调用返回某个值时,它会从堆栈中提取该值,进行计算并将结果返回到上层。这就是为什么另一个 return 语句是 for 的原因。
认为这就像解决你的问题:
addNumbers(3) = 3 + addNumbers(2) (this is returned by second one)
-> 2 + addNumbers(1) (this is returned by second one)
-> 1 (this is returned by base case)
【讨论】:
【参考方案4】:当然,你可以只用一个返回来写它:
public static int addNumbers(int value)
if (value > 1)
value += addNumbers(value - 1);
return value;
如你所见,它是通过让一些变量保留运行结果直到你到达终点来完成的。在这种情况下,我能够在value
中就地完成它,在其他情况下,您可能需要创建一个局部变量,但是将中间结果存储在某个地方直到您到达返回点的想法是一个普遍的想法。
【讨论】:
这绝对是这个问题最易读和最明智的答案。 下面的解释也很棒,但你的解释太直接了,我不能不把它标记为正确答案。谢谢。【参考方案5】:来自关于递归的***:
在数学和计算机科学中,一类对象或方法 当它们可以由两个属性定义时表现出递归行为:
一个简单的基本案例(或多个案例)——一个不使用递归来产生答案的终止场景
将所有其他情况减少到基本情况的一组规则
有两种退货,因为您必须处理上述两种情况。在您的示例中:
基本情况是value == 1
。
将所有其他情况减少到基本情况的情况是value + addNumbers(value-1);
。
来源:https://en.wikipedia.org/wiki/Recursion#Formal_definitions
当然还有其他的写法,包括一些不需要多次返回的,但一般来说多次返回是表达递归的一种清晰而正常的方式,因为递归自然会分为多种情况。
【讨论】:
【参考方案6】:应该有两个回报。你的第一次回报说
if at 1: stop recurstion
第二个说
continue recursion by returning my value plus computing the value less than me
您可以使用三元组合它们:
return value == 1 ? value : value + addNumbers(value - 1)
但它不那么可读。
递归函数,如
斐波那契数列 分形 等多次使用自己,因为它们包含自己。
【讨论】:
以上是关于关于 Java 新手的递归的主要内容,如果未能解决你的问题,请参考以下文章