lambda表達式雜談
var personInfo = [ { name: "張三", age: 20, gender: "male" },n { name: "李四", age: 18, gender: "male" },n { name: "王五", age: 19, gender: "male" },n { name: "春香", age: 17, gender: "female" },n { name: "夏香", age: 21, gender: "female" },n { name: "秋香", age: 22, gender: "female" },n { name: "冬香", age: 18, gender: "femalt" }n]n
上面的數據存放著一組人員姓名、年齡、性別相關的信息。 現在有一個需求, 獲得年齡20歲以上,性別為女的人的姓名。 看到需求後, 很多人腦袋中產生的解決方案可能是這樣的
var result = [];n for(var i = 0; i < personInfo.length; i++) {n var item = personInfo[i];n if( item.age >= 20 && item.gender == "female") {n result2.push(item.name);n }n }n console.log(result);n
代碼執行的結果是[「夏香」,「秋香」]
很好,結果正確,考試的話肯定給滿分。
但是這種實現方法有點過時,不瀟洒,現在流行的做法應該是這樣子
let result = personInfo.filter(item=>item.age >= 20 && item.gender == female)n .map(item=> item.name);n console.log(result);n
看, 一樣的實現了要求,代碼卻簡潔了許多。如果把代碼的實現方式和我們平時的穿著來做對比, 這種實現方式就像是走時尚潮流尖端的穿著打扮,比如說電視里走在t台上的模特的穿著,因為這是es6的語法,由於要兼容老式瀏覽器的緣故,很多程序員不敢明目張胆的使用。
而前一種方法就像是上世紀的中山裝,你穿出去沒問題,但肯定會被人家說是土鱉。
更一般的符合大眾審美的寫法是這樣子的
var result = personInfo.filter(function(item){n return item.age >= 20 && item.gender == female;n }).map(function(item){n return item.name;n });n console.log(result);n
這種寫法和第一種寫法思路是相同的, 不同點在於,第一種方法使用的是lamda表達式,而這種方法使用的是匿名函數。
事實上,匿名函數在功能上和lambda表達式是沒有差別的,只是叫法不一樣,此外,lambda表達式寫法更加簡潔一些。
在javascript中,lamda表達式可以這樣寫
()=>{}
而匿名函數要這樣寫
function(){return;}
lambda表達式相對於匿名函數可以省略function和return兩個關鍵字。
lambda的原理就是把函數當做另一個函數的參數來使用,以及不用聲明這個函數,使用的時候可以直接寫。
這一點我們的老前輩c語言和c++11之前的c++就做不到,在這兩位前輩的世界裡,要把函數當做參數傳遞需要使用函數指針, 使用前要先聲明函數
int callback(int val) {n return ++val;n}nnvoid test(int (*cb)(int)) {n int result = cb(100);n cout << result << endl;n}nnint main() {n test(callback);n return 0;n}n
test函數接受一個函數類型參數,callback函數就被作為這個參數傳遞。 這種寫法沒什麼不好的,就是不夠飄逸,就像金庸小說里的武功,這寫法如同少林寺的七十二絕技,威力巨大, 卻樸實無華,沒有如凌波微步、北冥神功、天三六陽掌、天山折梅手這些武功那樣吸引觀眾的眼球。
然而, 我們不能怪c和c++不夠華麗, 畢竟這兩門語言是上古時期的語言,是現在主流計算機語言們的祖宗, 從那個時代的角度去看待這樣語言特性,已經是非常先進了。
就連之後的後起之秀java也沒有做到這樣好, java發展出了另一種喪心病狂的模式。 java8之前沒有函數指針,沒有匿名函數,更加沒有lambda表達式。 在java中要實現這種編程模式只能使用類,可以是具名類, 也可以是匿名類, 在java的世界裡,函數不能獨立存在,它必須依賴於類。
於是,要把一個函數當參數傳遞, 只能傳遞一個類實例,這個類中再帶上這個函數。
代碼像這個樣子
abstract class OnClickListener{npublic void onClick() {n}n}nnprotected void onCreate(Bundle savedInstanceState) { n btn.setOnClickListener(onclick); n} nOnClickListener onclick = new OnClickListener(){ n @Override n public void onClick(View v) { n//do somethingn } n}; n
或者這樣
abstract class OnClickListener{npublic void onClick() {n}n}nprotected void onCreate(Bundle savedInstanceState) { n btn.setOnClickListener(new OnClickListener(){ n @Override n public void onClick(View v) { n //do somethingn } n }); n} n
原本幾行代碼可以搞定的事情卻被寫成了幾十行,而且全是毫無意義的儀式性代碼。怪不得人家常說java是用來寫大型軟體的,尼瑪,幾行代碼硬生生被寫成了幾十行,這種寫法能不大型嗎
後來的java8支持lambda表達式,改善了這個問題,使java代碼簡潔了不少,但是那已經是java的第一個版本發布20年以後的事情了, 在這二十年里,不知道讓多少java程序員寫了多少無用的代碼, 做了多少無用功。 但是誰叫它牛逼呢, 常年佔據20%的編程語言份額, 死皮賴臉不知上進也沒什麼,吃老本也餓不死。 看看人家c#, 夠上進吧, 還不是連自己的腰都摸不到。
說到這裡,不得不提一樣我們親愛的世界上最好的語言php,php有匿名函數,但是這個匿名函數算不算lambda表達式呢?從廣義的角度去理解,應該算,因為匿名函數和lambda表達式能實現同樣的功能。從狹義的角度理解,因該不算,lambda表達式講究的是飄逸瀟洒,匿名函數太臃腫了,如果自認是lambda表達式, 我都替他不好意思。
php中的lambda表達式是這樣寫的
function test($callback) {n$callback();n}nnfunction main() {n $data = [1,2,3,4,5];ntest(function() use($data){n//dosomethingn})n}n
main函數調用test函數傳遞的就是匿名函數(lambda表達式),這種寫法和老式的javascript一樣,雖然不優雅,但也挺和諧的。
且慢,這個匿名函數為什麼帶一個use關鍵字, 這個use關鍵字是什麼鬼?
話說php的匿名函數帶個use關鍵字這事吧, 我覺得有好有壞。 php的匿名函數默認是無法訪問匿名函數自身作用域除this關鍵字引用的類實例以外的變數的,從上面的示例來說, 如果不使用use關鍵字,匿名函數中的代碼將無法訪問$data變數。 use關鍵字的作用就是導入這個變數,使匿名函數可以訪問它。 所以, 使用不方便,就是use關鍵字的壞處, 函數用個變數, 還要特定的關鍵字導入, 坑了個爹。
然而,凡事要看兩面,使用use關鍵字也能帶來好處。用了use關鍵字,我們一眼就能看出這個匿名函素用了哪些外部變數,這對代碼的可維護性起到一定的正面作用。不過就我個人來講,還是非常討厭這個關鍵字的,我要的是方便,可維護性什麼的, 一邊玩去,哥揍是這麼任性。
最後呢,我們聊一聊lambda表達式的集大成者, kotlin。這門語言想必認識的人不多,用的人更少,不過它親爹做的產品程序員們肯定是家喻戶曉的, 如intellij idea、phpstorm、webstorm這些知名IDE都是, 這可都是除visual studio以外最好的集成開發環境了。至於它的乾爹,想必知道的人更多,google,成立不到二十年,市值超過微軟,智能手機系統安卓就是他家的,可惜他家的拳頭產品google搜索引擎,在我們天朝無法使用。
kotlin的lambda表達式可以這樣寫
{}
就一個花括弧,連常規的lambda表達式的括弧和箭頭都省了。 上面那個臃腫的java匿名類,換成kotlin就是這樣子
protected void onCreate(Bundle savedInstanceState) { n btn.setOnClickListener({n//dosomethingn }); n} n
代碼量瞬間減少了90%
有的同學要問了,這個lambda表達式如何接受參數
你可以這樣
protected void onCreate(Bundle savedInstanceState) { n btn.setOnClickListener({ item ->n//dosomethingn }); n} n
這個箭頭, 不用的時候可以省略,用的時候可以帶上,多麼人性化。
所以,綜上所述,論lambda表達式的易用性,kotlin排第二, 就沒語言敢排第一。 另外,程序員在編程的時候, 一定要多用lambda,因為這是一種提高生產力的有力工具, 畢竟現在是二十一世紀了,要是再寫上世紀80年代的代碼,我都替他感到丟人。
推薦閱讀: