Use function composition in JavaScript - Lee-hyuna/33-js-concepts-kr GitHub Wiki

Use function composition in JavaScript μžλ°”μŠ€ν¬λ¦½νŠΈ ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜ μ‚¬μš©

μ „μ œ 쑰건 :이 κ²Œμ‹œλ¬Όμ—μ„œ currying(컀링)을 μ‚¬μš©ν•˜λ―€λ‘œ 이에 λŒ€ν•΄ λͺ¨λ₯΄λŠ” 경우 이전 κ²Œμ‹œλ¬Ό 인 Javascript의 Currying을 읽어 λ³΄μ‹œκΈ° λ°”λžλ‹ˆλ‹€.

ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ΄λž€ λ¬΄μ—‡μž…λ‹ˆκΉŒ?

ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ€ μ—¬λŸ¬ κ°„λ‹¨ν•œ ν•¨μˆ˜λ₯Ό κ²°ν•©ν•˜μ—¬ 더 λ³΅μž‘ν•œ ν•¨μˆ˜λ₯Ό λ§Œλ“œλŠ” λ©”μ»€λ‹ˆμ¦˜μž…λ‹ˆλ‹€. 각 ν•¨μˆ˜μ˜ κ²°κ³ΌλŠ” λ‹€μŒ ν•¨μˆ˜λ‘œ μ „λ‹¬λ©λ‹ˆλ‹€. μˆ˜ν•™μ—μ„œ μš°λ¦¬λŠ” μ’…μ’… λ‹€μŒκ³Ό 같이 μ”λ‹ˆλ‹€ : f(g(x)). 이것은 f둜 전달 된 g(x)의 κ²°κ³Όμž…λ‹ˆλ‹€. ν”„λ‘œκ·Έλž˜λ°μ—μ„œ λΉ„μŠ·ν•œ 것을 μ“°λ©΄μ„œ κ·Έ μ»΄ν¬μ§€μ…˜μ„ 달성할 수 μžˆμŠ΅λ‹ˆλ‹€.
κ°„λ‹¨ν•œ 예λ₯Ό λ“€μ–΄ λ΄…μ‹œλ‹€. 2 + 3 * 5 연산을 μˆ˜ν–‰ν•˜μ—¬ μ‚°μˆ μ„ν•΄μ•Όν•œλ‹€κ³  κ°€μ •ν•©μ‹œλ‹€. μ•„μ‹œλ‹€μ‹œν”Ό κ³±μ…ˆμ€ λ§μ…ˆλ³΄λ‹€ μš°μ„ μž…λ‹ˆλ‹€. λ”°λΌμ„œ 3 * 5λ₯Ό 계산 ν•œ λ‹€μŒ 결과에 2λ₯Ό μΆ”κ°€ν•˜λ©΄λ©λ‹ˆλ‹€. 이것을 JavaScript둜 μž‘μ„±ν•΄ λ΄…μ‹œλ‹€. 기본적이고 κ°€μž₯ κ°„λ‹¨ν•œ 방법은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

const add = (a, b) => a + b;
const mult = (a, b) => a * b;
add(2, mult(3, 5))

이것은 addν•¨μˆ˜μ— 전달 된 κ³±μ…ˆμ˜ 결과이기 λ•Œλ¬Έμ— ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ˜ ν•œ ν˜•νƒœμž…λ‹ˆλ‹€.
ν•œ 걸음 더 λ‚˜μ•„κ°€μ„œ ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ΄ 맀우 μœ μš©ν•  수 μžˆλŠ” λ‹€λ₯Έ 사둀λ₯Ό 보도둝 ν•©μ‹œλ‹€. 이제, μ‚¬μš©μž λͺ©λ‘μ„ 가지고 있고 κ·Έμ€‘μ—μ„œ λͺ¨λ“  성인인 μ‚¬μš©μžμ˜ 이름을 μΆ”μΆœν•΄μ•Ό ν•œλ‹€κ³  κ°€μ •ν•΄λ΄…μ‹œλ‹€. λ‚˜λŠ” 개인적으둜 λ‹€μŒκ³Ό 같이 μ“Έ κ²ƒμž…λ‹ˆλ‹€.

const users = [
  { name: "Jeff", age: 14 },
  { name: "Jack", age: 18 }, 
  { name: "Milady", age: 22 },
]
const filter = (cb, arr) => arr.filter(cb);
const map = (cb, arr) => arr.map(cb);

