Library/Lodash

πŸ“š Lodash μ§€μ—° 평가(Lazy Evaluation) 원리

인파_ 2021. 11. 24. 17:49

lodash-Lazy-Evaluation

μ§€μ—° ν‰κ°€λž€?

컴퓨터 ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μ§€μ—° 평가(Lazy Evaluation)λŠ” ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ 자주 μ‚¬μš©λ˜λŠ” κ°œλ…μ΄λ‹€.

μ§€μ—° ν‰κ°€λŠ” 단어 κ·ΈλŒ€λ‘œ 계산이 ν•„μš”ν•œ μ‹œμ κΉŒμ§€ 계산을 λ―Έλ£¨λŠ” 것을 μ˜λ―Έν•œλ‹€. 즉, μ§€μ—° ν‰κ°€λŠ” 값을 κ³„μ‚°ν•˜λŠ” μ‹œμ μ„ λŠ¦μΆ”μ–΄μ„œ λΆˆν•„μš”ν•œ 계산을 λ°©μ§€ν•˜κ³  μ‹œμŠ€ν…œμ˜ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 있게 λœλ‹€. 이λ₯Ό 톡해, λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ κ°μ†Œν•˜κ³  ν”„λ‘œκ·Έλž¨μ˜ λ°˜μ‘μ„±μ΄ ν–₯μƒλœλ‹€.

 

μ§€μ—° 평가 λ™μž‘ 원리 #1

μ§€μ—° 평가(Lazy Evaluation)와 μ—„κ²©ν•œ 평가(strict evaluation)의 λ™μž‘ 방식과 비ꡐλ₯Ό 톡해 μ§€μ—° ν‰κ°€μ˜ λ™μž‘ 방식을 μ•Œμ•„λ³΄μž.

β€‹λ‹€μŒ μ½”λ“œμ™€ 같이, 0~5둜 이루어진 λ°°μ—΄μ—μ„œ 각 μ›μ†Œμ— λŒ€ν•΄ 10을 κ³±ν•œ λ’€ ν™€μˆ˜λ§Œ κ³ λ₯΄κ³  숫자λ₯Ό 문자둜 λ°”κΎΈκ³  첫2개만 μΆ”μΆœν•˜λŠ” λ‘œμ§μ„ μžλ°”μŠ€ν¬λ¦½νŠΈ ES6와 Lodash μ—μ„œ μ–΄λ–€μ‹μœΌλ‘œ 평가가 λ˜λŠ”μ§€ μ•Œμ•„λ³΄μž.

const arr = [0, 1, 2, 3, 4, 5]

const result = arr
    .map(num => num + 10) 
    .filter(num => num % 2) 
    .slice(0, 2)
                
console.log(result)

 

ES6 μ—„κ²©ν•œ 평가

μ—„κ²©ν•œ ν‰κ°€λŠ” μ§€μ—° ν‰κ°€μ˜ λ°˜λŒ€λ§λ‘œ, μš°λ¦¬κ°€ 일반적으둜 μƒκ°ν•˜λŠ” 평가 방식이닀. μ—„κ²©ν•œ ν‰κ°€λŠ” 평가 흐름이 μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ 흐λ₯Έλ‹€.

const arr = [0, 1, 2, 3, 4, 5]

const result = arr
    .map(num => num + 10)     // 1. λͺ¨λ“  λ°°μ—΄μ›μ†Œμ— λŒ€ν•΄ 10을 λ”ν•œλ‹€ → [0, 11, 12, 13, 14, 15]
    .filter(num => num % 2)   // 2. λͺ¨λ“  λ°°μ—΄μ›μ†Œμ— λŒ€ν•΄ ν™€μˆ˜λ§Œ κ΅¬ν•œλ‹€ → [11, 13, 15]
    .slice(0, 2)              // 3. λͺ¨λ“  λ°°μ—΄μ›μ†Œμ— λŒ€ν•΄ 2개만 μΆ”μΆœν•œλ‹€ → [11, 13]
                
console.log(result) // [11, 13]

lodash-Lazy-Evaluation
평가 흐름이 μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ 흐λ₯Έλ‹€.

μœ„ μ‹€ν–‰ 흐름을 보면 map, filter, slice κ³ μ°¨ν•¨μˆ˜λ“€μ΄ κ°κ°μ˜ 계산이 λͺ¨λ‘ μ’…λ£Œλ˜μ–΄μ•Ό λ‹€μŒ 단계λ₯Ό μˆ˜ν–‰ν•˜λŠ” 것을 λ³Ό 수 μžˆλ‹€. κ·Έλž˜μ„œ 총 계산 횟수λ₯Ό κ΅¬ν•˜μžλ©΄ map 6번 + filter 6번 + slice 2번으둜 총 14번이 λœλ‹€.

 

