본문 바로가기
memo/wanted 백엔드 1월 - 함수형 프로그래밍

함수형 프로그래밍 세션1-1

by 킹차니 2023. 1. 11.

함수형 프로그래밍은 말 그대로 함수가 중요하다. 즉 우리는 세상의 문제를 해결하기 위해 복잡한 메커니즘을 따른다.

output = f(input) 와 같은 함수를 통해 결과를 만들어 낸다. 

 

함수형 프로그래밍 패러다임에는 중요한 것이 있다.

1. No side-effect : pure function, no mutation

- 사이드 이펙트를 제거하기 위해 순수 함수를 사용하고, 변화를 없애라!

2. Higher Order Function : Function is Value

- 고차 함수의 사용. 함수도 결국 값이다!

 

 

 

순수 함수

순수 함수는 외부 환경에 노출되지 않고, 외부에게 방해받지 않는 함수이다.

 

BAD

아래의 함수는 외부의 방해를 받는 함수이다.

함수 스코프 외부에 존재하는 init의 값이 변함에 따라 add 함수의 결과도 변하기 때문이다.

즉 위의 add 함수는 순수함수가 아니다.

 

GOOD

위 함수는 순수함수이다. init의 값이 아무리 변해도 add함수의 결과에 영향을 주지 않는다.

 

BAD

위 함수 역시 순수함수가 아니다. 이유는 2가지 인데 먼저 name 변수가 외부에 있다.

다음으로는 console.log는 런타임 환경에서 제공하는 함수인데 greet함수는 즉 런타임 환경에 의존하는 것이다.

 

GOOD

위 함수는 순수함수이다. 변할 수 있는 name변수와 함수(log)를 인자로 받는다.

 

 

No-mutation

이번에는 사이드 이펙트를 없애기 위한 조건인 no-mutation을 예시로 살퍄보자.

만약 fruits 배열에 제일 앞에 존재하는 과일을 반환해달라는 요구사항을 받았다고 해보자. 그럼 아래와 같이 할 수 있을 것이다.

BAD

하지만 위의 어레이가 제공하는 shifh를 사용한 head 함수는 본래의 fruits 배열을 변경시킨다. 만약 다른 곳에서 해당 배열을 사용하고 있었다면 이는 큰일이다.

 

GOOD

위의 head 함수는 원본 배열을 수정하지 않고 원했던 요구사항을 충족한다.

 

 

이제 과일 배열에 포도를 딸기로 바꿔주세요! 라는 요구사항이 왔다고 치자 그럼 아래와 같이 할 수 있을 것이다.

BAD

하지만 위와 같은 경우 역시 원본을 수정한다. 다른 곳에서 포도를 사용하고 있었다면 큰일이다.

 

GOOD

위와 같은 방법은 새로운 배열을 만들어 낸다. 하여 원본 배열을  수정하지 않는다.

 

 

고차함수

고차함수는 함수를 인자로 받고, 함수를 반환한다. 즉 이는 다른 함수에 의존하게 된다. 함수는 값이다. 예로 위에서 예시로 살펴본 greet함수와 map을 사용하는 함수도 함수를 넘겨주는데, 이도 고차함수라 할 수 있다.

 

사실 사이드 이펙트를 만들지 말아야 한다고 했는데, 사실 개발자가 하는 일이 사이드 이펙트를 만드는 것이다.
사용자의 요청이 오면 로그를 수집하고(사이드 이펙트1), DB에 저장하고(사이드 이펙트2), 메일을 보낸다(사이드 이펙트3).

 

OOP에 대해 살짝 짚고 넘어가기.

OOP에는 상속, 다형성 등 수 많은 개념이 존재한다. 근데 OOP를 시초라고 불리는 앨런 케이는 아래와 같은 말을 했다고 한다.

즉 OOP에서 중요한 것은 다른게 아니라 메시징이다!

 

즉 아래 그림과 같다. 세모든 네모든 같은 기능을 가지고 있다면 내부 구현이 다를지라도 클라이언트는 같은 결과를 기대한다. 

세모, 네모에게 메시징을 전달하여 어떤 일을 부탁한다.

OOP는 현실 세계를 모델링하여 메시징을 전달하는 것이다.

 

그렇다면 FP(함수형 프로그래밍)은 어떨까?

FP도 똑같다. 특정 Input을 주면 특정 output을 기대한다. OOP와 FP가 그렇게 다른 것이 아니라는 것이다.

 

즉 우리는 FP와 OOP 둘다 놓쳐서는 안된다. 실용주의 프로그래밍을 하자

 

실용주의 프로그래밍이란

새로운 기술의 도입은 새로운 책임이고, 

유지보수가 쉽고 코드 생산성이 높고 신뢰 가능하고,

나, 우리, 그리고 팀원이 원하는 의사결정(rule 정하기)을 바탕으로 하고, 

적당히 괜찮으면서도 변경이 쉬운 소프트웨어를 만드는 프로그래밍 방식이다.

 

정리하면 아래와 같다.

 

 

 

 

 

 

reference : 

 

 

 

 

코딩해보기:

1. map, filter, reduce 고차함수 구현해보기

 

만약 1, 2, 3, 4, 5가 순서대로 들어있는 어레이가 있고, 이에 대해 다음과 같은 요구사항이 있다고 해보자.

1. 어레이에서 홀수만 걸러주세요.

2. 걸러진 수에 2를 곱해주세요.

3. 남은 어레이의 요소들을 모두 더해주세요.

 

절차지향적으로 코딩한다면 아래와 같이 할 수 있다.

 

이를 map, filter, reduce를 사용하여 FP방식으로 해결한다면 아래와 같다.

코드의 한줄 한줄이 요구사항와 잘 맞게 떨어진다.

 

 

 

map, filter, reduce

이제 map, filter, reduce를 한번 구현해보자.

 

map

map

filter

filter

reduce

reduce

현재 위의 reduce에는 초기값인 acc 인자로 0을 넣어준다. 그런데 초기값을 넣어주지 않고도 동작하는 reduce를 만들기 위해서는 아래와 같이 하면 된다.

reduce

iter = acc[Symbol.iterator]();   // <--- acc 어레이를 iterator 객체로 만들어반환하여 iter 변수가 참조하게 한다.
acc = iter.next().value();           // <--- 이제 iter는 iterator 객체이기 때문에 next()메서드를 통해 처음부터 값을 순회할 수 있다.

https://pks2974.medium.com/javascript%EC%99%80-iterator-cdee90b11c0f

 

Javascript와 Iterator

자바스크립트를 처음부터 다시 생각하고 공부해 보려고 한다.

pks2974.medium.com

 

 

 

Pipe(함수의 합성)

- 순회 가능한 객체를 받아서 함수의 파이프라인을 타고 최종 결과값을 리턴한다.

 

 

 

curry

함수의 평가시점 미루기

커리는 함수에 적절한 인자가 들어오지 않는다면 실행을 미룬다.