map(u => u.name, filter(u => u.age >= 18, users)); //["Jack", "Milady"]

쒋은 λ°©λ²•μ΄μ§€λ§Œ, μ»΄ν¬μ§€μ…˜μ„ μžλ™ν™”ν•˜λ©΄ 더 쒋을 수 μžˆμŠ΅λ‹ˆλ‹€. 적어도 더 읽기 μ‰¬μšΈ 수 μžˆμŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜ μžλ™ν™”

λ”°λΌμ„œ 이 μ„Ήμ…˜μ˜ λͺ©ν‘œλŠ” λ‘˜ μ΄μƒμ˜ ν•¨μˆ˜λ₯Ό κ°€μ Έμ™€μ„œ composeκ³ μ°¨ ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 미래 ν•¨μˆ˜μ˜ μ΅œμ’… μ„œλͺ…을 μ •μ˜ν•΄ λ΄…μ‹œλ‹€ :

compose(function1, function2, ... , functionN): Function

예λ₯Ό λ“€μ–΄ λ‹€μŒκ³Ό 같이 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ €κ³ ν•©λ‹ˆλ‹€.

compose(add1, add2)(3) //6  

λ”°λΌμ„œ μ΄λŸ¬ν•œ ν•¨μˆ˜μ˜ κ΅¬ν˜„μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

const compose = (...functions) => args => functions.reduceRight((arg, fn) => fn(arg), args);

ꡉμž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆκΉŒ?
이 단 ν•œ μ€„μ˜ ν•¨μˆ˜λ§ŒμœΌλ‘œ λ³΅μž‘ν•œ λ³€ν™˜μ„ μž‘μ„±ν•  수 μžˆλŠ” ν•¨μˆ˜λ₯Ό ꡬ성할 수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œ μΌμ–΄λ‚˜λŠ” 일을 μ„€λͺ…ν•˜κ² μŠ΅λ‹ˆλ‹€.

  • composeλŠ” κ³ μ°¨ ν•¨μˆ˜μž…λ‹ˆλ‹€. λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.

  • composeλŠ” μ—¬λŸ¬ ν•¨μˆ˜λ₯Ό 인수둜 μ·¨ν•΄μ„œ spread opeartor:...λ₯Ό μ‚¬μš©ν•˜μ—¬ ν•¨μˆ˜μ˜ λ°°μ—΄λ‘œ λ³€ν™˜ν•©λ‹ˆλ‹€.

  • 그런 λ‹€μŒ ν•΄λ‹Ή ν•¨μˆ˜μ— κ°„λ‹¨νžˆ reduceRightλ₯Ό μ μš©ν•©λ‹ˆλ‹€. 콜백의 첫 번째 맀개 λ³€μˆ˜λŠ” ν˜„μž¬ μΈμˆ˜μž…λ‹ˆλ‹€. 두 번째 μΈμˆ˜λŠ” ν˜„μž¬ ν•¨μˆ˜μž…λ‹ˆλ‹€. 그런 λ‹€μŒ ν˜„μž¬ 인수둜 각 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κ³  κ²°κ³ΌλŠ” λ‹€μŒ ν˜ΈμΆœμ— μ‚¬μš©λ©λ‹ˆλ‹€.

이제 이전 μ˜ˆμ œμ—μ„œ μš°λ¦¬λŠ” 이 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 더 읽기 μ‰½κ²Œ mapκ³Ό filter κΈ°λŠ₯을 μ •λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.

const filter = cb => arr => arr.filter(cb);
const map = cb => arr => arr.map(cb);

compose(
  map(u => u.name),
  filter(u => u.age >= 18)
)(users) //["Jack", "Milady"]

λ§ˆμ§€λ§‰ 예제λ₯Ό μ œμ•ˆν•©λ‹ˆλ‹€. 전톡적인 MapReduceλ₯Ό κ΅¬ν˜„ν•΄ λ΄…μ‹œλ‹€.

ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ„ 가진 MapReduce

