即時計算と遅延計算


関連コンテンツ


反復器とforの関係
ファンクションプログラミング実装
range/ゆったりrange+take

総クリーンアップコード

const go = (...list) => reduce((a, f) => f(a), list);

const curry = (f) => (a, ..._) =>
  _.length ? f(a, ..._) : (..._) => f(a, ..._);

const reduce = curry((f, acc, iter) => {
  if (!iter) {
    iter = acc[Symbol.iterator]();
    acc = iter.next().value;
  }
  for (const a of iter) {
    acc = f(acc, a);
  }
  return acc;
});

const map = curry((f, iter) => {
  let res = [];
  for (const a of iter) {
    res.push(f(a));
  }
  return res;
});

const filter = curry((f, iter) => {
  let res = [];
  for (const a of iter) {
    if (f(a)) res.push(a);
  }
  return res;
});

const range = (l) => {
  let i = -1;
  let res = [];
  while (++i < l) {
    res.push(i);
  }
  return res;
};

range(4);

//L관련
const L = {};

L.range = function* (l) {
  let i = -1;
  while (++i < l) {
    yield i;
  }
};

L.map = curry(function* (f, iter) {
  for (const a of iter) yield f(a);
});

L.filter = curry(function* (f, iter) {
  for (const a of iter) if (f(a)) yield a;
});

const take = curry((l, iter) => {
  let res = [];
  for (const a of iter) {
    res.push(a);
    if (res.length == l) return res;
  }
  return res;
});

const takeAll = take(Infinity);

go(
  range(10),
  map((n) => n + 10),
  filter((n) => n % 2),
  take(2),
  console.log
);

go(
  L.range(10),
  L.map((n) => n + 10),
  L.filter((n) => n % 2),
  take(2),
  console.log
);

高速計算(発電機X使用)と低速計算(発電機O使用)


range,map,filterを用いた高速計算とl.range,l.map,l.filterを用いた遅い計算では,進行中にも差がある.
クイック計算はrange->map->filter->takeの順に並べ替えの進捗を処理します.
スローコンピューティングはtake->filter->map->range->map->filter->takeの繰り返しを処理します.

どちらがいいですか。


配列が大きい場合は、高速計算ではすべての配列を作成し、その配列を処理する必要があります.
しかし,緩やかな計算では対応する値を処理する必要があり,配列がすべて生成されなくても,=>すなわち,要求を満たすと,まずその内容を終了する効率が高くなる.

こうりつしけん

  • console.時間とコンソール.timeEndを使用して効率を比較します.
  • console.time();
    go(
      range(10000000),
      map((n) => n + 10),
      filter((n) => n % 2),
      take(2),
      console.log
    );
    
    console.timeEnd();
    console.time();
    
    go(
      L.range(10000000),
      L.map((n) => n + 10),
      L.filter((n) => n % 2),
      take(2),
      console.log
    );
    console.timeEnd();
  • 結果
  • [ 11, 13 ]
    default: 1.503s
    [ 11, 13 ]
    default: 0.228ms
    大きく並べば並べるほど、差が大きくなります.

    このハーモニーなら?遅延と即時計算の組合せ

    let users = [
      {
        name: "a",
        age: 21,
        family: [
          { name: "a1", age: 53 },
          { name: "a2", age: 47 },
          { name: "a3", age: 16 },
          { name: "a4", age: 15 },
        ],
      },
      {
        name: "b",
        age: 21,
        family: [
          { name: "b1", age: 58 },
          { name: "b2", age: 51 },
          { name: "b3", age: 19 },
          { name: "b4", age: 22 },
        ],
      },
      {
        name: "c",
        age: 21,
        family: [
          { name: "c1", age: 64 },
          { name: "c2", age: 62 },
        ],
      },
      {
        name: "d",
        age: 21,
        family: [
          { name: "d1", age: 42 },
          { name: "d2", age: 42 },
          { name: "d3", age: 11 },
          { name: "d4", age: 7 },
        ],
      },
    ];
    
    const curry = (f) => (a, ..._) =>
      _.length ? f(a, ..._) : (..._) => f(a, ..._);
    
    const reduce = curry((f, acc, iter) => {
      if (!iter) {
        iter = acc[Symbol.iterator]();
        acc = iter.next().value;
      }
      for (const a of iter) {
        acc = f(acc, a);
      }
      return acc;
    });
    
    const go = (...list) => reduce((a, f) => f(a), list);
    const pipe = (...fs) => (a) => go(a, ...fs);
    
    let L = {};
    
    const isIterable = (a) => a && a[Symbol.iterator];
    
    L.flatten = function* (iter) {
      for (const a of iter) {
        if (isIterable(a)) for (const b of a) yield b;
        else yield a;
      }
    };
    
    L.map = curry(function* (f, iter) {
      console.log(iter, "L.map Iter");
      for (const a of iter) {
        console.log(a, "L.map a");
        yield f(a);
      }
    });
    
    L.map1 = curry(function* (f, iter) {
      console.log(iter, "L.map1 Iter");
      for (const a of iter) {
        console.log(a, "L.map1 a");
        yield f(a);
      }
    });
    
    L.filter = curry(function* (f, iter) {
      for (const a of iter)
        if (f(a)) {
          console.log(a, "L.Filter a");
          yield a;
        }
    });
    
    const filter = curry((f, iter) => {
      console.log(iter, "filter Iter");
      let res = [];
      for (const a of iter) {
        if (f(a)) res.push(a);
      }
      console.log(res, "Filter");
      return res;
    });
    
    const take = curry((l, iter) => {
      let res = [];
      for (const a of iter) {
        res.push(a);
        if (res.length == l) return res;
      }
      return res;
    });
    
    const add = (a, b) => a + b;
    
    go(
      users,
      L.map((u) => u.family),
      L.flatten,
      L.filter((u) => u.age < 20),
      L.map1((u) => u.age),
      take(3),
      reduce(add),
      console.log
    );
    
    go(
      users,
      L.map((u) => u.family),
      L.flatten,
      filter((u) => u.age < 20),
      L.map1((u) => u.age),
      take(3),
      reduce(add),
      console.log
    );
    
    最初のgoはLに接続されています
    2番目のgoはLの間にフィルタがあります.


    恐らくそうでしょう.すなわち、filterはすべてのa b c dを遍歴する.
    L.filterは毎回必要な部分が1つしか必要ないようで、巡回検索を行います.