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

學(xué)會(huì)像一個(gè)函數(shù)式程序員那樣思考

原創(chuàng)
開發(fā) 開發(fā)工具
函數(shù)式編程已經(jīng)開始流行起來(lái)了,它宣稱具有更少的缺陷和更高的生產(chǎn)力。盡管很多開發(fā)者曾經(jīng)嘗試過函數(shù)式編程,但是依然無(wú)法理解是什么讓函數(shù)式語(yǔ)言如此引人注目。通常學(xué)習(xí)一種新語(yǔ)言的語(yǔ)法是相對(duì)容易的,但是學(xué)會(huì)以不同的方式去思考卻很難。Neal Ford在他的函數(shù)式思考專欄里面介紹了一些函數(shù)式編程的概念,并討論如何在Java和Groovy中去運(yùn)用這些概念。

【51CTO獨(dú)家特稿】在開始進(jìn)入正題之前,我們先來(lái)做一個(gè)比喻。假設(shè)你是一個(gè)伐木工人,你擁有一把這個(gè)森林里最好的斧子,而它使也你成為了當(dāng)?shù)刈钣猩a(chǎn)力的伐木工人。 某一天,有人向你展示并稱贊了一個(gè)新的伐木工具--電鋸。由于銷售人員是一個(gè)非常能推銷的人,所以你買了一把電鋸回來(lái),盡管你并不知道如何去用。于是你嘗試像以前砍樹那樣的來(lái)回?cái)[動(dòng)去鋸樹。并且你很快得出了一個(gè)結(jié)論這個(gè)新式的電鋸毫無(wú)用處,于是你又重新拿起斧子去伐木。一直到有人過來(lái)并給你演示了如何去運(yùn)轉(zhuǎn)電鋸,你才明白這里的不同。

你可能聯(lián)想到了用函數(shù)式編程來(lái)代替故事中的電鋸。但是問題在于函數(shù)式編程是一種全新的編程模式,而不是一門新的語(yǔ)言,語(yǔ)法只是一個(gè)細(xì)節(jié)問題。而最不同的地方是要如何以不同的方式去思考。而我作為一名“電鋸演示者”和一個(gè)函數(shù)式程序員來(lái)到了這里。

歡迎來(lái)到函數(shù)式思維專欄。這個(gè)系列將探索函數(shù)式編程的話題,但是并不僅僅局限在函數(shù)式編程語(yǔ)言有關(guān)的內(nèi)容上。正如我描繪的那樣,以函數(shù)式的方法來(lái)寫代碼涉及到了設(shè)計(jì),權(quán)衡,代碼重用和其他一系列的觀點(diǎn)。我會(huì)嘗試著以Java(或是類Java語(yǔ)言)的方式盡可能多的展示函數(shù)式編程的概念, 進(jìn)而演示一些其他語(yǔ)言的能力-那些Java不具有的能力。當(dāng)然我不會(huì)直接切入的非常深,然后討論一些時(shí)髦的事物。取而代之的是,我會(huì)逐漸演示一種新的思考問題的方式(或許你已經(jīng)在某些地方用了,但還沒有意識(shí)到)。

在接下來(lái)的兩部分里,你可以把它當(dāng)作是有關(guān)于函數(shù)式編程話題的一個(gè)旅行。其中的某些概念將會(huì)有大量的細(xì)節(jié),在這個(gè)系列中我會(huì)用更多的情景和細(xì)節(jié)去描述。在旅程開始前,我將帶你看一下一個(gè)相同問題的兩個(gè)不同實(shí)現(xiàn),一個(gè)用傳統(tǒng)的方式來(lái)寫,另一個(gè)使用更多的函數(shù)式方式。

數(shù)字歸類

談?wù)搩煞N不同的編程模式,你必須用代碼來(lái)做比較。第一個(gè)例子是我另一本書《The Productive Programmer》和《測(cè)試驅(qū)動(dòng)設(shè)計(jì)1,2》兩篇文章中的一個(gè)變體。我選取了少量的代碼,因?yàn)樵谶@兩篇文章里已經(jīng)深入的分析了這段代碼。這些文章對(duì)這個(gè)設(shè)計(jì)所做的稱贊并沒有錯(cuò),但我想在這里進(jìn)一步提供一個(gè)不同的設(shè)計(jì)意圖。

