1. 程式人生 > 實用技巧 >函數語言程式設計 1

函數語言程式設計 1

小汽車前進的程式碼
優化為函式式的寫法

有狀態並不是一件很好的事情,無論是對程式碼重用,還是對程式碼的並行來說,都是有副作用的.
因此,想要個方法把這些狀態搞掉,於是出現了函數語言程式設計的程式設計正規化.

from random import random

def move_cars(car_positions):
      return map(lambda x: x + 1 if random() > 0.3 else x,
                 car_positions)

def output_car(car_position):
      return '-' * car_position

def run_step_of_race(state):
      return {'time':state['time'] - 1,
              'car_positions': move_cars(state['car_positions'])}

def draw(state):
      print ''
      print '\n'.join(map(output_car,state['car_positions']))

def race(state):
      draw(state)
      if state['time']:
            race(run_step_of_race(state))

race({'time':5,
      'car_positions':[1,1,1]})

上面程式碼呢依然把程式logic分成了函式,不過呢這些函式都是函式式的,它們有三個特點:

它們之間沒有共享的變數;
函式間通過引數和返回值來傳遞資料;
在函式裡沒有臨時變數.

for迴圈呢也被遞迴取代了(race函式) -- 遞迴就是函數語言程式設計中常用到的技術,正如前面所說,遞迴的本質就是描述問題是什麼.

函式式語言的三套件

函式式語言有三套件,Map啊,Reduce啊Filter啊,下面看Python的一個示例,這個示例需求為,把一個字串陣列中的字串都轉成小寫.
用常規的面向過程的方式如下:

# 傳統的非函式式
upname = ['G','CHEN','TANG']
lowname = []
for i in range(len(upname)):
      lowname.append( upname[i].lower() )

還真是通俗易懂呢,Python也不過如此
如果寫成函式式,用map()函式,是下面這個樣子:

# 函式式
def toUpper(item):
      return item.upper()

upper_name = map(toUpper,["r","chen","tang"])

print upper_name
# 輸出 ['R','CHEN','TANG']

其中map函式第一個引數是方法名,第二個引數是方法引數

上面Python程式碼呢可以看到,we declare one function method called toUpper,this function has no changed the variables be put in,
只是把傳進來的值做了個simple的operation,然後return,then,we using the toUpper to map function,就可以很清晰地描述出我們想要幹什麼,
而不是去理解一個在迴圈中怎樣實現的程式碼,最終在讀了很多迴圈的邏輯後才發現是什麼意思.

如果你覺得上面的程式碼在傳統的非函式式的方式下還是很容易讀的,那麼再看一個計算陣列平均值的程式碼:

# 計算陣列中正數的平均值
num = [2,-5,9,7,-2,5,3,1,0,-3,8]
positive_num_cnt = 0
positive_num_sum = 0
for i in range(len(num)):
      if num[i] > 0:
            positive_num_cnt += 1
            positive_num_sum += num[i]

if positive_num_cnt > 0:
      average = positive_num_sum / positive_num_cnt

print average

上面程式碼如果沒註釋,你需要看一會兒才能明白,只是計算陣列中正數的平均值.

再看下函式式下使用 filter/reduce 函式的玩法

# 計算陣列中正數的平均值
positive_num = filter(lambda x: x>0 , num)
average = reduce(lambda x,y: x+y, positive_num) / len( positive_num )

首先呢,我們使用filter函式把正數過濾出來(注意: lambda x : x>0 這個lambda表示式),
儲存在一個新的陣列中 -- positive_num , 然後呢,我們使用reduce函式對陣列positive_num求和後,再除以個數,就得到正數的平均值了

隱藏了陣列遍歷並且過濾陣列控制流程的filter和reduce,不僅讓程式碼更為簡潔,因為程式碼裡只有業務邏輯了,而且讓我們能更容易地理解程式碼.

1. 對num陣列filter條件 x > 0 的資料.
2. 然後對 positive_num 進行 x + y 操作的 reduce , 即求和
3 ..

感覺程式碼更親切了不是嗎?

- 資料集,對資料的操作和返回值都放在了一起.
- 沒有了迴圈體,就可以少了些臨時用來控制程式執行邏輯的變數, 也少了把資料倒來倒去的控制邏輯
- **程式碼變成了在描述你要幹什麼,而不是怎麼幹.**