이 글은 함수형 프로그래밍과 명령형 프로그래밍의 차이점을 학습하기 위해 인프런 함수형프로그래밍 강의를 따라하며 개인적으로 정리한 내용이다.
function add_maker(a) {
return function(b){
return a+b;
};
}
const add10 = add_maker(10); // f(b) { return a + b };
conosle.log(add10(20)); // 30
const add5 = add_maker(5); // f(b) { return a + b };
console.log(add5(10)); // 15
const add15 = add_maker(15); // f(b) { return a + b };
console.log(add15(10)); // 25
예제에서 add_maker이란 함수를 add10이란 이름으로 변수로 할당하고 콘솔로 찍어보면 function(b) 라는 함수를 리턴하는 것을 볼 수 있다. 하지만 add10이란 변수에 20이라는 숫자를 인자 값으로 넘어서 호출을 하게되면 30이라는 숫자가 출력되는 것을 볼 수 있다. 분명 인자 값으로 20을 넘겼는데 어떻게 이전에 넣었던 10과 합쳐진 30이라는 값으로 반환 되는 것일까? 바로 클로저 때문이다.
클로저: 함수(add_maker)의 호출이 끝난 시점에도 a라는 변수를 기억하고 있다가 함수의 리턴문의 또 다른 함수(클로저)가 a라는 변수를 접근 할 수 있는데 이 함수를 클로저라고 부른다.
일급함수: 함수를 리턴하고 있으므로 일급 함수 개념이 쓰였다.
순수함수: add_maker의 내부 함수인 클로저는 순수 함수이다. 동일한 참조값인 a에 b를 더해 항상 같은 결과를 반환하기 때문이다.
예제에서 본 것처럼 함수에 값을 넣고 부수효과가 없는 프로그래밍을 함수형 프로그래밍이라고 한다.
예제를 통해 명령형 코드를 함수형 코드로 바꿔보자.
const users = [
{ id: 1, name: "ID", age: 36 },
{ id: 2, name: "BJ", age: 32 },
{ id: 3, name: "JM", age: 32 },
{ id: 4, name: "PJ", age: 27 },
{ id: 5, name: "HA", age: 25 },
{ id: 6, name: "JE", age: 26 },
{ id: 7, name: "JI", age: 31 },
{ id: 8, name: "MP", age: 23 },
];
// 명령형 코드
// 1) 30세 이상인 users를 찾자.
const temp_users = [];
for(let i = 0; i < users.length; i++) {
if(users[i].age >= 30) {
temp_users.push(users[i]);
}
}
console.log(temp_users);
// 2) 30세 이상인 users의 names를 수집한다.
const names = [];
for(let i = 0; i < temp_users.length; i++){
names.push(temp_users[i].name);
}
console.log(names);
// 3) 30세 미만인 users를 찾자.
const temp_users2 = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age < 30) {
temp_users2.push(users[i]);
}
}
console.log(temp_users);
// 4) 30세 미만인 users의 ages를 수집한다.
const ages = [];
for (let i = 0; i < temp_users2.length; i++) {
ages.push(temp_users2[i].age);
}
console.log(ages);
function _filter(list, predi){
const new_list = [];
for(let i = 0; i < list.length; i++){
if(predi(list[i])){
// 조건에 해당하는 값만 새로운 리스트에 넣어준다.
new_list.push(list[i]);
}
}
return new_list;
}
function _map(list, mapper){
const new_list = [];
for(let i = 0; i < list.length; i++){
// 조거엔 해당하는 값만 새로운 리스트에 넣어준다.
new_list.push(mapper(list[i]));
}
return new_list;
}
const over_30 = _filter(users, function (user) {
return user.age >= 30;
});
console.log(over_30);
const under_30 = _filter(users, function (user) {
return user.age < 30;
});
console.log(under_30);
const over_30_names = _map(over_30, function (user) {
return user.name;
});
console.log(over_30_names);
const under_30_ages = _map(under_30, function (user) {
return user.age;
});
console.log(under_30_ages);
// users가 아닌 다른 데이터도 _filter를 활용할 수 있다.
/*
console.log(_filter([1,2,3,4], function(num){ return num % 2; }));
console.log(_filter([1,2,3,4], function(num){ return !(num % 2); }));
*/
응용형 프로그래밍: 함수가 함수를 받아서 원하는 시점에 해당 함수가 알고 있는 인자를 적용하는 식의 프로그래밍
// 함수형 프로그래밍에서는 위 코드와 같은 대입문을 줄이고 아래처럼 함수를 중첩하는 방식을 많이 쓴다.
console.log(
_map(
_filter(users, function (user) {
return user.age >= 30;
}),
function (user) {
return user.name;
}
)
);
console.log(
_map(
_filter(users, function (user) {
return user.age < 30;
}),
function (user) {
return user.age;
}
)
);