# 递归函数
# 如果一个函数在内部调用自己本身，这个函数就是递归函数
# 计算阶乘：
def fact(n):
	if n == 1:
		return 1;
	return n * fact(n - 1);
print(fact(1));
print(fact(5));
print(fact(100));
# 递归的优点是定义简单，逻辑清晰。理论上，所有的递归函数都是可以写成循环的方式，
# 但是循环逻辑不如递归清晰
# 使用递归函数需要注意防止栈溢出。在计算机中，函数调用是通过栈这种数据结构实现的，
# 每当进入一个函数调用，栈就会加一层栈帧，每当函数返回，栈就会减一层栈帧。由于栈
# 的大小不是无限的，所以，递归调用的次数过多，会导致栈溢出。
# 解决递归调用栈溢出的方法是通过尾递归优化，事实上尾递归和循环的效果是一样的，所以把循环看成是一种特殊的
# 尾递归函数也是可以的。
# 尾递归是指，在函数返回的时候，调用自己本身，并且，return语句不能包含表达式。这样编译器或者解释器就可以把
# 尾递归做优化，使递归本身无论调用多少次，都只占用一个栈帧，不会出现栈溢出的情况。
# 把上面的例子优化为尾递归：
def fact1(n):
	return fact_iter(n , 1);

def fact_iter(num, product):
	if num == 1:
		return product;
	return fact_iter(num - 1, num * product);

# 可以看到，return fact_iter(num - 1, num * product)仅返回递归函数本身，num - 1 和 num * product在
# 函数调用前就会被计算，不影响函数调用。
# fact(5)对应的fact_iter(5, 1)的调用如下：
# ===> fact_iter(5, 1)
# ===> fact_iter(4, 5)
# ===> fact_iter(3, 20)
# ===> fact_iter(2, 60)
# ===> fact_iter(1, 120)
# ===> 120
# 尾递归调用时，如果做了优化，栈不会增长，因此，无论多少次调用也不会导致栈溢出
# 遗憾的是，大多数编程语言没有针对尾递归做优化，Python解释器也没有做优化，所以，即使把上面的fact(n)函数
# 改成尾递归方式，也会导致栈溢出
# Python标准的解释器没有针对尾递归做优化，任何递归函数都存在栈溢出的问题。


# 练习
# 汉诺塔的移动可以用递归函数非常简单地实现。