본문 바로가기

JavaScript

[JavaScript] 함수형 프로그래밍 - 함수형으로 전환하기_1

이번에는 명령형 코드를 함수형 코드로 전환하는 작업을 할 계획이다. 


명령형 코드 작성

전환하기 전 명령형 코드를 먼저 작성하려고 한다. 

const user = [
    { id: 1, name: "ID", age: 36 },
    { id: 2, name: "BC", age: 48 },
    { id: 3, name: "QW", age: 25 },
    { id: 4, name: "RE", age: 32 },
    { id: 5, name: "WD", age: 12 },
    { id: 6, name: "AS", age: 10 },
    { id: 7, name: "DD", age: 50 },
    { id: 8, name: "SV", age: 23 },
]

 

30세 이상인 user를 거른다. 

const temp_users = []

for(let i = 0; i < users.length; i++) {
    if(user[i].age >= 30) {
        temp_users.push(user[i]);
    }
}

 

30세 이상인 user의 names를 수집한다. 

const names = [];

for(let i = 0; i < temp_users.length; i++) {
    names.push(temp_users[i].name);
}


30세 미만인 users를 거른다. 

const temp_users = []

for(let i = 0; i < users.length; i++) {
    if(user[i].age < 30) {
        temp_users.push(user[i]);
    }
}

 

30세 미만인 user의 ages를 수집한다. 

const ages = [];

for(let i = 0; i < temp_users.length; i++) {
    ages.push(temp_users[i].age);
}

 

위 4가지 코드는 각 목표를 달성하기 위해서 작성하는 일반적인 코드일 것이다.

이것을 함수형 방식으로 중복을 제거해보겠다. 

 

함수형 전환 - _filter

명령형 코드에서 30세 이상인 사용자를 가져오는 코드와 30세 미만인 사용자를 

가져오는 코드가 중복되는 부분이 많다. 

 

사실상 대부분의 코드는 동일하고 if문의 조건문만 다르다. 

function _filter (users, predicate) {
    const new_list = []

    for(let i = 0; i < users.length; i++) {
        if(predicate(users[i])) {
            new_list.push(user[i]);
        }
    }
    
    return new_list;
}

if 조건문은 명령형 프로그래밍 방식에서는 중복을 없애기엔 어렵게 느껴진다. 

 

하지만 함수형 프로그래밍에서는 predicate 함수를 통해서 어떤 조건일 때 if문을 수행할 것인지를 

위임할 수 있게 된다. 

이렇게 함수를 만들게 되면 원했던 조건인 30세 제약 조건 외에도 필요에 따라서 사용할 수 있는

필터링 기능이 가능하게 된다. 

 

위와 같은 _filter 함수를 응용형 함수라고 한다.  

응용형 함수란, 함수가 함수를 받아서 원하는 시점에 해당하는 함수가 알고있는 인자를 적용하는

방식으로 수행한다. 

 

또다른 명칭으로 고차함수라고 한다. 

고차함수는 많이 들어봤는데, 함수가 함수를 인자로 받거나, 함수를 리턴하는 함수 등을 말한다. 

 

함수형 전환 - _map

function _map(list,mapper) {
    const new_list = [];

    for(let i = 0; i < list.length; i++) {
        new_list.push(mapper(list[i]));
    }
    
    return new_list;
}

마찬가지로 나이와 이름을 뽑아내는 부분도 중복되는 부분이 많다. 

이것도 _filter와 마찬가지로 mapper라는 함수를 받아와서  new_list에 추가하게 된다. 

 

이렇게 작업하면 _map에서는 mapper라는 함수의 형태가 나와있지 않다.  

이것은 _filter도 마찬가지이다. 이것은 함수형 프로그래밍의 또다른 특징으로 관심사가 분리되어있다. 

 

이러한 특징 덕분에 함수형 프로그래밍은 재사용성이 높다. 

 

함수형 전환 - each

