Python與Erlang函數性的對比(一)
1. 清單推導式:
Python | Erlang | |
基本的 | [n*n for n in [1,2,3,4]] | [N*N || N <-[1,2,3,4]]. |
帶條件判斷的 | [n*n for n in [1,2,3,4] if n%2 == 0] | [N*N || N <-[1,2,3,4], N rem 2 =:= 0]. |
多變量 | [(x,y) for x in [1,2,3,4] for y in [1,2,3,4] if x%2==0 and y%2==0] | [{X,Y} || X<-[1,2,3,4], Y<-[1,2,3,4], X rem 2 =:=0, Y rem 2 =:=0]. |
上面是清單推導式在Python和Erlang中的差別。為了看懂上面的表達式,我做幾點說明,也算科普下基本知識。
1. 關于變量的說明:
共同點:變量不用聲明類型,都是動态類型綁定。
差別:Erlang中變量名大寫,小寫的是原子,而Python中沒有要求,但建議變量名使用駝峰标示;Python中可以修改變量的值,而Erlang中變量的值一旦指派,則不能再改變。
2. 關于元組的說明:
共同點:二者都是不可改變的,一經定義,不再可變,常用于函數的傳回值,元組可以嵌套定義;另外取值的時候都可以模式比對取值。
差別:定義方式不同:Python中定義元組使用(),Erlang中定義元組使用{}
3. 清單的說明:
共同點:清單的定義方式一樣都是[]
差別:Python中隊清單的操作取值方法很多,Erlang中也有一些方法,比如++ -- 二者是不同的。
4. 關于相等符号:
Python中是正常的==, Erlang中有== 和=:=兩種,
5. 關于清單推導式:
清單推導式是為了簡化指令式程式設計的循環,Python中使用for關鍵字,而Erlang中使用||符号
2.map, filter函數:
Python | Erlang | |
map | map(lambda x: 2*x, [1,2,3,4]) | lists:map(fun(X) -> 2*X end,[1,2,3,4]). |
filter | map(lambda x: 2*x, [1,2,3,4]) | lists:filter(fun(X) -> X rem 2 =:= 0 end, [1,2,3,4]). |
關于上面的幾點說明:
1. 匿名函數的說明:
相同點: 函數沒有名字(這是廢話)
差別:python中使用lambda建立匿名函數,格式為lambda arg1, arg2: <expression>
Erlang中匿名函數的格式為fun(Args1) -> Exp1, Exp2, ....,ExpN; (Args2) -> Exp1, Exp2,... End
2. 關于Map Reduce函數
Map函數是将函數應用于清單中的每一個元素,完成後傳回一個新的儲存了每個清單中元素經過函數處理後的值。
Filter函數式将函數應用于清單中的每一個元素,完成後傳回一個新的使元清單應用于函數值為true的元素組成的清單。
3. 遞歸與尾遞歸
遞歸的定義相信學過程式設計語言的都知道,尾遞歸是針對傳統遞歸算法而言的,尾遞歸是從最後開始計算,每遞歸一次就算出相應的結果,也就是說,函數調用出現在調用者函數的尾部,因為是尾部,是以根本沒有必要儲存任何局部變量,直接讓被調用的函數傳回時越過調用者,傳回到調用者的調用者
Erlang中例子
%% 尾遞歸調用
sum(Total, 0) -> Total;
sum(Total, X) ->
sum(Total+X, X-1).
s(X) ->
sum(X, X-1).
%% 遞歸調用
se(0) -> 0;
se(X) -> X + se(X-1)
Python 中例子
# 尾遞歸調用
def sum(total, x):
if (x == 0):
return total
else:
return sum(total+x, x-1)
def s(x):
return sum(x, x-1)
# 遞歸調用
def se(x):
if (x == 0):
return 0
else:
return x + se(x-1)
測試結果:
在python中尾遞歸并沒有轉換為循環,依然會報RuntimeError: maximum recursion depth exceeded,在erlang中正常,這種尾遞歸在Oracle jdk中一樣會報堆棧溢出錯誤。
4. 閉包
閉包簡單來說就是在一個函數中包括了另一個函數,關于閉包的價值有兩點:
1. 保護函數内的變量安全性和可通路性
2. 在記憶體中維持一個變量
Erlang中的閉包的定義是通過fun函數:
base(A) ->
B = A + 1,
F = fun() ->
A * B
end,
F().
簡潔版的:
base(A) ->
B = A + 1,
(fun() ->
A * B
end)().
由于Erlang中變量的不變形,匿名函數的上下文也是不變的
Python中
def base(a):
b = a+1
def iner():
return b
return iner()
Python中有一個有意思的問題,看看下面的代碼
def base(a):
b = a+1
def iner():
b = b+1
return b
return iner()
這段代碼會報一個UnboundLocalError: local variable 'b' referenced before assignment
奇怪吧?
Python3.0以前,閉包能通路外部函數的局部變量,但是不能修改外部函數的局部變量,
Python3.0為此引入了nonlocal關鍵字,完善了閉包通路外部變量的機制
def base(a):
b = a+1
def iner():
nonlocal b
b = b+1
return b
return iner()
這樣在python3.0中就不會報異常了,注意和global關鍵字是差別,這個關鍵字是在函數内部引用并重新綁定(修改)全局變量