张板书的Python的学习笔记,包括笔记与DeBug的经历。
笔记④
Python中的函数与函数递归,以及使用递归来解决汉诺塔问题
函数是一种仅在调用时运行的代码块,可以将数据(称为参数)传递到函数中,而函数可以把数据作为结果返回。
Python中的函数定义起来比较方便,输入、输出的数据类型不需要定义,支持直接输入输出各种数据格式。
参考网站
https://www.w3school.com.cn/python/python_functions.asp
def test_function(): # 定义函数
print("function done")
test_function() #运行函数
>>>function done
def test_function2(element): # 定义有输入的函数
print("你的输入是" + element)
test_function2("测试输入")
>>>你的输入是测试输入
创建函数时如果只定义了需要的参数,则在调用函数时必须要在函数后的括号中定义参数,否则就会报错。
如果在定义函数时就直接定义默认参数值,就可以在后面调用函数的时候省下一些参数。
def comefrom(country = "China"):
print("I am from " + country)
comefrom()
>>>I am from China
comefrom('USA')
>>>I am from USA
同时可以使用 key = value 语法发送参数。
import random
def test_function(who,where,do):
print(who + "在" + where + do)
who = ["张三","李四","王二麻子"]
where = ["厨房","教室","咖啡馆"]
do = ["洗澡","打牌","上厕所"]
i,m,p = random.randint(0,2),random.randint(0,2),random.randint(0,2)
test_function(who[i],where[m],do[p]) # 参数需要按照定义函数时的顺序进行定义
>>>张三在厨房洗澡
>>>王二麻子在教室上厕所
test_function(who = "板书",do = "码代码",where = "电脑前") # 此时的参数顺序没关系
>>>板书在电脑前码代码
使用return来在函数的最后来返回值
def test_function(who,where,do):
return who + "在" + where + do
sentence = test_function(who = "板书",do = "码代码",where = "电脑前")
print(sentence)
>>>板书在电脑前码代码
如果您不知道将传递给您的函数多少个参数,可在函数定义的参数名称前添加 *。
这样,函数将接收一个参数元组,并可以相应地访问各项:
def my_function(*kids):
print("The youngest child is " + kids[2])
my_function("Phoebe", "Jennifer", "Rory")
>>>The youngest child is Rory
函数定义不能为空,但是如果出于某种原因写了无内容的函数定义,则使用 pass 语句来避免错误。
def myfunction:
pass
Python 也接受函数递归,这意味着定义的函数能够调用自身。
递归是一种常见的数学和编程概念。它意味着函数调用自身。这样做的好处是可以循环访问数据以达成结果。
开发人员应该非常小心递归,因为它可以很容易地编写一个永不终止的,或者使用过量内存或处理器能力的函数。但是,在被正确编写后,递归可能是一种非常有效且数学上优雅的编程方法。
例:实现一个函数recsum(N),它递归地计算从1到所有数字的和。
def recsum(N):
if N == 1: # 递归函数最重要的一点即设置终止条件
return 1
else:
return N+recsum(N-1)
print(recsum(5))
>>>15 # 1 + 2 + 3 + 4 + 5 = 15
>
一个经典的递归函数:汉诺塔问题
问题是印度古老的传说。 世界的创立者梵天在一座神殿里留下了三根钻石棒,在最初的一根上盖上了64枚圆型金片,最大的在底部,向上依次减小。
要求在搬动金片时小的金片不能放在大的下面。
现在要将这样一组金片从第一根柱子上完全搬到另一根上,请你说出搬动的具体方案.
分析问题,64个金片太过复杂,可以先简化问题,以三个为例进行简化推演
同时我们要意识到,本质问题是按照规则将所有的金片从A柱移动到空的柱子上,所以B、C两个柱子其实是等价的;
分析可以看到,如果是4个金片,可以看出相当于是在3个金片完成换柱子的基础上,在A处还有一个最大的金片;
之后将A柱上最大的金片移动到B柱子上之后,4个金片的问题就变成了将C柱上的3个金片移动到B柱上的次级汉诺塔问题(因为现在B柱上是最大的金片,其性质与地面一致)
所以,高级的汉诺塔问题可以通过一定的规律转化成次级汉诺塔问题,故我们可以利用函数的递归来解决汉诺塔问题。
def Hanoi(n,F,M,L): # 汉诺塔问题:将n个金碟从F柱移动到L柱
if n==1: # 递归的终止条件:一级汉诺塔
move(F,L)
else: # 非一级汉诺塔,将A柱上还剩的那一个放到B柱上,
# 之后转化问题未从C柱到B柱的次级汉诺塔
Hanoi(n-1,F,L,M) # (非一级汉诺塔)将F塔上n-1个碟按照汉诺塔规则移动到M柱
move(F,L) # 将F塔上剩下的最小的金碟移动到L柱
Hanoi(n-1,M,F,L) # 将之前移动到M柱上的一堆从小到大的碟按照汉诺塔规则移动到L柱
def move(F,L): # 打印出移动方式:从第一位参数(F)移动到第二位参数(L)
# 每一次调用 move 函数就是移动了一次
# 故数一数调用move函数的次数即汉诺塔需要移动多少次的问题
print('把金碟从 %s 移动到 %s.'%(F,L))
F='A塔'
L='B塔'
M='C塔'
n=int(input('汉诺塔中有多少个金碟?')) # 输入汉诺塔级数
Hanoi(n,F,M,L)
>>>汉诺塔中有多少个金碟?3
>>>把金碟从 A塔 移动到 B塔.
>>>把金碟从 A塔 移动到 C塔.
>>>把金碟从 B塔 移动到 C塔.
>>>把金碟从 A塔 移动到 B塔.
>>>把金碟从 C塔 移动到 A塔.
>>>把金碟从 C塔 移动到 B塔.
>>>把金碟从 A塔 移动到 B塔.
至于计数移动次数的问题,则需要使用class,给move函数添加一个不会随着函数调用刷新的计数变量,或许可以使用其他方法,在这里只写出使用class的来:
def Hanoi(n,F,M,L,mymotion):
if n==1:
# print('把金碟从 %s 移动到 %s.'%(F,L))
mymotion.move(F,L)
else:
Hanoi(n-1,F,L,M,mymotion)
mymotion.move(F,L)
Hanoi(n-1,M,F,L,mymotion)
class motion():
def __init__(self):
self.count = 0
def move(self,F,L):
self.count += 1
# print('把金碟从 %s 移动到 %s.'%(F,L))
F='A塔'
L='B塔'
M='C塔'
n=int(input('汉诺塔中有多少个金碟?'))
mymotion = motion()
Hanoi(n,F,M,L,mymotion)
print(mymotion.count)
>>>汉诺塔中有多少个金碟?3
>>>7