커링함수만들기(_curry,_curryr)-part2

커링(Curry)

커링이란 다중 인수 (혹은 여러 인수의 튜플)을 갖는 함수를 단일 인수를 갖는 함수들의 함수열로 바꾸는 것을 말한다.


한마디로, 함수에 인자를 하나씩 적용해 나가다가 필요한 인자가 모두 채워지면
함수 본체를 실행하는 기법
입니다.

자바스크립트에서는 커링기법이 지원되지 않지만
앞에 포스팅에서 다뤘듯이 자바스크립트는 일급함수가 지원되고,
얼마든지 평가시점을 마음대로 다룰 수 있기 때문에 커링과 같은 기법을 구현할 수 있습니다.

커링함수는 다음과 같습니다.

1
2
3
4
5
6
7
function _curry(fn){
return function(a){
return function(b){
return fn(a, b);
}
}
}

커링은 인자로 함수(fn)를 받고, 커링함수를 실행하는 즉시
안쪽함수가 리턴됩니다. 그리고 a라는 인자를 받은 뒤 또 b를 인자로 받는 함수가
리턴됩니다. b를 인자로 받으면 fn(a,b)이라는 본체함수가 실행됩니다.

만약 인자 두개를 받아서 더하는 add함수를 만든다고 하면
일반적으로 아래와 같이 만듭니다.

1
2
3
4
var add= function(a, b){
return a+b;
};
console.log(add(10,5));

결과값으로 15라는 결과를 얻겠죠.

커리함수같은 경우에, add함수를 만들때 아래처럼 _curry로 감싸주기만하면
add함수는 커링이 되는 함수로 구현됩니다.

1
2
3
var add= _curry(function(a, b){
return a+b;
});

함수가 동작하는 순서는 다음과 같습니다.

  1. add라는 함수를 만들때 _curry로 감쌉니다.
  2. add라는 함수는 fn이 됩니다.
  3. add함수는 return fn(a, b); 이곳에 위치하게된다.

  4. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function(a){
    return function(b){
    return fn(a, b);
    }
    ```
    이 부분이 제일 처음 호출된다.
    다시말해,
    ```js
    var add = function(a){
    return function(b){
    return fn(a, b);
    }

위처럼 된다고 볼수있겠다.

  1. 인자 a를 먼저 받고, 그 다음에 b를 받은 다음에,
    처음에 받아두었던 fn이 나중에 실행된다.

아래 예제를 통해 이해를 도와봅시다ㅎㅎ

결과값 맞춰보기

1
2
var add10 = add(10);
console.log(add10(5));

결과값은?? 10+5인 15가 됩니다.

동작순서는 다음과 같습니다.

  1. 먼저 인자 10을 받은 add함수의 리턴값이 add10이라는 객체로 들어갑니다.

    1
    2
    3
    4
    var add10 = function(10){
    return function(b){
    return fn(a, b);
    }
  2. add10 이라는 함수에 인자 5가 들어갑니다.
    그리고, add10이 실행되는 즉시

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function(b){
    return fn(a, b);
    }
    ```
    이부분이 실행됩니다. 인자 b에 5가 들어가구요.

    3. 그럼 본체함수인 fn(a,b)가 실행됩니다.
    ```js
    function(a, b){
    return a+b;
    }

바로 이부분이 fn이었죠.
그래서 a에는 10, b에는 5가 차례대로 모두 받아졌을때
본체함수가 실행되어 5 + 10 인 15라는 결과값을 얻게됩니다.

그래서 ,

1
2
console.log(add(5)(3)); //1
console.log(add(10)(3)); //2

1번은 결과가 8이되고
2번은 13이 됩니다.




결국은, 본체함수인

1
2
3
function(a, b){
return a+b;
}

이 부분을 값으로 들고있다가 나중에 원하는 시점에 최종적으로 평가하는 기법입니다.

계속해서 이러한 기법을 통해서.
함수가 함수를 대신 실행하거나, 함수가 함수를 리턴하면서
함수를 조합해 나가는 것이 바로 함수형 프로그래밍입니다.

_curry는 함수형 프로그래밍의 응용사례를 잘 보여주고 있습니다.
_curry함수로 적용했을 때에는 함수로 인자를 하나만 적용하게 되면 함수를 리턴하게 됩니다.

하지만 위의 방식으로는
console.log(add(1,2));
이것의 결과값은 3이 아니라 함수를 리턴하게 됩니다.

그 이유는

1
2
3
function(b){
return fn(a, b);
}

위의 함수를 리턴하는 것에서 그치기 때문입니다.
만약 커리함수를 위처럼 인자두개가 들어오면 함수를 리턴하지 않고
즉시 값을 도출하게끔 만들려면 조건문을 추가합니다.

1
2
3
4
5
6
7
8
function _curry(fn){
return function(a,b){
if(agument.length == 2) return fn(a, b);
return function(b){
return fn(a, b);
}
}
}

위처럼 조건문을 통해서 인자값이 2개면 본체함수를 실행하도록 만들수 있습니다.

하지만 저기에도 return을 두번한다는 문제가 있어요.
그래서 그걸 해결하기 위해 아래처럼 수정합니다.

1
2
3
4
5
function _curry(fn){
return function(a,b){
return agument.length == 2 ? fn(a, b) : function(b){ return fn(a, b);};
}
}

이번에는 인자 두개를 받아서 빼기를 하는 함수를 구현해보겠습니다.

1
2
3
4
5
 var sub = _curry(function(a,b){
return a - b;
});

console.log(sub(10, 5));

결과값은 5가 되겠죠.

더 나아가서

1
2
var sub10 = sub(10);
console.log(sub10(5));

위를 실행하면, 5가 됩니다.

하지만 , 표현이 좋지만은 않습니다.

sub10이라는 객체에 sub(10)이라는 함수가 들어오고
sub10(5)라는 것은 sub(10)함수안에 5가 들어간 것이라고 볼 수 있습니다.

그렇게 되면, 들어오는 인자인 5에 sub10이라는 함수를 적용하는것이니까
5-10이 되어야 좀 더 자연스러운 표현같지 않나요…?

1
2
console.log(sub(10, 5)); //1
console.log(sub10(5)); //2

1번경우는 10-5가 자연스럽지만,
2번은 5에 10을 빼는 것처럼 표현되죠.
그래서 표현을 좀더 자연스럽게 하기위해서
원래 같은 경우 _curry는 왼쪽에서 부터 인자를 적용해나가는데,

새롭게 _curryr이라는 함수를 생성해서
오른쪽에서부터 인자를 적용해나가도록 할 수 있습니다.
_curry뿐만아니라 다른 함수에서도 자연스러운 표현을 위해서
뒤에 r이라고 많이 붙인다고 해요. r은 right를 의미하겠죠!하하

_curryr
_curry와 같은 동작을 하지만 단지, 오른쪽에서부터 인자를 적용해 나간다~ 는 점만
다를 뿐입니다!!

_curryr은 다음과 같습니다.

1
2
3
4
5
function _curryr(fn){
return function(a, b) {
return agument.length == 2 ? fn(a, b) : function(b){ return fn(b, a);};
}
}

인자값이 2개일 때는 아까처럼 순서대로 적용하지만,
인자가 1개인 경우에는 return fn(b, a); 이런 식으로 a와 b의 순서를 바꿔
두번째 인자에서 처음에 들어왔던 인자를 빼도록 합니다.

그럼 위의 2번은 5 - 10 인 -5가 리턴됩니다.


지금까지 커링기법에 대해서 알아봤어요.

다음 포스팅은 원하는 값을 얻어오는 _get함수에 대해서 다룰게요ㅎㅎ

Share