命令式至函式式隨記(三)

2021-06-10 08:05:57 字數 2800 閱讀 7419

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 乙個微軟推出的醫療系統 專案的乙個開發者,也是微軟公司的一位全職員工。此人不寫部落格 此乃世界的一...