カリー化された関数は、JavaScriptコードの可読性と表現力を高めるのに役立ちます。複雑なロジックをより小さく、自己完結的で、管理しやすいコードに分解したい場合に、カリー化手法は理想的です。
JavaScriptのカリー化された関数、関数カリー化の手法を使用して部分適用関数を作成する方法、およびカリー化された関数と部分適用関数の両方の実生活のユースケースについてすべて学びましょう。
カリー化とは何ですか?
カリー化は数学者のハスケル・B・カリーにちなんで名付けられ、その概念はラムダ計算から派生しています。カリー化は、複数のパラメーターを受け取る関数を取り、それを一連の単項(1パラメーター)関数に分解します。言い換えると、カリー化された関数は一度に1つのパラメーターのみを受け取ります。
カリー化の基本的な例
以下は、カリー化された関数の例です。
function buildSandwich(ingredient1) {return (ingredient2) => {return (ingredient3) => {return ${ingredient1},${ingredient2},${ingredient3}}}}
buildSandwich()関数は、ingredient2引数を受け取る別の関数(匿名関数)を返します。次に、この匿名関数は、ingredient3を受け取る別の匿名関数を返します。最後に、この最後の関数は、JavaScriptで文字列をフォーマットする方法であるテンプレートリテラルを返します。
作成したのは、各関数がその下にある関数を呼び出すネストされた関数であり、最後に到達するまで続きます。これで、buildSandwich()を呼び出して単一のパラメーターを渡すと、まだ提供していない引数がある関数の一部が返されます。
console.log(buildSandwich("Bacon"))
出力から、buildSandwichが関数を返すことがわかります。
関数の呼び出しを完了するには、3つの引数をすべて指定する必要があります。
buildSandwich("Bacon")("Lettuce")("Tomato")
このコードは、「ベーコン」を最初の関数に、「レタス」を2番目の関数に、「トマト」を最後の関数に渡します。言い換えると、buildSandwich()関数は実際には3つの関数に分割され、各関数は1つのパラメーターのみを受け取ります。
従来の関数を使用してカリー化することは完全に有効ですが、ネストが深くなるほど、すべてのネストがかなり見苦しくなる可能性があります。これを回避するには、矢印関数を使用して、よりクリーンな構文を利用できます。
const buildMeal = ingred1 => ingred2 => ingred3 =>${ingred1}, ${ingred2}. ${ingred3};
このリファクタリングされたバージョンはより簡潔で、通常の関数と比較して矢印関数を使用する利点があります。以前と同じように関数を呼び出すことができます。
buildMeal("Bacon")("Lettuce")("Tomato")
部分適用カリー関数
部分適用関数は、カリー化の一般的な使用例です。この手法では、一度に必要な引数のみを指定します(すべての引数を指定するのではなく)。必要なすべてのパラメーターを渡して関数を呼び出すたびに、その関数を「適用」したと言います。
例を見てみましょう。
const multiply = (x, y) => x * y;
以下はmultiplyのカリー化されたバージョンです。
const curriedMultiply = x => y => x * y;
curriedMultiply()関数は、最初の関数のx引数と2番目の関数のyを受け取り、次に両方の値を掛けます。
最初の部分適用関数をを作成するには、curriedMultiple()を最初の引数で呼び出し、返された関数を変数に代入します。
const timesTen = curriedMultiply(10)
この時点で、コードはcurriedMultiply()関数を「部分適用」しています。そのため、timesTen()を呼び出すときはいつでも、1つの数値を渡すだけで、その数値は自動的に10(適用された関数内に格納されている)と掛けられます。
console.log(timesTen(8)) // 80
これにより、単一の複雑な関数に基づいて複数のカスタム関数を構築し、それぞれに独自の機能をロックできます。
実際のWeb開発のユースケースに近い例を見てみましょう。以下に、最初の呼び出しで要素のid、2番目の呼び出しでコンテンツを受け取り、指定されたidとコンテンツに基づいて要素を更新するupdateElemText()関数があります。
const updateElemText = id = content=> document.querySelector(#${id}).textContent = content// Lock the element's id into the function:const updateHeaderText = updateElemText('header')// Update the header textupdateHeaderText("Hello World!")
カリー化関数による関数合成
カリー化のもう1つの一般的な使用例は関数合成です。これにより、特定の順序で小さな関数を呼び出し、それらを1つのより複雑な関数にまとめることができます。
たとえば、仮説的なeコマースWebサイトでは、次の3つの関数を正確な順序で1つずつ実行したい場合があります。
const addCustomer = fn => (...args) => {console.log("顧客情報を保存しています")return fn(...args)}const processOrder = fn => (...args) => {console.log(注文 #${args[0]} を処理しています)return fn(...args);}let completeOrder = (...args) => {console.log(注文 #${[...args].toString()} が完了しました。);}
このコードはletキーワードを使用してcompleteOrder()関数を定義していることに注意してください。これにより、変数に値を再代入することができ、これはJavaScriptのスコープの仕組みの一部です。
次に、顧客を最初に追加したいので、関数を逆順(内側から外側)で呼び出す必要があります。
completeOrder = (processOrder(completeOrder)); completeOrder = (addCustomer(completeOrder)); completeOrder("1000")
これにより、次の出力が得られます。
上記の関数を通常の方法で記述すると、コードは次のようになります。
function addCustomer(...args) { return function processOrder(...args) { return function completeOrder(...args) { // end } } }
addCustomer()関数を呼び出して引数を渡すと、内側から始めて関数の先頭まで進みます。
カリー関数を使用して通常の関数をカリー化された関数に変換する
カリー化された関数をたくさん使用する予定がある場合は、ヘルパー関数を使用してプロセスを効率化できます。
この関数は、通常の関数をカリー化された関数に変換します。これは再帰を使用して任意の数の引数を処理します。
const curry = (fn) => { return curried = (...args) => { if (fn.length !== args.length) { return curried.bind(null, ...args) } return fn(...args); } }
この関数は、複数の引数を受け取る標準的に記述された関数をすべて受け入れ、その関数のカリー化されたバージョンを返します。実際に使ってみるには、3つのパラメーターを受け取ってそれらを合計する次のサンプル関数を使用します。
const total = (x, y, z) => x + y + z
この関数を変換するには、curry()関数を呼び出してtotalを引数として渡します。
const curriedTotal = curry(total)
これで関数を呼び出すには、すべての引数を渡すだけです。
console.log(curriedTotal(10)(20)(30)) // 60
JavaScriptの関数についてさらに詳しく
JavaScriptの関数は非常に柔軟であり、関数のカリー化はその一部にすぎません。矢印関数、コンストラクタ関数、匿名関数など、他にも多くの種類の関数があります。これらの関数とそのコンポーネントに慣れることは、JavaScriptを習得するための鍵です。
コメントする