千鋒扣丁學(xué)堂Java培訓(xùn)之了解java函數(shù)式編碼結(jié)構(gòu)及優(yōu)勢(shì)
2019-06-12 13:44:46
5248瀏覽
今天千鋒扣丁學(xué)堂Java培訓(xùn)老師給大家分享一篇關(guān)于了解java函數(shù)式編碼結(jié)構(gòu)及優(yōu)勢(shì),本文將探討三種下一代JVM語(yǔ)言:Groovy、Scala和Clojure,比較并對(duì)比新的功能和范例,讓Java開發(fā)人員對(duì)自己近期的未來(lái)發(fā)展有大體的認(rèn)識(shí),下面我們一起來(lái)看一下吧。
當(dāng)垃圾回收成為主流時(shí),它消除了所有類別的難以調(diào)試的問(wèn)題,使運(yùn)行時(shí)能夠?yàn)殚_發(fā)人員管理復(fù)雜的、容易出錯(cuò)的進(jìn)程。函數(shù)式編程旨在為您編寫的算法實(shí)現(xiàn)同樣的優(yōu)化,這樣您就可以從一個(gè)更高的抽象層面開展工作,同時(shí)運(yùn)行時(shí)執(zhí)行復(fù)雜的優(yōu)化。
Java下一代語(yǔ)言并不都占用從命令式到函數(shù)式的語(yǔ)言頻譜的同一位置,但都展現(xiàn)出函數(shù)功能和習(xí)語(yǔ)。函數(shù)式編程技術(shù)有明確定義,但語(yǔ)言有時(shí)為相同的函數(shù)式概念使用不同的術(shù)語(yǔ),使得我們很難看到相似之處。在本期文章中,我比較了Scala、Groovy和Clojure的函數(shù)式編碼風(fēng)格并討論了它們的優(yōu)勢(shì)。
命令式處理
我要首先探討一個(gè)常見問(wèn)題及其命令式解決方案。假如給定一個(gè)名稱列表,其中一些名稱包含一個(gè)字符。系統(tǒng)會(huì)要求您在一個(gè)逗號(hào)分隔的字符串中返回名稱,該字符串中不包含單字母的名稱,每個(gè)名稱的首字母都大寫。實(shí)現(xiàn)該算法的Java代碼如清單1所示。
清單1.命令式處理
public class TheCompanyProcess {
public String cleanNames(List<String> listOfNames) {
StringBuilder result = new StringBuilder();
for(int i = 0; i < listOfNames.size(); i++) {
if (listOfNames.get(i).length() > 1) {
result.append(capitalizeString(listOfNames.get(i))).append(",");
}
}
return result.substring(0, result.length() - 1).toString();
}
public String capitalizeString(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1, s.length());
}
}
由于您必須處理整個(gè)列表,解決清單1中問(wèn)題最簡(jiǎn)單的方式是使用一個(gè)命令式循環(huán)。對(duì)于每個(gè)名稱,都需要進(jìn)行檢查,確認(rèn)其長(zhǎng)度是否大于1,然后(如果長(zhǎng)度大于1)將首字母大寫的名稱附加到result字符串,并在后面加逗號(hào)。最終字符串中的最后一個(gè)名稱不應(yīng)包含逗號(hào),所以我將它從最后返回值中移走。
在命令式編程中,建議您在較低級(jí)上別執(zhí)行操作。在清單1中的cleanNames()方法中,我執(zhí)行了三個(gè)任務(wù):我篩選列表以消除單字符,將列表中每個(gè)名稱的首字母變換為大寫,然后將列表轉(zhuǎn)化為一個(gè)字符串。在命令式語(yǔ)言中,我不得不為三個(gè)任務(wù)都使用同一低級(jí)機(jī)制(對(duì)列表進(jìn)行迭代)。函數(shù)式語(yǔ)言將篩選、變換和轉(zhuǎn)化視為常見操作,因此它們提供給您從不同視角解決問(wèn)題的方式。
函數(shù)式處理
函數(shù)編程語(yǔ)言與命令式語(yǔ)言的問(wèn)題分類方式不同。篩選、變換和轉(zhuǎn)化邏輯類別表現(xiàn)為函數(shù)。那些函數(shù)實(shí)現(xiàn)低級(jí)變換并依賴于開發(fā)人員來(lái)編寫作為參數(shù)傳遞的函數(shù),進(jìn)而定制函數(shù)的行為。我可以用偽代碼將清單1中的問(wèn)題概念化為:
listOfEmps -> filter(x.length > 1) -> transform(x.capitalize) ->
convert(x, y -> x + "," + y)
利用函數(shù)式語(yǔ)言,您可以建模這一概念性解決方案,無(wú)需擔(dān)心實(shí)現(xiàn)細(xì)節(jié)。
Scala實(shí)現(xiàn)
清單2使用Scala實(shí)現(xiàn)清單1中的處理示例。它看起來(lái)就像是前面的偽代碼,包含必要的實(shí)現(xiàn)細(xì)節(jié)。
清單2.Scala處理
val employees = List("neal", "s", "stu", "j", "rich", "bob")
val result = employees
.filter(_.length() > 1)
.map(_.capitalize)
.reduce(_ + "," + _)
對(duì)于給定的名稱列表,我首先篩選它,剔除長(zhǎng)度不大于1的所有名稱。然后將該操作的輸出提供給map()函數(shù),該函數(shù)對(duì)集合的每個(gè)元素執(zhí)行所提供的代碼塊,返回變換后的集合。最后,來(lái)自map()的輸出集合流向reduce()函數(shù),該函數(shù)基于代碼塊中提供的規(guī)則將每個(gè)元素結(jié)合起來(lái)。
在本例中,我將每對(duì)元素結(jié)合起來(lái),用插入的逗號(hào)連接它們。我不必考慮三個(gè)函數(shù)調(diào)用中參數(shù)的名稱是什么,所以我可以使用方便的Scala快捷方式,也就是說(shuō),使用_跳過(guò)名稱。reduce()函數(shù)從前兩個(gè)元素入手,將它們結(jié)合成一個(gè)元素,成為下一個(gè)串接中的第一個(gè)元素。在“瀏覽”列表的同時(shí),reduce()構(gòu)建了所需的逗號(hào)分隔的字符串。
我首先展示Scala實(shí)現(xiàn)是因?yàn)槲覍?duì)它的語(yǔ)法比較熟悉,而且Scala分別為篩選、變換和轉(zhuǎn)化概念使用了行業(yè)通用的名稱,即filter、map和reduce。
Groovy實(shí)現(xiàn)
Groovy擁有相同的功能,但對(duì)它們進(jìn)行命名的方式與腳本語(yǔ)言(比如Ruby)更加一致。清單1中處理示例的Groovy版本如清單3所示。
清單3.Groovy處理
class TheCompanyProcess {
public static String cleanUpNames(List listOfNames) {
listOfNames
.findAll {it.length() > 1}
.collect {it.capitalize()}
.join(',')
}
}
盡管清單3在結(jié)構(gòu)上類似于清單2中的Scala示例,但方法名稱不同。Groovy的findAll集合方法應(yīng)用所提供的代碼塊,保留代碼塊為true的元素。如同Scala,Groovy包含一個(gè)隱式參數(shù)機(jī)制,為單參數(shù)代碼塊使用預(yù)定義的it隱式參數(shù)。collect方法(Groovy的map版本)對(duì)集合的每個(gè)元素執(zhí)行所提供的代碼塊。Groovy提供一個(gè)函數(shù)(join()),使用所提供的分隔符將字符串集合串聯(lián)為單一字符串,這正是本示例中所需要的。
Clojure實(shí)現(xiàn)
Clojure是一個(gè)使用reduce、map和filter函數(shù)名的函數(shù)式語(yǔ)言,如清單4所示。
清單4.Clojure處理示例
(defn process [list-of-emps]
(reduce str (interpose ","
(map clojure.string/capitalize
(filter #(< 1 (count %)) list-of-emps)))))
Clojure的thread-first宏
thread-last宏使集合的處理變得更加簡(jiǎn)單。類似的Clojure宏thread-first可簡(jiǎn)化與JavaAPI的交互。例如普遍的Java代碼語(yǔ)句person.getInformation().
getAddress().getPostalCode(),這體現(xiàn)了Java違反迪米特法則的傾向。這種類型的語(yǔ)句給Clojure編程帶來(lái)一些煩惱,迫使使用JavaAPI的開發(fā)人員不得不構(gòu)建由內(nèi)而外的語(yǔ)句,比如(getPostalCode(getAddress(getInformationperson)))。thread-first宏消除了這一語(yǔ)法困擾。您可以使用宏將嵌套調(diào)用編寫為(->persongetInformationgetAddressgetPostalCode),想嵌套多少層都可以。
如果您不習(xí)慣查看Clojure,可以使用清單4中的代碼,其結(jié)構(gòu)可能不夠清晰。Clojure這樣的Lisp是“由內(nèi)而外”進(jìn)行工作的,所以必須從最后的參數(shù)值list-of-emps著手。Clojure的(filter)函數(shù)接受兩個(gè)參數(shù):用于進(jìn)行篩選的函數(shù)(本例中為匿名函數(shù))和要篩選的集合。
您可以為第一個(gè)參數(shù)編寫一個(gè)正式函數(shù)定義,比如(fn[x](<1(countx))),但使用Clojure可以更簡(jiǎn)潔地編寫匿名函數(shù)。與前面的示例一樣,篩選操作的結(jié)果是一個(gè)較少的集合。(map)函數(shù)將變換函數(shù)接受為第一個(gè)參數(shù),將集合(本例中是(filter)操作的返回值)作為第二個(gè)參數(shù)。Clojure的(map)函數(shù)的第一個(gè)參數(shù)通常是開發(fā)人員提供的函數(shù),但接受單一參數(shù)的任何函數(shù)都有效;內(nèi)置capitalize函數(shù)也符合要求。
最后,(map)操作的結(jié)果成為了(reduce)的集合參數(shù)。(reduce)的第一個(gè)參數(shù)是組合函數(shù)(應(yīng)用于(interpose)的返回的(str))。(interpose)在集合的每個(gè)元素之間(除了最后一個(gè))插入其第一個(gè)參數(shù)。
當(dāng)函數(shù)嵌套過(guò)多時(shí),即使最有經(jīng)驗(yàn)的開發(fā)人員也會(huì)倍感頭疼,如清單4中的(process)函數(shù)所示。所幸的是,Clojure包含的宏支持您將結(jié)構(gòu)“調(diào)整”為更可讀的順序。清單5中的功能與清單4中的功能一樣。
清單5.使用Clojure的thread-last宏
(defn process2 [list-of-emps]
(->> list-of-emps
(filter #(< 1 (count %)))
(map clojure.string/capitalize)
(interpose ",")
(reduce str)))
Clojurethread-last宏采取對(duì)集合應(yīng)用各種變換的常見操作并顛倒典型的Lisp的順序,恢復(fù)了從左到右的更自然的閱讀方式。在清單5中,首先是(list-of-emps)集合。代碼塊中每個(gè)隨后的表單被應(yīng)用于前一個(gè)表單。Lisp的優(yōu)勢(shì)之一在于其語(yǔ)法靈活性:任何時(shí)候代碼的可讀性變得很差時(shí),您都可以將代碼調(diào)整回具有較高可讀性。
函數(shù)式編程的優(yōu)勢(shì)
在一篇標(biāo)題為“BeatingtheAverages”的著名文章中,PaulGraham定義了BlubParadox:他“編造”了一種名為Blub的虛假語(yǔ)言,并且考慮在其他語(yǔ)言與Blub之間進(jìn)行功能比較:
只要我們假想的Blub程序員往下看一連串功能,他就知道自己是在往下看。不如Blub功能強(qiáng)大的語(yǔ)言顯然不怎么強(qiáng)大,因?yàn)樗鼈內(nèi)鄙俪绦騿T習(xí)慣使用的一些功能。但當(dāng)我們假想的Blub程序員從另一個(gè)方向,也就是說(shuō),往上看一連串功能時(shí),他并沒(méi)有意識(shí)到自己在往上看。他看到的只不過(guò)是怪異的語(yǔ)言。他可能認(rèn)為它們?cè)诠δ苌吓cBlub幾近相同,只是多了其他難以理解的東西。Blub對(duì)他而言已經(jīng)足夠好,因?yàn)樗窃贐lub環(huán)境中可以思考問(wèn)題。
對(duì)于很多Java開發(fā)人員而言,清單2中的代碼看起來(lái)陌生而又奇怪,因此難以將它看作是有優(yōu)勢(shì)的代碼。但當(dāng)您停止過(guò)于細(xì)化任務(wù)執(zhí)行細(xì)節(jié)時(shí),就釋放了越來(lái)越智能的語(yǔ)言和運(yùn)行時(shí)的潛能,從而做出了強(qiáng)大的改進(jìn)。例如,JVM的到來(lái)(解除了開發(fā)人員的內(nèi)存管理困擾)為先進(jìn)垃圾回收的創(chuàng)建開辟了全新的研發(fā)領(lǐng)域。使用命令式編碼時(shí),您深陷于迭代循環(huán)的細(xì)節(jié),難以進(jìn)行并行性等優(yōu)化。從更高的層面思考操作(比如filter、map和reduce)可將概念與實(shí)現(xiàn)分離開來(lái),將并行性等修改從一項(xiàng)復(fù)雜、詳細(xì)的任務(wù)轉(zhuǎn)變?yōu)橐粋€(gè)簡(jiǎn)單的API更改。
想一想如何將清單1中的代碼變?yōu)槎嗑€程代碼。由于您密切參與了for循環(huán)期間發(fā)生的細(xì)節(jié),所以您還必須處理煩人的并發(fā)代碼。然后思考一下清單6所示的Scala并行版本。
清單6.實(shí)現(xiàn)進(jìn)程并行性
val parallelResult = employees
.par
.filter(f => f.length() > 1)
.map(f => f.capitalize)
.reduce(_ + "," + _)
清單2與清單6之間惟一的差別在于,將.par方法添加到了命令流中。.par方法返回后續(xù)操作依據(jù)的集合的并行版本。由于我將對(duì)集合的操作指定為高階概念,所以底層運(yùn)行時(shí)可以自由地完成更多的工作。
面向命令式對(duì)象的開發(fā)人員往往會(huì)考慮使用重用類,因?yàn)樗麄兊恼Z(yǔ)言鼓勵(lì)將類作為構(gòu)建塊。函數(shù)編程語(yǔ)言傾向于重用函數(shù)。函數(shù)式語(yǔ)言構(gòu)建復(fù)雜的通用功能(比如filter()、map()和reduce())并通過(guò)作為參數(shù)提供的函數(shù)來(lái)實(shí)現(xiàn)定制。在函數(shù)式語(yǔ)言中,將數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為列表和映射等標(biāo)準(zhǔn)集合是很尋常的事,因?yàn)樗鼈兘又涂梢员粡?qiáng)大的內(nèi)置函數(shù)所操控。
例如,在Java環(huán)境中存在許多XML處理框架,每個(gè)框架都封裝自己的私有版本的XML結(jié)構(gòu),并通過(guò)自己的方法交付它。在Clojure這樣的語(yǔ)言中,XML被轉(zhuǎn)換為基于映射的標(biāo)準(zhǔn)數(shù)據(jù)結(jié)構(gòu),該結(jié)構(gòu)對(duì)已經(jīng)存在于語(yǔ)言中的強(qiáng)大的變換、約簡(jiǎn)和篩選操作開放。
結(jié)束語(yǔ)
所有現(xiàn)代語(yǔ)言都包含或添加了函數(shù)式編程結(jié)構(gòu),使函數(shù)式編程成為未來(lái)開發(fā)中不可或缺的一部分。Java下一代語(yǔ)言都實(shí)現(xiàn)了強(qiáng)大的函數(shù)式功能,有時(shí)使用不同的名稱和行為。在本期中,我介紹了Scala、Groovy和Clojure中的一種新編碼風(fēng)格并展示了一些優(yōu)勢(shì)。
以上就是關(guān)于千鋒扣丁學(xué)堂Java培訓(xùn)之了解java函數(shù)式編碼結(jié)構(gòu)及優(yōu)勢(shì)的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,
希望對(duì)大家的學(xué)習(xí)有所幫助,想要了解更多關(guān)于Java開發(fā)方面內(nèi)容的小伙伴,請(qǐng)關(guān)注扣丁學(xué)堂Java培訓(xùn)官網(wǎng)、微信等平臺(tái),扣丁學(xué)堂IT職業(yè)在線學(xué)習(xí)教育有專業(yè)的Java講師為您指導(dǎo),此外扣丁學(xué)堂老師精心推出的Java視頻教程定能讓你快速掌握J(rèn)ava從入門到精通開發(fā)實(shí)戰(zhàn)技能??鄱W(xué)堂Java技術(shù)交流群:850353792。
【關(guān)注微信公眾號(hào)獲取更多學(xué)習(xí)資料】 【掃碼進(jìn)入HTML5前端開發(fā)VIP免費(fèi)公開課】
查看更多關(guān)于“Java開發(fā)資訊”的相關(guān)文章>>
標(biāo)簽:
Java培訓(xùn)
Java視頻教程
Java多線程
Java面試題
Java學(xué)習(xí)視頻
Java開發(fā)