함수형 프로그래밍

김동하·2022년 1월 29일
0

함수형프로그래밍

목록 보기
2/4

함수형 자바스크립트 기본

  • 평가 : 코드가 계산되어 값을 만드는 것
  • 일급 : 값으로 다룸, 변수에 담을 수 있음, 함수의 인자로 사용 가능, 함수의 결과로 사용 가능
const a = 10
const add10 = a => a + 10
const res = add10(a)
  • 일급함수 : 함수를 값으로 다룰 수 있다. 조합성과 추상화의 도구로 사용됨
const add5 = a => a +5
add5
add5(5)

const f1 = () => () => 1
const f2 = f1()
  • 고차함수 : 함수를 값으로 다루는 함수

    • 함수를 인자로 받아서 실행하는 함수

      • apply

        const apply1 = f => f(1)
         const add2 = a => a + 2
         apply1(add2) // 3
      • times

        const times = (f, n) => {
         let i = -1
         
         while(++i < n){
           f(i)
         }
        }
        
        times(console.log, 3) // 1, 2, 3

        이러한 프로그래밍을 어플리케이티브 프로그래밍이라고도 함

    • 함수를 만들어서 리턴 (클로저 만들어 리턴)

    const addMaker = a => b => a + b
    const add10 = addMaker(10)
    add10(10) // 20

ES6 리스트 순회

이터러블

1) Array

const arr = [1, 2, 3]
for(const a of arr) console.log(a)

Array는 key로 접근해서 내부 요소 조회

2) Set

const set = new Set([1, 2, 3])
for(const a of set) console.log(a)

Set은 key로 접근하지 않음 -> for문으로 순회하는 것임 아님

3) Map

const map = new Map([["a", 1], ["b", 2], ["c",3]])
for(const a of map) console.log(a)
  • Symbol.iterator

arr[Symbol.iterator] = null을 할 경우 이터러블이 아니라고 에러남

Array, Map, Set은 이터러블/이터러블 프로토콜을 따른다.

이터러블이란 이터레이터를 리턴하는 [Symbol.iterator]()를 가진 값

이터레이터{value, done} 이라는 객체를 리턴하는 next()를 가진 값

예시)

let iterator = arr[Symbol.iterator]()
iterator.next() // {value:1, done:false}
iterator.next() // {value:2, done:false}
iterator.next() // {value:3, done:false}
iterator.next() // {value:undefined, done:true}

이터러블/이터레이터 프로토콜은 이터러블을 for..of, 전개 연산자 등과 함께 동작하도록 하는 규약

사용자 정의 이터러블 구현

const iterable = {
  [Symbol.iterator](){
    let i = 3
    return {
      next(){
        return i===0 ? {done:true}:{
          value:i--, done:false
        }
      }
    }
  }
}

let iterator = iterable[Symbol.iterator]();


for(const a of iterable) console.log(a) //1, 2, 3

여기까지는 ES6에서 제공하는 이터레이터와 비슷하다. 다시 기존의 이러레이터를 보면

const arr2 = [1, 2, 3]
let iter2 = arr2[Symbol.iterator]()

// 이터레이터를 미리 한 번 실행함
iter2.next()

// 그리고 for of 를 진행
for(const a of iter2){
  console.log(a) // 2,3
}

// 먼저 진행된 1을 제외하고 리턴
       

잘 구현 된 이터레이터는 실행을 진행하고, 멈추고를 자유롭게 할 수 있다.

즉, iter2[Symbol.iterator] === iter2true일 때 잘 만든 이터레이터다.

다시 사용자 정의 이터러블로 넘어가서

const iterable = {
  [Symbol.iterator](){
    let i = 3
    return {
      next(){
        return i===0 ? {done:true}:{
          value:i--, done:false
        }
      },
      [Symbol.iterator](){
        return this
      }
    }
  }
}

[Symbol.iterator]()를 리턴할 때 자기 자신도 리턴하게 한다.

-> DOM객체 순회 가능한 것도 웹 API 등도 이터러블 규약을 따르고 있어서

제너레이터

  • 이터레이터이자 이터러블을 생성하는 함수

즉, 이터레이터를 리턴하는 함수

function *gen(){
  yield 1;
  yield 2; 
  yield 3;
  
  // done:true 일 때 리턴함
  return 100 
  
}

let iter = gen()
iter.next()
iter.next()
iter.next()

// for of 결과와 동일함

어떠한 값도 제너레이터를 통해 순회 가능하게 만들 수 있다.

예시

제너레이터로 홀수만 발생시키는 함수

// 실행할 때 마다 i를 증가시킴
function* infinity(i = 0) {
  while (true) {
    yield i++;
  }
}

// iter를 돌다가 l를 만나면 멈춤
function* limit(l, iter) {
  for (const a of iter) {
    yield a;
    if (a === l) return;
  }
}

function* odds(l) {
  for (const a of limit(l, infinity(1))) {
    if (a % 2) yield a;
  }
}

지연평가

  • 코드의 평가를 지연함. L.map 이런 식으로 함수명에 L.을 붙임

예시

// range

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

// L.range

const L = {}

L.range = function *(l) {
  let i = -1
  while(++i < l){
    yield i 
  }
}
profile
프론트엔드 개발

0개의 댓글