μ½”λ“œ μƒμœΌλ‘œλŠ” λ¬Έμ œλŠ” μ—†μ§€λ§Œ 생각을 ν™˜κΈ°ν•΄λ³΄μž.

μ΅œμ’… 결과물인 11κ³Ό 13을 μ–»κΈ° μœ„ν•΄μ„œ map을 톡해 λͺ¨λ“  λ°°μ—΄ μ›μ†Œμ— λ§μ…ˆμ„ ν•˜κ³ , ν™€μˆ˜λ₯Ό κ΅¬ν•˜κΈ° μœ„ν•΄ λͺ¨λ“  λ°°μ—΄ μ›μ†Œμ— ν•„ν„°λ§μ„ν•˜μ˜€λ‹€. ν•˜μ§€λ§Œ μœ„μ˜ 그림을 보면 11κ³Ό 13을 μ–»κΈ° μœ„ν•΄ λ°°μ—΄ μ›μ†Œ 0 ~ 3 κΉŒμ§€λŠ” 평가 λ˜μ•Ό ν• ν…Œμ§€λ§Œ κ·Έ λ’€μ˜ 4와 5λŠ” λ…Όλ¦¬μ μœΌλ‘œ 평가할 ν•„μš”κ°€ μ—†μ–΄ 보인닀. 즉, μ“Έλ°μ—†λŠ” 연산이 λ“€μ–΄κ°„ 것과 κ°™λ‹€.

 

Lodash μ§€μ—° 평가

λ‹€μŒμ€ μœ„μ˜ 예제λ₯Ό Lodash ν•¨μˆ˜λ₯Ό μ΄μš©ν•œ μ§€μ—° 평가λ₯Ό ν™œμš©ν•΄ 예제λ₯Ό μˆ˜ν–‰ν•œ μ½”λ“œμ΄λ‹€.

Loadash λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œλŠ” _.chain , _.value ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ μ§€μ—° 평가λ₯Ό κ΅¬ν˜„ν•  수 μžˆλ‹€. chain ν•¨μˆ˜μ˜ 인자둜 μ‚¬μš©ν•  데이터λ₯Ό λ„˜κΈ°λ©΄ ν•΄λ‹Ή 데이터λ₯Ό lodash 객체둜 감싼닀. 이후 μ œκ³΅λ˜λŠ” μ§€μ—° 평가 ν•¨μˆ˜λ“€μ„ μ΄μš©ν•΄ μ›ν•˜λŠ” 결과둜 κ°€κ³΅ν•œλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ value ν•¨μˆ˜λ₯Ό 톡해 wrapping λ˜μ–΄ μžˆλŠ” κ²°κ³Όλ₯Ό μ‹€μ œ κ°’μœΌλ‘œ μΉ˜ν™˜ν•œλ‹€.

const arr = [0, 1, 2, 3, 4, 5]

const result = _.chain(arr)   // μΌλ°˜λ°°μ—΄μ„ lodash 체인 객체둜 λ³€ν™˜ν•œλ‹€.
    .map(num => num + 10)     
    .filter(num => num % 2)   
    .take(2)                  
    .value()                  // μ§€μ—° ν‰κ°€λœ κ²°κ³Όκ°€ ν‰κ°€λ˜μ–΄ μ‹€μ œκ°’μœΌλ‘œ λ³€ν™˜
    
console.log(result) // [11, 13]

lodash-Lazy-Evaluation
평가 흐름이 μœ„μ—μ„œ μ•„λž˜λ‘œ 흐λ₯Έλ‹€

μœ„ κ·Έλ¦Όμ—μ„œ λ³Ό 수 μžˆλ“―μ΄ μ§€μ—° ν‰κ°€λŠ” 평가 흐름은 μœ„μ—μ„œ μ•„λž˜λ‘œ 흐λ₯Έλ‹€. μ΄λŸ¬ν•œ νλ¦„μ˜ μž₯점은 λ°°μ—΄ μ›μ†Œ 3 κΉŒμ§€ 평가가 μ™„λ£Œλ˜μ—ˆμ„ λ•Œ 이미 μ›ν•˜λŠ” κ²°κ³Όκ°€ λ‚˜μ™”κΈ° λ•Œλ¬Έμ—, 4와 5에 λŒ€ν•œ 계산은 ν•˜μ§€ μ•ŠλŠ”κ±Έ λ³Ό 수 μžˆλ‹€. λ”°λΌμ„œ 총 계산 횟수λ₯Ό κ΅¬ν•˜μžλ©΄ map 4번 + filter 4번 + slice 2번으둜 총 10번이 λœλ‹€.

