欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

進(jìn)一步理解Python中的函數(shù)編程

系統(tǒng) 1673 0

我們最好從最難的問(wèn)題開(kāi)始:“到底什么是函數(shù)編程 (FP)?”一個(gè)答案可能會(huì)說(shuō) FP 就是您在使用例如 Lisp、Scheme、Haskell、ML、OCAML、Clean、Mercury、Erlang(或其它一些)語(yǔ)言進(jìn)行編程時(shí)所做的。這是一個(gè)穩(wěn)妥的答案,但不能很確切地闡明問(wèn)題。不幸的是,即使是函數(shù)程序員他們自己也很難對(duì) FP 究竟是什么有個(gè)一致的認(rèn)識(shí)。“盲人摸象”的故事用來(lái)形容這一情況似乎很合適。還可以放心地將 FP 與“命令編程”(使用例如 C、Pascal、C++、Java、Perl、Awk、TCL 以及其它大多數(shù)語(yǔ)言所執(zhí)行的操作,至少是在很大程度上)進(jìn)行對(duì)比。

從個(gè)人角度來(lái)說(shuō),我會(huì)將函數(shù)編程粗略地描繪為至少具有以下幾個(gè)特征。稱得上函數(shù)性的語(yǔ)言使這些事情變得簡(jiǎn)單,而使其它事情變得困難或不可能:

  • ??? 函數(shù)是第一類(對(duì)象)。即,可以對(duì)“數(shù)據(jù)”進(jìn)行的每樣操作都可以使用函數(shù)本身做到(例如將一個(gè)函數(shù)傳遞給另一個(gè)函數(shù))。
  • ??? 將遞歸用作主要的控制結(jié)構(gòu)。在某些語(yǔ)言中,不存在其它“循環(huán)”構(gòu)造。
  • ??? 重點(diǎn)集中在列表 LISt 處理(例如,名稱 Lisp )。列表經(jīng)常和子列表的遞歸一起使用以替代循環(huán)。
  • ??? “純”函數(shù)語(yǔ)言能夠避免副作用。這不包括在命令語(yǔ)言中最普遍的模式,即指定第一個(gè),然后將另一個(gè)值指定給同一個(gè)變量來(lái)跟蹤程序狀態(tài)。
  • ??? FP 不鼓勵(lì)或根本不允許出現(xiàn) 語(yǔ)句,取而代之是使用表達(dá)式求值(換句話說(shuō),即函數(shù)加上自變量)。在很純粹的情況下,一個(gè)程序就是一個(gè)表達(dá)式(加上支持的定義)。
  • ??? FP 關(guān)心的是計(jì)算 什么而不是 如何計(jì)算。
  • ??? 許多 FP 利用了“更高等級(jí)”函數(shù)(換句話說(shuō),就是函數(shù)對(duì)一些函數(shù)操作,而這些函數(shù)又對(duì)其它函數(shù)操作)。

函數(shù)編程的提倡者認(rèn)為所有這些特征都導(dǎo)致更快速的開(kāi)發(fā)更短以及錯(cuò)誤更少的代碼。而且,計(jì)算機(jī)科學(xué)、邏輯和數(shù)學(xué)領(lǐng)域的高級(jí)理論學(xué)家發(fā)現(xiàn)證明函數(shù)語(yǔ)言和程序的正式性能比命令語(yǔ)言和程序容易得多。
固有的 Python 函數(shù)能力

自從 Python 1.0 以來(lái),Python 具有上面列出的大多數(shù) FP 特征。但對(duì)于大多數(shù) Python 特性,它們以一種非常混合的語(yǔ)言呈現(xiàn)。很大程度上是因?yàn)?Python 的 OOP 特性,您可以使用希望使用的部分而忽略其余部分(直到在稍后需要它為止)。使用 Python 2.0, 列表內(nèi)涵添加了一些 非常棒的“句法上的粉飾”。雖然列表內(nèi)涵沒(méi)有添加什么新的能力,但它們使許多舊的能力看起來(lái)好了 許多。