問題的需求是這樣的:假設(shè)給定任意一個(gè)正整數(shù)都大于1,你必須按照完美的,過剩的和不足的進(jìn)行歸類。一個(gè)完美數(shù)正好是它所有整除因子的總和。同樣地,一個(gè)過剩數(shù)的所有整除因子總和大于該數(shù),而一個(gè)不足數(shù)的所有整除因子總和小于該數(shù)。

快速數(shù)字歸類器

列表1中的類(NumberClassifier)滿足所有這些需求:

  1. public class Classifier6 { 
  2.     private Set<Integer> _factors; 
  3.     private int _number; 
  4.   
  5.     public Classifier6(int number) { 
  6.         if (number < 1) 
  7.             throw new InvalidNumberException("Can't classify negative numbers"); 
  8.         _number = number; 
  9.         _factors = new HashSet<Integer>>(); 
  10.         _factors.add(1); 
  11.         _factors.add(_number); 
  12.     } 
  13.   
  14.     private boolean isFactor(int factor) { 
  15.         return _number % factor == 0; 
  16.     } 
  17.   
  18.     public Set<Integer> getFactors() { 
  19.         return _factors; 
  20.     } 
  21.   
  22.     private void calculateFactors() { 
  23.         for (int i = 1; i <= sqrt(_number) + 1; i++) 
  24.             if (isFactor(i)) 
  25.   
  26.                 addFactor(i); 
  27.     } 
  28.   
  29.     private void addFactor(int factor) { 
  30.         _factors.add(factor); 
  31.         _factors.add(_number / factor); 
  32.     } 
  33.   
  34.     private int sumOfFactors() { 
  35.         calculateFactors(); 
  36.         int sum = 0; 
  37.         for (int i : _factors) 
  38.             sum += i; 
  39.         return sum; 
  40.     } 
  41.   
  42.     public boolean isPerfect() { 
  43.         return sumOfFactors() - _number == _number; 
  44.     } 
  45.   
  46.     public boolean isAbundant() { 
  47.         return sumOfFactors() - _number > _number; 
  48.     } 
  49.   
  50.     public boolean isDeficient() { 
  51.         return sumOfFactors() - _number < _number; 
  52.     } 
  53.   
  54.     public static boolean isPerfect(int number) { 
  55.         return new Classifier6(number).isPerfect(); 
  56.     } 

這段代碼有幾處地方需要關(guān)注一下:

它擁有大范圍的測(cè)試(有一部分我是為了討論測(cè)試驅(qū)動(dòng)開發(fā)而寫的)注:這條所說的測(cè)試位于作者另一篇文章中。

這個(gè)類由大量的緊耦合方法組成,在它的構(gòu)造函數(shù)中擁有測(cè)試驅(qū)動(dòng)開發(fā)的邊際效應(yīng)。

在calculateFactors()方法里內(nèi)嵌了性能優(yōu)化算法。這個(gè)類的主體是由采集因子組成,因此我可以在之后對(duì)它們進(jìn)行求和并進(jìn)行最終的歸類。整除因子總是以成對(duì)的形式被獲取。例如,如果這個(gè)數(shù)是16,當(dāng)我采集的因子為2時(shí),我就能得到另一個(gè)因子為8,因?yàn)?x2=16。如果我獲得的因子是成對(duì)的,那么我只需要去檢查那些有平方根的數(shù),這就是calculateFactors()方法所做的事情。

更多的功能歸類

