高階関数・クロージャ・ジェネレータ・カリー化と関数の部分適用

高階関数

// 引数に関数をとる
arr = [1, 2, 3]
arr.map(n => n * 2);

// 戻り値として関数を返す
const hof = (ex, fn) => {
  retrun n => fn(n + ex);
};

const plusOneDouble = hof(1, n => n * 2);
console.log(plusOneDouble(4));
// (4 + 1) * 2 = 10

// returnを省略して書くと
const hof = (ex, fn) => n => fn(n + ex);

クロージャ

const counterMaker = (initialCount) => {
  let c = initialCount;
  const increment = () => c++;

  return increment;
};

const count = countMaker(1);
console.log(count(), count(), count());
// 1 2 3

countMakerの環境の中で実行されているから変数cの値が毎回リセットされることなく蓄積されていく。

ちなみにクロージャは必ずしも関数を返す必要はない。 あくまでクロージャとは、単に親関数スコープの変数を参照する関数のこと。

ジェネレータ

値を保持しつつ繰り返し処理や逐次処理を行うための手段。

function* rangeGenerator(end, start = 0) {
  let n = 0;

  for (let i = start; i < end; i++) {
    n += 1;
    yield i;
  }
}

const gen = rangeGenerator(3);
console.log(gen.next());    // { value: 0, done: false }
console.log(gen.next());    // { value: 1, done: false }
console.log(gen.next());    // { value: 2, done: false }
console.log(gen.next());    // { value: undefined, done: true }

これは??? 使いどころがちょっとわからない!

カリー化と関数の部分適用

ネストした関数。

const multi = (n, m) => n + m;
console.log(multi(2, 4));    // 8

const curriedMulti = n => {
  return m => n * m;
}

console.log(curriedMulti(2)(4);    // 8

const simpleCurriedMulti = n => m => n * m;
console.log(simpleCurriedMulti(2)(4));    // 8

ひとつめの関数は今まで見てきたものと同じ。 ふたつめは、nを引数に取り、mを引数にとってnとmの積を返す関数。 関数がネストしているので、引数を渡すかっこがふたつ必要になる。 みっつめは単にreturnを省略したもの。

カリー化された関数の一部の引数を固定して新しい関数をつくることができる。 「関数の部分適用」という。

const multi = n=> m => n * m;

const triple = multi(3);
console.log(triple(5));    // 15

ちょっと関数型プログラミングと仲良くなれたような気持ち!