国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

首頁 php教程 PHP源碼 如何讀懂並寫出裝逼的函數(shù)式程式碼

如何讀懂並寫出裝逼的函數(shù)式程式碼

Nov 12, 2016 am 10:17 AM

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

如何讀懂並寫出裝逼的函數(shù)式程式碼

先看程式碼

這個程式碼平淡無奇,就是從一個陣列中找到一個數(shù),O(n)的演算法,找不到就回傳 null。

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

//正常的版本
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))

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

//函數(shù)式的版本
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的箭頭函數(shù)



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

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

下面是一些範例:

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 的例子都把這箭頭函數(shù)賦值給了一個變量,於是它就有了一個名字。有時候,某些函數(shù)在宣告的時候就是在呼叫的時候,尤其是在函數(shù)式程式設計中,一個函數(shù)還對外傳回函數(shù)的時候。例如下在這個例子:

function MakePowerFn(power) {
  return function PowerFn(base) {
    return Math.pow(base, power);
  } 
}
power3 = MakePowerFn(3); //制造一個X的3次方的函數(shù)
power2 = MakePowerFn(2); //制造一個X的2次方的函數(shù)
console.log(power3(10)); //10的3次方 = 1000
console.log(power2(10)); //10的2次方 = 100

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

function MakePowerFn(power) {
  return function(base) {
    return Math.pow(base, power);
  } 
}

如果用箭頭函數(shù),可以寫成:

rereee

reee更簡潔(如果用表達式的話,就不需要{ 和}, 以及return 語句):

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

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

MakePowerFn = power  => {
  return base => {
    return Math.pow(base, power);
  } 
}

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


函數(shù)式程式設計立志於用函數(shù)表達式消除有狀態(tài)的函數(shù),以及for/while循環(huán),所以,在函數(shù)式程式設計的世界裡是不應該用for/while循環(huán)的,而要改用遞歸(遞歸的效能很差,所以,一般是用尾遞歸來做優(yōu)化,也就是把函數(shù)的計算的狀態(tài)當成參數(shù)一層一層的往下傳遞,這樣語言的編譯器或解釋器就不需要用函數(shù)堆疊來幫你保存函數(shù)的內部變數(shù)的狀態(tài)了)。

好了,那麼,匿名函數(shù)的遞歸該怎麼做?

一般來說,遞歸的程式碼就是函數(shù)自己呼叫自己,例如我們求階乘的程式碼:

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

在匿名函數(shù)下,這個遞歸該怎麼寫呢?對匿名函數(shù)來說,我們可以把匿名函數(shù)當成一個參數(shù)傳給另一個函數(shù),因為函數(shù)的參數(shù)有名字,所以就可以呼叫自己了。 如下:

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

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

function combinator(func) {
  func(func);
}


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

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

(func) => (func(func))

然後,我們再把上面這個版本變成箭頭函數(shù)的匿名函數(shù)版:var?fact?=?(func,?n)?=>?(?n==0???1?:??n?*?func(func,?n-1)?)

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

在這裡,我們仍可使用這個一個一個匿名來保存這個一個函數(shù),我們繼續(xù),我們要讓匿名函數(shù)宣告的時候,就自己呼叫自己。

也就是說,我們要把

fact(fact, 5)

這個函數(shù)當成呼叫參數(shù),傳給下面這個函數(shù):

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


最後我們得到下面的程式碼:

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


最後我們得到下面的程式碼:

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

最後我們得到下面的程式碼:

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


最後我們得到下面的程式碼:

fact = HighOrderFact(HighOrderFact);
fact(5);
看懂了嗎?沒事,我們繼續(xù)。

動用高階函數(shù)的遞歸

????????但是上面這個遞歸的匿名函數(shù)在自己調用自己,所以,程式碼中有hard code的實參。我們想實參去掉,如何去掉呢?我們可以參考前面說的那個 MakePowerFn 的例子,但這回是遞歸版的高階函數(shù)了。 ??????
HighOrderFact ( HighOrderFact ) ( 5 )
??我們可以看,上面的程式碼簡單說來就是,需要一個函數(shù)做參數(shù),然後回傳這個函數(shù)的遞歸版本。那麼,我們要怎麼調用呢? ??????
fact = HighOrderFact(HighOrderFact);
fact(5);

連起來寫就是:

HighOrderFact ( HighOrderFact ) ( 5 )

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

fact = function ( hifunc ) {
  return hifunc ( hifunc );
} (
  //調用參數(shù)是一個函數(shù)
  function (func) { 
    return function(n){
      return n==0 ? 1 : n * func(func)(n-1);
    };
  }
);
fact(5); //于是我們就可以直接使用了

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

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

上面就是我們最終版的階乘的函數(shù)式代碼。
回顧之前的程序

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

//正常的版本
function find (x, y) {
  for ( let i = 0; i < x.length; i++ ) {
    if ( x[i] == y ) return i;
  }
  return null;
}

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

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

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

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

最后,引入高階函數(shù),去除實參:

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

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

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

現(xiàn)在,你可以體會到,如此逼裝的是怎么來的了吧?。
其它

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

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發(fā)現(xiàn)涉嫌抄襲或侵權的內容,請聯(lián)絡admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)