english
函式式的思考中心就是分解問題,舉例來說,計算list長度命令式如下:
def length(list):
c = 0
for i in list:
c += 1
return c
將之改為函式式是許多介紹函式式的文章會有的範例:
def length(list):
return 0 if list == else 1 + length(list[1:])
若傳入list給length,如果是空list,那長度當然是0,如果可以取得首元素則計數為1,然後持續拆解下去至空list為止,很簡單的概念。類 似地,如果想對一組整數作加總呢?如果是命令式可以如下定義:
def sum(list):
acct = list[0]
for i in range(1, len(list)):
acct += list[i]
return acct
正如命令式至函式式隨記(二)談過,使用迴圈循序處理list中元素的問題,基本上都可轉為遞迴解,不必使用計數器,前乙個length是個例子,而這邊 的sum可以改為:
def sum(list):
def rsum(lt, at):
return at if lt == else rsum(lt[1:], at + lt[0])
return rsum(list, 0)
這邊感覺rsum有點像上面的length,如果把上面的length調整一下:
def length(list):
def rlen(lt, at):
return at if lt == else rlen(lt[1:], at + 1)
return rlen(list, 0)
rsum與rlen結構一模一樣,就差在函式名稱與rsum/rlen遞迴時,第二個引數該如何處理。如果寫個通用的foldleft呢?
def foldleft(lt, func, at):
return at if lt == else foldleft(lt[1:], func, func(at, lt[0]))
那length就可以寫為:
def length(list):
return foldleft([1, 2, 3], lambda at, elem: at + 1, 0)
而sum就可以寫成:
def sum(list):
return foldleft([1, 2, 3], lambda at, elem: at + elem, 0)
foldleft很好用,可以有一百萬個用法。在python中有個functools.reduce,就是foldleft的實現,這在命令式至函式式 隨記(一)中看過例項,基本上用迴圈對list迭代以計算出某值,都可以用foldleft來作,不過實際運用可能沒像這邊的sum或length那麼清 楚簡單,如命令式至函式式隨記(一)中看過的,要有乾淨點的程式碼,以及對流程的敏感度,例如:
def eval(expr):
stack =
for c in topostfix(expr):
if c in "+-*/":
p2 = stack.pop()
p1 = stack.pop()
'-': float.__sub__,
'*': float.__mul__,
'/': float.__floordiv__}[c](p1, p2))
else:
return stack[-1]
這是命令式的寫法,感覺得出哪邊有foldleft嗎?在for c in topostfix(expr)與最後的return stack[-1],簡單來說,迭代expr,最後得到stack尾端值,如果剛開始練習函式式,相信很難看出來,這時建議從簡單的length、sum 等一看就看出來的開始,慢慢就會對這種較複雜的流程有感覺。
那上面怎麼改為使用foldleft?一開始的stack就告訴你了,初始是從stack為空開始,咦?可是expr不是list嗎?初始不用是list 中的元素,或至少是list元素同型態嗎?誰說的?沒那回事,foldleft的初始與回傳可以是不同於list元素的任何型態。在這邊,初始與回傳會是list。接下來就是 傳入的函式抽離出來就好了:
from functools import reduce
def eval(expr):
def dostack(stack, c):
if c in "+-*/":
return stack[0:-2] + [
[c](stack[-2], stack[-1])]
else:
return stack + [float(c)]
return reduce(dostack, topostfix(expr), )[-1]
上面直接用python的foldleft實現reduce來修改了。先前談過,基本上用迴圈對list迭代以計算出某值,都可以用foldleft來 作,不過不建議著了魔般,什麼都用foldleft作,要說的話,命令式至函式式隨記(二)中的procexpr也可以用foldleft作,不過寫完後 並不好讀,foldleft是為了重用迭代計值的流程,但某些程度上會降低可讀性,使用時兩者間得略為斟酌。
當然,有foldleft就會有foldright,可以自己實現看看,foldleft與foldright在沒有結合律考量下,是可以互換的,另乙個 考量是在某些語言中,list是代數資料型態(algebraic data type)結構,在這樣的結構下進行list的+串接與cons,會有效能差異,此時若可以使用foldright與cons,尤其是結果的list很長時,效能會比較好,這之後有機會再來聊了。
9 4 1 函式式和命令式類
9.4.1 函式式和命令式類 在類或者類的建構函式的引數值中的 let 繫結,就像我們在其他 f 中看到的 let 繫結一樣,也是不可變值 此外,使用 member 關鍵字宣告的屬性,建立的是唯讀屬性 只有 getter 因此,如果類只引用其他不可變型別的值,那麼,類也不可變。比方說,在前面的例子中...
函式式程式設計和命令式程式設計
所謂命令式程式設計,是以命令為主的,給機器提供一條又一條的命令序列讓其原封不動的執行。程式執行的效率取決於執行命令的數量。因此才會出現大o表示法等等表示時間空間複雜度的符號。而函式式語言並不是通常意義上理解的 通過函式的變換進行程式設計 注意到純的函式式語言中是沒有變數的 沒有可以改變的東西,所有的...
地道的F 函式式 vs 命令式
我們的故事要從這個叫 stuart bowers 的傢伙開始,下圖中,他正拿著乙個來自 caf press 的杯子喝著咖啡,此杯子正是他對函式式程式設計做出出色貢獻的象徵。stuart 是amalga 乙個微軟推出的醫療系統 專案的乙個開發者,也是微軟公司的一位全職員工。此人不寫部落格 此乃世界的一...