成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

可愛的Python函數式編程(一)

開發 后端
我們最好從艱難的問題開始出發:“到底什么是函數化編程呢?”其中一個答案可能是這樣的,函數化編程就是你在使用Lisp這樣的語言時所做的(還有Scheme,Haskell,ML,OCAML,Mercury,Erlang和其他一些語言)。

摘要:雖然人們總把Python當作過程化的,面向對象的語言,但是他實際上包含了函數化編程中,你需要的任何東西。這篇文章主要討論函數化編程的一般概念,并說明用Python來函數化編程的技術。

我們最好從艱難的問題開始出發:“到底什么是函數化編程呢?”其中一個答案可能是這樣的,函數化編程就是你在使用Lisp這樣的語言時所做的(還有Scheme,Haskell,ML,OCAML,Mercury,Erlang和其他一些語言)。這是一個保險的回答,但是它解釋得并不清晰。不幸的是對于什么是函數化編程,很難能有一個協調一致的定義,即使是從函數化變成本身出發,也很難說明。這點倒很像盲人摸象。不過,把它拿來和命令式編程(imperative programming)做比較也不錯(命令式編程就像你在用C,Pascal,C++,Java,Perl,Awk,TCL和很多其他類似語言時所做的,至少大部分一樣 )。

我個人粗略總結了一下,認為函數式編程至少應該具有下列幾點中的多個特點。在謂之為函數式的語言中,要做到這些就比較容易,但要做到其它一些事情不是很難就是完全不可能:

·函數具有首要地位 (對象)。也就是說,能對“數據”做什么事,就要能對函數本身做到那些事(比如將函數作為參數傳遞給另外一個函數)。

·將遞歸作為主要的控制結構。在有些函數式語言中,都不存在其它的“循環”結構。

·列表處理作為一個重點(例如,Lisp語言的名字)。列表往往是通過對子列表進行遞歸取代了循環。

·“純”函數式語言會完全避免副作用。這么做就完全棄絕了命令式語言中幾乎無處不在的這種做法:將第一個值賦給一個變量之后為了跟蹤程序的運行狀態,接著又將另外一個值賦給同一個變量。

·函數式編程不是不鼓勵就是完全禁止使用語句,而是通過對表達式(換句話說,就是函數加上參數)求值(evaluation of expressions)完成任務. 在最純粹的情形下,一個程序就是一個表達式(再加上輔助性的定義)

·函數式編程中最關心的是要對什么進行計算,而不是要怎么來進行計算。

·在很多函數式編程語言中都會用到“高階”(higher order)函數 (換句話說,高階函數就是對對函數進行運算的函數進行運算的函數)。

函數式編程的倡導者們認為,所有這些特性都有助于更快地編寫出更多更簡潔并且更不容易出Bug的代碼。而且,計算機科學、邏輯學和數學這三個領域中的高級理論家發現,函數式編程語言和程序的形式化特性在證明起來比命令式編程語言和程序要簡單很多。

Python內在的函數式功能

自Python 1.0起,Python就已具有了以上所列中的絕大多數特點。但是就象Python所具有的大多數特性一樣,這些特點出現在了一種混合了各種特性的語言中。 和Python的OOP(面向對象編程) 特性非常象,你想用多少就用多少,剩下的都可以不管(直到你隨后需要用到它們為止)。在Python 2.0中,加入了列表解析(list comprehensions)這個非常好用的”語法糖“。 盡管列表解析沒有添加什么新功能,但它讓很多舊功能看起來好了不少。

Python中函數式編程的基本要素包括functionsmap()、reduce()、filter()和lambda算子(operator)。 在Python 1.x中,apply()函數也可以非常方便地拿來將一個函數的列表返回值直接用于另外一個函數。Python 2.0為此提供了一個改進后的語法。可能有點讓人驚奇,使用如此之少的函數(以及基本的算子)幾乎就足以寫出任何Python程序了;更加特別的是,幾乎用不著什么執行流程控制語句。

所有(if,elif,else,assert,try,except,finally,for,break,continue,while,def)這些都都能通過僅僅使用函數式編程中的函數和算子就能以函數式編程的風格處理好。盡管真正地在程序中完全排除使用所有流程控制命令可能只在想參加”Python混亂編程“大賽(可將Python代碼寫得跟Lisp代碼非常象)時才有意義,但這對理解函數式編程如何通過函數和遞歸表達流程控制很有價值。

剔除流程控制語句

剔除練習首先要考慮的第一件事是,實際上,Python會對布爾表達式求值進行“短路”處理。這就為我們提供了一個if/elif/else分支語句的表達式版(假設每個分支只調用一個函數,不是這種情況時也很容易組織成重新安排成這種情況)。 這里給出怎么做:

