天天看點

Python與Erlang函數性的對比(一)

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關鍵字是差別,這個關鍵字是在函數内部引用并重新綁定(修改)全局變量