Python 中 FP 的基本元素是函數(shù) map() 、 reduce() 和 filter() ,以及運(yùn)算符 lambda 。在 Python 1.x 中, apply() 函數(shù)對(duì)于將一個(gè)函數(shù)的列表返回值直接應(yīng)用于另一個(gè)函數(shù)也很方便。Python 2.0 為這一目的提供了改進(jìn)的語(yǔ)法。可能讓人吃驚,但很少的這幾個(gè)函數(shù)(以及基本運(yùn)算符)幾乎足以編寫任何 Python程序;特別是,所有的流控制語(yǔ)句( if 、 elif 、 else 、 assert 、 try 、 except 、 finally 、 for 、 break 、 continue 、 while 、 def )可以只使用 FP 函數(shù)和運(yùn)算符以函數(shù)風(fēng)格處理。雖然實(shí)際上消除程序中的所有流控制命令可能只對(duì)加入“混亂的 Python”競(jìng)爭(zhēng)(與看上去非常象 Lisp 的代碼)有用,但是理解 FP 是如何使用函數(shù)和遞歸來(lái)表示流控制是值得的。


消除流控制語(yǔ)句

在我們執(zhí)行消除聯(lián)系時(shí)要考慮的第一件事是 Python “短路”了布爾表達(dá)式的求值這一事實(shí)。這樣就提供了表達(dá)式版本的 if / elif / else 塊(假設(shè)每塊都調(diào)用一個(gè)函數(shù),通常總有可能這樣安排)。下面是具體方法:
清單 1. Python 中的“短路”條件調(diào)用

            
# Normal statement-based flow control 
  
if
   
            
              : func1() 
  elif
   
              
                : func2() 
  else
  :   func3() 
 
  # Equivalent "short circuit" expression 
(
                
                   
  and
   func1()) 
  or
   (
                  
                     
  and
   func2()) 
  or
   (func3()) 
 
  # Example "short circuit" expression 
>>> x = 3 
>>> 
  defpr
  (s): 
  return
   s 
>>> (x==1 
  and
   pr(
  'one')) 
  or
   (x==2 
  and
   pr(
  'two')) 
  or
   (pr(
  'other')) 
  'other' 
>>> x = 2 
>>> (x==1 
  and
   pr(
  'one')) 
  or
   (x==2 
  and
   pr(
  'two')) 
  or
   (pr(
  'other')) 
  'two'


                  
                
              
            
          

表達(dá)式版本的條件性調(diào)用似乎不過(guò)是個(gè)位置訣竅;不過(guò),如果我們注意到 lambda 運(yùn)算符必須返回表達(dá)式時(shí),就更有趣了。因?yàn)?-- 如前所示 -- 表達(dá)式可以通過(guò)短路來(lái)包含條件塊,所以 lambda 表達(dá)式在表達(dá)條件返回值中非常普通。在我們的示例上構(gòu)建:
清單 2. Python 中 Lambda 短路

            
>>> pr = 
  lambda
   s:s 
>>> namenum = 
  lambda
   x: (x==1 
  and
   pr(
  "one")) \ 
....     
  or
   (x==2 
  and
   pr(
  "two")) \ 
....     
  or
   (pr(
  "other")) 
>>> namenum(1) 
  'one' 
>>> namenum(2) 
  'two' 
>>> namenum(3) 
  'other'


          


函數(shù)作為第一類對(duì)象

上面的示例已經(jīng)顯示出函數(shù)在 Python 中所處的第一類的地位,但以很微妙的方式。在使用 lambda 操作創(chuàng)建 函數(shù)對(duì)象 時(shí),我們有一些完全常規(guī)的事物。正是因?yàn)檫@樣,我們可以將對(duì)象與名稱 "pr" 和 "namenum" 綁定,使用的方法和將數(shù)字 23 或字符串 "spam" 與這些名稱綁定的方法完全相同。但正如我們可以使用數(shù)字 23 而無(wú)需將它與任何名稱綁定一樣(換句話說(shuō),象函數(shù)自變量一樣),我們可以使用用 lambda 創(chuàng)建的函數(shù)對(duì)象而不用將它與任何名稱綁定。一個(gè)函數(shù)只是我們?cè)?Python 中對(duì)其執(zhí)行某些操作的另一個(gè)值。

