「JavaScript関数式プログラミング思想」――部分的応用と複合
4610 ワード
第5章 部分的応用と複合
一等値の関数は、関数式プログラミングの基礎となります.部分的応用と複合は関数的プログラミングの重要な特徴である.命令式プログラミングを採用する時、抽象的に新しい機能を生み出す必要があると感じるたびに、関数を定義します.関数式プログラミングでは、同じように必要とされる新しい関数を定義する必要がなく、魔術のように生成することができます.二人の魔術師の名前は部分応用と複合といいます.5.1 部分的に適用
5.2 コリ化
部分的に一つの関数を適用するメリットを実感しましたが、部分的に適用して得られた関数は、再部分的に適用する必要があれば、当然このようにできない理由があります.やはりレンゲRoutine 2関数の例です.レンゲRoutine 2関数のStepパラメータを部分的に適用して、生成間隔が1のシーケンスの関数rangeを得ます.この新しい関数は圧倒的に多くの場合の需要を満たすことができます.レンゲRoutine関数を呼び出すときにStepパラメータを省略するように、元の関数よりも便利です.次に、ほとんどのシーンのシーケンスの起点は0であり、そのためにレンゲ関数のstartパラメータを一部適用して、一つの呼び出し時に一つのパラメータを提供するレンゲFroom 0関数を得ると、レンゲRoutine関数を呼び出したときにstartパラメータを省略するようになります.より柔軟なのは、シーケンスの起点が他の数字である場面に対して、レンゲ関数のstartパラメータをこの数字で部分的に適用することができます.例えば、レンゲFrom 1関数が戻るのは1から始まるシーケンスです.
いくつかの関数式プログラミング言語の中の関数は自動的にカリー化されています.例えば、MLやHaskellなどです.JavaScriptはこの機能を備えていないので、私達が関数を編纂して実現するしかないです.特定の要素関数に対するcurry関数は、二元関数をコリック化するcurry 2のように書きやすい.
5.2.1 強化されたコリ化5.2.2 右から左へ柯里化5.2.3 さらに強化したコリ化5.2.4 柯里化の性能コスト5.2.5 コリック化の方式を適用する5.2.6 パラメータの順序5.2.7 カリン化と高次関数5.3 複合5.3.1 パイプとデータフロー5.3.2 関数の種類とカリー化5.4 すべては関数5.4.1です. 操作子の関数化5.4.2 方法の関数化5.4.3 フロー文の関数化を制御する 性能と可読性 結び目
詳細は、拙著をご覧ください.
『JavaScript関数式プログラミング思想』(京東)
『JavaScript関数式プログラミング思想』(ダダ)
「JavaScript関数式プログラミング思想」(アマゾン)
「JavaScript関数式プログラミング思想」(天猫)
一等値の関数は、関数式プログラミングの基礎となります.部分的応用と複合は関数的プログラミングの重要な特徴である.命令式プログラミングを採用する時、抽象的に新しい機能を生み出す必要があると感じるたびに、関数を定義します.関数式プログラミングでは、同じように必要とされる新しい関数を定義する必要がなく、魔術のように生成することができます.二人の魔術師の名前は部分応用と複合といいます.5.1 部分的に適用
5.2 コリ化
部分的に一つの関数を適用するメリットを実感しましたが、部分的に適用して得られた関数は、再部分的に適用する必要があれば、当然このようにできない理由があります.やはりレンゲRoutine 2関数の例です.レンゲRoutine 2関数のStepパラメータを部分的に適用して、生成間隔が1のシーケンスの関数rangeを得ます.この新しい関数は圧倒的に多くの場合の需要を満たすことができます.レンゲRoutine関数を呼び出すときにStepパラメータを省略するように、元の関数よりも便利です.次に、ほとんどのシーンのシーケンスの起点は0であり、そのためにレンゲ関数のstartパラメータを一部適用して、一つの呼び出し時に一つのパラメータを提供するレンゲFroom 0関数を得ると、レンゲRoutine関数を呼び出したときにstartパラメータを省略するようになります.より柔軟なのは、シーケンスの起点が他の数字である場面に対して、レンゲ関数のstartパラメータをこの数字で部分的に適用することができます.例えば、レンゲFrom 1関数が戻るのは1から始まるシーケンスです.
const range = f.partial(rangeRoutine2, 1);
const rangeFrom0 = f.partial(range, 0);
const rangeFrom1 = f.partial(range, 1);
f.log(rangeFrom0(10));
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
f.log(rangeFrom1(10));
//=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
そこで、想像力の翼に乗って、自動化のプロセスを設計することができます.一つの多パラメータ関数を一つのパラメータ関数チェーンに変えて、各関数は一つのパラメータを順番に受け取り、チェーンの次の関数を返します.最後のパラメータの関数を受け取るまで、これらのパラメータで得られた値に元関数を適用します.関数タイプの表記で直観的に表現できます.// , a b, c。
(a, b) -> c
// 。
a -> ( b -> c)
// -> , :
a -> b -> c
// 。
(a, b, c) -> d
// 。
a -> b -> c -> d
この関数に対する変換は最初に数学者Gottlob Fregeによって提出されました.その後、同行Moses SchenfinkelとHaskell Brooks Curryの発展を経て、その後の名前はカリー化と名づけられました.つまり、コーリィ化の語源ですので、カリー化という難解な用語をカレーに訳せば、関数的なプログラミングを学び、本の売り上げを増やすことができると思います.」総じて言えば、コリゼーションは多パラメータ関数に対する処理を単パラメータ関数に対する処理に簡略化することを意味する.関数式プログラミングでは、同様の部分的なアプリケーションの役割を果たすことができますが、両者は挙動に違いがあります.部分的なアプリケーションは通常の関数です.もう一度呼び出して元の関数に戻る必要があります.一部のアプリケーションでは、すべてのパラメータが入っていても、パラメータなしの関数が得られます.カリー化によって得られたのは関数チェーンで、チェーンの次の関数を得るたびに呼び出されます.チェーンの最後の関数、つまりすべてのパラメータを伝達し終わったら、直ちに元の関数の結果を返します.以下に続々と紹介する多くの運用と利点は部分的な応用とコリック化にとって同じで、簡便のために、時にはコリー化だけを呼ぶことがあります.いくつかの関数式プログラミング言語の中の関数は自動的にカリー化されています.例えば、MLやHaskellなどです.JavaScriptはこの機能を備えていないので、私達が関数を編纂して実現するしかないです.特定の要素関数に対するcurry関数は、二元関数をコリック化するcurry 2のように書きやすい.
function curry2(fn) {
return function (a) {
return function (b) {
return fn(a, b);
}
}
}
興味のある読者はcurry 3を練習とすることができます.難しいのは任意の要素関数に対するcurryを作成することです.その後もcurry関数の他のバージョンに出会いますので、以下のような古典的な柯里化概念に対応する関数はcurryClasicと名づけられます.curryClashicは高次関数であり、関数パラメータに基づいて新しい関数を返すだけでなく、更新された関数をこのままにします.この一連の関数は動作上の共通性もあれば、違いもあります.これは各関数がこれまでの関数伝達のパラメータを覚えておくことです.以下のコードのコメントは、この関数を実現する際に発生する問題と使用される解決策を説明しています./**
* @param fn 。
* @param arity 。 ,
* length , 。 (
* 、 ) ,length
* , 。
*/
export function curryClassic(fn, arity = fn.length) {
function _curry(savedArgs) {
// , ,
// , 。
// , ,
// 。
return function (arg) {
let curArgs = append(arg, savedArgs);
if (gte(curArgs.length, arity)) {
return fn(...curArgs);
} else {
return _curry(curArgs);
}
}
}
return _curry([]);
}
私たちはカリー化が様々な場所でプログラミングに便利なのを見に来ました.// 。
export function get(name, object) {
return object[name];
}
// get 。
const get = f.curryClassic(f.get);
const name = get('name');
const length = get('length');
f.log(name({name: 'Jack', age: 13}));
//=> Jack
f.log(length([1, 2]));
//=> 2
// add ++ -- 。
const add = f.curryClassic(f.add);
const inc = add(1);
const dec = add(-1);
f.log(inc(0));
//=> 1
f.log(dec(3));
//=> 2
// nAry 。
function nAry(arity, fn) {
return function (...args) {
let accepted = take(arity, args);
return fn(...accepted);
}
}
const binary = f.curryClassic(nAry)(2);
4.3.2 , ,nAry 。
function nAry(arity) {
return function (fn) {
return function (...args) {
let accepted = f.take(arity, args);
return fn(...accepted);
}
}
}
これは具体的な関数を作成する際のコリック化の効果に相当します.5.2.1 強化されたコリ化5.2.2 右から左へ柯里化5.2.3 さらに強化したコリ化5.2.4 柯里化の性能コスト5.2.5 コリック化の方式を適用する5.2.6 パラメータの順序5.2.7 カリン化と高次関数5.3 複合5.3.1 パイプとデータフロー5.3.2 関数の種類とカリー化5.4 すべては関数5.4.1です. 操作子の関数化5.4.2 方法の関数化5.4.3 フロー文の関数化を制御する 性能と可読性 結び目
詳細は、拙著をご覧ください.
『JavaScript関数式プログラミング思想』(京東)
『JavaScript関数式プログラミング思想』(ダダ)
「JavaScript関数式プログラミング思想」(アマゾン)
「JavaScript関数式プログラミング思想」(天猫)