[JavaScript ES6+] 지연성 2

Yujin Lee·2021년 5월 11일
0

JavaScript

목록 보기
13/14
post-thumbnail

1. 결과를 만드는 함수 reduce, take

reduce와 take는 map, filter와 같이 지연 평가를 진행한 값을 실제 연산하는 단계이다.

take도 지연 평가를 통해 값을 특정 갯수만 yield할 수 있지만, take는 몇 개 일지 모르는 리스트에서 몇 개를 사용할지 정하는 함수이기 때문에 take까지 지연평가로 할 필요는 없다.

reduce

객체로 부터 url의 queryStr을 얻는 코드

const queryStr = obj => go(
    obj,
    Object.entries,
    map(([k, y]) => `${k}=${v}`),
    reduce((a, b) => `${a}&${b}`)
);
    
log(queryStr({limit: 10, offset: 10, type: 'notice'}));

아래는 동일한 코드이다.

const queryStr = pipe(
    L.entries,
    L.map(([k, v]) => `${k}=${v}`),
    join('&'));

log(queryStr({limit: 10, offset: 10, type: 'notice'}));




2. Array.prototype.join 보다 다형성이 높은 join 함수

const join = curry((sep = ',', iter) =>
    reduce((a, b) => `${a}${sep}${b}`, iter));
function *a() {
    yield 10;
    yield 11;
    yield 12;
    yield 13;
}

log(join(' - ', a()));
const sharpJoin = join("#");
const obj = {limit : 10 , offset : 10 , type : "notice"};

for (const a in obj) {
  log(sharpJoin([a , obj[a]]));
}
//  limit#10
//  offset#10
//  type#notice




3. take, find

take를 통해 find 함수를 만들었다. (find는 take를 통해서 이터러블 값을 받는 함수)
find는 결국 특정값 하나를 뽑는 함수이고, find로 가기 전에 어떤 함수들을 필터 조건으로 합성해놓고 사용할 수 있다.

const users = [
    {age: 32},
    {age: 31},
    {age: 37},
    {age: 28},
    {age: 25},
    {age: 32},
    {age: 31},
    {age: 37}
];

const find = curry((f, iter) => go(
    iter,
    L.filter(f),
    take(1),
    ([a]) => a));

log(find(u => u.age < 30)(users));

go(users,
    L.map(u => u.age),
    find(n => n < 30),
    log);




4. L.map, L.filter로 map과 filter 만들기

1) L.map + take로 map 만들기

const map = curry((f, iter) => go(
    L.map(f, iter),
    take(Infinity)
));

는 아래와 같이 바꿀 수 있다.

const map = curry(pipe(L.map, takeAll));

2) L.filter + take로 filter 만들기

const filter = curry((f, iter) => {
    let res = [];
    iter = iter[Symbol.iterator]();
    let cur;
    while (!(cur = iter.next()).done) {
        const a = cur.value;
        if (f(a)) res.push(a);
    }
    return res;
});

는 아래와 같이 바꿀 수 있다.

const filter = curry(pipe(L.filter, takeAll));




5. L.flatten, flatten

값을 펼쳐서 하나의 배열로 만들게 하면서 동작지연을 수행하는 함수

const isIterable = a => a && a[Symbol.iterator];
//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;
    }
};

//출력해보기
var it = L.flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]]);
    log(it.next());
    log(it.next());
    log(it.next());
    log(it.next());


log(take(6, L.flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]])));


const flatten = pipe(L.flatten, takeAll);
log(flatten([[1, 2], 3, 4, [5, 6], [7, 8, 9]]));




6. yield *, L.deepFlat

1) yield *

yield * 을 활용하면 위 코드를 아래와 같이 변경할 수 있다.
yield *iterablefor (const val of iterable) yield val; 과 같다.

L.flatten = function* (iter) {
    for (const a of iter) {
        if (isIterable(a)) yield* a;
        else yield a;
    }
};

2) L.deepFlat

만일 깊은 Iterable을 모두 펼치고 싶다면 아래와 같이 L.deepFlat 을 구현하여 사용할 수 있다. L.deepFlat 은 깊은 Iterable을 펼쳐준다.

L.deepFlat = function* f(iter) {
    for (const a of iter) {
        if (isIterable(a)) yield* f(a);
        else yield a;
    }
};
log([...L.deepFlat([1, [2, [3, 4], [[5]]]])]);
// [1, 2, 3, 4, 5];




7. L.flatMap, flatMap

flatMap : map과 flatten을 동시에 하는 함수

log([[1, 2], [3, 4], [5, 6, 7]].flatMap(a => a)); //자바스크립트에 기본으로 있는 flatMap
log([[1, 2], [3, 4], [5, 6, 7]].flatMap(a => a.map(a => a * a)));
log(flatten([[1, 2], [3, 4], [5, 6, 7]].map(a => a.map(a => a * a))));
log(flatten([[1, 2], [3, 4], [5, 6, 7]].map(a => a.map(a => a * a))));

map과 flatten이 조금 비효율적으로 동작하기 때문에 사용한다.

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

// var it = L.flatMap(map(a => a * a), [[1, 2], [3, 4], [5, 6, 7]]);
// 는 아래와 동일한 코드이다.
var it = L.flatMap(a => a, [[1, 2], [3, 4], [5, 6, 7]]);
log([...it]);

log(flatMap(a => a, [[1, 2], [3, 4], [5, 6, 7]]));

log(flatMap(L.range, map(a => a + 1, [1, 2, 3])));


var it = L.flatMap(L.range, map(a => a + 1, [1, 2, 3]));
log(it.next());
log(it.next());
log(it.next());
log(it.next());

log(take(3, L.flatMap(L.range, map(a => a + 1, [1, 2, 3]))));




8. 2차원 배열 다루기

const arr = [
    [1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [9, 10]
];

go(arr,
    L.flatten,
    L.filter(a => a % 2), // 홀수만 남기기
    L.map(a => a * a), // 제곱
    take(4), // 앞에서부터 4개만 뽑아서
    reduce(add), // 합산
    log);




9. 이터러블 중심 프로그래밍 실무적인 코드

var 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: 24, family: [
            {name: 'b1', age: 58}, {name: 'b2', age: 51},
            {name: 'b3', age: 19}, {name: 'b4', age: 22}
        ]
    },
    {
        name: 'c', age: 31, family: [
            {name: 'c1', age: 64}, {name: 'c2', age: 62}
        ]
    },
    {
        name: 'd', age: 20, family: [
            {name: 'd1', age: 42}, {name: 'd2', age: 42},
            {name: 'd3', age: 11}, {name: 'd4', age: 7}
        ]
    }
];

1

go(users,
    L.map(u => u.family),
    L.flatten,
    takeAll,
   log);

2

go(users,
    L.map(u => u.family),
    L.flatten,
    L.filter(u => u.age < 20),
    L.map(u => u.name),
    take(4),
    log);

3

go(users,
    L.flatMap(u => u.family),
    L.filter(u => u.age > 20),
    L.map(u => u.age),
    take(4),
    reduce(add),
    log);

profile
I can be your Genie🧞‍♀️ How ‘bout Aladdin? 🧞‍♂️

0개의 댓글