許多函數式文章講述的是組合,流水線和高階函數這樣的抽象函數式技術。本文不同,它展示了人們每天編寫的命令式,非函數式代碼示例,以及將這些示例轉換為函數式風格。
文章的第一部分將一些短小的數據轉換循環重寫成函數式的maps和reduces。第二部分選取長一點的循環,把他們分解成單元,然后把每個單元改成函數式的。第三部分選取一個很長的連續數據轉換循環,然后把它分解成函數式流水線。
示例都是用Python寫的,因為很多人覺得Python易讀。為了證明函數式技術對許多語言來說都相同,許多示例避免使用Python特有的語法:map,reduce,pipeline。
導引
當人們談論函數式編程,他們會提到非常多的“函數式”特性。提到不可變數據1,第一類對象2以及尾調用優化3。這些是幫助函數式編程的語言特征。提到mapping(映射),reducing(歸納),piplining(管道),recursing(遞歸),currying4(科里化);以及高階函數的使用。這些是用來寫函數式代碼的編程技術。提到并行5,惰性計算6以及確定性。這些是有利于函數式編程的屬性。
忽略全部這些。可以用一句話來描述函數式代碼的特征:避免副作用。它不會依賴也不會改變當前函數以外的數據。所有其他的“函數式”的東西都源于此。當你學習時把它當做指引。
這是一個非函數式方法:
a = 0def increment1(): global a a += 1
這是一個函數式的方法:
def increment2(a): return a + 1
不要在lists上迭代。使用map和reduce。
Map(映射)
Map接受一個方法和一個集合作為參數。它創建一個新的空集合,以每一個集合中的元素作為參數調用這個傳入的方法,然后把返回值插入到新創建的集合中。最后返回那個新集合。
這是一個簡單的map,接受一個存放名字的list,并且返回一個存放名字長度的list:
name_lengths = map(len, ["Mary", "Isla", "Sam"]) print name_lengths# => [4, 4, 3]
接下來這個map將傳入的collection中每個元素都做平方操作:
squares = map(lambda x: x * x, [0, 1, 2, 3, 4]) print squares# => [0, 1, 4, 9, 16]
這個map并沒有使用一個命名的方法。它是使用了一個匿名并且內聯的用lambda定義的方法。lambda的參數定義在冒號左邊。方法主體定義在冒號右邊。返回值是方法體運行的結果。
下面的非函數式代碼接受一個真名列表,然后用隨機指定的代號來替換真名。
import random names = ['Mary', 'Isla', 'Sam']code_names = ['Mr. Pink', 'Mr. Orange', 'Mr. Blonde'] for i in range(len(names)): names[i] = random.choice(code_names) print names# => ['Mr. Blonde', 'Mr. Blonde', 'Mr. Blonde']
新聞熱點
疑難解答