_filter 함수와 _map 함수도 중복되는 부분이 존재한다. 

for(let i = 0; i < list.length; i++) {
    // ...   
}

반복하는 부분이다. 

반복을 해서 내부에서 특정 작업을 하는 부분이 중복해서 들어가 있다. 

 

function _earch(list, iter) {
    for(let i = 0; i < list.length; i++) {
        iter(list[i])
    }
    
    return list
}

_each는 list를 받아와서 for문을 돌면서 실제 함수에서 수행해야하는 작업을 대신 해주는 

함수이다. 

 

이것을 이제 _filter와 _map에서 사용해보면 

function _map(list,mapper) {
    const new_list = [];

    _each(list, function (val) {
       new_list.push(mapper(val));
    })
    
    return new_list;
}

function _filter (users, predicate) {
    const new_list = []
    
    _each(list, function (val) {
        if(predicate(val)) {
            new_list.push(val);
        }
    })
    
    return new_list;
}

" 반복하는 부분에서 for을 어떻게 사용해야 하는 것인가 "에 대한 부분을 해결했다. 

 

다형성 

다형성이란 여러가지 형태를 받아들일 수 있는 성질을 말한다.

외부 다형성

지금까지 만든 _map 함수와 _filter, _each 함수는 자바스크립트의 Array 객체에는 모두 존재한다. 

하지만 작지만 큰 차이점이 존재하는데, Array 객체의 map, filter, forEach는 메서드이다. 

 

함수가 아닌 메서드라는 것은 객체의 상태에 따라 결과가 다르며, 클래스에 정의되어 있기 때문에 

정의되어 있는 클래스가 아니면 사용할 수 없다. 

특정 클래스에서 사용하기 위해서 정의된 것인데 당연히 다른 클래스에서는 사용할 수 없다고

생각할 수 있다. 

 

하지만 우리는 의식하지 않은 수많은 array_like를 사용하고 있다. 

즉, 자바스크립트에서는 Array가 아닌데 Array처럼 사용되고 있는 객체가 있다. 

document.querySelectorAll을 사용할 경우 출력되는 부분이 겉으로는 Array처럼 보인다. 

하지만 map을 사용하면 해당 함수가 없다고 오류를 출력하게 된다. 

왜냐하면 document.querySelectorAll의 결과는 Array를 리턴하는 것이 아니기 때문이다. 

 

하지만 우리가 만들어둔 _map 함수를 사용하면 반복해서 출력이 된다. 

 

이것이 가능한 이유는 우리가 만든 _map 함수는 배열의 유무는 상관없고 들어오는 인자인 list가 

length가 있고 length에 맞는 크기의 key-value 쌍이 있는 array_like라면 동작하게 된다. 

 

즉, 외부 다형성은 Array, array_like 같은 반복할 수 있는 객체를 특정 객체만 적용 가능한 메서드가 아닌

함수의 인자에 타입만 맞으면 모두 실행가능한 특징을 가진다.  

 

이처럼 함수를 사용하면 외부적으로 다형성을 만들 수 있다. 

내부 다형성

우리가 함수에 함수를 인자로 사용할 때 흔히 인자로 사용되는 함수를 콜백함수라고 한다. 

하지만 함수형 프로그래밍에서는 이러한 함수의 인자명을 중요하게 생각하는데, 

 

함수의 역할에 따라 

조건을 리턴하는 함수 predicate, 반복적으로 실행되는 함수 iterator, 매핑하는 함수 mapper 등 다양한

명칭에 맞는 보조 함수의 이름을 사용하는 것이 좋다. 

 

_map([1,2,3,4], function(v){
  return v + 10;
});

내부적으로는 이처럼 배열에 해당하는 특정 작업을 수행할 수 있게 만드는 역할은 보조 함수가 하며, 

이러한 보조 함수로 인해서 내부 다형성을 구현한다. 

 

반응형