[Javascript] 코드를 값으로 다루어 표현력 높이기

DaeHoon·2022년 6월 14일
0

Javascript

목록 보기
5/5

1. go


const reduce = (f, acc, iter) => {

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


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

go(
  0,
  a => a + 1,
  a => a + 10,
  a => a + 100,
  log) 

// output 
111
  • 함수를 인자로 받는 go 메서드

2. pipe

const pipe = (...fs) => (a) => go(a, ...fs);

const f = pipe(
  (a, b) => a + b,
  a => a + 10,
  a => a + 100);

  log(f(0, 1));

3.curry

  • 함수를 값으로 다루면서 원하는 시점에 평가시키는 함수.
  • 함수를 받아서 함수를 리턴하고, 인자를 받아 그 인자를 받아놓았던 함수에 대입하여 평가한다.
const log = console.log;
const curry = f =>
	(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);

const mult = curry((a, b) => a * b);
log(mult(1))
log(mult(1,2))
log(mult(1)(2))

//output
(..._) => f(a, ..._)
2
2
  • 맨 처음 출력에서, 인자가 하나 밖에 없으므로 curry의 인자로 들어간 함수를 리턴한다.
  • 인자가 두 개가 들어와 curry 함수로 들어온 곱셈 연산 시행
  • 1을 받은 함수 리턴 후, f(a, ...) 함수에서 ... 부분에 2가 들어와 곱셈 연산을 시행한다.



const log = console.log;

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

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 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;
});
  • 인자 받는 부분에 curry 함수를 적용했다.
/* before */
  go(
    products,
    products => filter(p => p.price < 20000, products),
    products => map(p => p.price, products),
    prices => reduce(add, prices),
    log);
/* after */
  go(
    products,
    filter(p => p.price < 20000),
    map(p => p.price),
    reduce(add),
    log);

// output
30000
30000

두 코드의 평가 값이 같은 이유.

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);
    log(a, acc)
  }
  return acc;
});
  • reduce의 for.. of문 안에 log를 찍어보면 첫 번째 경우는 사용자가 정의한 메서드 (filter, map, reduce)에 인자가 1개 이상으로 들어와 f(acc, a)에서 바로 결과가 나온다.

  • 반면 두 번째 경우는 go 메서드 안에서 부터 filter, map, reduce에서 인자를 넘길 때 curry 함수를 호출한다. 이 때 1개의 인자만 받으므로 curry 메서드에서 f(a, ...) : (...)를 반환한다. 이 후 for .. of 문에서 다시 curry를 실행해 filter, map, reduce를 실행한다.'

4. 함수 조합으로 코드 만들기


  const go = (...args) => reduce((a, f) => f(a), args);
  const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);

  const total_price = pipe(
    map(p => p.price),
    reduce(add));

  const base_total_price = predi => pipe(
    filter(predi),
    total_price);

  go(
    products,
    base_total_price(p => p.price < 20000),
    log);

  go(
    products,
    base_total_price(p => p.price >= 20000),
    log);
  • pipe 메서드로 중복 사용되는 메서드들을 하나로 만들어서 사용하고 있다.
profile
평범한 백엔드 개발자

0개의 댓글