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

ホームページ php教程 PHP源碼 クールな関數(shù)コードを読み書(shū)きする方法

クールな関數(shù)コードを読み書(shū)きする方法

Nov 12, 2016 am 10:17 AM

今日、Weibo で次の機(jī)能コードを共有している人を見(jiàn)かけました。私は元のコードを少し変更しましたが、一見(jiàn)したところ、それは確かに非常に印象的です。よく見(jiàn)ると気絶してしまいます。まるで天國(guó)からの完全な本のようです(笑)。しかし、その関數(shù)コードを解析することは、より興味深いプロセスである可能性があると感じています。また、以前に「関數(shù)型プログラミング」の入門(mén)記事を書(shū)きましたが、この例を元の記事に昇華することができます?;A(chǔ)知識(shí)をたくさん紹介したほうが良いと思い、この記事を書(shū)きました。

クールな関數(shù)コードを読み書(shū)きする方法

まずコードを見(jiàn)てみましょう

このコードは、配列から數(shù)値を見(jiàn)つけるための O(n) アルゴリズムです。見(jiàn)つからない場(chǎng)合は、null を返します。

以下は通常の昔ながらの方法です。言うまでもなく。

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

その結(jié)果、関數(shù)式は次のようになります(上記のコードが下にうっすら見(jiàn)えているように見(jiàn)えますが、少し異なります。if言語(yǔ)を削除して式のように見(jiàn)せるには、次を使用します) ? 式):

//函數(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))

このコードを明確に説明するには、まずいくつかの知識(shí)を追加する必要があります。
JavaScriptのアロー関數(shù)



まずはECMAScript2015で導(dǎo)入されたアロー式について簡(jiǎn)単に説明します。アロー関數(shù)は実際には匿名関數(shù)であり、その基本的な構(gòu)文は次のとおりです:

(param1, param2, …, paramN) => { statements } 
(param1, param2, …, paramN) => expression
     // 等于 :  => { return expression; } 
// 只有一個(gè)參數(shù)時(shí),括號(hào)才可以不加:
(singleParam) => { statements }
singleParam => { statements }
//如果沒(méi)有參數(shù),就一定要加括號(hào):
() => { 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]

複雑そうには見(jiàn)えません。ただし、上記の最初の 2 つの単純な例と最大の例は両方ともアロー関數(shù)を変數(shù)に割り當(dāng)てているため、名前が付いています。特に関數(shù)プログラミングでは、関數(shù)が外部関數(shù)も返す場(chǎng)合、特定の関數(shù)が宣言されたときに呼び出されることがあります。たとえば、この例では、

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

実際、MakePowerFn 関數(shù)內(nèi)の PowerFn には名前を付ける必要はまったくありません。

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

アロー関數(shù)を使用する場(chǎng)合は、次のように記述できます。

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

さらに簡(jiǎn)潔に書(shū)くこともできます (式を使用する場(chǎng)合は、{ と } と return ステートメントは必要ありません):

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

ここでもかっこを追加しますが、改行するとわかりやすくなるかもしれません:

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

さて、上記の知識(shí)があれば、より高度なトピックである匿名関數(shù)の再帰に入ることができます。
匿名関數(shù)の再帰


関數(shù)型プログラミングは、関數(shù)式を使用したステートフル関數(shù)と for/while ループを排除することを目的としています。そのため、関數(shù)型プログラミングの世界では、for/while ループを使用するべきではありません。再帰のパフォーマンスは非常に低いため、一般に末尾再帰は最適化に使用されます。つまり、関數(shù)の計(jì)算ステータスがパラメーターとしてレイヤーごとに渡されるため、言語(yǔ)コンパイラーやインタープリターは関數(shù)スタックを使用する必要がありません。関數(shù)の內(nèi)部変數(shù)の狀態(tài)を保存するのに役立ちます)。

それでは、匿名関數(shù)の再帰をどのように行うのでしょうか?

一般に、再帰的コードとは、関數(shù)がそれ自體を呼び出すことを意味します。たとえば、階乗を見(jiàn)つけるためのコード:

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