對Python中的條件調用進行短路處理

  1. # Normal statement-based flow control  
  2. if <cond1>:   func1()  
  3. elif <cond2>: func2()  
  4. else:         func3()  
  5.  
  6. # Equivalent "short circuit" expression  
  7. (<cond1> and func1()) or (<cond2> and func2()) or (func3())  
  8.  
  9. # Example "short circuit" expression  
  10. >>> x = 3 
  11. >>> def pr(s): return s  
  12. >>> (x==1 and pr('one')) or (x==2 and pr('two')) or (pr('other'))  
  13. 'other' 
  14. >>> x = 2 
  15. >>> (x==1 and pr('one')) or (x==2 and pr('two')) or (pr('other'))  
  16. 'two' 

我們的表達式版本的條件調用看上去可能不算什么,更象是個小把戲;然而,如果我們注意到lambda算子必須返回一個表達式,這就更值得關注了。既然如我們所示,表達式能夠通過短路包含一個條件判斷,那么,lambda表達式就是個完全通用的表達條件判斷返回值的手段了。我們來一個例子:

Python中短路的Lambda

  1. >>> pr = lambda s:s  
  2. >>> namenum = lambda x: (x==1 and pr("one")) \  
  3. ....                  or (x==2 and pr("two")) \  
  4. ....                  or (pr("other"))  
  5. >>> namenum(1)  
  6. 'one' 
  7. >>> namenum(2)  
  8. 'two' 
  9. >>> namenum(3)  
  10. 'other' 

將函數作為具有首要地位的對象

前面的例子已經表明了Python中函數具有首要地位,但有點委婉。當我們用lambda操作創建一個函數對象時, 我們所得到的東西是完全通用的。就其本質而言,我們可以將我們的對象同名字"pr"和"namenum"綁定到一起, 以完全相同的方式,我們也也完全可以將數字23或者字符串"spam" 同這些名字綁定到一起。但是,就象我們可以無需將其綁定到任何名字之上就能直接使用數字23(也就是說,它可以用作函數的參數)一樣,我們也可以直接使用我們使用lambda創建的函數對象,而無需將其綁定到任何名字之上。在Python中,函數就是另外一種我們能夠就像某種處理的值。

我們對具有首要地位的對象做的比較多的事情就是,將它們作為參數傳遞給函數式編程固有的函數map()、reduce()和filter()。這三個函數接受的第一個參數都是一個函數對象。

·map()針對指定給它的一個或多個列表中每一項對應的內容,執行一次作為參數傳遞給它的那個函數 ,最后返回一個結果列表。

·reduce()針對每個后繼項以及最后結果的累積結果,執行一次作為參數傳遞給它的那個函數;例如,reduce(lambda n,m:n*m, range(1,10))是求"10的階乘"的意思(換言之,將每一項和前面所得的乘積進行相乘)

·filter()使用那個作為參數傳遞給它的函數,對一個列表中的所有項進行”求值“,返回一個由所有能夠通過那個函數測試的項組成的經過遴選后的列表。

我們經常也會把函數對象傳遞給我們自己定義的函數,不過一般情況下這些自定義的函數就是前文提及的內建函數的某種形式的組合。

通過組合使用這三種函數式編程內建的函數, 能夠實現范圍驚人的“執行流程”操作(全都不用語句,僅僅使用表達式實現)。

Python中的函數式循環

替換循環語言和條件狀態語言塊同樣簡單。for可以直接翻譯成map()函數。正如我們的條件執行,我們會需要簡化語句塊成簡單的函數調用(我們正在接近通常能做的):

替換循環

  1. for e in lst:  func(e)      # statement-based loop  
  2. map(func,lst)           # map()-based loop 

通過這種方法,對有序程序流將有一個相似的函數式方式。那就是,命令式編程幾乎是由大量“做這,然后做那,之后做其它的”語句組成。map()讓我們只要做這樣:

Map-based 動作序列

  1. # let's create an execution utility function  
  2. do_it = lambda f: f()  
  3.  
  4. # let f1, f2, f3 (etc) be functions that perform actions  
  5.  
  6. map(do_it, [f1,f2,f3])   # map()-based action sequence 

通常,我們的整個主要的程序都可以使用一個map表達式加上一些函數列表的執行來完成這個程序。最高級別的函數的另一個方便的特性是你可以把它們放在一個列表里。

翻譯while會稍稍復雜一些,但仍然可以直接地完成:

Python中的函數式"while"循環

  1. # statement-based while loop  
  2. while <cond>:  
  3.     <pre-suite>  
  4.     if <break_condition>:  
  5.         break 
  6.     else:  
  7.         <suite>  
  8.  
  9. # FP-style recursive while loop  
  10. def while_block():  
  11.     <pre-suite>  
  12.     if <break_condition>:  
  13.         return 1 
  14.     else:  
  15.         <suite>  
  16.     return 0 
  17.  
  18. while_FP = lambda: (<cond> and while_block()) or while_FP()  
  19. while_FP() 

在翻譯while循環時,我們仍然需要使用while_block()函數,這個函數本身里面可以包含語句而不是僅僅包含表達式。但我們可能還能夠對這個函數再進行更進一步的剔除過程(就像前面模版中的對if/else進行短路處理一樣)。 還有,<cond>很難對普通的測試有什么用,比如while myvar==7,既然循環體(在設計上)不能對任何變量的值進行修改(當然,在while_block()中可以修改全局變量)。有一種方法可以用來為 while_block()添加更有用的條件判斷,讓 while_block()返回一個有意義的值,然后將這個返回值同循環結束條件進行比較。現在應該來看一個剔除其中語句的具體例子了:

Python中'echo'循環

  1. # imperative version of "echo()"  
  2. def echo_IMP():  
  3.     while 1:  
  4.         x = raw_input("IMP -- ")  
  5.         if x == 'quit':  
  6.             break 
  7.         else 
  8.             print x  
  9. echo_IMP()  
  10.  
  11. # utility function for "identity with side-effect"  
  12. def monadic_print(x):  
  13.     print x  
  14.     return x  
  15.  
  16. # FP version of "echo()"  
  17. echo_FP = lambda: monadic_print(raw_input("FP -- "))=='quit' or echo_FP()  
  18. echo_FP() 

在上面的例子中我們所做的,就是想辦法將一個涉及I/O、循環和條件判斷的小程序,表達為一個遞歸方式的純粹的表達式 (確切地說,表達為一個可以在需要的情況下傳遞到別的地方的函數對象)。我們 的確仍然使用了實用函數monadic_print(),但這個函數是完全通用的,而且可以用于以后我們可能會創建的每個函數式程序的表達式中(它的代價是一次性的)。請注意,任何包含monadic_print(x)的表達式的 值都是一樣的,好像它只是包含了一個x而已。函數式編程中(特別是在Haskell中)的函數有一種叫做"monad"(一元)的概念,這種一元函數“實際什么都不做,只是在執行過程中產生一個副作用”。

避免副作用

在做完這些沒有非常明智的理由陳述,并把晦澀的嵌套表達式代替他們之后,一個很自然的問題是“為什么要這樣做?!” 我描述的函數式編程在Python中都實現了。但是最重要的特性和一個有具體用處——就是避免副作用(或至少它們阻止如monads的特殊區域)。程序錯誤的大部分——并且這些問題驅使程序員去debug——出現是因為在程序的運行中變量獲取了非期望的值。函數式編程簡單地通過從不給變量賦值而繞過了這個問題。

現在讓我們看一段非常普通的命令式代碼。這段代碼的目的是打印出乘積大于25的一對一對數字所組成的一個列表。組成每對數字的每一個數字都是取自另外的兩個列表。這種事情和很多程序員在他們的編程中經常做的一些事情比較相似。命令式的解決方式有可能就象下面這樣:

命令式的"打印大乘積"的Python代碼

  1. # Nested loop procedural style for finding big products  
  2. xs = (1,2,3,4)  
  3. ys = (10,15,3,22)  
  4. bigmuls = []  
  5. # ...more stuff...  
  6. for x in xs:  
  7.     for y in ys:  
  8.         # ...more stuff...  
  9.         if x*y > 25:  
  10.             bigmuls.append((x,y))  
  11.             # ...more stuff...  
  12. # ...more stuff...  
  13. print bigmuls 

這個項目足夠小了,好像沒有地方會出什么差錯。但有可能在這段代碼中我們會嵌入一些同時完成其它任務的代碼。用"more stuff"(其它代碼)注釋掉的部分,就是有可能存在導致出現bug的副作用的地方。在那三部分的任何一點上,變量sxs、ys、bigmuls、x、y都有可能在這段按照理想情況簡化后的代碼中取得一個出人意料的值。還有,這段代碼執行完后,后繼代碼有可能需要也有可能不需要對所有這些變量中的值有所預期。顯而易見,將這段代碼封裝到函數/實例中,小心處理變量的作用范圍,就能夠避免這種類型的錯誤。你也可以總是將使用完畢的變量del掉。但在實踐中,這里指出的這種類型的錯誤很常見。

以一種函數式的途徑一舉消除這些副作用所產生的錯誤,這樣就達到了我們的目的。一種可能的代碼如下:

以函數式途徑達到我們的目的

  1. bigmuls = lambda xs,ys: filter(lambda (x,y):x*y > 25, combine(xs,ys))  
  2. combine = lambda xs,ys: map(None, xs*len(ys), dupelms(ys,len(xs)))  
  3. dupelms = lambda lst,n: reduce(lambda s,t:s+t, map(lambda l,n=n: [l]*n, lst))  
  4. print bigmuls((1,2,3,4),(10,15,3,22)) 

在例子中我們綁定我們的匿名(lambda)函數對象到變量名,但嚴格意義上講這并不是必須的。我們可以用簡單的嵌套定義來代替之。這不僅是為了代碼的可讀性,我們才這樣做的;而且是因為combine()函數在任何地方都是一個非常好的功能函數(函數從兩個輸入的列表讀入數據生成一個相應的pair列表)。函數dupelms()只是用來輔助函數combine()的。即使這個函數式的例子跟命令式的例子顯得要累贅些,不過一旦你考慮到功能函數的重用,則新的bigmuls()中代碼就會比命令式的那個要稍少些。

這個函數式例子的真正優點在于:在函數中絕對沒有改變變量的值。這樣就不可能在之后的代碼(或者從之前的代碼)中產生不可預期的副作用。顯然,在函數中沒有副作用,并不能保證代碼的正確性,但它仍然是一個優點。無論如何請注意,Python(不像很多其它的函數式語言)不會阻止名字bigmuls,combine和dupelms的再次綁定。如果combine()運行在之后的程序中意味著有所不同時,所有的預測都會失效。你可能會需要新建一個單例類來包含這個不變的綁定(也就是說,s.bigmuls之類的);但是這一例并沒有空間來做這些。

一個明顯值得注意的是,我們特定的目標是定制Python 2的一些特性。而不是命令式的或函數式編程的例子,最好的(也是函數式的)方法是:

  1. print [(x,y) for x in (1,2,3,4for y in (10,15,3,22if x*y > 25

結束語

我已經列出了把每一個Python控制流替換成一個相等的函數式代碼的方法(在程序中減少副作用)。高效翻譯一個特定的程序需要一些額外的思考,但我們已經看出內置的函數式功能是全面且完善的。在接下來的文章里,我們會看到更多函數式編程的高級技巧;并且希望我們接下來能夠摸索到函數式編程風格的更多優點和缺點。

原文鏈接:http://www.oschina.net/translate/python-functional-programming-part1

責任編輯:張偉 來源: oschina
相關推薦

2013-03-04 10:03:17

Python函數式編程

2013-03-05 10:01:29

Python函數式編程

2023-12-14 15:31:43

函數式編程python編程

2016-08-11 10:11:07

JavaScript函數編程

2022-07-19 15:24:45

Python編程技術

2013-09-09 09:41:34

2022-07-07 09:03:36

Python返回函數匿名函數

2012-09-21 09:21:44

函數式編程函數式語言編程

2018-11-15 10:20:59

Python函數式編程編程語言

2022-10-31 08:02:07

Python函數式編程

2012-08-23 14:23:33

函數式編程

2015-05-25 15:06:28

JavaScript函數式編程

2022-07-06 12:07:06

Python函數式編程

2024-02-28 07:59:25

2019-01-17 10:25:56

Python編程語言程序員

2024-09-11 16:30:55

Python函數編程

2016-10-31 20:46:22

函數式編程Javascript

2011-03-08 15:47:32

函數式編程

2020-09-24 10:57:12

編程函數式前端

2025-03-11 10:00:20

Golang編程函數
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久99精品久久久久久国产越南 | 国产区视频在线观看 | 国产一级视频在线播放 | 国产午夜在线 | 成人久久18免费网站图片 | 日韩视频在线播放 | 一区二区三区视频在线观看 | 午夜精品一区二区三区在线播放 | av网站免费观看 | 波多野结衣精品在线 | 九九久久久| 成人在线一区二区三区 | 日本综合在线观看 | 伊人天堂网 | 精品毛片在线观看 | 青娱乐一区二区 | 黄网免费 | 久久9热 | 4hu最新网址 | 精品国模一区二区三区欧美 | 精品91视频 | 永久免费av | 日韩av大片免费看 | 欧美精品网站 | 日韩精品一区二区三区高清免费 | 先锋影音资源网站 | 日本午夜在线视频 | 男女羞羞视频在线 | 欧美激情a∨在线视频播放 成人免费共享视频 | 欧美不卡视频 | 特级毛片爽www免费版 | 国产女人与拘做受免费视频 | 欧美中文字幕 | 国产午夜精品一区二区三区嫩草 | 色综合国产 | 99国产精品久久久久老师 | 国产福利资源在线 | 成人av免费| 国产精品久久久久久一区二区三区 | a免费视频| 婷婷色网 |