函数应该是我在接触python时遇到的第一块感觉学习起来比较费脑的地方,看似几个简单的规则,实际上在理解和手动测试上出了很多错误,这也给予了我很大的打击,以至于在后面两天不断地去巩固去复习,想要尽量的去把他们理清,可能后面多加练习后自然就掌握了,但是前面那种随时学习随时忘,某一处想不起来的抓心感着实让人无法将进度进行下去
#递归函数的内容不多,例子虽然也能看懂,但是却没有那种思维顺畅的感觉,这一块不仅看了例子还专门找了视频学习
递归函数:如果一个函数调用自身本身,就是递归函数,例:
>>> def fact(n):
... if n == 1:
... return 1
... return n * fact(n - 1)
>>> fact(5)
120
以fact(5)为例,详细的计算方式为:
>>>fact(5):
>>>5 * fact(5 - 1 = 4) #返回的n还是不等于1,所以继续循环
>>>5 * (4 * fact(4 - 1 = 3)) #返回的n还是不等于1,所以继续循环
>>>5 *( 4 * (3 * fact(3 - 1 = 2) )) #返回的n还是不等于1,所以继续循环
>>>5 * (4 * (3 * (2 * fact(2 - 1 = 1) ))) #返回的n等于1,所以返回1,不再代入自身
>>>5 * (4 * (3 * (2 * 1))) #接下来再往回计算
>>>5 * (4 * (3 * 2))
>>>5 * (4 * 6)
>>>5 * 24
>>>120
#其实上述函数计算我们也可以使用循环来完成,只不过递归函数的逻辑更加清晰简单。
此外,上述算法拆解中,每增加一层计算都属于增加一个栈,而每返回一层结果也都会减少一个栈,在python中,栈的大小不是无限的,所以如果计算量过大的话,就会导致栈溢出,从而出错。所以接下来要学习使用尾递归优化
尾递归:在返回结果时调用函数自身本身
#在return时不能包含表达式,这样就会被系统认为是尾递归。具体应该是第一层函数在return时返回第二层函数,而第二层函数如果是最后一层,则可以使用表达式。
上述例子中的return中包含了n * fact(n - 1),所以不算是尾递归,如果改为尾递归则为:
>>> def fact(n):
... return fact_iter(n,1)
...
>>> def fact_iter(num,product):
... if num == 1:
... return product
... return fact_iter(num - 1,num * product)
...
>>> fact(5)
120
#我们还是以fact(5)为例:
>>> fact(5)
>>> fact_iter(5,1)
>>> fact(5 - 1,5 * 1) #返回的num等于4,所以继续向下计算
>>> fact(4 - 1,5 * 4) #返回的num等于3,所以继续向下计算
>>> fact(3 - 1,20 * 3) #返回的num等于2,所以继续向下计算
>>> fact(2 - 1,60 * 2) #返回的num等于1,所以直接返回product,也就是60 * 2 = 120
>>>120
虽然学了尾递归,栈也不再增加,不过丫计算过多的话还是会溢出,看到最后发现原因居然为:大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以是不是如果计算更大的数据,还是老老实实的导入到循环中,比如while,或者range(6),这样就算出来了。
个人练习:
循环写法1
>>> sum = 1
>>> n = 5
>>> while n > 0:
... sum = sum * n
... n = n - 1
... print(sum)
循环写法2
>>> sum = 1
>>> for x in range(1,6):
... sum = sum * x
... print(sum)
#因为range是区间,range(6)就等于0到6的区间,不包含6,因为0代入到上述简单的计算中就会出问题,所以将区间设定为1开始就好了。
最后的作业:用递归函数还原汉诺塔原理,看到这道题的时候完全是懵逼的
刚开始还不了解什么是汉诺塔,了解之后发现自己也完全不会我那个递归里面放,甚至只了解举得那个递归的例子,放在其他地方就是渣渣一个,这让我对以后的学习以及运用产生了极大怀疑。
最后还是看了下方评论以及知乎的答案后才真正了解,学习受到打击!
关于python中的汉诺塔具体运算过程,感谢知乎里面的几个大神详细解答,学到不少,如果也同样有弄不清楚的同学,可以参考这篇文章的思维导图,清晰易懂python中的汉诺塔递归算法的具体运算过程是怎样的?www.zhihu.com