[함수형프로그래밍]- 지연성

Dorr·2021년 8월 15일
0

지연성

불필요한 함수 호출을 삼가고 꼭 필요한 입력만 넣고 실행하면 성능향상을 기대할 수 있다.

range

range를 실행하자마자 평가된 후 바로 할당.

const range = length => {
  let i = -1;
  let res = [];

  while (++i < length) {
    res.push(i)
  }

  return res;
}

let list = range(4); // range를 실행한 순간 list에는 [0, 1, 2, 3]이 즉시 할당.
console.log(list)) // [0, 1, 2, 3]
console.log(reduce((a,b) => a+b), list) //6

느긋한 L.range

즉시 평가되지 않고 값이 필요할 때 까지 기다렸다가 평가됨

const L = {};
L.range = function* (length) {
  let i = -1;

  while (++i < length) {
    yield i;
  }
};

let list = L.range(4);

// reduce 내부의 for...of문을 만나고 나서야 평가된다.
console.log(
  reduce((a, b) => a + b),
  list
); //6

take

지정한 길이만큼의 결과들만 반환하게 하는 함수

const take = (limit, iter) => {
  let res = [];

  for (const a of iter) {
    res.push(a);
    if (res.length === limit) break;
  }
  return res;
};

조급한 평가 Vs 지연 평가

1. 조급한 평가

// range함수는 조급한 평가를 하기에 원소를 10개 모두 생성한다.
const arr = range(10);

console.log(arr); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

//처음 3원소만 가져오고 나머지는 버리기에 불필요한 입력이 생긴다.
console.log(take(3, arr)); //[0, 1, 2]

2. 지연 평가

//L.range 함수는 느긋한 평가를 하기에 함수 호출을 나중으로 미룬다.
const generator = L.range(10);

// 원소를 필요한 만큼만 생성하므로 불필요한 입력을 없앨 수 있다.
console.log(take(3, generator)); // [0, 1, 2]

지연 평가하는 L.map, L.filter, L.entries, L.flatten, L.flatMap

L.map

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

var it = L.map((a) => a + 10, [1, 2, 3]);

// it는 기본적으로 제네레이터이기 때문에
// for...of 또는 전개연산자 같은 문법을 마주쳐야 작동하며
// yield를 만나면 잠시 중지했다가 next() 메서드를 통해 재개한다.
log(it.next()); //{value: 11, done: false}
log(it.next()); //{value: 12, done: false}
log(it.next()); //{value: 13, done: false}

L.filter

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

var it = L.filter(a => a % 2, [1, 2, 3, 4]);

// it는 기본적으로 제네레이터이기 때문에
// for...of 또는 전개연산자 같은 문법을 마주쳐야 작동하며
// yield를 만나면 잠시 중지했다가 next() 메서드를 통해 재개한다.
console.log(it.next()); {value: 1, done: false}
console.log(it.next()); {value: 3, done: false}
console.log(it.next()); {value: undefined, done: true}

L.entries

L.entries = function* (obj) {
  for (const k in obj) {
    yield [k, obj[k]];
  }
};

L.flatten

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.flatMap

const L.flatMap = curry(pipe(L.map, L.flatten))

L.map과 L.filter로 구현하는 map, filter

L.map으로 구현하는 map

const map = curry(pipe(L.map, take[infinity]));

console.log(map((a) => a + 10, L.range(4))); // [10, 11, 12, 13]

L.filter로 구현하는 filter

const filter = curry(pipe(L.filter, take[infinity]));

console.log(filter((a) => a % 2, L.range(4))); // [1, 3]

0개의 댓글