본문 바로가기

Study/Today I Learn

[TIL] 2019.03.30

Object.entries

> Object.entries({'a': 10, 'b': 20, 'c': 30})
< (3) [Array(2), Array(2), Array(2)]
[
  ["a", 10 ],
  ["b", 20 ],
  ["c", 30 ]
]

range

인자로 limit을 받아 limit 만큼의 숫자 배열을 반환

const range = l => {
  let i = -1
  let res = []
  while(++i < l) {
    res.push(i)
  }
  return res
}

log(range(10)) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

take

인자로 limit과 이터러블을 받아 해당 이터러블을 limit 만큼 잘라서 반환

const take = curry((l, iter) => {
  let res = []
  for(const a of iter) {
    res.push(a)
    if(res.length === l) return res
  }
  return res
})

log(take(5, range(10))) // [0, 1, 2, 3, 4]

사용할 함수 코드

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
})

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

const pipe = (f, ...fs) => (...args) => go(f(...args), ...fs)

const take = curry((l, iter) => {
    let res = []
    for(const a of iter) {
        res.push(a)
        if(res.length === l) return res
    }
    return res
})

const range = l => {
    let i = -1
    let res = []
    while(++i < l) {
        res.push(i)
    }
    return res
}

// 지연 평
const L = {}

L.range = function *(l) {
    let i = -1
    while (++i < l) yield i
}

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

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

지연평가

해당 계산이 필요할 때 까지 미뤄 평가하는 것

기존 평가와 지연 평가를 비교해보는 코드를 작성해 보았다.

// 평가
go(
  range(10),
  map(v => v + 10),
  filter(v => v % 2),
  take(2),
  log
)

// 지연 평가
go(
  L.range(10),
  L.map(v => v + 10),
  L.filter(v => v % 2),
  take(2),
  log
)

기존 평가의 동작 순서는 다음과 같다.

  1. range(10) 를 통해 길이가 10인 배열 생성 [0, 1, 2..., 8, 9]
  2. map을 통해 해당 배열에 10씩을 더한다. [10, 11, 12..., 18, 19]
  3. filter을 통해 해당 배열의 홀수를 걸러냄 [11, 13, 15, 17, 19]
  4. take(2) 를 통해 앞에서 2개만 자름 [11, 13]
  5. log 를 찍음

지연 평가의 동작순서는 다음과 같다.

  1. range, map, filter 에 이터레이터들은 아직 평가가 되고 있지 않기 때문에 take 함수를 실행
  2. take 함수에 있는 for of 문으로 인하여 인자로 넘겨 받은 이터레이터 에 next() 가 실행됨
  3. 이터레이터를 평가하기 위해 filter 함수를 실행
  4. filter 함수에 있는 for of 문으로 인하여 인자로 넘겨 받은 이터레이터 에 next() 가 실행됨
  5. 이터레이터를 평가하기 위해 map 함수를 실행
  6. map 함수에 있는 for of 문으로 인하여 인자로 넘겨 받은 이터레이터 에 next() 가 실행됨
  7. 이터레이터를 평가하기 위해 range 함수를 실행
  8. range 함수에서 next() 를 통해서 0을 map 에게 넘겨줌
  9. map에서 0을 받아 10으로 만든 후 filter에게 넘겨줌
  10. filter 에서 10은 홀수가 아니므로 false 로 판단해 다음 데이터를 요청
  11. 위 동작을 반복하면서 filter 에서 참인 값을 take 함수에게 넘겨줌
  12. take 함수에 조건인 2개의 값이 차면 즉시 함수를 종료하고 채워진 값인 [11, 13] 을 리턴

지연 평가 효율성 비교

console.time("")
go(
  range(10000),
  map(v => v + 10),
  filter(v => v % 2),
  take(2),
  log
)
console.timeEnd("")

console.time("L")
go(
  L.range(10000),
  L.map(v => v + 10),
  L.filter(v => v % 2),
  take(2),
  log
)
console.timeEnd("L")

'Study > Today I Learn' 카테고리의 다른 글

[TIL] 2019.04.02  (0) 2019.04.02
[TIL] 2019.04.01  (0) 2019.04.01
[TIL] 2019.03.24  (0) 2019.03.24
[TIL] 2019.03.22  (0) 2019.03.23
[TIL] 2019.03.21  (0) 2019.03.22