자바스크립트는 다음을 지원하기 때문에 함수형 프로그래밍이 가능하다
var f1 = function (input) {
var result;
// 암호화 작업 수행
result = 1;
return result;
};
var f2 = function (input) {
var result;
// 암호화 작업 수행
result = 2;
return result;
};
var f3 = function (input) {
var result;
// 암호화 작업 수행
result = 3;
return result;
};
var get_encrypted = function (func) {
var str = "zzoon";
return function() {
return func.call(null, str);
}
};
var encrypted_value = get_encrypted(f1)();
console.log(encrypted_value); // 1
var encrypted_value = get_encrypted(f2)();
console.log(encrypted_value); // 2
var encrypted_value = get_encrypted(f3)();
console.log(encrypted_value); // 3
자바스크립트에서 함수는 일급 객체이므로 함수의 인자로 함수를 넘기고, 결과로 함수를 반환할 수도 있다
변수 str 값이 영향을 받지 않게 하려고 클로저를 사용하였다
get_encrypted() 함수에서 반환하는 익명 함수가 클로저이다
이 클로저에서 접근하는 변수 str은 자유변수이며 외부에서 접근할 수 없으므로 클로저로 함수형 프로그래밍의 개념을 정확히 구현할 수 있다
다음 코드는 일반적으로 배열의 합을 구할 때 작성하는 코드이다
function sum(arr) {
var len = arr.length;
var i = 0;
var sum = 0;
for (; i < len; i++)
sum += arr[i];
return sum;
}
var arr = [1, 2, 3, 4];
console.log(sum(arr));
배열의 원소를 모두 곱한 값을 구하고 싶어졌다
function multiply(arr) {
var len = arr.length;
var i = 0;
var result = 0;
for (; i < len; i++)
result *= arr[i];
return result;
}
var arr = [1, 2, 3, 4];
console.log(multiply(arr));
이 역시 간단하다
이 두 코드는 모두 명령형 프로그래밍 방식으로 작성한 코드이다
문제 하나하나를 각각의 함수를 구현하여 문제를 풀고 있고, 배열의 각 원소를 또 다른 방식으로 산술하여 결고값을 얻으려면 새로운 함수를 다시 구현해야 한다
이를 함수형 프로그래밍 방식으로 해결 할 수 있다
function reduce(func, arr, memo) {
var len = arr.length;
var i = 0;
var accum = memo;
for (; i < len; i++)
accum = func(accum, arr[i])
return accum;
}
var sum = function (x, y) {
return x + y;
};
var multiply = function (x, y) {
return x * y;
};
var arr = [1, 2, 3, 4];
console.log(reduce(sum, arr, 0));
console.log(reduce(multiply, arr, 1));
위와 같이 함수형 프로그래밍을 이용하여 코드를 훨씬 간결하게 작성할 수 있고 다른 문제가 나오더라도 사용자가 해당 연산을 하는 함수를 작성하여 reduce() 함수로 결과를 얻을 수도 있다
이처럼 함수형 프로그래밍은 기존 프로그래밍 방식보다 한단계 높은 모듈화를 이룰 수 있다
다음은 팩토리얼을 명령형 프로그래밍 방식으로 구현한 코드이다
// 일반
function factorial(num) {
var value = 1;
for (var i = 2; i <= num; i++)
value = value * i;
return value;
}
console.log(factorial(3));
// 재귀
function recursive_factorial(num) {
if (num === 0)
return 1;
else
return num * recursive_factorial(num - 1);
}
console.log(recursive_factorial(5));
이 두 예제는 큰 무리 없이 팩토리얼을 구현하지만 이를 함수형 프로그래밍 방법으로 구현하여 성능이 향상될 수 있다
먼저 팩토리얼의 특성을 살펴볼 필요가 있다
처음 10!을 실행한 후 20!을 실행하면 20!을 실행할 때는 앞에서 실행한 10!을 중복하여 계산한다
이렇게 중복되는 값, 즉 앞서 연산한 결과를 캐시에 저장하여 사용할 수 있는 함수를 작성한다면 성능 향상에 도움이 된다
var factorial = function () {
var cache = {
'0': 1
};
var func = function (n) {
var result = 0;
if (typeof (cache[n]) === 'number')
result = cache[n];
else {
result = n * func(n-1);
cache[n] = result;
}
return result;
};
return func;
}();
console.log(factorial(10));
console.log(factorial(20));
위 예제에서 factorial은 cache에 접근할 수 있는 클로저를 반환받는다
클로저로 숨겨지는 cache에는 팩토리얼을 연산한 값을 저장하고 있고 연산을 수행하는 과정에서 캐시에 저장된 값이 있으면 곧바로 그 값을 반환하고 없으면 계산후 캐시에 저장하는 방식이다
메모이제이션(memoization) 패턴
메모이제이션 패턴은 계산된 결과를 함수 프로퍼티값으로 담아 놓고 나중에 사용하는 패턴이다
function Calculate(key, input, func) {
Calculate.data = Calculate.data || {};
if (!Calculate.data[key]) {
var result;
result = func(input);
Calculate.data[key] = result;
}
return Calculate.data[key];
}
var result = Calculate(1, 5, function(input) {
return input + input;
});
console.log(result);
result = Calculate(2, 5, function(input) {
return input * input / 4;
});
console.log(result);
console.log(Calculate(1));
console.log(Calculate(2));
console.dir(Calculate);
예제에서 보는 것과 같이 함수 Calculate() 프로퍼티에 data 프로퍼티를 만들어 객체를 할당하였다
이곳에 사용자는 자신이 원하는 값을 원하는 키로 저장해 놓을 수 있다
jQuery 에서는 data()라는 메서드로 이 메모이제이션 패턴을 사용하였다
마지막으로 피보나치 수열을 함수형 프로그래밍 방식으로 구현해보자
var fibo = function () {
var cache = {
'0': 0,
'1': 1
};
var func = function (n) {
var result;
if (typeof cache[n] === 'number')
result = cache[n];
else {
result = func(n - 1) + func(n - 2);
cache[n] = result;
}
return result;
};
return func;
}();
console.log(fibo(10));
클로저를 이용하여 cache를 캐시로 활용한다는 것이 똑같다
피보나치 수열을 계산하는 함수를 인자로 받는 함수를 만들어보자
var cacher = function (cache, func) {
var calculate = function (n) {
var result;
if (typeof (cache[n]) === 'number')
result = cache[n];
else {
result = func(calculate, n);
cache[n] = result;
}
return result;
};
return calculate;
};
cacher 함수는 사용자 정의 함수와 초기 cache값을 받아 연산을 수행한다
사용자는 이 함수의 인자로 피보나치 수열을 연산하는 함수 혹은 팩토리얼을 연산하는 함수를 전달하여 사용한다
var fact = cacher({"0": 1}, function (func, n) {
return n * func(n - 1);
});
var fibo = cacher({"0": 0, "1": 1}, function (func, n) {
return func(n - 1) + func(n - 2);
});
console.log(fact(10));
console.log(fibo(10));
출처: 송형주, 고현준 저, 『인사이드 자바스크립트』, 한빛미디어(2014)