1. 程式人生 > >Python基礎-----函數式編程含義及特點(及尾遞歸)

Python基礎-----函數式編程含義及特點(及尾遞歸)

優化 北京 棧溢出 括號 int global 不一定 傳遞 需要

一、定義
函數式就是用編程語言去實現數學函數。這種函數內對象是永恒不變的,要麽參數是函數,要麽返回值是函數,沒for和while循環
所有的循環都由遞歸去實現,無變量的賦值(即不用變量去保存狀態),無賦值即不可改變。
二、特點
1、不可變數據
不可變:不用變量保存狀態,不修改變量
a. 非函數式
a = 1
def test():
global a
a += 1
return a
test()
print(a)
>>> 2
b. 函數式
n = 1
def test1(n):
return n+1
print(test1(2))
print(n)
>> 3
1
2、第一類對象
第一類對象:函數即變量
a.函數名可以作為參數傳遞
def a(n):
print(n)
def b(name):
print(‘My name is %s‘%name)
a(b)
>>> 上述調用為:b不帶括號則表示b函數的內存地址,將內存地址作為實參傳遞給a函數,則a函數打印的為b函數的內存地址
a(b(‘Liming‘))
>>> ‘My name is Liming‘
None
上述調用為:首先會執行函數b,則先打印出‘My name is Liming’,但是b函數沒有定義返回值,則默認為None
則將None作為實參傳遞給a函數,則a函數打印出None
b.返回值可以是函數名
def a():
print(‘from a‘)
def b():
print(‘from b‘)
return a
n = b()
n()
>>> ‘from a‘
‘from b‘
3、尾調用優化(尾遞歸)
尾調用:在函數的最後一步調用另外一個函數(最後一行不一定是函數的最後一步)
尾調用優化:尾調用的關鍵就是在於函數的最後一步調用別的函數,這樣的好處是:根據函數即‘變量’的定義,
定義a函數,a內調用b函數,b內調用c函數,在內存中會形成一個調用記錄,又稱調用幀,用於保存調用位置和內部變
量等信息,即a->b->c直到c返回結果給b,c的調用記錄才會消失,b返回給a,b的調用記錄消失,a返回結果,a的調用
記錄消失,所有的調用記錄都是先進後出,形成了一個調用棧。尾調用由於是函數的最後一步操作,所以不需要保留外
層函數的調用記錄,因為調用位置、內部變量等信息都不會用到了,只要直接用內層函數的調用記錄,取代外層函數的
調用記錄就可以了。
def bar(n)
return n
def foo(x):
return bar(x)
print(foo(3))
>>> 3
上述foo(3)就等於bar(3),也就是說foo在最後一步調用了bar,然後foo的調用記錄就清楚了,剩下的就是bar
自己的事情了。所以內存裏永遠只保留一個調用記錄。

尾遞歸:函數調用自身成為遞歸,如果尾調用自身,就成為尾遞歸。
尾遞歸特點:重復相同的事情,每次重復會使問題規模減少。
尾遞歸舉例:
問題:師傅,北京怎麽去啊?
解決方法:a去問b,b不知道,b去問c,c去問d,最有由d得出結果,此過程可以發現,問題最有只需要d去
結局即可,a b c都無需保存任何記錄,不幹事,因為他們啥都不知道。
尾遞歸優點:遞歸通常非常耗費內存,因為需要同時保存成千上百個調用記錄,很容易發生‘棧溢出’錯誤。
但對於尾遞歸來說,由於只存在一個調用記錄,所以不會發生‘棧溢出’錯誤。
def cal(l):
print(l)
if len(l) == 1:
return l[0]
first,second,*args = l
l[0] = first + second
l.pop(1)
return cal(l)
x = cal([i for i in range(10)])
print(x)
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[3, 3, 4, 5, 6, 7, 8, 9]
[6, 4, 5, 6, 7, 8, 9]
[10, 5, 6, 7, 8, 9]
[15, 6, 7, 8, 9]
[21, 7, 8, 9]
[28, 8, 9]
[36, 9]
[45]
45
上例就是用到尾遞歸,最終求得1~9的和

Python基礎-----函數式編程含義及特點(及尾遞歸)