성공적인 프로그래밍?
좋은 프로그램의 척도에는 "사용성", "성능", "확장성", "기획변경" 에 대한 대응력 등이 있으며, 이것들을 효율적이고 생산적으로 이루는 일이 성공적인 프로그래밍이다.
함수형 프로그래밍은 성공적인 프로그래밍을 위해 부수 효과를 최대한 멀리하고 조합성을 강조하는 프로그래밍 패러다임이다.
function addMaker (a) {
return function (b) {
return a + b;
}
}
addMaker(10)(5); // 15
let add5 = addMaker(5)
add5(3); // 8
add5(5); // 10
function add (a, b) {
return a + b;
}
이렇게 두가지의 function (addMaker, add) 을 본다면,
parameter a는 불변하고 상수로 쓰인다.
절차지향적으로 작성된 코드를 함수형으로 변경하면서 함수형 자바스크립트의 실용성을 알아 보자.
let users = [
{ id: 1, name: 'ID', age: 32 },
{ id: 2, name: 'HA', age: 25 },
{ id: 3, name: 'BJ', age: 32 },
{ id: 4, name: 'PJ', age: 28 },
{ id: 5, name: 'JE', age: 27 },
{ id: 6, name: 'JM', age: 32 },
{ id: 7, name: 'HI', age: 24 },
];
이러한 users table 이있다고 가정해보자.
// 1. 유저중에 age 가 30 미만인 유저를 출력
let temp_users = [];
let len = users.length;
for (let i = 0; i < len; i++) {
if (users[i].age < 30) temp_users.push(users[i]);
}
console.log('temp', temp_users.length); // 4
// 2. 30 미만인 유저의 나이를 출력
let ages = [];
for (let i = 0; i < temp_users.length; i++) {
ages.push(temp_users[i].age);
}
console.log('ages', ages);
// 3. user 중에 age 가 30 이상인 유저를 출력
let temp_users = [];
for (let i = 0; i < users.length; i++) {
if (users[i].age >= 30) temp_users.push(users[i]);
}
console.log(temp_users.length);
// 4. age 가 30 이상인 유저의 이름을 출력
let names = [];
for (let i = 0; i < temp_users.length; i++) {
names.push(temp_users[i].name);
}
console.log(names);
이렇게 필터링을 할수있지만, 위 코드에서 함수형으로 Refactoring 해보자. 중복된 코드들을 뺀다면 훨신 가독성 좋고 짧은 코드를 만들어낼 수 있다.
함수형 프로그래밍에서는 "항상 동일하게 동작하는 함수" 를 만들고 보조함수를 조합하는식으로 조합을 완성한다.
보조함수
function filter(list, predicate) { // 이 함수의 if문은 predicate의 결과에 의존하게 된다.
let new_list = [];
let len = list.length;
for (let i = 0; i<len; i++){
if(predicate(list[i])) new_list.push(list[i])
}
return new_list;
}
이렇게 보조함수를 만들어서 사용한다면,
let users_under_30 = filter(users, function (user) { // 30세 미만의 갯수
return user.age < 30;
});
console.log(users_under_30.length); // 4
let ages = [];
for (let i = 0; i < users_under_30.length; i++) { // 30세 미만 각각의 나이
ages.push(users_under_30[i].age);
}
console.log(ages); // [25, 28, 27, 24]
let users_over_30 = filter(users, function (user) { // 30세 이상
return user.age >= 30;
});
console.log(users_over_30.length); // 3
let names = [];
for (let i = 0; i < users_over_30.length; i++) {
names.push(users_over_30[i].name);
}
console.log(names); // [ 'ID', 'BJ', 'JM' ]
이 보조 함수를 통해 코드가 짧아지고 재사용성이 보다 높은 함수 filter가 생겼다.
function filter(list, predicate) {
let new_list = [];
let len = list.length;
for (let i = 0; i < len; i++) {
if (predicate(list[i])) {
new_list.push(list[i]);
}
}
return new_list;
}
const map = (list, itaratee) => {
let new_list = [];
for (let i = 0; i < list.length; i++) {
new_list.push(itaratee(list[i]));
}
return new_list;
};
let users_under_30 = filter(users, function (user) {
return user.age < 30;
});
console.log(users_under_30.length); /// 4
let ages = map(users_under_30, function (user) {
return user.age;
});
console.log(ages); // [ 25, 28, 27, 24 ]
let users_over_30 = filter(users, function (user) {
return user.age >= 30;
});
console.log(users_over_30.length); // 3
let names = map(users_over_30, function (user) {
return user.age;
});
console.log(names); // [ 32, 32, 32 ]
filter와 map 을 사용함으로서, 코드가 매우 단순해졌다 for도 없고 if 도 없다.
filter와 map 을 중첩해서 사용하면 변수할당을 모두 제거하고 원하는 값을 출력할수있다.
function log_length(value) {
console.log(value.length);
return value;
}
console.log(
log_length(
map(
filter(users, function (user) {
return user.age < 30;
}),
function (user) {
return user.age;
}
)
)
);
// 4
// [ 25, 28, 27, 24 ]
console.log(
log_length(
map(
filter(users, function (user) {
return user.age >= 30;
}),
function (user) {
return user.age;
}
)
)
);
// 3
// [ 32, 32, 32 ]
이로서 처음 만든 for문으로 필터링을 하는 방식과 함수형프로그래밍을 통해 "같은동작을 하는 함수" + "보조함수" 를 통한 리팩토링을 비교해보았다.
다음편에서 계속....