我有这段代码,它想知道记录到控制台的内容。
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₁ - 1
、j₃ = j₂ - 1
等等......一般公式是jₙ = jₙ₋₁ - 1
.
观察此公式正在减小,因此最终将导致值小于或等于0
(这是我们的退出条件)。因此,我们知道递归结束。
因为该函数只有在初始值大于0
时才会做一些有趣的事情。从现在开始,我们将假设j₀
大于0
(其他明智的是,代码不输出任何内容,仅此而已)。
现在我知道序列结束了,我将翻转命名法。让N
j₀
.让N-1
j₁
等等。最后一个值是N-N
,即0
。为什么?因为随着值的减小,0
是第一个不大于0
的值。这是第一个不会产生输出的值。最后一个输出是1
。
函数返回什么?return j
.j
是函数获得的任何输入,因为j
没有被修改。因此,该函数返回它获得的任何输入。并输出一些值。
由于我们将通过i-1
,我们知道它将返回i-1
。因此,i
的值递减。因此,我们知道迭代也会结束。由于递归和迭代都结束了,我们知道代码终止了。
所以,第一次i=j
给i
值N
。我们检查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
。 - 当我们用值调用它时
2
序列2 (values for 1) 1
即211
. - 当我们用值调用它时
3
序列3 (values for 2) 2 (values for 1) 1
即3211211
. - 当我们用值调用它时
4
序列4 (values for 3) 3 (values for 2) 2 (values for 1) 1
即432112113211211
.
您可以在线试用。
不,它不输出212
.如果有的话,对于任何大于或等于2
的输入,它以211
结尾。你是怎么212
的?
让我颠倒序列的顺序,像这样表达112112311211234
.这样更容易掌握:
- 从数字 1 开始。
- 复制序列。
- 添加下一个数字。
- 转到步骤 2。
所以1
,然后11
,然后112
,然后112112
然后1121123
然后11211231121123
然后112112311211234
等等。
在整数序列在线百科全书上搜索它,我发现它被编目为A082850。在那里,您可以看到获得相同序列的替代方法。
<小时 />我已经开始回答了,所以我想我会在这里发布它。也许看到它以另一种方式编写会有所帮助?
这是了解调试器的好时机。您可以轻松地单步执行代码以查看发生了什么(递归调用期间值如何变化等)。
要说明为什么在调用Printer(2)
时输出211
:
Printer(2)
从Main()
调用i
初始化为2
i > 0
true
Console.Write(2)
被称为- 执行
i = Printer(i - 1)
(在for
循环中) - 调用
Printer(1)
(从步骤4
递归) i
初始化为1
i > 0
true
Console.Write(1)
被称为- 执行
i = Printer(i - 1)
(在for
循环中) - 调用
Printer(0)
(从步骤9
递归) i
初始化为0
i > 0
false
- 返回
0
;堆栈展开至步骤9
i = 0
分配i > 0
false
- 返回
1
;堆栈展开至步骤4
i = 1
分配i > 0
true
Console.Write(1)
被称为- 执行
i = Printer(i - 1)
(在for
循环中) - 调用
Printer(0)
(从步骤20
递归) - 进行
i = 0
分配 i > 0
false
- 返回
0
;堆栈展开至步骤20
- 进行
i = 0
分配 i > 0
false
- 返回
2
;控制从步骤0
返回到main()