匿名関數(shù)の下でこの再帰を記述するにはどうすればよいですか?匿名関數(shù)の場(chǎng)合、匿名関數(shù)をパラメータとして別の関數(shù)に渡すことができます。関數(shù)のパラメータには名前があるため、自分自身を呼び出すことができます。 以下に示すように:

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

これは少し不正行為の疑いがありますか?とにかく、さらに進(jìn)んで、上記の関數(shù)をアロー関數(shù)スタイルの無(wú)名関數(shù)に変換しましょう。

(func) => (func(func))


今のところ、あなたは浮気しているようには見(jiàn)えません。上記の階乗関數(shù)を挿入すると次のようになります:

まず、ファクトを再構(gòu)築し、ファクト內(nèi)で自分自身と呼ぶ名前を削除します:

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

次に、上記のバージョンをアロー関數(shù)に変換します。 匿名関數(shù)のバージョン: var fat = (func, n) => ( n==0 ? 1 : n * func(func, n-1) )

fact(fact, 5)

ここでは、この匿名関數(shù)を保存するためにまだファクトを使用する必要があります。続けましょう。匿名関數(shù)が宣言されたときにそれ自體を呼び出すようにしたい。

言い換えると、関數(shù)

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

を呼び出しパラメータとして扱い、それを次の関數(shù)に渡す必要があります:

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


最後に、次のコードを取得します:

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

とにかく複雑です、わかりますか?大丈夫、続けましょう。
高階関數(shù)の再帰を使用する


しかし、上記の再帰匿名関數(shù)はそれ自體を呼び出すため、コード內(nèi)にはハードコードの実際のパラメータが存在します。実際のパラメータを削除したいのですが、どうすれば削除できますか?前述の MakePowerFn の例を參照できますが、今回は高階関數(shù)の再帰バージョンです。

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

上記のコードは単にパラメーターとして関數(shù)を必要とし、この関數(shù)の再帰バージョンを返すことがわかります。では、それを何と呼ぶのでしょうか?

fact = HighOrderFact(HighOrderFact);
fact(5);

連起來(lái)寫(xiě)就是:

HighOrderFact ( HighOrderFact ) ( 5 )

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

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

用箭頭函數(shù)重構(gòu)一下,是不是簡(jiǎn)潔了一些?

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

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

我們?cè)賮?lái)看那個(gè)查找數(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);
}

然后,寫(xiě)出帶實(shí)參的匿名函數(shù)的版本(注:其中的if代碼被重構(gòu)成了 ?號(hào)表達(dá)式):

( (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)
  ), //第一個(gè)調(diào)用參數(shù)
  arr, //第二調(diào)用參數(shù)
  2 //第三調(diào)用參數(shù)
)

最后,引入高階函數(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ù)式編程裝逼時(shí)一定要用const字符,這表示我寫(xiě)的函數(shù)里的狀態(tài)是 immutable 的,天生驕傲!

再注:我寫(xiě)的這個(gè)比原來(lái)版的那個(gè)簡(jiǎn)單了很多,原來(lái)版本的那個(gè)又在函數(shù)中套了一套 next, 而且還動(dòng)用了不定參數(shù),當(dāng)然,如果你想裝逼裝到天上的,理論上來(lái)說(shuō),你可以套N層,呵呵。

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

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

このウェブサイトの聲明
この記事の內(nèi)容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰屬します。このサイトは、それに相當(dāng)する法的責(zé)任を負(fù)いません。盜作または侵害の疑いのあるコンテンツを見(jiàn)つけた場(chǎng)合は、admin@php.cn までご連絡(luò)ください。

ホットAIツール

Undress AI Tool

Undress AI Tool

脫衣畫(huà)像を無(wú)料で

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード寫(xiě)真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

寫(xiě)真から衣服を削除するオンライン AI ツール。

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無(wú)料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡(jiǎn)単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無(wú)料のコードエディター

SublimeText3 中國(guó)語(yǔ)版

SublimeText3 中國(guó)語(yǔ)版

中國(guó)語(yǔ)版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強(qiáng)力な PHP 統(tǒng)合開(kāi)発環(huán)境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開(kāi)発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)