使用相同的測(cè)試開發(fā)技術(shù),我創(chuàng)建了一個(gè)修改后的版本。列表2,更豐富的功能數(shù)字歸類器

  1. public class NumberClassifier { 
  2.   
  3.     static public boolean isFactor(int number, int potential_factor) { 
  4.         return number % potential_factor == 0; 
  5.     } 
  6.   
  7.     static public Set<Integer> factors(int number) { 
  8.         HashSet<Integer> factors = new HashSet<Integer>(); 
  9.         for (int i = 1; i <= sqrt(number); i++) 
  10.             if (isFactor(number, i)) { 
  11.                 factors.add(i); 
  12.                 factors.add(number / i); 
  13.             } 
  14.         return factors; 
  15.     } 
  16.   
  17.     static public int sum(Set<Integer> factors) { 
  18.         Iterator it = factors.iterator(); 
  19.         int sum = 0; 
  20.         while (it.hasNext()) 
  21.             sum += (Integer) it.next(); 
  22.         return sum; 
  23.     } 
  24.   
  25.     static public boolean isPerfect(int number) { 
  26.         return sum(factors(number)) - number == number; 
  27.     } 
  28.   
  29.     static public boolean isAbundant(int number) { 
  30.         return sum(factors(number)) - number > number; 
  31.     } 
  32.   
  33.     static public boolean isDeficient(int number) { 
  34.         return sum(factors(number)) - number < number; 
  35.     } 

這兩個(gè)版本的類盡管差別細(xì)微但是很重要。最主要的區(qū)別是例2的版本缺少了狀態(tài)共享。消除狀態(tài)共享在函數(shù)式編程中是比較受歡迎的一種抽象手法。作為跨方法共享狀態(tài)的替代方案,我采用直接調(diào)用的方式來(lái)消除狀態(tài)共享。從設(shè)計(jì)的角度來(lái)說,它讓factors()方法變的更長(zhǎng),但是它也防止了factors字段暴漏到方法之外。注意,例2是完全由靜態(tài)方法組成的。在方法間不存在知識(shí)共享的問題,因此我可以在更少函數(shù)范圍上做封裝。一旦你給它們輸入?yún)?shù)和期待值,這些方法都會(huì)工作的很好(這個(gè)是一個(gè)純函數(shù)例子,這個(gè)概念我在將來(lái)會(huì)進(jìn)一步探索它)。

函數(shù)

函數(shù)式編程屬于一個(gè)寬泛的計(jì)算機(jī)科學(xué)范疇,它已經(jīng)受到了極大的關(guān)注。有新的基于JVM上開發(fā)的函數(shù)式語(yǔ)言(如scala和clojure)和框架(如Functional Java和Akka),它們都聲稱能夠帶來(lái)更少的缺陷,更高的生產(chǎn)力,更易讀,更賺錢等等。相比駐足門外的去解決函數(shù)式編程這一大話題,我更愿意將注意力放在一些概念以及這些概念衍生出來(lái)的話題上。

函數(shù)式編程的核心就是函數(shù), 正如在面向?qū)ο笳Z(yǔ)言里面類是主要的抽象那樣。函數(shù)形成了處理過程的基礎(chǔ),同時(shí)它具有其他傳統(tǒng)語(yǔ)言沒有的一系列特性。

高階函數(shù)

高階函數(shù)可以將其他函數(shù)作為參數(shù)或者作為返回結(jié)果。這在Java語(yǔ)言中是無(wú)法想像的。最接近的方案是你使用一個(gè)類(通常是匿名類)作為執(zhí)行方法的“持有者”。Java沒有獨(dú)立的函數(shù)(或方法),因此它們不能作為返回值或參數(shù)出現(xiàn)。

這個(gè)能力對(duì)函數(shù)式語(yǔ)言來(lái)說很重要的原因有兩點(diǎn):第一,擁有高階函數(shù)就意為著你可以在如何連結(jié)語(yǔ)言元素上作出一個(gè)假設(shè)。例如,你可以構(gòu)建一個(gè)機(jī)制來(lái)消除一個(gè)類繼承體系上的一大堆方法,通過遍歷列表并對(duì)每一個(gè)元素應(yīng)用一個(gè)(或多個(gè))高階函數(shù)來(lái)實(shí)現(xiàn)。(我將展示一個(gè)簡(jiǎn)短的例子給你)第二,通過將函數(shù)作為返回值,你有機(jī)會(huì)去創(chuàng)建一個(gè)高動(dòng)態(tài),適應(yīng)性的系統(tǒng)。

