[Functional JS] map, filter, each, 다형성

함수형으로 전환하기 (1)

map, filter

추상화의 단위로 ‘함수’를 사용해서 프로그래밍을 하는 것이 바로 함수형 프로그래밍이다. 즉, 필터링의 조건을 함수에 위임해서 필터링 시 발생하는 중복을 제거한다.

// 아래와 같은 회원 목록이 있다. 이를 filter, map 하는 함수를 짜보자.
var 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 }
];

// 아래의 _filter 함수를 응용형 함수(or 고차함수)라고 한다.
// 응용형 함수는 함수가 함수를 받아서 원하는 시점에 평가를 하면서 해당하는 함수가 알고있는 인자를 적용하는 식으로 프로그래밍 하는 것이다.
function _filter(users, predi) {	// 여기에서 인자로 들어오는 predi는 함수이다.
    var new_list = [];
    for(var i=0; i < users.length; i++) {
        if(predi(users[i])) {
            new_list.push(users[i]);
        }
    }
    return new_list;
}

console.log(_filter(users, function(user) { return user.age >= 30; }));

console.log(_filter(users, function(user) { return user.age < 30; }));

어떤 정보를 수집할 것인가에 대한 map 함수도 비슷하게 적용해볼 수 있다.

// 데이터가 어떻게 생겼는지에 대해 전혀 알지 못해도 적용할 수 있기 때문에 재사용성이 굉장히 높은 함수이다. 즉, 관심사가 완전히 분리되는 것이다.
// list 인자에 어떤 것이 들어와도 상관이 없다. 즉, 매우 다형성이 높다.
function _map(list, mapper) {
    var new_list = [];
    for(var i=0; i < users.length; i++) {
        new_list.push(mapper(list[i]));
    }
    return new_list;
}

// 30세 이상 유저들의 나이만 뽑아내는 경우
var over_30 = _filter(users, function(user) {return user.age >= 30});

var names = _map(over_30, function(user) {
    return user.name;
})

// 30세 이하 유저들의 이름만 뽑아내는 경우
var under_30 = _filter(users, function(user) {return user.age < 30});

var ages = _map(under_30, function(user) {
    return user.age;
})

// 위의 filter와 map을 한번에 할 수 있다.
_map(
	_filter(users, function(user) { return user.age >= 30; }),
    function(user) { return user.name; };
)

_map(
	_filter(users, function(user) { return user.age < 30; }),
    function(user) { return user.age; };
)

each

위의 _filter, _map 함수에도 반복문을 돌리는 루프와 해당 i번째 값을 순회하는 데 있어서 중복이 발생한다. 이를 해결해보자.

function _each(list, iter) {
    for(var i=0; i < list.length; i++) {
        iter(list[i]);
    }
    return list; // 받은 값을 그대로 리턴한다.
}

// 위의 _each 함수를 이용해서 기존의 _filter, _map 함수를 고쳐보자.

// _filter 함수 수정
function _filter(users, predi) {
    var new_list = [];
    _each(list, function(val) {
        if(predi(val)) new_list.push(val);
    });
    return new_list;
}

// _map 함수 수정
function _map(list, mapper) {
    var new_list = [];
    _each(list, function(val) {
        new_list.push(mapper(val));
    });
    return new_list;
}

다형성

자바스크립트에서 메서드는 해당 클래스에 정의되었기 때문에, 해당 클래스의 인스턴스에서만 사용할 수 있다.

–> map이나 filter 같은 메서드들이 array 타입 데이터에만 사용할 수 있는 것이 그 예시이다. (array_like 데이터에는 map, filter 메서드 사용 불가)

예시) document.querySelectorAll('node') 의 결과는 array처럼 보이지만 __proto__ : NodeList 이기 때문에 실제로는 array_like 데이터이다.

–> 즉, 객체지향 프로그래밍에서는 해당하는 클래스에 준비되어 있지 않은 메서드는 사용할 수 없다는 것이다. (다형성이 떨어진다.)

함수형 프로그래밍으로 정의한 _map, _filter 함수를 이용하면 데이터가 정확히 array 타입이 아닌, key-value 쌍을 지닌 array_like 데이터라도 map과 filter의 기능을 수행할 수 있다는 점에서 다형성이 좋은 것이다.

함수형 프로그래밍으로 고차 함수를 만들어서 사용하면 외부 다형성 (어떤 타입의 데이터가 들어와도 처리를 할 수 있는가)이 좋아지게 되고, 해당 고차 함수의 인자로 들어가는 보조 함수를 어떻게 정의하느냐에 따라서 내부 다형성 (어떤 타입의 값이 들어오는지에 상관없이 개발자의 의도대로 다양한 형태로 가공해서 리턴할 수 있는가)이 좋아지게 된다.

댓글남기기