본문 바로가기

카테고리 없음

함수형 자바스크립트 공부 5. 코드를 값으로 다루어 표현력 높이기

반응형

인프런 - 함수형 프로그래밍과 JavaScript ES6+ (inflearn.com)

 

함수형 프로그래밍과 JavaScript ES6+ - 인프런

ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,

www.inflearn.com

해당 강의 공부 중....!

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

go

  • args를 함수로 축약하는 함수
  • 인자를 계속해서 전달해나감
// 이전 리듀스...
const reduce = (f, acc, iter) => {
    if (!iter) { // 인자 2개이거나 iterator가 아니면
        iter = acc[Symbol.iterator]();
        acc = iter.next().value;
    }
    for (const value of iter) {
        acc = f(acc, value);
    }
    return acc;
};

const add = (a, b) => a + b;
// ...args =? args는 리스트가 됨 [1, f ,f ,f]
const go = (...args) => reduce((a, f) => f(a), args); // ...args가 배열로 바뀜
// args를 특정 함수로 축약하는 함수
// a인자 위치가 acc 위치, f위치가 원래 value 위치인데 함수가 옴
// 인자가 2개이므로 args의 이터레이터를 뽑아오고 acc는 첫값(1)이 된다.
// ((a, f) => f(a))(acc, value)가 되는꼴이므로 이전 함수 결과의 누적 값에 함수를 적용함
go(
    add(0, 1), // 1
    a => a + 10, // 10
    a => a + 100, // 100
    log);// 111

pipe

  • go 함수를 리턴하는 함수
    • go는 즉시 평가
      • 함수 평가 결과를 누적하여 연쇄 적용
    • 함수들이 나열되어 있는 합성 함수
const pipe0 = (...fs) => (a) => go(a,...fs); 
// 함수 배열을 전개하여 인자로 넣으면 go 함수를 리턴. a를 초기값으로 받아서 수행

// 첫번째 함수는 인자를 여러개 받을 수 있게 개조해준 함수
// 해당값 적용 값이 누적 시작값이 됨.
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 

const f = pipe(
    (a, b) => a + b, 
    a => a + 10,
    a => a + 100); // (...as) => go(f(...as), ...fs); 

log(f(0, 1)); // (1,2) =>go(add(1,2)

go로 더 읽기 좋은 함수 만들기

// 조합하던 함수를 위에서 아래로 펼칠 수 있게 됨.
go(
    products,
    products => filter(p => p.price < 20000, products),
    products => map(p => p.price, products),
    prices => reduce(add, prices),
    log);

curry

  • 함수를 값으로 다룸
  • 함수를 원하는 시점에 평가
  • 함수를 받아서 함수를 리턴
  • 인자를 받아서 원하는 인자 만큼 들어왔을 때 평가함
const curry = f =>
  (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
// 1. f 함수를 받아서 함수를 리턴함.
// 2. _.length 함수 인자 2개 이상? f 즉시 실행
// 3. 아니라면 함수를 리턴한 후에 인자를 더 받아서 실행
const mult = curry((a, b) => a * b);
mult(3)// 함수 리턴
log(mult(3)(2)); // 6

// 아래 모든 함수들이 인자가 하나만 들어오면 나머지 인자를 기다리도록 되어 있음.
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;
});
// a를 나중에 받아서 실행할 수 있음
// filter(p => p.price < 20000)는 리턴 결과가 함수임.
go(
    products,
    filter(p => p.price < 20000), 
    map(p => p.price),
    reduce(add),
    log
);

함수 조합으로 함수 만들기

// 첫번째 함수는 인자를 여러개 받을 수 있게 만든 지연 평가 함수
// 해당값 적용 값이 누적 시작값이 됨.
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs); 


// 파이프라인 코드 중복 제거 
// go + curry + pipe
const total_price = pipe(map(p => p.price),
    reduce(add))

// before
go(
    products,
    filter(p => p.price < 20000), 
    map(p => p.price),
    reduce(add),
    log
);
// after
go(
    products,
    filter(p => p.price < 20000), 
    total_price,
    log
);

// 중복 제거를 위한 지연 평가 함수
const total_price = pipe(
    map(p => p.price),
    reduce(add));

// filter 인수 predicate 함수를 받아서 pipe를 만드는 함수
const base_total_price = predi => pipe(
    filter(predi),
    total_price); // total_price가 여기에 들어가 있음.
// 파이프는 값이 들어와야 실행됨
go(
    products,
    base_total_price(p => p.price < 20000),
    log);
반응형