通過使用高階函數(shù)我們就可以使問題服從于方案,但高階函數(shù)對(duì)函數(shù)式語(yǔ)言來(lái)說并不是唯一的。因此,當(dāng)你在使用函數(shù)式思維的時(shí)候, 你解決問題思路就會(huì)不一樣。考慮一下列表3中的例子,一個(gè)保護(hù)數(shù)據(jù)訪問的方法:

  1. public void addOrderFrom(ShoppingCart cart, String userName, 
  2.                      Order order) throws Exception { 
  3.     setupDataInfrastructure(); 
  4.     try { 
  5.         add(order, userKeyBasedOn(userName)); 
  6.         addLineItemsFrom(cart, order.getOrderKey()); 
  7.         completeTransaction(); 
  8.     } catch (Exception condition) { 
  9.         rollbackTransaction(); 
  10.         throw condition; 
  11.     } finally { 
  12.         cleanUp(); 
  13.     } 
  14. }   

列表3中的代碼執(zhí)行初始化等具體任務(wù),如果所有操作都成功就完成事務(wù),反之回滾,并在最后清理掉資源。很明顯,代碼有一部分可以被重用,并且我們?cè)诿嫦驅(qū)ο笳Z(yǔ)言中也常常創(chuàng)建這樣的結(jié)構(gòu)。在這個(gè)例子中,我組合使用了兩個(gè)“四人團(tuán)”的設(shè)計(jì)模式:模版方法和命令模式。模版方法建議我應(yīng)該移動(dòng)一些通用的模版代碼到繼承體系中,并推遲算法細(xì)節(jié)到子類。命令行模式提供了一個(gè)方法以眾所周知的執(zhí)行語(yǔ)義來(lái)封裝行為到類,列表4就是列表3代碼應(yīng)用這兩個(gè)模式之后的樣子:

