[JS]함수형프로그래밍

이번 글에서는 자바스크립트의 함수형 프로그래밍에 대해 알아볼 예정입니다.
여러 자료를 찾아보고 공부하며 생각을 정리하여 쓴 글이라 오타와 오류가 있을 수 있습니다.
언제나 오타와 내용의 오류 지적은 환영입니다 :)

함수형 프로그래밍

함수형 프로그래밍을 위해선 크게 세가지 개념이 필요합니다.

  1. 순수함수(pure function)
    부작용(side-effect)이 없는 함수, 즉, 함수의 실행이 외부에 영향을 끼치지 않는 함수를 뜻 합니다. 따라서 순수한 함수는 스레드에 안전하고, 병렬적인 계산이 가능합니다.
  2. 익명함수(anonymous function)
    이름이 없는 함수를 뜻 합니다. 이는 함수명을 메모리에 할당하지 않을 수 있다는 장점이 있습니다.
  3. 고계함수(higher-order function)
    함수를 다루는 함수를 뜻 합니다. 함수형 언어에서는 함수도 ‘값(value)’으로 취급합니다. 예를 들어, 정수 1이나 인수를 제곱하는 함수나 동등한 입장에서 다룰 수 있습니다.
    정수를 함수의 인수로 전달할 수 있듯이 어떤 함수도 다른 함수의 인수로 전달할 수 있습니다. 마찬가지로 함수의 결과 값으로 정수를 반환할 수 있듯이 함수를 반환할 수도 있습니다.

자바스크립트의 함수형 프로그래밍

자바스크립트에서도 함수형 프로그래밍이 가능합니다. 그 이유는 자바스크립트가 다음을 지원하기 때문입니다.

  • 일급 객체로서의 함수
  • 클로저
  • Arrow function 등의 익명함수

함수형 프로그래밍의 특징 3가지와 밀접한 세가지 기능으로 각각 일급 객체로서의 함수는 고계함수의 특성, 클로저는 순수함수의 특성, 화살표함수등의 익명함수는 익명함수의 특성을 가지고 있습니다.


일급객체로서의 함수

함수가 일급 객체로 취급된다는 이야기 입니다.
특정 언어의 일급 객체 (first-class citizens, 일급 값, 일급 엔티티, 혹은 일급 시민)라 함은 일반적으로 다른 객체들에 적용 가능한 연산을 모두 지원하는 객체를 가리킵니다.
함수에 매개변수로 넘기기, 변수에 대입하기와 같은 연산들이 여기서 말하는 일반적인 연산의 예에 해당합니다.
아래는 좀더 구체적인 일급객체의 특징을 정리한 것입니다.

  • 특정 언어의 일급 객체 특징
    • 변수나 데이터 구조안에 담을 수 있다.
    • 파라미터로 전달 할 수 있다.
    • 반환값(return value)으로 사용할 수 있다.
    • 할당에 사용된 이름과 관계없이 고유한 구별이 가능하다.
    • 동적으로 프로퍼티 할당이 가능하다.

가장 간단한 스칼라 타입인 정수(Integer)나 실수(Floating point number)의 경우 거의 모든 언어에서 항상 일급 객체에 해당합니다.

자바스크립트에서는 특별히 함수도 일급객체로 취급된다는 점이 함수형 프로그래밍이 가능한 이유입니다.
이런 경우 함수의 인자로 함수를 넘길수도 있고, 결과로 함수를 반환할 수도 있습니다.

클로저(Closure)

사실 클로저는 위에서 언급한 일급객체로서의 함수가 존재하지 않으면 성립할 수 없는 개념입니다.
리턴으로 함수를 통째로 반환할 수 있는 성질을 이용한 것으로, 이미 생명 주기가 끝난 외부 함수의 변수를 참조하는 함수를 클로저라고 합니다.

1
2
3
4
5
6
7
8
9
function 외부함수() {
var 외부함수의지역변수 = "something";
return function() { // 클로저
/* 외부함수의지역분수, 외부함수의 arguments와 arguments를 활용한 로직 */
}
}
var 사용할함수 = 외부함수();
/* 외부함수의 실행 컨텍스트가 끝남 */
사용할함수(); // 실행

