JavaScript

커링 (Currying)

da.som 2018. 12. 14. 00:39

커링(Currying)

커링 은 함수 하나가 n개의 인자를 받는 과정을 n개의 단일 인자 함수열로 만드는 것 을 말하며, 특히 함수를 재사용 할 때 유용하게 쓰이는 JavaScript의 함수형 프로그래밍 기법 중 하나다.

잠시 왜 함수형 JavaScript를 쓰는지 간단히 설명하자면, 반복을 최소화하고 간결하게 작성할 수 있기 떄문에 직관적이고 테스트하기 쉬운 코드를 구현할 수 있다는 강점이 있다.

실제로 커링 함수를 어떻게 작성해야 하는지 알아보자.

function multiplyThree(x, y, z) {
    console.log(x * y * z);
}

multiplyThree(4, 5, 2); // 40

위와 같이 인자 3개를 받아 곱셈을 처리하는 multiplyThree 함수가 있다.
이 함수를 앞에서 언급한 정의에 따라 커링을 하기 위해서는 x, y, z(n개의 인자)를 단일 인자 x, y, z를 가지는 함수로 나누면 된다. 즉, 커링할 원래 함수인수를 유지하는 클로저 를 만드는 방식으로 커링함수를 만든다.

// 커링 함수
function multiplyThree(x) {
    // 클로저로 생성된 공간
    return function(y) {
        return function(z) {
            console.log(x * y * z);
        };
    };
}

multiplyThree(4)(5)(2); // 40

위 코드는 ES6의 화살표 함수 (arrow function)를 이용하면, 좀 더 간결하게 표현할 수 있다.

let multiplyThree = x => y => z => console.log(x * y * z); // 커링 함수

multiplyThree(4)(5)(2); // 40

앞에서 말했듯이 커링의 가장 큰 장점은 재사용성 이다.
재사용성이 높을 수록 생산성 도 좋아지고 유지보수 할 때도 유용하다.

일반함수

var fullName = function(last, first) {
    console.log(last + first);
};

fullName('조', '다솜'); // 조다솜
fullName('조', '다영', ); // 조다영
fullName('조', '성준'); // 조성준

보통 일반 함수로는 성이 같은 여러 사람을 모두 출력할 때 위와 같이 출력할 것이다.
커링을 활용해 위와 같은 결과를 내면서 재사용성을 높여보면 다음과 같다.

커링함수

let fullName = last => first => console.log(last + first); // 커링 함수

let family = fullName('조'); // 클로저로 저장
family('다솜'); // 조다솜
family('다영'); // 조다영
family('성준'); // 조성준

첫 번째 인자에서 공통인 데이터를 클로저로 저장해두고, family는 이름만 전달받아 성과 함께 출력해주는 새로운 함수로 만든다. 마찬가지로 다른 가족 이름을 출력하려면 fullName(last)로 새로운 가족을 만들어 사용하면 된다.

간단한 예로 설명하다보니 재사용성에 대한 느낌이 오지 않아 예를 하나 더 들자면,

let sentence =
    greeting => seperator =>  end => name =>
        console.log(greeting + seperator + name + end);

let introduce = sentence('안녕하세요')('. ')('입니다.');
introduce('다솜'); // 안녕하세요. 다솜입니다.

let greet = sentence('안녕하세요')('? ')('씨.');
greet('다솜'); // 안녕하세요? 다솜씨.

let bye = sentence('안녕히가세요')(', ')('씨.');
bye('다솜'); // 안녕히가세요, 다솜씨.

위와 같이 sentence 라는 커링 함수 하나를 가지고 다양한 함수를 정의하고 사용할 수 있으며, 부분적으로 정의한 함수를 다시 정의해서 사용하는 패턴도 가능해 중복을 최소화할 수 있다.

전통적인 사용법

위에서는 ES6 문법을 사용해 쉽게 표현을 했지만 전통적으로 JavaScript는 기본적으로 커리함수를 지원하지 않는다. 그래서 화살표 함수 없이 커링을 구현하려면 코드가 매우 길어지게 된다. 그래서 전통적인 방법으로 구현하려면 Function.prototypecurry 메소드를 추가하는 방식으로 사용할 수 있다.

Function.prototype.curry = function() {
    var slice = Array.prototype.slice;
    var args = slice.apply(arguments);
    var that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
}

var sum1 = sum.curry(1);
console.log(sum1(5));        // 6
console.log(sum1(3));        // 4

Reference