함수형 프로그래밍 기본 함수

김준엽·2022년 7월 2일
0

함수형 프로그래밍에서 가장 기본적으로 사용하는 함수 map, filter, reduce를 구현하고 사용법을 알아보겠습니다.

map

map 함수는 원하는 형태로 값을 변경시킵니다.

[예제 1] 일반적인 스크립트 코드입니다.

const products = [
  { name: '반팔티', price: 15000 },
  { name: '긴팔티', price: 20000 },
  { name: '핸드폰케이스', price: 15000 },
  { name: '후드티', price: 30000 },
  { name: '바지', price: 25000 },
]

const names = []
for (const p of products) {
  names.push(p.name)
}
console.log(names)

products에서 name속성만 뽑아서 출력하는 코드입니다. 위 코드에서 map 함수를 만들어 사용해보겠습니다.

[예제 2] map함수를 구현하고 사용한 코드입니다.

const map = (f, iter) => {
  const res = []
  for (const a of iter) {
    res.push(f(a)) // 함수로 어떤 값을 반환할 지 결정
  }
  
  // 내부가 외부에 영향을 주는 포인트는 없어야 한다.
  // console.log(names) 
  
  return res
}

// products는 예제 1에서 사용한 변수를 이용
console.log(map((p) => p.name, products))
console.log(map((p) => p.price, products))

map 함수를 구현해서 원하는 형태로 변경할 수 있게 함수를 전달인자로 받아서 유연성을 더했습니다. console.log(names) 같은 함수 내부에서 외부에 영향을 주는 부분이 있으면 안됩니다. 전달인자로 로직을 구성해야 합니다.

map은 고차 함수 입니다.고차 함수(Higher order function)는 함수를 인자로 전달받거나 함수를 결과로 반환하는 함수를 말합니다.


다형성

이터러블 프로토콜을 따르는 함수가 어떻게 다형성이 높은 지 알아보겠습니다.

console.log([1, 2, 3].map((a) => a + 1)) // array prototype에 있는 map 함수를 사용

// map 함수는 예제 2에서 구현한 함수입니다.
console.log(document.querySelectorAll('*').map((el) => el.nodeName)) // error
console.log(map((el) => el.nodeName, document.querySelectorAll('*')))

function* gen() {
  yield 2
  yield 4
}
console.log(map((a) => a * a, gen()))

const m = new Map()
m.set('a', 10)
m.set('b', 20)
console.log(new Map(map(([k, v]) => [k, v * 2], m)))

log(document.querySelectorAll('*').map(el => el.nodeName))가 에러가 발생한 이유는 NodeList 프로토타입에 map함수가 없기 때문입니다. 그래서 앞에서 구현한 map함수에 값으로 전달해서 원하는 형태로 맵핑합니다. 예제 2에서 구현한 map함수는 이터러블 프로토콜을 따르는 객체(Array, String, Map, Set, NodeList 등)를 값으로 받을 수 있기 때문에 다양한 타입에 사용가능합니다.

이터러블 프로토콜을 따르는 함수들을 사용하는 것은 다른 함수들과의 조합성이 좋아지고 유연성, 다형성이 올라간다고 할 수 있습니다.


filter

filter는 조건에 만족하는 값을 찾는 함수입니다.

[예제 1] 일반적인 스크립트 코드입니다.

const products = [
  { name: '반팔티', price: 15000 },
  { name: '긴팔티', price: 20000 },
  { name: '핸드폰케이스', price: 15000 },
  { name: '후드티', price: 30000 },
  { name: '바지', price: 25000 },
]

const under20000 = []
for (const p of products) {
  if (p.price < 20000) under20000.push(p)
}
console.log(...under20000)

위 코드에서 filter 함수로 만들어 사용해보겠습니다.

[예제 2] filter 함수를 구현하고 사용한 코드입니다.

const filter = (f, iter) => {
  const res = []
  for (const a of iter) {
    if (f(a)) res.push(a)
  }
  return res
}

// products는 예제 1에서 사용한 변수를 이용
console.log(...filter((p) => p.price < 20000, products))

reduce

reduce는 줄이고 축약하는 함수입니다.

const reduce = (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 products = [
  { name: '반팔티', price: 15000 },
  { name: '긴팔티', price: 20000 },
  { name: '핸드폰케이스', price: 15000 },
  { name: '후드티', price: 30000 },
  { name: '바지', price: 25000 },
]
console.log(reduce((total, product) => total + product.price, 0, products))

const nums = [1, 2, 3, 4, 5]
const add = (a, b) => a + b
console.log(reduce(add, 0, nums))
console.log(reduce(add, nums))

전달 인자가 2개인 경우 전달 인자의 이터러블를 활용해 초기값을 설정했습니다.

profile
프론트엔드 개발자

0개의 댓글