日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何读懂并写出装逼的函数式代码

發布時間:2025/3/21 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何读懂并写出装逼的函数式代码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天在微博上看到了 有人分享了下面的這段函數式代碼,我把代碼貼到下面,不過我對原來的代碼略有改動,對于函數式的版本,咋一看,的確令人非常費解,仔細看一下,你可能就暈掉了,似乎完全就是天書,看上去非常裝逼,哈哈。不過,我感覺解析那段函數式的代碼可能會一個比較有趣過程,而且,我以前寫過一篇《函數式編程》的入門式的文章,正好可以用這個例子,再升華一下原來的那篇文章,順便可以向大家更好的介紹很多基礎知識,所以寫下這篇文章。

先看代碼

這個代碼平淡無奇,就是從一個數組中找到一個數,O(n)的算法,找不到就返回 null。

下面是正常的 old-school 的方式。不用多說。

1 2 3 4 5 6 7 8 9 10 11 //正常的版本 function find (x, y) { ??for (?let i = 0; i < x.length; i++ ) { ????if ( x[i] == y )?return i; ??} ??return null; } let arr = [0,1,2,3,4,5] console.log(find(arr, 2)) console.log(find(arr, 8))

結果到了函數式成了下面這個樣子(好像上面的那些代碼在下面若影若現,不過又有點不太一樣,為了消掉if語言,讓其看上去更像一個表達式,動用了 ? 號表達式):

1 2 3 4 5 6 7 8 9 10 11 //函數式的版本 const find = ( f => f(f) ) ( f => ??(next => (x, y, i = 0) => ????( i >= x.length) ???null : ??????( x[i] == y ) ? i : ????????next(x, y, i+1))((...args) => ??????????(f(f))(...args))) let arr = [0,1,2,3,4,5] console.log(find(arr, 2)) console.log(find(arr, 8))

為了講清這個代碼,需要先補充一些知識。

Javascript的箭頭函數

首先先簡單說明一下,ECMAScript2015 引入的箭頭表達式。箭頭函數其實都是匿名函數,其基本語法如下:

(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression ?????// 等于 :? => { return expression; } // 只有一個參數時,括號才可以不加: (singleParam) => { statements } singleParam => { statements } //如果沒有參數,就一定要加括號: () => { statements }

下面是一些示例:

1 2 3 4 5 6 7 8 9 10 11 12 var simple = a => a > 15 ? 15 : a; simple(16);?// 15 simple(10);?// 10 let max = (a, b) => a > b ? a : b; // Easy array filtering, mapping, ... var arr = [5, 6, 13, 0, 1, 18, 23]; var sum = arr.reduce((a, b) => a + b);??// 66 var even = arr.filter(v => v % 2 == 0);?// [6, 0, 18] var double = arr.map(v => v * 2);???????// [10, 12, 26, 0, 2, 36, 46]

看上去不復雜吧。不過,上面前兩個 simple 和 max 的例子都把這箭頭函數賦值給了一個變量,于是它就有了一個名字。有時候,某些函數在聲明的時候就是調用的時候,尤其是函數式編程中,一個函數還對外返回函數的時候。比如下在這個例子:

1 2 3 4 5 6 7 8 9 10 11 function MakePowerFn(power) { ??return function PowerFn(base) { ????return Math.pow(base, power); ??} } power3 = MakePowerFn(3);?//制造一個X的3次方的函數 power2 = MakePowerFn(2);?//制造一個X的2次方的函數 console.log(power3(10));?//10的3次方 = 1000 console.log(power2(10));?//10的2次方 = 100

其實,在 MakePowerFn 函數里的那個 PowerFn 根本不需要命名,完全可以寫成:

1 2 3 4 5 function MakePowerFn(power) { ??return function(base) { ????return Math.pow(base, power); ??} }

如果用箭頭函數,可以寫成:

1 2 3 4 5 MakePowerFn = power? => { ??return base => { ????return Math.pow(base, power); ??} }

我們還可以寫得更簡潔(如果用表達式的話,就不需要 { 和 }, 以及 return 語句 ):

1 MakePowerFn = power => base => Math.pow(base, power)

我還是加上括號,和換行可能會更清楚一些:

1 2 3 MakePowerFn = (power) => ( ??(base) => (Math.pow(base, power)) )

好了,有了上面的知識,我們就可以進入一個更高級的話題——匿名函數的遞歸。

匿名函數的遞歸

函數式編程立志于用函數表達式消除有狀態的函數,以及for/while循環,所以,在函數式編程的世界里是不應該用for/while循環的,而要改用遞歸(遞歸的性能很差,所以,一般是用尾遞歸來做優化,也就是把函數的計算的狀態當成參數一層一層的往下傳遞,這樣語言的編譯器或解釋器就不需要用函數棧來幫你保存函數的內部變量的狀態了)。

好了,那么,匿名函數的遞歸該怎么做?

一般來說,遞歸的代碼就是函數自己調用自己,比如我們求階乘的代碼:

1 2 3 4 function fact(n){ ??return n==0 ? 1 :? n * fact(n-1); }; result = fact(5);

在匿名函數下,這個遞歸該怎么寫呢?對于匿名函數來說,我們可以把匿名函數當成一個參數傳給另外一個函數,因為函數的參數有名字,所以就可以調用自己了。 如下所示:

1 2 3 function combinator(func) { ??func(func); }

這個是不是有點作弊的嫌疑?Anyway,我們再往下,把上面這個函數整成箭頭函數式的匿名函數的樣子。

1 (func) => (func(func))

現在你似乎就不像作弊了吧。把上面那個求階乘的函數套進來是這個樣子:

首先,先重構一下fact,把fact中自己調用自己的名字去掉:

1 2 3 4 5 function fact(func, n) { ??return n==0 ? 1 :? n * func(func, n-1); } fact(fact, 5);?//輸出120

然后,我們再把上面這個版本變成箭頭函數的匿名函數版:

1 2 var fact = (func, n) => ( n==0 ? 1 :? n * func(func, n-1) ) fact(fact, 5)

這里,我們依然還要用一個fact來保存這個匿名函數,我們繼續,我們要讓匿名函數聲明的時候,就自己調用自己。

也就是說,我們要把

1 (func, n) => ( n==0 ? 1 :? n * func(func, n-1) )

這個函數當成調用參數,傳給下面這個函數:

1 (func, x) => func(func, x)

最終我們得到下面的代碼:

1 2 3 4 ( (func, x) => func(func, x) ) (??//函數體 ??(func, n) => ( n==0 ? 1 :? n * func(func, n-1) ),?//第一個調用參數 ??5?//第二調用參數 );

好像有點繞,anyway, 你看懂了嗎?沒事,我們繼續。

動用高階函數的遞歸

但是上面這個遞歸的匿名函數在自己調用自己,所以,代碼中有hard code的實參。我們想實參去掉,如何去掉呢?我們可以參考前面說過的那個 MakePowerFn 的例子,不過這回是遞歸版的高階函數了。

1 2 3 4 5 HighOrderFact =?function(func){ ??return function(n){ ????return n==0 ? 1 : n * func(func)(n-1); ??}; };

我們可以看,上面的代碼簡單說來就是,需要一個函數做參數,然后返回這個函數的遞歸版本。那么,我們怎么調用呢?

1 2 fact = HighOrderFact(HighOrderFact); fact(5);

連起來寫就是:

1 HighOrderFact ( HighOrderFact ) ( 5 )

但是,這樣讓用戶來調用很不爽,所以,以我們一個函數把?HighOrderFact ( HighOrderFact )?給代理一下:

1 2 3 4 5 6 7 8 9 10 11 12 fact =?function ( hifunc ) { ??return hifunc ( hifunc ); } ( ??//調用參數是一個函數 ??function (func) { ????return function(n){ ??????return n==0 ? 1 : n * func(func)(n-1); ????}; ??} ); fact(5);?//于是我們就可以直接使用了

用箭頭函數重構一下,是不是簡潔了一些?

1 2 3 fact = (highfunc => highfunc ( highfunc ) ) ( ??func => n =>? n==0 ? 1 : n * func(func)(n-1) );

上面就是我們最終版的階乘的函數式代碼。

回顧之前的程序

我們再來看那個查找數組的正常程序:

1 2 3 4 5 6 7 //正常的版本 function find (x, y) { ??for (?let i = 0; i < x.length; i++ ) { ????if ( x[i] == y )?return i; ??} ??return null; }

先把for干掉,搞成遞歸版本:

1 2 3 4 5 function find (x, y, i=0) { ??if ( i >= x.length )?return null; ??if ( x[i] == y )?return i; ??return find(x, y, i+1); }

然后,寫出帶實參的匿名函數的版本(注:其中的if代碼被重構成了 ?號表達式):

1 2 3 4 5 6 7 8 ( (func, x, y, i) => func(func, x, y, i) ) (??//函數體 ??(func, x, y, i=0) => ( ??????i >= x.length ???null : ?????????x[i] == y? ?? i : func (func, x, y, i+1) ??),?//第一個調用參數 ??arr,?//第二調用參數 ??2?//第三調用參數 )

最后,引入高階函數,去除實參:

1 2 3 4 5 6 const find = ( highfunc => highfunc( highfunc ) ) ( ???func => (x, y, i = 0) => ( ?????i >= x.length ???null : ???????????x[i] == y? ?? i : func (func) (x, y, i+1) ???) );

注:函數式編程裝逼時一定要用const字符,這表示我寫的函數里的狀態是 immutable 的,天生驕傲!

再注:我寫的這個比原來版的那個簡單了很多,原來版本的那個又在函數中套了一套 next, 而且還動用了不定參數,當然,如果你想裝逼裝到天上的,理論上來說,你可以套N層,呵呵。

現在,你可以體會到,如此逼裝的是怎么來的了吧?

其它

你還別說這就是裝逼,簡單來說,我們可以使用數學的方式來完成對復雜問題的描述,那怕是遞歸。其實,這并不是新鮮的東西,這是Alonzo Church 和 Haskell Curry 上世紀30年代提出來的東西,這個就是 Y Combinator 的玩法,關于這個東西,你可以看看下面兩篇文章:

《The Y Combinator (Slight Return)》,

《Wikipedia: Fixed-point combinator》

(全文完)

from:?http://coolshell.cn/articles/17524.html

總結

以上是生活随笔為你收集整理的如何读懂并写出装逼的函数式代码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 久久99精品久久久久子伦 | 黄频在线观看 | 久久精品国产亚洲av久 | 久久国产美女视频 | 欧美大片在线播放 | 亚洲激情区 | 九色视频在线观看 | 国产精品一区二区三区四区视频 | 521a人成v香蕉网站 | 日韩欧美久久 | 五月花成人网 | 桃色91| 欧美黄色网络 | 男女黄色录像 | sesese99| av手机免费在线观看 | 国产中文一区二区三区 | 一级美女视频 | 深爱婷婷网 | 午夜激情福利在线 | 亚洲视频在线观看一区二区三区 | 欧美丝袜一区二区三区 | 中国一极毛片 | 黄色小视频在线播放 | 成人小视频在线观看 | 亚洲国产视频一区二区 | 涩涩视频网 | 国产自产一区二区 | 操伊人| 成人激情社区 | avtt2015 | 久久久久久九九九九 | 91麻豆产精品久久久久久夏晴子 | 天天干天天操天天拍 | 久久色在线观看 | 网红福利视频 | 免费在线视频你懂的 | 久久精品成人 | 精品无码在线视频 | 国内精品国产三级国产aⅴ久 | 在线观看黄色av网站 | 69堂精品 | 91九色在线观看 | 前任攻略在线观看免费完整版 | 日本欧美韩国国产精品 | 欧美肉大捧一进一出免费视频 | 一级做a爱片久久毛片 | 骚婷婷 | 国产男男gay | 久久国内精品视频 | 欧美成人高清视频 | 四川少妇xxx奶大xxx | 成年人福利网站 | 日韩欧美中文一区 | 91在线日韩| 99er在线观看| 亚洲不卡在线 | 国产一区二区啪啪啪 | 在线天堂视频 | 国产精品久久久久久中文字 | 伊人网址 | 中文字幕黑人 | 99成人国产精品视频 | 法国空姐在线观看免费 | 99热手机在线观看 | 岛国大片在线 | 欧美性videos高清精品 | 日韩成人一区二区 | 9i免费看片黄| 天天想你在线观看完整版电影免费 | 久久九九久久九九 | 国产日产精品一区 | 欧美1区| 欧美mv日韩mv国产网站 | 天堂av资源 | 国产在线一区二区 | 在线h网 | 久久久穴 | 九热精品| 天天综合天天色 | 给我看高清的视频在线观看 | 国产午夜三级 | 手机av网 | 成人小视频免费在线观看 | 欧洲综合色 | 亚洲三级影院 | 欧美不卡视频在线观看 | 精品人妻av一区二区三区 | 91av免费看 | 99国内揄拍国内精品人妻免费 | 国产精品扒开腿做爽爽 | 人妻无码一区二区三区四区 | 亚洲自拍三区 | 成人免费看av | 69xxx免费视频 | 精品一区91 | 伊人久综合| 亚洲天堂中文字幕在线观看 | 国产精品国产三级国产三级人妇 |