カリー化とは?的なことはすでに諸先達が書かれているので、そちらをご覧ください。

関数をカリー化したいとき

いっぺんにたくさんということでなければ、その都度、一個ずつカリー化でいいと思います。
何をしてるか自分でわかるので安心です。

// 例1。引数が二つの非カリー化関数をカリー化する。
const uncurriedF = (x, y) => (何か返り値);//があるとして
const curriedF = x => y => uncurriedF(x, y)

// 例2。配列のメソッドmapをカリー化関数にする。
const curriedMap = f => xs => xs.map( f )

汎用の関数を作ってラップする

const curry2 = f => x => y => f(x, y)
const curry3 = f => x => y => z =>f(x, y, z)

たいがい2~3引数の場合が多いので、こんなので十分実用的かと思います。

でも、引数の数が可変だったら便利だし、かっこいいですよね。

引数の数可変バージョン

いろんな方が書かれていますが、この方の記事で初めて意味がわかりました。
javascriptで簡単なカリー化関数を作った(ES6版)より、ただの関数版を引用。

function curry(fn) {
  return function partial(...args) {
    return (args.length >= fn.length)
      ? fn.apply(null, args)
      : function(...ret_args) {
          return partial.apply(null, [...args, ...ret_args]);
        }
  }
};

わかりやすい!
自分で理解するためにさらに簡単になるか変形してみました。

const curry = f => part = (...xs) => (xs.length < f.length) ? y => part(...xs, y) : f(...xs)

おっと、これはまずかったですね。partがグローバル変数になってしまう。
修正しました。

const curry = f =>{ 
  //関数partの定義。
  const part = (...xs) => (xs.length < f.length) ? y => part(...xs, y) : f(...xs);
  // partを呼び、返す。
  return part;
}

※ 複数の引数を取らないようにしてますが( y のくだり)、本筋は一緒かと思います。

  • curry は関数fを引数にとって、関数partを返す。
  • part は複数の引数(配列xsになる)をとって、
  • もし xsの要素数がfが必要とする引数の数に足りないなら、yを引数に、展開したxsの後にyを付けた引数でpartを返す関数を返す。
  • そうでないなら、展開したxsを引数にfを返す。

ちょっとわかりにくいですが、再帰になってます。

ループを追って並べてみるとわかります。
関数fが必要とする引数の個数をnとして、一番目の引数(...xs)に単一の引数y1を入れることにします。

curry(f) 
  //=> part
curry(f)(y1) 
  //=> part(y1) 
  //=> y=>part(y1, y)
curry(f)(y1)(y2) 
  //=> (y=>part(y1, y))(y2) 
  //=> part(y1,y2) 
  //=> y=>part(y1,y2,y)
.
.
.
curry(f)(y1)(y2)....(yn) 
  //=> part(y1,y2,....,yn) 
  //=> f(y1,y2,....,yn)

改良バージョン

このままでも使えますが、より使いやすいように改造してみます。

  • 関数 f が必要とする引数の数が0個の時に、即時実行 f() しないで関数 f を返すようにする
  • 複数個の実引数を最初の括弧に入れると(...xs)で複数個評価されてしまうので、最初のpart(...xs)は実引数無しとし、次の y から実引数が入るようにすることで、複数個入れても括弧あたり最初の一個だけ評価するようにする。
const curry = f =>{ 
  // f が必要とする引数が無ければ f を返す。
  if(f.length === 0) return f;  
  //関数partの定義。
  const part = (...xs) => (xs.length < f.length) ? y => part(...xs, y) : f(...xs);
  // 引数無しでpartを呼び、返す。
  return part();
}

改悪?バージョン

改悪、は言い過ぎですね。ちょっと変えるだけで、別用途、別概念っていうか...
y...ysに変えて引用元と同じくしてみました。
どの括弧にいくつ実引数を入れても、fが必要とする数だけ全部評価されます。

const bindPartial = f =>{ 
  if(f.length === 0)return f;
  const part = (...xs) => (xs.length < f.length) ? (...ys) =>  part(...xs, ...ys) : f(...xs);
  return part();
}

カレー化もできるし、しなくても自由なやりかたで部分適用できる。

便利! と思う方はこちらで。
間違いを起こす元凶? と感じる方は改良バージョンで。

注意点

  • 関数 f に可変長引数があるとうまくいかない。
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.