위의 코드를 보면 외부함수의 호출이 이루어지고, 이 외부 함수에서 사용할함수가 반환됩니다.
반환된 함수가 클로저이고 사용할함수로 사용됩니다.
이렇게 최종 반환되는 함수가 외부함수의 지역변수에 접근하고 있다는 것이 중요한데, 이 지역변수에 접근하려면, 함수가 종료되어 외부 함수의 컨텍스트가 반환되더라도 변수 객체는 내부 함수의 스코프 체인에 그대로 남아있어야만 접근할 수 있습니다.
이 클로저가 자바스크립트의 함수형 프로그래밍에서 중요한 이유는 위에 언급했듯이 순수함수의 기능을 갖고 있기 때문입니다.
클로저는 참조되는 외부변수를 내부에 감싸고 참조만하면서 실행되는 시점과 상관없이 항상 일정한 결과값을 주기 때문에 이를 순수함수라 할 수 있습니다.
다만, 모든 클로저가 순수함수인것은 아닙니다.
클로저를 만들때 순수함수가 되도록 작성해주어야 사용자들이 원하는 대로 함수형프로그래밍 패러다임에 따라 동작하게 될 것입니다.

익명함수

익명함수는 사실 효율성에 초점이 맞춰져 있다고 생각합니다.(함수형 프로그래밍을 하려면 필수로 알아야한다고 생각)
주로 일급객체로서 함수를 다루다보면 리턴시 혹은 함수의 파라미터에 함수를 넣을때 굳이 변수로 따로 할당을 하지 않고 싶을때라던가 스코프를 나눌때 사용합니다.
함수형 프로그래밍에서 익명함수는 그 자체가 함수가 할당된 변수와 같다고 생각하고 사용하시면 됩니다.
사실 익명함수가 있기에 위에 나온 클로저라던가 일급객체로서의 함수가 더 효율적으로 활용된다고 생각합니다.


함수형 프로그래밍 활용

간단한 알고리즘 문제를 예로 들어보겠습니다. 클로저와 cache를 활용한 피보나치와 팩토리얼 함수가 있습니다.
이 두 함수는 초기값이 있고, 캐쉬를 활용하며, 재귀 호출을 활용한다는 공통점이 있습니다.
차이점은 내부의 산술식이 다르다는 것이죠.
그 공통점을 함수형 프로그래밍을 활용해 구현해보면 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var functional = function(cache, func) {
var calculate = function(n) {
var result = 0;
if (typeof(cache[n]) === 'number') result = cache[n];
else result = cache[n] = func(calculate, n);
return result;
}
return calculate;
};
var factorial = functional({'0':1}, function(func, n) {
return n * func(n-1);
});
var fibonacci = functional({'0':0, '1':1}, function(func, n) {
return func(n-1) + func(n-2);
});

console.log(factorial(10));
console.log(fibonacci(10));

위 코드를 간단히 설명하자면,
functional이라는 함수를 정의하여 각 계산에 필요한 함수를 입력하여 활용할 수 있도록틀을 만들고, factorial과 fibonacci라는 함수를 정의할때 기존에 있는 functional함수를 이용하여 각각 독립적인 로직을 갖는 구현체를 만드는 개념입니다.

좀 더 자세히 위의 코드를 분석하자면,
처음에 큰 그림이 되는 functional이라는 함수를 선언합니다. 이때 리턴값은 calculate라는 함수가 됩니다.
그 후 각각 factorial과 fibonacci라는 변수를 선언시 위의 functional함수에 인자로 원하는 캐쉬와 산술식을 넣고 실행합니다.
실행결과로 해당 factorial과 fibonacci 변수에는 각각 functional함수를 실행할때 리턴된 calculate함수를 할당받습니다.
calculate함수를 실행할땐 n이라는 변수 하나가 필요합니다.
따라서 각 함수를 실행시킬때 원하는 n 값을 입력하여 실행하면 우리가 원하는 결과값을 리턴받을 수 있습니다.


이 글을 쓰며 도움받은 레퍼런스

Share 0 Comments