μ •λ¦¬ν•˜μžλ©΄ μ§€μ—° 평가λ₯Ό μ‚¬μš©ν•˜λ©΄ ν•„μš”ν•œ κ³„μ‚°λ§Œ μˆ˜ν–‰ν•˜λ―€λ‘œ, μ„±λŠ₯ ν–₯상에도 도움이 되게 λœλ‹€.


μ§€μ—° 평가 λ™μž‘ 원리 #2

μ΄λ²ˆμ—” 쒀더 μ‹œκ°μ μœΌλ‘œ μ• λ‹ˆλ©”μ΄μ…˜μ„ 톡해 μ§€μ—° ν‰κ°€μ˜ μ„±λŠ₯ ν–₯상 원리에 λŒ€ν•΄ μ‚΄νŽ΄λ³΄μž

 

ES6 μ—„κ²©ν•œ 평가

μ•„λž˜ μ• λ‹ˆλ©”μ΄μ…˜μ„ 보면 λ¨Όμ € λͺ¨λ“  λ°°μ—΄ μ›μ†Œμ— λŒ€ν•΄ 10보닀 μž‘μ€ 숫자만 λ”°λ‘œ μž„μ‹œ 배열에 ν•œκΊΌλ²ˆμ— λͺ¨μ•„놓고 μ›μ†Œ 3개만 κ°€μ Έκ°€λŠ” 것을 λ³Ό 수 μžˆλ‹€. 즉, filterλ₯Ό λͺ¨λ‘ μˆ˜ν–‰ν•˜κ³  λ§ˆμ§€λ§‰μœΌλ‘œ sliceλ₯Ό μˆ˜ν–‰ν•œλ‹€.

const arr = [4, 15, 20, 7, 3, 13, 2, 20]

const result = arr
    .filter(num => num < 10)  // 1. 10보닀 μž‘μ€ 숫자만 필터링 → [4, 7, 3, 2]
    .slice(0, 3)              // 2. 3개만 고름 → [4, 7, 3]

console.log(result); // [4, 7, 3]

lodash-Lazy-Evaluation

 

Lodash μ§€μ—° 평가

μ§€μ—° 평가가 적용된 μ• λ‹ˆλ©”μ΄μ…˜μ„ 보면 λ°°μ—΄ μ›μ†Œλ§ˆλ‹€ 각각 filterλ₯Ό ν•œλ²ˆ μˆ˜ν–‰ν•˜κ³  sliceλ₯Ό μˆ˜ν–‰ν•˜κ³  λ‹€μŒ μ›μ†Œλ‘œ λ„˜μ–΄κ°€λŠ” μ‹μœΌλ‘œ 평가가 μˆ˜ν–‰λ¨μ„ λ³Ό 수 μžˆλ‹€. 그리고 남은 13, 2, 20 μ›μ†Œμ— λŒ€ν•΄μ„  평가 연산을 μ μš©ν•˜μ§€ μ•Šμ•„ 훨씬 더 λΉ λ₯΄κ²Œ 결과값을 μ–»κ²Œ λœλ‹€.

const arr = [4, 15, 20, 7, 3, 13, 2, 20];

const result = _.chain(arr)
  .filter(num => num < 10) // μ§€μ—° 평가λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€.
  .take(3)
  .value(); // 평가λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€.

console.log(result); // [4, 7, 3]

lodash-Lazy-Evaluation

 

이처럼 μ§€μ—° ν‰κ°€λŠ” μ—°μ‚° 횟수 자체λ₯Ό 쀄여 보닀 쒋은 μ„±λŠ₯을 μ€€λ‹€. 그리고 μ§€μ—° ν‰κ°€λŠ” λŒ€μƒμ΄ 크면 클수둝 κ·Έ 효과λ₯Ό λ°œνœ˜ν•œλ‹€.

λ‹€μŒμ€ μœ„μ˜ 예제 μ½”λ“œμ—μ„œ λ°°μ—΄μ˜ μ›μ†Œ 개수λ₯Ό 1,000,000개둜 μˆ˜μ •ν•œ λ’€ μ μš©ν•œ μ„±λŠ₯ 비ꡐ 결과이닀. κ·Έλž˜ν”„μ—μ„œ 보듯 μ§€μ—° 평가가 압도적인 μ„±λŠ₯을 μžλž‘ν•œλ‹€.

lodash-지연평가


# 참고자료

https://lodash.com/docs/4.17.15#chain

https://blog.weirdx.io/post/16662

https://armadillo-dev.github.io/javascript/whit-is-lazy-evaluation/