C# 如果循环条件问题(这不应该是一个无限循环吗?



我有这段代码,它想知道记录到控制台的内容。


private static int Printer (int j)
{
for(var i = j; i > 0; i = Printer(i - 1))
{
Console.Write(i);
}
return j;
}
Printer(2)

显然这返回 211,但为什么这不创建一个无限循环,因为 Printer(anyNumber) 将始终返回 2,从而将 i 分配为始终> 0?

可以使用调试器单步执行代码并自行查看。我建议学习使用调试器。取决于你问谁,我刚才说的不是一个好的答案。所以,要么我想提供更好的解释,要么剩下的答案是我弯曲......让我们说两者兼而有之。


我们设置i=j.我们检查i>0.

该检查可以是true的,也可以是false的。


如果这是false(即如果i小于或等于0),我们返回j。这是我们的退出条件。


否则,我们使用i-1递归调用函数,这将是我们j的新值。

但是j的价值是什么?第一次运行代码时,j将具有一些价值...让我们称之为j₀.那么i=j意味着i=j₀.因此,我们正在调用该函数j₀-1.我们称之为j₁

只要i>0继续true,我们就会j₂ = j₁ - 1j₃ = j₂ - 1等等......一般公式是jₙ = jₙ₋₁ - 1.

观察此公式正在减小,因此最终将导致值小于或等于0(这是我们的退出条件)。因此,我们知道递归结束。


因为该函数只有在初始值大于0时才会做一些有趣的事情。从现在开始,我们将假设j₀大于0(其他明智的是,代码不输出任何内容,仅此而已)。

现在我知道序列结束了,我将翻转命名法。让Nj₀.让N-1j₁等等。最后一个值是N-N,即0。为什么?因为随着值的减小,0是第一个不大于0的值。这是第一个不会产生输出的值。最后一个输出是1


函数返回什么?return j.j是函数获得的任何输入,因为j没有被修改。因此,该函数返回它获得的任何输入。并输出一些值。

由于我们将通过i-1,我们知道它将返回i-1。因此,i的值递减。因此,我们知道迭代也会结束。由于递归和迭代都结束了,我们知道代码终止了。


所以,第一次i=jiN。我们检查i>0.请记住,为了论证,我们说N大于0(否则代码返回,仅此而已)。

然后我们用i-1递归调用该函数,我们称之为N-1。由于该函数返回它获得的任何值,它将返回N-1。然后我们打印该值。并重复。

到目前为止,我们知道函数的输出遵循以下顺序:

N
(some values)
N-1
(some values)
N-2
(some values)
·
·
·

但是这些(some values)是什么样子的呢?嗯,一样。由于序列是递归的,因此序列是分形的(自相似)。

N
(values for N-1)
N-1
(values for N-2)
N-2
(values for N-3)
·
·
·
1

因此,序列以这样的方式结束:

·
·
·
4
3
2
1
1
2
1
1
3
2
1
1
2
1
1

通过一些缩进更容易理解:

4
3
2
1
1
2
1
1
3
2
1
1
2
1
1

或者一些ASCII艺术:

·           ·
·           ·
·           ·
4-----------+
3--------+  |
2        |  |
1        |  |
1        |  |
2        |  |
1        |  |
1--------+  |
3--------+  |
2 ----+  |  |
1     |  |  |
1 ----+  |  |
2 ----+  |  |
1 -+  |  |  |
1 -+ -+ -+ -+

在那里我们看到:

  1. 当我们调用值为1的函数时,序列是1
  2. 当我们用值调用它时2序列2 (values for 1) 1211.
  3. 当我们用值调用它时3序列3 (values for 2) 2 (values for 1) 13211211.
  4. 当我们用值调用它时4序列4 (values for 3) 3 (values for 2) 2 (values for 1) 1432112113211211.

您可以在线试用。

不,它不输出212.如果有的话,对于任何大于或等于2的输入,它以211结尾。你是怎么212的?


让我颠倒序列的顺序,像这样表达112112311211234.这样更容易掌握:

  1. 从数字 1 开始。
  2. 复制序列。
  3. 添加下一个数字。
  4. 转到步骤 2。

所以1,然后11,然后112,然后112112然后1121123然后11211231121123然后112112311211234等等。

在整数序列在线百科全书上搜索它,我发现它被编目为A082850。在那里,您可以看到获得相同序列的替代方法。

<小时 />

我已经开始回答了,所以我想我会在这里发布它。也许看到它以另一种方式编写会有所帮助?

这是了解调试器的好时机。您可以轻松地单步执行代码以查看发生了什么(递归调用期间值如何变化等)。

要说明为什么在调用Printer(2)时输出211

  1. Printer(2)Main()调用
  2. i初始化为2
  3. i > 0true
  4. Console.Write(2)被称为
  5. 执行i = Printer(i - 1)(在for循环中)
  6. 调用Printer(1)(从步骤4递归)
  7. i初始化为1
  8. i > 0true
  9. Console.Write(1)被称为
  10. 执行i = Printer(i - 1)(在for循环中)
  11. 调用Printer(0)(从步骤9递归)
  12. i初始化为0
  13. i > 0false
  14. 返回0;堆栈展开至步骤9
  15. i = 0分配
  16. i > 0false
  17. 返回1;堆栈展开至步骤4
  18. i = 1分配
  19. i > 0true
  20. Console.Write(1)被称为
  21. 执行i = Printer(i - 1)(在for循环中)
  22. 调用Printer(0)(从步骤20递归)
  23. 进行i = 0分配
  24. i > 0false
  25. 返回0;堆栈展开至步骤20
  26. 进行i = 0分配
  27. i > 0false
  28. 返回2;控制从步骤0返回到main()

最新更新