我們對(duì)第一類對(duì)象所執(zhí)行的主要操作是將它們傳遞給 FP 內(nèi)置函數(shù) map() 、 reduce() 和 filter() 。這些函數(shù)中的每一個(gè)都接受函數(shù)對(duì)象作為其第一個(gè)自變量。

??? map() 對(duì)指定列表中每個(gè)對(duì)應(yīng)的項(xiàng)執(zhí)行傳遞的函數(shù),并返回結(jié)果列表。
??? reduce() 對(duì)每個(gè)后續(xù)項(xiàng)執(zhí)行傳遞的函數(shù),返回的是最終結(jié)果的內(nèi)部累加;例如 reduce(lambda n,m:n*m, range(1,10)) 意味著“10 的階乘”(換句話說(shuō),用每一項(xiàng)乘上前一次相乘的乘積)。
??? filter() 使用傳遞的函數(shù)對(duì)列表中的每一項(xiàng)“求值”,然后返回經(jīng)過(guò)甄別的,通過(guò)了傳遞函數(shù)測(cè)試的項(xiàng)的列表。

我們還經(jīng)常將函數(shù)對(duì)象傳遞給自己的定制函數(shù),但它們通常等同于上述內(nèi)置函數(shù)的組合。

通過(guò)將這三種 FP 內(nèi)置函數(shù)進(jìn)行組合,可以執(zhí)行驚人的一系列“流”操作(都不使用語(yǔ)句,而只使用表達(dá)式)。


Python 中的函數(shù)循環(huán)

替換循環(huán)與替換條件塊一樣簡(jiǎn)單。 for 可以直接轉(zhuǎn)換成 map() 。對(duì)于我們的條件執(zhí)行,我們需要將語(yǔ)句塊簡(jiǎn)化成單一函數(shù)調(diào)用(我們正逐步接近通常的做法):
清單 3. Python 中的函數(shù) 'for' 循環(huán)

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


          

另外,對(duì)于連續(xù)程序流的函數(shù)方法有類似的技術(shù)。即,命令編程通常包含接近于“做這樣,然后做那樣,然后做其它事。”這樣的語(yǔ)句。 map() 讓我們正好做到這一點(diǎn):
清單 4. Python 中的函數(shù)連續(xù)操作

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


          

通常,我們的整個(gè) main 程序可以是 map() 表達(dá)式和一系列完成程序所需要執(zhí)行的函數(shù)。第一類函數(shù)的另一個(gè)方便的特性就是可以將它們放在一個(gè)列表中。

while 的轉(zhuǎn)換稍微復(fù)雜了一些,但仍然可以直接進(jìn)行:
清單 5. Python 中的函數(shù) 'while' 循環(huán)

            
# statement-based while loop
  
while
   
            
              :
 
              
                
 
  if
   
                
                  :
 
  break
 else
  :
 
                  
                    
  # FP-style recursive while loopp
  
defwhile_block
  ():
 
                    
                      
 
  if
   
                      
                        :
 
  return
   1
 
  else
  :
 
                        
                          
 
  return
   0
while_FP = 
  lambda
  : (
                          
                             
  and
   while_block()) 
  or
   while_FP()
while_FP()


                          
                        
                      
                    
                  
                
              
            
          

while 的轉(zhuǎn)換仍需要 while_block() 函數(shù),它本身包含語(yǔ)句而不僅僅是表達(dá)式。但我們需要對(duì)該函數(shù)做進(jìn)一步的消除(例如對(duì)模板中的 if/else 進(jìn)行短路)。另外,因?yàn)檠h(huán)主體(按設(shè)計(jì))無(wú)法更改任何變量值,所以 很難用在一般的測(cè)試中,例如 while myvar==7 (那么,將在 while_block() 中修改全部?jī)?nèi)容)。添加更有用條件的一個(gè)方法是讓 while_block() 返回一個(gè)更有趣的值,然后將這個(gè)返回值與終止條件進(jìn)行比較。有必要看一下這些消除語(yǔ)句的具體示例:
清單 6. Python 中的函數(shù) 'echo' 循環(huán)

            
# imperative version of "echo()"
  
