-
[Functional] 동시성을 가진 C.reduce, C.take, C.map, C.filterprograming/Language 2019. 8. 21. 18:31
안녕하세요, Einere입니다.
(ADblock을 꺼주시면 감사하겠습니다.)
2019/06/25 - [programing/JavaScript] - [Functional] 평가와 일급, 고차함수
2019/07/15 - [programing/JavaScript] - [Functional] 순회와 이터러블
2019/07/15 - [programing/JavaScript] - [Functional] 제네레이터와 이터러블
2019/07/15 - [programing/JavaScript] - [Functional] map, filter, reduce
2019/07/18 - [programing/JavaScript] - [Functional] 코드를 값으로 다루어 표현력 높이기
2019/07/22 - [programing/JavaScript] - [Functional] range, take, 느긋한 L.range, L.take, L.reduce
2019/07/22 - [programing/JavaScript] - [Functional] 지연 평가와 L.map, L.filter
2019/08/12 - [programing/JavaScript] - [Functional] join, find, flatten
2019/08/15 - [programing/JavaScript] - [Functional] promise와 monad, kleisli composition
이번 포스트에서는 동시성을 가진 C.reduce와 C.take, C.map, C.filter에 대해 알아보도록 하겠습니다.
C.reduce
const {L, C} = require("./myUtils.js"); L.go( [1, 2, 3, 4, 5, 6, 7, 8, 9], L.map(val => new Promise(res => setTimeout(()=> res(val), 1000))), L.filter(val => Promise.resolve(!(val % 2))), L.reduce((acc, val) => acc + val), console.log ).catch(console.log); /* 출력 20 */
기존의 L.reduce를 이용하는 경우, 이전에 실행될 함수들 중에서 시간을 많이 잡아먹는 함수가 있는 경우, 해당 시간이 지난 뒤 결과를 평가합니다. 예를 들어, 위 코드의 경우, reduce가 데이터를 하나씩 순회할 때 마다 L.map의 결과가 평가됩니다. L.map을 한번 실행할 때 마다 1초의 시간이 걸리므로, 총 9초의 시간이 걸립니다.
L.map을 순차적으로 실행하지 않고, 병렬적으로 동시에 실행시키고 싶다면 다음과 같이 C.reduce를 이용하면 됩니다.
// myUtils.js 에서 C.reduce = L.curry((f, acc, iterator) => iterator ? L.reduce(f, acc, [...iterator]) : L.reduce(f, [...acc])); // main.js 에서 const {L, C} = require("./myUtils.js"); L.go( [1, 2, 3, 4, 5, 6, 7, 8, 9], L.map(val => new Promise(res => setTimeout(()=> res(val), 1000))), L.filter(val => Promise.resolve(!(val % 2))), C.reduce((acc, val) => acc + val), console.log ).catch(console.log); /* 출력 20 */
C.reduce를 쓰면, 9번의 L.map실행이 병렬적으로 실행되어, 매우 빠르게 결과를 출력하는 것을 알 수 있습니다.
C.reduce를 리팩토링하면 다음과 같이 구현할 수 있습니다.
// 터미널에 불필요한 uncaught promise 에러 로그가 찍히는 것을 방지 C.catcher = arr => { arr.forEach(e => e instanceof Promise ? e.catch(function(){}) : e); return arr; }; C.reduce = L.curry((f, acc, iterator) => { // 원래는 iterator에서 하나씩 평가하는 방식이지만, 병렬처리를 위해 배열로 변환 후 전달한다 iterator ? L.reduce(f, acc, C.catcher([...iterator])) : L.reduce(f, [...acc]); });
catcher를 이용해 불필요한 uncaught promise 에러 로그가 출력되는 것을 방지할 수 있습니다.
C.take
// 원래는 iterator에서 하나씩 평가하는 방식이지만, 병렬처리를 위해 배열로 변환 후 전달한다 C.take = L.curry((limit, iterator) => L.take(limit, C.catcher([...iterator])));
C.take는 더 간편하게 구현할 수 있습니다.
C.map
C.map = L.curry(L.pipe(L.map, C.take(Infinity)));
C.filter
C.filter = L.curry(L.pipe(L.filter, C.take(Infinity)));
참고
마이클 포거스, 『함수형 자바스크립트 : 새롭고 올바른 자바스크립트 프로그래밍 기법』, 한빛미디어(2014).
유인동, 인프런 - 함수형 프로그래밍과 JavaScript ES6+
'programing > Language' 카테고리의 다른 글
[JS] vanillaJS repository 모음 (0) 2019.09.07 [Express] cookie에 대하여 (0) 2019.09.06 [Functional] promise와 monad, kleisli composition (0) 2019.08.15 [Functional] join, find, flatten (0) 2019.08.12 [JS] Date 객체 사용시 주의할 점 (0) 2019.08.04 댓글