day29——函数定义、函数的参数、 函数的默认参数、 函数的变量、 函数的返回值、多类型传值和冗余参数、函数的递归调用
函数是完成特定功能的一个语句组,这组语句可以作为一个单位使用,并且给它取一个名字
可以通过函数名在程序的不同地方多次执行(这通常叫函数调用)
函数分为:
●预定义函数
可以直接使用
●自定义函数
用户自己编写
为什么使用函数呢?
●降低编程难度
通常将一个复杂的大问题分解成一系列的小问题,然后将小问题划分成更小的问题,当问题细化为足够简单时,我们就可以分而治之。各个小问题解决了,大问题就迎刃而解了。
●代码重用
直接调用这个函数名来调用这个代码,避免重复劳作,提供效率
函数的定义和调用
dev 函数名([参数列表]): 定义函数
函数名([参数列表]) 调用函数
函数名命名也是有规则的,跟变量命名一样,函数名可以是数字、字母、下划线组成,但是不能数字开头,
函数名定义一般跟变量也有区别,变量名一般是定义小写的,函数名如果由两个单词组成的,那么第二个单词的首字典要大写,如果由多个单词组成,后面单词的首字母大写。如果类的名字定义呢,如果是由多个单词组成,那么每个单词的首字母都要大写。养成这样编程的好的习惯。
定义一个函数,函数名叫fun,(),括号里面没有参数,下面是具体的语句了,比如说只打印一句话,这样的函数就定义完了,怎么调用呢?当执行fun()的时候就是在调用,就是在执行函数里面的语句(代码块)
写一个复杂的函数,写一个脚本,这个脚本来接收标准输入(从键盘上读取),当执行脚本时提示从键盘上读取标准输入,从键盘上输入一些东西时,我们去判断一下,就是判断输入是否是数字,然后提示打印。
先用def来定义一个函数(名字随便起),这个函数没有参数,读取完之后保存到一个变量里,然后对这个变量做个判断,看是否是数字,怎么判断是数字呢?raw_input输入就是一个字符串,就是判断这个字符串是否是纯数字。比如a = '123' ,可以通过type(a)来看一下a是str字符串。int(a)函数会把字符串变成数值,但是只有纯数字组成的才能变成int类型,假如字符串还是纯数字组成的就会有一个ValueError的错误,当遇到错误时程序就退出了,怎么来避免这个错误呢?后面课程会讲异常怎么处理,如果这个变量int(sth)是纯数字的话是不会报ValueError错,如果不是纯数字就会报错误,如果有这样的错误做一个处理,通常情况下遇到这样的错误脚本会退出,遇到异常要干什么呢?就是打个提示,说它不是个数字。如果是数字做个判断,判断它的类型,如果类型是数值的话(纯数字是个int类型)打印一下。如果现在执行的话什么都没有,应该这个函数只是定义,定义之后要调用它才能执行代码块,调用它就是直接函数名加括号()
如果有空格就不是纯数字了,
在这里多了个异常处理,try是捕获异常,如果if type(int(sth)) == type(1): 这句话有问题会报一个错误,通常代码出现错误会退出,但是这里通过try来捕获异常,捕获异常做什么呢?通过except做一个print "%s is not number" %sth 的打印动作。如果try代码块执行没问题,except是不会执行的,只有程序出现异常(错误),才会执行except,except英文单词的意思就是除了的意思。except如果写全的话可以把这个异常写上(except ValueError:),后面视频有对异常专门做介绍。
def aun()就是一个定义的函数,后面的代码块起个名字,名字就是fun,最后一行就是对这个函数调用。
假如在其他程序里面也想从键盘的输入是否是数字做判断,那在其他程序就不用写这么多代码,可以直接使用这个文件,后面会讲怎么在其他程序用这个函数。
函数参数
形式参数和实际参数
在定义函数时,函数名后面括号中的变量名称叫做"形式参数",或者称为"形参"
在调用函数时,函数名后面括号中的变量名称叫做"实际参数",或者称为"实参"
比如定义一个函数,x,y是一个形参,在定义函数的时候,要求要有两个参数,在调用的时候也必须传入两个参数进去,括号里面也叫变量,
如果在调用的时候直接fun()不行,会有一个TypeError类型错误,说这两个函数应该接受两个参数,但是一个都没给
如果传一个也是这个报错,确切的说要两个参数但只给了一个,传多传少都不行,必须得是两个
字符串也可以,在这里是通过+,如果+号是用在字符串上是连接两个字符串的,如果是用在数值上则是进行相加。
定义一个函数,来判断两个数的大小,调用函数时输入两个数,完了之后把大的数打印出来。对这两个形参进行判断。
把刚才脚本重写一下,把从键盘输入是不是一个纯数字,用到了异常,判断输入是不是一个数值类型的,先把从键盘的输入存到变量里,然后再把这个变量用int做一个类型转换,如果转换的时候没有出现异常就是纯数字,否则打印不是一个number,接下来不用异常处理。
让脚本去接收一个参数,就是不从键盘上读取数字了,判断这个参数是不是数字。怎么让脚本实现一个参数呢,在这里我们用到了一个模块sys。这个sys模块有一个属性sys.argv,返回的结果就是一个列表,而这个列表的元素呢正好是脚本的名字,运行脚本时在后面加上两个参数,这个参数呢也做为列表的元素存到了这个列表里,而且这两个参数都是字符串,说明这个列表的元素呢是个字符串,即使写个数字也是个字符串
在这里就比较容易地取到脚本中第一个参数了,脚本的第一个参数下标是0就表示是脚本的本身,脚本的第一个参数是下标1
其实就是对它来做一个判断,判断是否是数值,在这里对它定义一个函数,这个函数带一个参数,不用异常的方式那通过什么方式来判断参数是不是数字呢,可以这样做,s参数是个字符串,字符串又是序列,既然是序列可以通过for做个遍历,遍历之后得到的每个字符是i,然后判断这个i是不是数字就行了,i虽然是字符但也是字符串,如果这个i在0到9之间先pass一下(不知道这里写什么可以先pass一下),如果i不在这个字符串('0123456789')里则它不是一个数字,则打印s不是一个数字,在这里s是一个字符串,前面的%s是格式化输出,后面%s可以加空格,如果是有两个变量的话要把它放到括号()里面当成一个元组,打印完之后这样脚本就可以退出了,因为它不是纯数字嘛,再往下执行就没有这个必要了。如果循环结束了(表示循环正常结束了)打印%s是个数字,后面去调用一下函数,后面加个参数,当遇到sys.exit()时整个脚本都退出不再执行了。else什么时候执行呢?在for正常结束了才会去执行。比如输入abc那if i in '0123456789': 这个条件肯定不符合,不符合就执行for下面的else后面的代码了,而else(跟for在同一层语句): print "%s is a number " %s就没有机会去执行了。如果for循环正常退出,那s遍历的每个字符呢都在0到9里,表示都纯数字的,都会执行print "%s is a number " %s 这条语句
如果加个空格会把后面两个当成是两个参数,我们在程序判断的只是第一个参数,如果有空格是判断不出是否是数字的。
对脚本的第一个参数做了一个遍历,因为它是一个字符串,字符串可以做遍历,遍历后可以得到每个字符,每个字符去判断是否在0到9里,如果是的话什么都不做,如果不是话马上打印并退出,如果print '%s is not a number ' %s 这句话没有执行的话表示这个for循环正常遍历完了去执行print "%s is a number " %s 语句,当然这句话上面的else可以去掉把后面语句放到跟for同一层循环上去。
练习:
打印系统的所有PID
要求从/proc读取
os.listdir()
/proc目录除了PID外还有很多其他的信息,我们只要数字就行了,可以使用os模块中的listdir()。
先import os模块,help(os.listdir),后面跟的是路径,返回的是一个list列表,然后列表里面的每个元素是strings字符串,返回一个目录里面所有文件的内容(所有文件名)。path是一个目录,就是查看目录里所有的文件。这个不包括一个.和两个..,
思路:对这个列表做个遍历,遍历之后得到的每个元素是字符串,完了之后字符串通过上节的函数处理一下(上节写的函数就是处理字符串的),打到上节12.py的脚本进行修改。
打印所有的PID,其实就是把这些PID给print一下就ok了。
os.listdir('/proc')就得到一个列表,对这个列表用for做遍历,在遍历的时候呢可以调用一下isNum(s)函数,让这个函数处理下(这个函数是只打印数字),如果不是数字的话直接退出不打印了。
s是一个形参,而在调用函数的时候i是一个实参,而i又是列表里面的每一个元素。for i in os.listdir('/proc'): 是对列表做遍历,遍历之后的每个元素是i,这个i通过这个isNum函数来处理,处理的结果就是在调用这个函数,调用的结果就是如果是纯数字则打印,如果不是纯数字则退出这个函数,
如果在脚本当中出现sys.exit() 这个的话则整个程序都退出了是不会有什么结果的,因为第一次过滤的时候是找那些字符串的,字符串的话则else: sys.exit() 条件成立退出脚本,需要把sys.exit()修改为break退出这个for循环,如果这个文件名不是数字的话那么就满足了else条件退出for循环继续下一次循环,如果break的话,else: print "%s is a number " %s 语句也是不会去执行的,退出这个for循环之后最下面for也是一个循环,进行下一个文件名的检查,看下一个文件名是否是纯数字的,如果是的话第一个for循环正常执行,执行之后还会执行else,
else: print "%s is a number " %s 我们只打印文件名(PID)else: print s
到时候我们学这个函数返回有return用return写起来更方便了,在这里我们只是用print s来测试一下,在这里注意一下else,在for循环里有可选项else,else: print s这个代码块执行是什么条件呢?是for循环正常结束,所谓正常结束就是循环没有执行到break语句,表示这个for循环是正常结束的,正常结束了才会执行这个else代码,if i in '0123456789': 它正常结束意味着字符串都是纯数字的,才会执行else: print s 这个,如果不是纯数字的话会break,
函数的默认参数
缺省参数(默认参数)
定义函数的时候可以定义一些默认参数,比如下面例子给y设置了一个默认参数,如果在调用参数的时候没给y设置一个参数则这个y是100,
def fun(x,y = 100):
print x,y
fun(1,2)
fun(1)
把这个2赋值给x,5赋值给y(虽然y给了一个默认值100),但实际在调用函数时这个实参会代替这个100,如果只给一个实参2,表示这个x的值是2,y没有的话就是100。
也可以这样传参:如果y不写的话就是100
这样传参的话必须写x和y,因为形参是x和y,传参不能写什么a=20,b=30,要跟形参的名字一致。
比如下面定义参数,两个都有默认值,直接调用(没有写任何参数),没有写的话就会按照这个默认参数来传参。
如果只写一个实参是给x传呢还是给y传呢?这怎么去判断呢?结果是给x的,如果只给一个的话会跟第一个参数对应
如果这样定义就会语法错误了,报这个函数没有默认参数,默认参数呢只能在最后(默认只能在最后的形参上),不能说在前面或者中间,只能在最后传参,比如这个函数有三个参数,那么在定义默认参数的话可以三个都定义,或者最后两个或者最后一个定义,只能是从右到左,
也可以这么写,默认参数要写到最后面(就是写到最右边)
如果传fun(3) 参数,则是传给y(主要是看位置,这个3是第一个实参跟第一个参数对应)。
会把12这个实参传给形参x,
在调用函数的时候,如果没有给函数传值的话会使用默认参数来传值。
●局部变量和全局变量
Python中的任何变量都有特写的作用域
在函数中定义的变量一般只能在该函数内部使用,这些只能在程序的特写部分使用的变量我们称之为局部变量
在一个文件顶部定义的变量可以供文件中的任何函数调用,这些可以为整个程序所使用的变量称为全局变量
下列脚本中,x就是在函数内部定义的,只在涵数内部有效,当函数运行完之后(调用完)这个变量x会销毁,先调用fun()函数,调用它呢会输出100,
比如说在函数外面查看这个x的值有没有,运行后提示x没有被定义,说明这个局部变量x只能在函数内部有效。在外面就无效了。
全局变量通常都是在程序上面来定义的,比如在程序上面定义一个x,在最后print x值时应该是上面的值,想打印函数内部的值时是打印不出来的,函数执行完之后变量就被销毁了就没有这个变量了。最后一行打印的x是外部的,可以把它叫做全局变量。
比如不给这个x赋值,在函数里面直接调用这个x,发现涵数内部的还是可以使用全局变量。
在函数内部定义一个变量x = 1,这个只能在函数内部使用。比如在外面给x定义一个100,print x时会是100,这里对x不print x而进行其他操作,当x += 1后print,会报一个异常,local表示局部变量错误,要参考以前定义的。print可以而对它进行操作却不行。在这里不能对全局变量做一些实际的操作只能print,如果想要进行实行的操作呢就会有异常,它认为x是个局部变量,要参考之前定义的,因为之前定义的有x = 100这样的变量,这样就会报这样的异常。假如想在函数里对这个局部全局变量想做一些x += 1这些的操作,其实这也是可以的,可以把这个x申明成在函数内部能使用的全局变量,不仅能print还可以做这些赋值操作。通过global关键字全申明一下。
这样执行完之后其实它是改的全局变量x,把它的值呢变成了101了。
比如在外部打印一下x的值(这个变量x = 100是全局变量,但是这个全局变量呢在函数内部我也把它申明成全局变量,而且做了一个重新赋值的操作)。打印完之后说明函数内部对这个x值的改变已经生效了,因为有global语句,这个global语句就是把它申明成全局变量,这样申明之后那么x = 100这个变量就可以完全使用了,否则只能print而不能做实际性的操作(比如给它重新赋个值这样的实际性操作),
global x这个语句呢不能这么写:global x +=1,只能先申明再进行操作。
如果没有调用(fun() )那这个x的值还是100。比如在函数里面再定义一个函数y,那这个y只能在函数内部可见,那如何在函数外面可见呢,其实方法一样,也是使用global申明成全局变量,然后给y赋值,print y看有没有赋值成功。
在外部也能看到这个y了,在函数内部通过global申明成全局变量,在全局变量意味成在函数内部是可以使用的。如果把fun()给注释掉那y还能否正确打印出来呢?是打印不出的。因为这个函数没有调用的话那这个y是没有的,只有函数被调用了,那global y y =1这两条语句才生效,生效了之后才能打印出y的值。就是先调用之后再使用这个变量。
在函数内部可以对全局变量做一些实际性的操作,我们要做这个外部的全局变量通过gblobal来申明一下,还能一种方式呢是把函数内部的变量在函数外部也能使用,也是通过global去申明的,否则这些变量只能在函数内部使用,一旦退出函数了这个变量就销毁了。
在这里打印locals,那么locals是什么东西呢?locals返回的是一个字典,这么这个字典里保存了这个函数里面的两个变量。
在外部也可以执行这个print locals() ,首先第一个打印的是函数调用,因为这个程序执行的先是这个函数,然后再执行print locals(),那么下面这个字典呢打印的是整个脚本执行时有哪个变量,有些是内部变量,'__file__': '13.py' 是脚本的名字,这个变量'__file__'它的值是13.py,表示脚本的名字,是内置变量,'__package__'是有没有包None是没有的,'x'是100是在程序开头定义的那个,还有一个函数'fun',后面是函数的对象<function fun at 0x7f00cfbfb5f0>,'__name__'是'__main__',doc等于是None。其实这个locals返回的是一个字典,里面的元素可以是脚本在执行过程当中都有哪些变量。
print locals()这个把它写到函数里面了,是统一这个函数里面的变量,print locals()写到整个程序里了它是统一整个程序的一些变量
函数返回值
函数被调用后会返回一个指定的值
函数调用后默认返回None
通过return来指定返回值
返回值可以是任意类型
return执行后,函数终止
return与print区别
写一个脚本,定义一个函数,打印一句话,如果print一下这个函数呢,
不仅输出helloworld,还多出一行,其实None就是函数的返回值,因为在这个函数里面没有让它返回东西所以默认值是None,可以加上return关键值,
return后面可以是任意类型的,比如True
如果return后面还有语句呢,如print 'abde'显然没有执行,因为函数一遇到return呢函数就结束了。
return True注释则返回:
更改12.py脚本:
当时写的是print,其实在函数里很少用print,
# cp 12.py 15.py
把这个函数可以做一些简化,如果i不在0到9里,那么直接return一个False,最后再return True(跟for在同一级缩写里),当在循环的时候,如果没有return False时就表示这个i呢是个纯数字,是个纯数字则返回个Trun,if i in '0123456789': 这个条件是不成立的,那这个return False就不会执行,return则返回一个True。其实上面isNum(s):函数返回两个值,一个是False或True其中的一个,后面判断一下,把函数的返回值作为判断条件,如果返回是True的话则这个i是纯数字,如果是纯数字的话则打印出来,
当然有的时候根据需要使用print,大部分情况下是使用return的,return的目的就是为了在调用函数的时候能用上return的值(函数的返回值),用这个返回值来做一些事情。这个函数不仅在主程序来调用,也可以作为其他语句的表达式,在这里就是作为if的表达式来调用。15.py代码上面是返回值而下面部分是函数的调用。
还可以简化,这里可以使用字符串的一个方法来对这个字符串进行判断,判断是不是纯数字。
这个方法返回的是一个bool值,return true表示如果所有的字符在这个序列(S表示一个序列)都是数字的话,如果在这序列最少有一个字符的话则返回False,用这个字符串方法判断会更好一些。
因为这个a是'abc',不是纯数字,所以返回False
可以把这个脚本重新修改一下就不用for循环来遍历了 # cp 15.py 16.py
直接这么来判断,如果s.isdigit()返回是个Ture的话则s是个纯数字就直接return一下,(字符串s是个序列),函数里面就三行,如果s.isdigit(): 结果是True的话则返回 True,如果这个条件不成立(s不是纯数字)的话返回False,下面代码不用改,因为下面代码也是判断是True的话则打印这个i。
函数的多类型传值和冗余参数
例子:
经常定义这样的函数,比如函数有x,y两个参数,x,y就是形参,然后调用一下这个函数。在传的时候传了两个实参3和5。假设这里面有一个元组t,里面的元素是1,2。函数在传参数的时候这个函数后面是个括号然后里面是两个参数,那现在定义两个元素的元组,两个元素的元组的形状和上面实参的开头是一样的,都是一个括号里面放两个数值,那能还能直接把t去替换3和5呢?
报TypeError错,提示这个函数呢应该传两个参数,但是只给了一个,它把这个t当成一个参数了,它应该是两个参数才可以。![]()
现在传t,1两个参数,一个元组和一个数值还能进行相加运算,怎样把这个元组里面的两个元素作为参数传递进去呢?这里就涉及到了多类型传值。
如果是元组的话这里可以加一个*,这样就把t(1,2)元组里面的1和2进行相加了,*后面是元组名,这样就把元组里面的元素作为参数把它传到函数里面了,而这个元组t里面的元素作为实参传进来,传进来后返回它俩的和
定义三个参数,然后再定义三个元素的元组,在调用的时候直接*加元组名,或者直接写上元组,它把元组里面的元组作为参数传进来,传进来之后在函数内部执行三个参数的相加。也可以这么传,第一个传正常的参数,第二个传一个元组,t里面的两个元素,加上前面的1正好三个参数,而这个函数定义的也是三个形参,所以传的时候也是三个实参,参数的个数一定要一致。
是否可以倒过来写呢?先写元组再写参数,这样是不行的,会有语法错误,提示只有名字参数才可以跟*expression这样的表达式。2是一个命名参数,它后面才可以加上百分号表达式,也就说它应该放在命名参数的后面还能放到前面,否则就有语法的错误。
调用函数也可以这样来调用,其实也相当于存参,形参的名字分别等于这些值,在这里面x不要加上引号,因为它把x当成一个变量,加引号它就是一个字符串了。
上面定义的形参名为x,y,z,那下面实参名为a,b,c则会报类型错误,还能这么写,它会认为这个a没有定义,不知道a是什么。传值参数一定要和形参名字一致,但是位置可以无所谓。
还可以这样传,定义一个字典,dic = {'x':1,'y':3,'z':5},在调用函数的时候可以把这个字典传进去,前面加两个*,fun(**dic)。在定义字典的时候key应该也和上面形参的名字是一样的,key要加上引号,不加引号的话字典会把它认为是变量,这样就会在定义字典的时候报错,NameError未被定义。字典的顺序也可以调换。
可以传元组、字典,可以把元组里面的元素作为参数传到函数,也可以把字典里面的value作为参数传给函数,但是定义字典的时候字典的key一定要和形参的名字一致,这叫多类型传值。
下面介绍冗余参数
向函数传元组和字典
处理多余实参
之前在定义函数的时候,比如这个函数有三个参数,在调用函数的时候,只能给它传三个参数,不能传多,也不能传少,有的时候我们可以让这个函数接收多一点的函数,但是传的时候可能传了几个,或者是传了更多,那么怎样把更多的参数处理呢?在这里面在定义函数的时候可以这样来定义,比如前面两个参数是正常的,叫名字参数,后面两个参数呢可以把它叫做冗余参数,
例:定义一个函数,像这个*args和**kwargs这些可以写成其他字符串,为什么这么写呢?可以看Python模块的源码,有的时候函数定义的时候也会去接收冗余参数,一般都是这么来定义的,在这个函数内部,把这三个参数打印出来,接下来去调用它,在调用时如果什么都不写指定会报错,因为这个函数至少有一个命名参数,报错提示至少要有一个参数。
比如写一个1,x是1,args和kwargs分别是一个元组和字典,但是都是空的,第一个参数1肯定对应的是形参x,2对应的是(存到了)args这个变量里,2就是一个冗余参数,因为命名参数只有一个x,多余的参数会传到args,并且是以元组的形式存的,把多余的参数存到了元组里。
下面字典是怎么产生呢,字典是这样产生的,之前介绍过默认参数,比如在这里面传个参数,这里面不能写x了,因为x上面已经定义了,x在形参里是一个命名参数,这样会报错的,比如换成一个y
它把这个y分别当成字典的key和value,存到了kwargs这个变量里,这里可以传很多变量,这里也不需要加引号,
还可以这样写,这叫多类型传值,把元组也传进来,当然这个元组t有两个元素,它会作为两个参数传进来,这两个参数也是冗余的,凡是多余的参数都存到元组里。
那什么样的参数存到字典里呢?就是类似于默认参数的,
如果把前面已经定义的dic字典传进去则会报错,因为dic字典里面有x元素
如果已经有值是不能再重新赋值的
定义函数的时候这个函数有一个命名函数,下面'a',[1,2,],*t,a=3, 是*args,最后是两个*,是**kwargs,那么当这个命名函数在调用的时候是个1,其他的参数像'a',[1,2,],*t 这几个都作为冗余的参数存到元组里,而a=3,**{'b':2222} 这种形式(比如等号、或者字典的形式)来传参会把这些参数存到一个字典里。
多余参数有两种类型,一种类型存到了元组里,就看我们去调用函数的时候怎么去传参了,如果是以等号那种或者以字典的方式传的话会把这些参数存到字典里,因为传的时候有个key和value两个值,就是说一个是变量一个是值,它把这两个值呢作为字典的key和value存到了字典里。
函数的递归调用
什么是递归呢?就是在定义函数的时候呢在函数内部调用自己。就是函数自己调用自己,这跟我们以前定义的函数有点不一样,以前定义的函数呢是定义完之后在函数外部调用这个函数,而递归是在函数内部调用它自己。通常用它来计算阶层。
计算阶层:
●普通方法:
先使用普通方法循环来计算阶层。
先定义一个函数,函数名为factorial,然后对它做个遍历,range时不能从0开始(怎么乘都是0了),要从1开始,比如5的阶乘是5*4*3*2*1等于120,在这还得定义一个初始的值(比如sum=1),则sum *= i,就是说把每个值都是相乘,值都保存到sum里,比如当第一次sum = 1时,sum = sum * 1,第二次sum = 2时,sum = 1 * 2,第三次sum = 3时,sum = 2 * 3,因为之前sum的值是2,就是当i每次都等于一个值时都于这个sum进行累积相乘,当循环结束之后呢return sum,这个sum的值是我们想要的值。接下来去调用它,当参数为5时(factorial(5)) print一下这个值,返回结果为120说明没问题。这是一个循环的方式来求的阶乘。求和也一样,默认值是0,后面*= i变成+= i
下面使用函数递归的方式来做一下阶乘:
写递归其实是自己调用自己,比如现在参数是n,函数名还是factorial,在递归的时候要有一个条件,必须要有一个默认的结果(就是说不能一直来调用自己,得有一个默认结果),如果一直在调用自己则出不来了。这个默认结果是什么呢?是这个参数等于0的时候返回是1,比如当n == 0: 的时候return 1(不能是0,因为是0的话整个结果都是0了),如果还是0的话返回 n * factorial(n-1) (自己在调用自己)。还有这函数里面的参数factorial(n-1)应该向默认参数收敛,(n-1)这个参数一直在减小,当减小为0的时候就有个默认结果为1。递归的第二个注意事项是递归参数必须向默认结果收敛。在这呢两个条件都具备了,第一是有一个默认值(不能一直去调用自己),第二个是递归的参数向默认结果去收敛。
把相乘求阶乘改成相加,r: 1 * f(0) 应该返回0,然后把*改成+号。
如下图,比如说当我这个函数定义的是f,当这个参数是3的时候(当等于3的时候),那么当调用这个函数f等于3时(假如n = 3),执行调用了这个函数,那么调用这个函数,在内部调用自己,怎么调用呢?3 * f(2)就是3 * f(n - 1),比如计算3的阶乘,3的阶乘就是3*2*1,那么当在调用f(2)的时候,那么执行的代码其实是2*f(1),就是n * f(n - 1),当调用f(1)的时候,其实就是1* f(0),那么当n=0的时候就不再继续调用这个函数了,得有一个返回结果(默认值),写递归的时候必须要有一个默认结果让它出来。当每调用一个函数的时候,都会有一个return,这r就是return一个返回值,这个返回值在这里面接着再调用这个函数,只不过参数不一样了。它会把这个返回值呢临时保存到内存里,最后n = 0的时候返回1就不再调用函数了,当最后等于1时再把前面内存里面这些值再依次进行相乘。

浙公网安备 33010602011771号