defecho_IMP
  ():
 
  while
   1:
 x = raw_input(
  "IMP -- ")
 
  if
   x == 
  'quit':
  
  break
 else
  print
   x
echo_IMP()
  # utility function for "identity with side-effect"
  
defmonadic_print
  (x):
 
  print
   x
 
  return
   x
  # FP version of "echo()"
echo_FP = 
  lambda
  : monadic_print(raw_input(
  "FP -- "))==
  'quit' 
  or
   echo_FP()
echo_FP()


          

我們所完成的是設(shè)法將涉及 I/O、循環(huán)和條件語(yǔ)句的小程序表示成一個(gè)帶有遞歸的純表達(dá)式(實(shí)際上,如果需要,可以表示成能傳遞到任何其它地方的函數(shù)對(duì)象)。我們 的確 仍然利用了實(shí)用程序函數(shù) monadic_print() ,但這個(gè)函數(shù)是完全一般性的,可以在我們以后創(chuàng)建的每個(gè)函數(shù)程序表達(dá)式中重用(它是一次性成本)。請(qǐng)注意,任何包含 monadic_print(x) 的表達(dá)式所 求值 的結(jié)果都是相同的,就象它只包含 x 一樣。FP(特別是 Haskell)對(duì)于“不執(zhí)行任何操作,在進(jìn)程中有副作用”的函數(shù)具有“單一體”意思。


消除副作用

在除去完美的、有意義的語(yǔ)句不用而代之以晦澀的、嵌套的表達(dá)式的工作后,一個(gè)很自然的問(wèn)題是:“為什么?!”我對(duì) FP 的所有描述都是使用 Python 做到的。但最重要的特性 -- 可能也是具體情況中最有用的特性 -- 是它消除了副作用(或者至少對(duì)一些特殊領(lǐng)域,例如單一體,有一些牽制作用)。絕大部分程序錯(cuò)誤 -- 和促使程序員求助于調(diào)試來(lái)解決的問(wèn)題 -- 之所以會(huì)發(fā)生,是因?yàn)樵诔绦驁?zhí)行過(guò)程期間,變量包含了意外的值。函數(shù)程序只不過(guò)根本就不為變量分配值,從而避免了這一特殊問(wèn)題。

讓我們看一段相當(dāng)普通的命令代碼。它的目的是打印出乘積大于 25 的幾對(duì)數(shù)字的列表。組成各對(duì)的數(shù)字本身是從另外兩個(gè)列表中挑選出的。這種操作與程序員在他們程序段中實(shí)際執(zhí)行的操作差不多。實(shí)現(xiàn)這一目的的命令方法如下:
清單 7. “打印大乘積”的命令 Python 代碼

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


          

這個(gè)項(xiàng)目太小,以至于沒(méi)有什么可能出錯(cuò)。但我們的目的可能嵌在要同時(shí)實(shí)現(xiàn)許多其它目的的代碼中。用 "more stuff" 注釋的那些部分是副作用可能導(dǎo)致錯(cuò)誤發(fā)生的地方。在這些地方中的任何一處,變量 xs 、 ys 、 bigmuls 、 x 、 y 有可能獲得假設(shè)節(jié)略代碼中的意外值。而且,在執(zhí)行完這一段代碼后,所有變量都可能具有稍后代碼可能需要也可能不需要的一些值。很明顯,可以使用函數(shù)/實(shí)例形式的封裝和有關(guān)作用域的考慮來(lái)防止出現(xiàn)這種類型的錯(cuò)誤。而且,您總是可以在執(zhí)行完變量后 del 它們。但在實(shí)際中,這些指出類型的錯(cuò)誤非常普遍。

