1.最初のステップ
2.機能を組み合わせる
3.部分使用(カリー化)
4.宣言型プログラミング
5. Quintessential表記
6.不変性とオブジェクト
7.不変性と配列
8.レンズ
9.結論
この投稿は、関数型プログラミングに関するRamda Style Thinkingシリーズの記事の始まりです。
これらの記事では、 Ramdaライブラリを使用しますが、説明されているアイデアの多くは、UnderscoreやLodashなどの他の多くのライブラリ、および他のプログラミング言語にも適用できます。
私は関数型プログラミングの軽くて学術的な側面に固執します。 これは主に、より多くの人がシリーズにアクセスできるようにしたいだけでなく、部分的には私自身が本当に機能的な道にあまり近づいていないためです。
ラムダ
このブログでJavaScriptのRamdaライブラリに何度か触れました。
-「 ReduxでRamdaを使用する 」(後でこの記事を翻訳したいと思います-およそPer。)で、Reduxアプリケーションを作成するときに、さまざまなコンテキストでRamdaを使用する方法の例を示しました。
-「 Redux-api-middleware with Railsの使用」では、Ramdaを使用してペイロードを要求に変換し、応答を返しました。
Ramdaは、JavaScriptでクリーンでエレガントな関数型プログラミングのための多くのツールを提供する美しく設計されたライブラリであることがわかりました。
この一連の記事を読みながらRamdaを試してみたい場合は、Ramda Webサイトに実験用の便利なブラウザーサンドボックスがあります。
機能
名前が示すように、関数型プログラミングには関数と多くの共通点があります。 この状況では、関数を、ゼロ以上の引数の数で呼び出される再利用コードの一部として定義し、結果を返します。
これは、JavaScriptで記述された単純な関数です。
function double(x) { return x * 2 }
ES6の矢印関数と一緒に、同じ関数をより簡単に書くことができます。 これについては、記事の進行に合わせて多くの矢印関数を使用するため、ここで言及します。
const double = x => x * 2
一部の言語では、さらに進んで、ファーストクラスの構成要素などの機能をサポートしています。 「一流の構造」とは、関数を他の値と同じように使用できることを意味します。 たとえば、次のことができます。
-定数と変数でそれらを参照する
-それらをパラメーターとして他の関数に渡す
-他の関数の結果としてそれらを返します
JavaScriptはこれらの言語の1つであり、この利点を利用します。
純粋な機能
関数型プログラムを作成するとき、最終的に、いわゆる「クリーン」関数を使用することの重要性を理解するようになります。
純粋な関数は、副作用のない関数です。 外部変数には何も割り当てません。入力データを破棄したり、出力を生成したり、データベースの読み取りや書き込みを行ったり、渡されたパラメーターを変更したりしません。
基本的な考え方は、同じパラメーターを繰り返し使用して関数を呼び出すと、常に同じ結果が得られるということです。
もちろん、汚れた関数を使用してさまざまなことを行うことができます(プログラムが何か面白いことをする場合はそうすべきです)が、ほとんどのコードでは関数をきれいに保つ必要があります。
不変性
( またはfp'shnikiがしばしば表現されるため、「不変性」-約per )
関数型プログラミングのもう1つの重要な概念は、「不変性」です。 これはどういう意味ですか? 「不変」とは「不変」を意味します。
不変で作業する場合、値またはオブジェクトの初期化後、それらを再び変更することはありません。 これは、配列の要素やオブジェクトのプロパティを変更しないことを意味します。
配列またはオブジェクト内の何かを変更する必要がある場合は、変更された値で新しいコピーを返します。 後続の投稿では、これについて詳しく説明します。
免疫は純粋な機能と密接に関連しています。 純粋な関数では副作用を作成できないため、外部データ構造を変更することはできません。 彼らは不変のスタイルでデータを扱うことを余儀なくされています。
どこから始めますか?
関数型パラダイムで考え始める最も簡単な方法は、ループを反復関数で置き換えることです。
これらの機能を備えた別の言語から来た場合(RubyとSmalltalkは2つの例にすぎません)、それらに既に精通している可能性があります。
Martin Flowerには、 これらの機能の使用 方法と既存のコードをコレクションストリームにリファクタリングする方法を示す「コレクションストリーム」に関する優れた記事のコレクションがあります 。
Array.prototypeでこれらの関数(rejectを除く)はすべて使用できるため、Ramdaを使用して開始する必要はありません。 ただし、残りの記事との一貫性を保つために、Ramdaバージョンを使用します。
forEach
明示的なループを記述する代わりに、代わりにforEach関数を使用してみてください。 このように:
// : for (const value of myArray) { console.log(value) } // : forEach(value => console.log(value), myArray)
forEachは関数と配列を取り、配列の各要素でこの関数を呼び出します。
forEachはこれらの関数の中で最もアクセスしやすいものですが、関数型プログラミングを実行するときには最も使用されません。 値を返さないため、実際には副作用のある関数を呼び出すためにのみ使用されます。
地図
次に学習する最も重要な機能はマップです。 forEachと同様に、mapは配列の各要素に関数を適用します。 ただし、forEachとは異なり、mapはこの関数を新しい配列に適用した結果を収集して返します。
以下に例を示します。
map(x => x * 2, [1, 2, 3]) // --> [2, 4, 6]
無名関数を使用しますが、ここで名前付き関数を使用できます。
const double = x => x * 2 map(double, [1, 2, 3])
フィルター / 拒否
それでは、フィルターとリジェクトを見てみましょう。 名前が示すように、フィルターは関数に基づいて配列から要素を選択します。 以下に例を示します。
const isEven = x => x % 2 === 0 filter(isEven, [1, 2, 3, 4]) // --> [2, 4]
filterは、この関数(この場合はisEven)を配列の各要素に適用します。 関数が「true」値を返すたびに、対応する要素が結果に含まれます。 また、関数が「false」値を返すたびに、対応する要素が配列から除外(フィルター)されます。
rejectはまったく同じことを行いますが、反対の意味です。 false値を返すすべての関数の要素を保存し、true値を返す関数の要素を除外します。
reject(isEven, [1, 2, 3, 4]) // --> [1, 3]
見つける
findは、配列の各要素に関数を適用し、関数が真の値を返す最初の要素を返します。
find(isEven, [1, 2, 3, 4]) // --> 2
減らす
reduceは、今日検討した他の機能よりも少し複雑です。 知っておく価値はありますが、彼女の作品の本質を理解するのに問題がある場合は、これを止めさせないでください。 彼女の作品の本質を理解しなくても、かなり長い道のりを歩むことができます。
reduceは、元の値とそれを処理する配列という2つの引数を持つ関数を受け入れます。
関数に渡される最初の引数は「アキュムレータ」と呼ばれ、2番目の引数は反復配列の値です。 関数は、新しい「バッテリー」値を返す必要があります。
例を見て、それで何が起こるか見てみましょう:
const add = (accum, value) => accum + value reduce(add, 5, [1, 2, 3, 4]) // --> 15
- reduceは、配列の最初の要素(1)に初期値(5)を指定して関数(add)を呼び出します。 addは新しいバッテリー値(5 + 1 = 6)を返します。
- reduce呼び出しが再び追加されます。これは、新しいアキュムレーター値(6)と次の配列値(2)の時間です。 返品8。
- reduce呼び出しは8と次の値(3)で再び追加され、結果は11です。
- reduce呼び出しは、最後に11と配列の最後の値(4)を追加し、結果は15です。
- reduceは、結果として最終的な累積値を返します(15)
おわりに
これらの反復関数から始めて、関数を他の関数にスローするというアイデアをつかむことができます。 その時点で関数型プログラミングを行っていることに気付かずに、すでに他の言語でこれを使用している可能性さえあります。
次のシリーズで
このシリーズの次の投稿「 関数の結合 」では、次のステップに進み、新しい興味深い方法で関数の結合を開始する方法を示します。