列表4 重構(gòu)順序后的代碼

  1. public void wrapInTransaction(Command c) throws Exception { 
  2.     setupDataInfrastructure(); 
  3.     try { 
  4.         c.execute(); 
  5.         completeTransaction(); 
  6.     } catch (Exception condition) { 
  7.         rollbackTransaction(); 
  8.         throw condition; 
  9.     } finally { 
  10.         cleanUp(); 
  11.     } 
  12.   
  13. public void addOrderFrom(final ShoppingCart cart, final String userName, 
  14.                          final Order order) throws Exception { 
  15.     wrapInTransaction(new Command() { 
  16.         public void execute() { 
  17.             add(order, userKeyBasedOn(userName)); 
  18.             addLineItemsFrom(cart, order.getOrderKey()); 
  19.         } 
  20.     });                 
  21. }   

在列表4中,我提取了一部分通用的代碼到wrapInTransaction()(這個(gè)樣式你可能認(rèn)識(shí)-這是最簡(jiǎn)單的Spring事務(wù)模版的版本)方法中,傳遞一個(gè)命令對(duì)象作為工作單元。addOrderFrom()方法包含了一個(gè)匿名內(nèi)部類的創(chuàng)建,這個(gè)類以命令模式封裝了兩個(gè)工作單元。

封裝行為純粹是Java的設(shè)計(jì)產(chǎn)物,我需要用到一個(gè)不包含任何形式的,獨(dú)立行為的命令類。Java中所有的行為都必須駐留在一個(gè)類中。甚至語(yǔ)言的設(shè)計(jì)者很早的就看到了這個(gè)不足,但是顯然在發(fā)布后再去考慮不將行為聯(lián)接到類上就有些晚了。因此在JDK1.1中糾正了這個(gè)缺陷,通過添加匿名內(nèi)部類的方式來(lái)實(shí)現(xiàn)。這只是以一種語(yǔ)法糖的方式來(lái)為少量的方法創(chuàng)建一大堆小類,這樣做僅僅是從純功能角度出發(fā),而非從結(jié)構(gòu)上。如果想看有關(guān)Java這方面有趣的文章,請(qǐng)看Steve Yegge’s的《Execution in the Kingdom of Nouns》。

盡管我非常想要類里面的這個(gè)方法,但Java還是強(qiáng)制我去創(chuàng)建一個(gè)命令類的實(shí)例。這個(gè)類本身沒有任何用處:它沒有字段,沒有構(gòu)造器(這個(gè)由java自動(dòng)生成),并且也沒有狀態(tài)。它純粹的目的就是為了在方法里包裝行為。在函數(shù)式語(yǔ)言里,我們通過高階函數(shù)來(lái)取代這個(gè)模式。

如果我不準(zhǔn)備用Java的類,那么我可能采用最接近的語(yǔ)義是函數(shù)式編程里面的閉包。列表5顯示了重構(gòu)后的例子,但是使用Groovy代替了Java。

列表5, 使用Groovy的閉包代替命令類

  1. def wrapInTransaction(command) { 
  2.   setupDataInfrastructure() 
  3.   try { 
  4.     command() 
  5.     completeTransaction() 
  6.   } catch (Exception ex) { 
  7.     rollbackTransaction() 
  8.     throw ex 
  9.   } finally { 
  10.     cleanUp() 
  11.   } 
  12.   
  13. def addOrderFrom(cart, userName, order) { 
  14.   wrapInTransaction { 
  15.     add order, userKeyBasedOn(userName) 
  16.     addLineItemsFrom cart, order.getOrderKey() 
  17.   } 
  18. }   

在Groovy里面,任何位于大括號(hào){}之間的東西都是一個(gè)代碼塊,并且代碼塊可以被當(dāng)作參數(shù)來(lái)模仿一個(gè)高階函數(shù)。在這種情景下,Groovy為你實(shí)現(xiàn)了命令模式。Groovy中的每一個(gè)閉包塊就是一個(gè)Groovy的閉包類型,它包含一個(gè)call()方法。當(dāng)你把一對(duì)空括號(hào)放到變量后面用于保存閉包實(shí)例時(shí),該方法會(huì)被自動(dòng)調(diào)用。Groovy啟用了一些類函數(shù)式編程的行為,通過在語(yǔ)言本身使用相應(yīng)的語(yǔ)法糖來(lái)構(gòu)建適當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)。正如我將會(huì)逐步展示的那樣,Groovy也包含其他函數(shù)式語(yǔ)言的能力。我將在下面的部分繼續(xù)對(duì)閉包和高階函數(shù)做一些有意思的比較。

第一級(jí)函數(shù)

函數(shù)被認(rèn)為是函數(shù)式語(yǔ)言里面的一等公民,這就意味著函數(shù)可以出現(xiàn)在任何地方,正如其他語(yǔ)言的構(gòu)造體(如變量)那樣。在思考不同解決方案的時(shí)候,第一級(jí)函數(shù)的存在允許函數(shù)以一種特別的方式來(lái)使用,如應(yīng)用同樣的比較操作到相同的數(shù)據(jù)結(jié)構(gòu)上。這就體現(xiàn)了函數(shù)式語(yǔ)言的一個(gè)基本思考原則:關(guān)注結(jié)果,而不是過程。

在命令式的編程語(yǔ)言里,我必須考慮算法的每一個(gè)原子操作。如列表1的代碼顯示的那樣。為了實(shí)現(xiàn)數(shù)字歸類器,我不得不精確的識(shí)別如何去采集整除因子,這就意為著為了確定一個(gè)因子,我不得不寫代碼去遍歷所有數(shù)字。但是像遍歷列表,然后對(duì)每一個(gè)元素實(shí)施操作,這聽起來(lái)像是很通用的東西。考慮使用Functional Java框架來(lái)重新實(shí)現(xiàn)數(shù)字歸類器的代碼,代碼如列表6所示:

列表6. 函數(shù)式的數(shù)字歸類器

  1. public class FNumberClassifier { 
  2.   
  3.     public boolean isFactor(int number, int potential_factor) { 
  4.         return number % potential_factor == 0; 
  5.     } 
  6.   
  7.     public List<Integer> factors(final int number) { 
  8.         return range(1, number+1).filter(new F<Integer, Boolean>() { 
  9.             public Boolean f(final Integer i) { 
  10.                 return number % i == 0; 
  11.             } 
  12.         }); 
  13.     } 
  14.   
  15.     public int sum(List<Integer> factors) { 
  16.         return factors.foldLeft(fj.function.Integers.add, 0); 
  17.     } 
  18.   
  19.     public boolean isPerfect(int number) { 
  20.         return sum(factors(number)) - number == number; 
  21.     } 
  22.   
  23.     public boolean isAbundant(int number) { 
  24.         return sum(factors(number)) - number > number; 
  25.     } 
  26.   
  27.     public boolean isDeficiend(int number) { 
  28.         return sum(factors(number)) - number < number; 
  29.     } 
  30. }   

列表6和列表2的不同在于兩個(gè)方法:sum()和factors()。在Functional Java里, Sum()方法具有List類的foldLeft()方法優(yōu)勢(shì)。列表操作概念上的一個(gè)具體變化就是被稱之為catamorphism,它是列表折疊上的一般化。在這里“向左折疊”的意思是:

1. 攜帶一個(gè)初始值并組合它到列表的第一個(gè)元素上

2. 攜帶結(jié)果并應(yīng)用相同的操作到下一個(gè)元素上

3. 一直操作直到列表結(jié)束

注意當(dāng)你對(duì)一堆數(shù)求和的時(shí)候,所做的事情是非常明顯的:從零開始,加上第一關(guān)元素,攜帶結(jié)果去加第二個(gè),重復(fù)這個(gè)過程直到所有列表的元素都被處理。Functional Java提供高階函數(shù)(在這個(gè)例子里就是Intergers.add枚舉器)并小心翼翼的為你的代碼啟用它。(當(dāng)然Java真的沒有高階函數(shù),但是你可以通過限制具體的數(shù)據(jù)結(jié)構(gòu)和類型來(lái)寫一個(gè)較類似的東西)。

在列表6里面另一奇妙的方法是factors(),它充分說明了我關(guān)于“關(guān)注結(jié)果,而不是過程”的建議。發(fā)現(xiàn)一個(gè)數(shù)的整除因子這個(gè)問題的本質(zhì)是什么?換個(gè)方式來(lái)說,給出一個(gè)到目標(biāo)數(shù)的所有可能數(shù)的列表。那么我該如何確定哪個(gè)數(shù)是這個(gè)數(shù)的整除因子?這里的建議是進(jìn)行一次過濾操作 – 我能過濾整個(gè)列表,消除那些不符合我標(biāo)準(zhǔn)的數(shù)。這個(gè)方法基本上就如同以下描述:取得1到這個(gè)數(shù)的范圍;用f()方法來(lái)過濾這個(gè)列表,F(xiàn)unctional Java的方式將允許你創(chuàng)建一個(gè)具有特殊數(shù)據(jù)類型的類,并返回結(jié)果。

這段代碼同時(shí)也描繪了一個(gè)更大的概念,一個(gè)編程語(yǔ)言的趨勢(shì)。回到過去,開發(fā)人員不得不處理一大堆煩人的東西,如內(nèi)存分配,垃圾回收和指針。隨著時(shí)間的推移,語(yǔ)言本身背負(fù)起了更多這方面的職責(zé)。就像計(jì)算機(jī)越來(lái)越強(qiáng)大一樣,我們把越來(lái)越多的現(xiàn)實(shí)任務(wù)丟給了語(yǔ)言和運(yùn)行時(shí)。作為一名Java開發(fā)者,我比較傾向于把所有的內(nèi)存問題都交給語(yǔ)言處理。函數(shù)式編程擴(kuò)大了這個(gè)需求,并包含了更多的細(xì)節(jié)。隨著時(shí)間的推移,我們將花費(fèi)更少的時(shí)間去關(guān)心每一個(gè)步要解決的問題和思考的過程。隨著本系列的進(jìn)展,我將展示更多相關(guān)的例子。

結(jié)論

函數(shù)式編程更多的是一種觀念而不是一個(gè)工具或者一門語(yǔ)言。在開始的部分,我曽提到過一些函數(shù)式編程的議題,范圍從簡(jiǎn)單設(shè)計(jì)的討論到某些宏觀問題的重新思考。我寫了一個(gè)簡(jiǎn)單的Java類,并讓它更符合函數(shù)式編程理念,然后開始用傳統(tǒng)的命令式語(yǔ)言來(lái)構(gòu)建部分函數(shù)式語(yǔ)言的方式去深入探討了這些議題。

另外引申出兩個(gè)重點(diǎn):首先是關(guān)注結(jié)果而非過程。函數(shù)式編程嘗試著使用不同的方式去表達(dá)問題,因?yàn)槟阋呀?jīng)構(gòu)建不同的代碼塊來(lái)幫助項(xiàng)目成長(zhǎng)。第二點(diǎn)是我一直在這個(gè)系列中展示的那樣,將那些單調(diào)的細(xì)節(jié)交給編程語(yǔ)言和運(yùn)行時(shí)去處理。這樣將有助于我們將注意力集中我們的編程問題上。在下一部分,我將繼續(xù)著眼與函數(shù)式編程語(yǔ)言的常規(guī)方面,并介紹如何將它應(yīng)用于現(xiàn)時(shí)的軟件開發(fā)當(dāng)中。

 

 