目標(biāo)的函數(shù)方法完全消除了這些副作用錯(cuò)誤。以下是可能的一段代碼:
清單 8. “打印大乘積”的函數(shù) Python 代碼

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


          

在示例中,我們將匿名 ( lambda ) 函數(shù)對(duì)象與名稱進(jìn)行綁定,但這不是一定必要的。我們可以只嵌套定義。這樣做是出于可讀性目的;但也是因?yàn)?combine() 是一種隨處可得的很好實(shí)用程序函數(shù)(從兩個(gè)輸入列表中產(chǎn)生所有元素對(duì)的列表)。隨后的 dupelms() 主要只是幫助 combine() 發(fā)揮作用的一種方法。即使這一函數(shù)示例比命令示例更冗長(zhǎng),但一旦考慮到實(shí)用程序函數(shù)可以重用,那么 bigmuls() 中的新代碼本身可能比命令版本中的代碼數(shù)量還要少一些。

這種函數(shù)示例真正的優(yōu)勢(shì)在于絕對(duì)不會(huì)有變量更改其中的任何值。稍后的代碼中沒(méi)有 可能的不曾預(yù)料到的副作用(較早的代碼中也不會(huì)有)。很明顯,它本身沒(méi)有副作用并不能保證代碼 正確,但即使這樣,這也是個(gè)優(yōu)點(diǎn)。不過(guò)請(qǐng)注意,Python(與許多函數(shù)語(yǔ)言不同) 不能 防止名稱 bigmuls 、 combine 和 dupelms 的重新綁定。如果 combine() 在程序的稍后部分中開(kāi)始有其它意義,則所有努力都前功盡棄。您可以逐步建立一個(gè) Singleton 類來(lái)包含這種類型的不可變綁定(例如 s.bigmuls 等);但本專欄并不涉及這一內(nèi)容。

特別值得注意的一個(gè)問(wèn)題是我們的特定目的是對(duì) Python 2 中的新特性進(jìn)行定制。最好的(也是函數(shù)的)技術(shù)既不是上面提供的命令示例,也不是函數(shù)實(shí)例,而是:
清單 9. "bigmuls" 的列表內(nèi)涵 Python 代碼

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


          


結(jié)束語(yǔ)

我已介紹了使用函數(shù)等價(jià)物替換每個(gè) Python 流控制構(gòu)造所使用的方法(在過(guò)程中消除了副作用)。對(duì)特定程序進(jìn)行有效轉(zhuǎn)換將帶來(lái)一些額外的考慮,但我們已經(jīng)知道內(nèi)置函數(shù)是常規(guī)而完整的。在稍后的專欄中,我們將考慮一些更高級(jí)的函數(shù)編程技術(shù);希望能夠探索函數(shù)風(fēng)格的更多利弊。


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 鲁一鲁影院| www.sewang| 久久国产精品免费 | 91社区在线高清 | 国产精品一码二码三码在线 | 欧美日韩中文字幕在线 | 日韩欧美中文字幕视频 | 97国产精品最新 | 一级毛片免费 | 99久久精品免费看国产免费 | 亚洲一区二区三区福利在线 | 特黄特色大片免费视频大全 | 国产网址在线观看 | 日韩福利视频导航 | 一级黄色毛片 | 青娱乐免费视频观看 | 久久久久网站 | 天天看高清特色大片 | 青娱乐免费 | 四虎影片国产精品8848 | 奇米影视888狠狠狠777不卡 | 四虎4hutv永久在线影院 | 欧美日韩精品一区二区在线播放 | 日韩欧美在线观看视频 | 污视频免费在线观看 | 日本一级特黄a大片在线 | 91p在线观看| 欧美一级在线观看 | 欧美1024性视频 | 你懂的91 | 一级黄毛片 | 亚洲中午字幕 | 婷婷在线视频 | 久久电影精品久久99久久 | 91在线观看视频 | 天堂在线观看中文字幕 | www.com黄 | 欧美成人免费在线视频 | 久久国产精品一区 | 久草在线国产视频 | 干片网|