この記事では、JavaScript 関数の組み合わせとカリー化について詳しく説明します (例を示します)。必要な方は参考にしていただければ幸いです。
私たちは皆、オブジェクト指向 SOLID における単一責任原則 (SRP、単一責任原則) を知っています。関数型プログラミングでは、各関数は 1 つの単位であり、実行することは 1 つだけです。しかし、現実の世界は常に複雑であり、現実の世界をプログラミングにマッピングする場合、単一の関数ではあまり意味がありません。このとき、関数の合成とカリー化が必要になります。
jQuery を使用したことがある場合は、$('.post').eq(1).attr('data などのチェーン コールが何であるかをご存知でしょう。 - test', 'test')
.JavaScript のネイティブ文字列および配列メソッドの一部は、チェーン呼び出しスタイルを記述することもできます:
'Hello, world!'.split('').reverse().join('') // "!dlrow ,olleH"
まず第一に、チェーン呼び出しは上記のオブジェクトに基づいています。メソッド split
、reverse
、join
は、前のオブジェクト「Hello, world!」から分離されている場合は再生できません。
関数型プログラミングでは、メソッドはデータから独立しています。関数型の方法で上記を書くことができます。
const split = (tag, xs) => xs.split(tag) const reverse = xs => xs.reverse() const join = (tag, xs) => xs.join(tag) join('',reverse(split('','Hello, world!'))) // "!dlrow ,olleH"
あなたは間違いなく、冗談だと言うでしょう。これは連鎖呼び出しよりもどのように優れているのでしょうか?これは依然としてデータに依存しています。「Hello, world!」を渡さないと、一連の関数の組み合わせは機能しません。ここでの唯一の利点は、個々のメソッドを再利用できることです。パニックにならないでください。後でコンテンツがたくさんあるので、私が最適化します (愚かにも)。変換を進める前に、まず部分適用とカリー化という 2 つの概念を紹介します。
部分アプリケーションは、いくつかのパラメータを受け取り、受け取ったパラメータの少ない関数を返すプロセスです。これはアプリケーションの一部です。 bind
を使用して実装します。
const addThreeArg = (x, y, z) => x + y + z; const addTwoArg = addThreeNumber.bind(null, 1) const addOneArg = addThreeNumber.bind(null, 1, 2) addTwoArg(2, 3) // 6 addOneArg(7) // 10
上記は bind
を使用して、残りのパラメーターをそれぞれ受け入れる他の 2 つの関数を生成します。これはアプリケーションの一部です。もちろん、他の方法でも行うことができます。
一部のアプリケーションの主な問題は、返される関数の型を直接推論できないことです。前述したように、一部のアプリケーションは、返されるパラメーターの数を指定せずに、より少ないパラメーターを受け入れる関数を返します。これは暗黙的なものなので、コードを確認する必要があります。そうして初めて、返された関数が受け取るパラメーターの数がわかります。
カリー化の定義: 関数を呼び出すことはできますが、すべてのパラメーターを一度に渡すことはできません。この関数は、次の 1 つの パラメータを受け取る関数を返します。
const add = x => y => x + y const plusOne = add(1) plusOne(10) // 11
カリー化された関数は、パラメーターを 1 つだけ受け取る関数を返し、返される関数の型は予測可能です。
もちろん、実際の開発では、多くの関数はカリー化されていません。いくつかのツール関数を使用して変換できます。
const curry = (fn) => { // fn可以是任何参数的函数 const arity = fn.length; return function $curry(...args) { if (args.length < arity) { return $curry.bind(null, ...args); } return fn.call(null, ...args); }; };
オープン ソース ライブラリ Ramda で提供されているカリー メソッドを使用することもできます。
例
const currySplit = curry((tag, xs) => xs.split(tag)) const split = (tag, xs) => xs.split(tag) // 我现在需要一个函数去split "," const splitComma = currySplit(',') //by curry const splitComma = string => split(',', string)
カリー関数が新しい関数を生成するとき、それはデータとは何の関係もないことがわかります。新しい関数を生成する 2 つのプロセスを比較すると、カリー化を行わないプロセスは比較的冗長です。
最初にコードを指定します:
const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
実際、compose は合計 2 つのことを行います:
Receives関数のセット、関数を返します。関数はすぐには実行されません。
結合関数。渡された関数を左から右に結合します。
一部の学生は上記のreduceRightにあまり慣れていないかもしれません。2元と3元の例を挙げましょう:
const compose = (f, g) => (...args) => f(g(...args)) const compose3 = (f, g, z) => (...args) => f(g(z(...args)))
関数呼び出しは左から順に行われます。右、データ 左から右への流れも同様です。もちろん、右から左へを定義することはできますが、意味的にはそれほど意味がありません。
さて、最初の例を最適化しましょう:
const split = curry((tag, xs) => xs.split(tag)) const reverse = xs => xs.reverse() const join = curry((tag, xs) => xs.join(tag)) const reverseWords = compose(join(''), reverse, split('')) reverseWords('Hello,world!');
これははるかにシンプルで理解しやすいでしょうか?ここの reverseWords
は、前に説明した Pointfree コード スタイルでもあります。データや外部状態に依存せず、組み合わせた関数です。
Pointfree 前回の記事で JS 関数型プログラミングの概念を紹介し、その利点と欠点についても説明しました。興味のある方はぜひご覧ください。
まず、小学校の知識の足し算の結合法則: a (b c)=(a b) c
を確認しましょう。説明はしませんが、理解できるはずです。
振り返ってみると、実際には関数の組み合わせには結合法則があります。
compose(f, compose(g, h)) === compose(compose(f, g), h);
これは、関数の組み合わせを自由に組み合わせてキャッシュできるという利点です。
const split = curry((tag, xs) => xs.split(tag)) const reverse = xs => xs.reverse() const join = curry((tag, xs) => xs.join(tag)) const getReverseArray = compose(reverse, split('')) const reverseWords = compose(join(''), getReverseArray) reverseWords('Hello,world!');
補足。心理図: ###########################
以上がJavaScriptでの関数の組み合わせとカリー化を詳しく解説(例付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。