責(zé)任編輯:彭凡 來(lái)源: 51CTO
相關(guān)推薦

2020-07-10 09:55:15

程序員技能開發(fā)者

2014-01-08 09:26:05

程序員招聘

2013-06-05 10:49:57

招聘招聘程序員

2012-11-12 10:46:37

2020-02-22 21:51:43

程序員Microsoft SServerSQL

2012-12-17 10:50:27

程序員

2020-10-05 21:13:37

程序員技能開發(fā)者

2014-01-06 09:33:32

程序員管理

2021-07-01 07:43:41

項(xiàng)目程序員代碼

2014-05-12 10:12:09

程序員

2015-06-16 10:31:36

程序員

2012-11-28 13:25:27

程序員

2023-12-26 18:47:32

2015-06-08 10:48:39

程序員程序員自白

2011-02-14 13:05:17

PythonWeb

2013-07-25 09:47:40

程序員職業(yè)發(fā)展

2013-07-09 09:11:50

程序員

2009-02-12 15:07:57

程序員創(chuàng)業(yè)經(jīng)驗(yàn)

2019-04-22 10:25:52

程序員技術(shù)職場(chǎng)

2012-04-12 14:49:31

程序員
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产在线a| 日韩精品| 国产视频91在线 | 欧美色图另类 | 色站综合 | 国产精品99久久久久久www | 久久不射网 | 91亚洲一区 | 在线播放中文字幕 | 精品综合在线 | 91爱爱·com| 一区二区三区四区在线视频 | 亚洲精品www久久久 www.蜜桃av | 伊人伊人 | 婷婷99 | 毛片久久久 | av色站 | 国产偷录叫床高潮录音 | 一级中国毛片 | 99在线精品视频 | 日韩中文一区二区三区 | 在线观看中文字幕 | 国产污视频在线 | 蜜桃在线视频 | 精品一区欧美 | 一区二区三区在线免费看 | 久久久久久91香蕉国产 | 欧美 日韩 国产 成人 | 亚洲精品99 | 久久久久久久av麻豆果冻 | 涩涩视频在线看 | 91网视频 | 欧美成人精品在线观看 | 国产精品99精品久久免费 | 爱综合 | 欧美精品日韩精品 | 日韩av在线一区 | 97人人草 | 婷婷综合网| 天天想天天干 | 九九热国产视频 |