MapReduce의 μ›λ¦¬λŠ” κ°„λ‹¨ν•©λ‹ˆλ‹€. 데이터 셋에 맡을 μ μš©ν•˜κ³  κ²°κ³Όλ₯Ό 쀄여 단일 κ²°κ³Όλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 이것이 일반적으둜 ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ˜ μ›λ¦¬μž…λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μš°λ¦¬λŠ” μ—¬λŸ¬ 단어듀을 μ„ΈκΈ° μœ„ν•΄ 전톡적 단어 μΉ΄μš΄ν„°λ₯Ό κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. map은 값을 λ§Œλ‚˜λ©΄ 1을 λ³΄λ‚΄μ•Όν•˜λ©° reduceλŠ” μ΅œμ’… 배열을 μš”μ•½ν•˜μ—¬ κ²°κ³Όλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

const reduce = cb => arr => arr.reduce(cb); //Just currify the reduce function

const mapWords = map(() => 1);
const reduceWords = reduce((acc, curr) => acc += curr)(0)

compose(reduceWords, mapWords)(['foo', 'bar', 'baz']); //3

Pipe or composition?(νŒŒμ΄ν”„ ν˜Ήμ€ μ»΄ν¬μ§€μ…˜)

Yeiner Canoκ°€ λ¨Όμ € composeλŒ€μ‹  pipe둜 κ΅¬ν˜„ν–ˆλ‹€κ³  μ–ΈκΈ‰ ν•œ 이후이 뢀뢄을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€. 이 기사 λ°”λ‘œ μ•„λž˜μ—μ„œ 그의 μ˜κ²¬μ„ 읽을 수 μžˆμŠ΅λ‹ˆλ‹€.

λ”°λΌμ„œ compose와 pipe의 μ£Όμš” 차이점은 μ»΄ν¬μ§€μ…˜ μˆœμ„œμž…λ‹ˆλ‹€. PipeλŠ” μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ μ»΄ν¬μ§€μ…˜μ„ μˆ˜ν–‰ν•˜λ©° ComposeλŠ” 였λ₯Έμͺ½μ—μ„œ μ™Όμͺ½μœΌλ‘œ ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. pipe κ³ μ°¨ ν•¨μˆ˜λ₯Ό μž‘μ„±ν•΄ λ΄…μ‹œλ‹€ :

const pipe = (...functions) => args => functions.reduce((arg, fn) => fn(arg), args);

λ”°λΌμ„œ 이 경우 reduceRightλŒ€μ‹  reduceλ₯Ό μ‚¬μš©ν•˜μ—¬ μ»΄ν¬μ§€μ…˜μ„ μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ μˆ˜ν–‰ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ μƒˆλ‘œ λ§Œλ“  ν•¨μˆ˜λ₯Ό 이전 μ˜ˆμ œμ— 적용 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

pipe(
  filter(u => u.age >= 18),
  map(u => u.name),
)(users) //["Jack", "Milady"]

pipe(mapWords, reduceWords)(['foo', 'bar', 'baz']);

μ–΄λ–€ μ‚¬λžŒλ“€μ€ compose보닀 pipeλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 더 읽기 μ‰½μŠ΅λ‹ˆλ‹€. 적어도 μš°λ¦¬λŠ” 더 μžμ—°μŠ€λŸ½λ‹€λŠ” 것에 λ™μ˜ ν•  수 μžˆμŠ΅λ‹ˆλ‹€!

κ²°λ‘ 

이 μ˜ˆμ œλŠ” μ‚¬μ†Œν•œ μ˜ˆμ΄μ§€λ§Œ 더 λ³΅μž‘ν•œ μ˜ˆμ œμ—μ„œλ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. composeν•¨μˆ˜λŠ” lodash λ˜λŠ” ramda와 같은 κ°€μž₯ κΈ°λŠ₯적인 λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ κ΅¬ν˜„λ©λ‹ˆλ‹€. ν•¨μˆ˜ μ»΄ν¬μ§€μ…˜μ— λŒ€ν•œ λ³€ν˜•μ„ 찾을 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ RamdaλŠ” promisesλ₯Ό λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜λ₯Ό μž‘μ„±ν•  μˆ˜μžˆλŠ”composeP ν•¨μˆ˜λ₯Ό μ œμ•ˆν•©λ‹ˆλ‹€. http://ramdajs.com/docs/#composeP

λ²ˆμ—­ μ£Όμ†Œ : https://www.codementor.io/michelre/use-function-composition-in-javascript-gkmxos5mj