Dev./javascript

[JS] curry, currying (커리, 커링)

인쥭 2021. 6. 23. 12:30
반응형

커링이란?

 

커링 - 위키백과, 우리 모두의 백과사전

수학과 컴퓨터 과학에서 커링(currying)이란 다중 인수 (혹은 여러 인수의 튜플)을 갖는 함수를 단일 인수를 갖는 함수들의 함수열로 바꾸는 것을 말한다. 모지즈 쇤핑클에 의해 도입되었고[1][2][3],

ko.wikipedia.org

수학과 컴퓨터 과학에서 커링(currying)이란 다중 인수(혹은 여러 인수의 튜플)을 갖는 함수를
단일 인수를 갖는 함수들의 함수열로 바꾸는 것을 말한다.
  • 나는 커링을 '여러 매개 변수를 입력받는 함수를 단일 매개 변수를 받는 함수로 나누는' 개념으로 이해하였음.
    • 커링은 여러 매개 변수가 필요한 함수 A를 커링 함수로 변환하여 반환하고, 이후 필요한 인자가 모두 모였을 때 A를 호출한다.
    • 함수형 프로그래밍에서는 함수를 일급 함수로서, 적시에 호출하여 평가한다.
    • 즉, 이 관점에서 커링은 함수 자체를 변수처럼 기억해두고(일급 함수) 모든 매개 변수가 구해졌을 때 기존 함수를 호출(적시 호출)하는 기법이다.
  • JS는 자체적으로 내장된 커링 기능을 지원하지는 않지만, 일급 함수와 적시 호출이 가능한 언어이므로 커링을 직접 구현할 수 있다.

 

커리 함수의 예시

명세

  • 커리 함수는 '함수를 매개 변수로' 받는다.
  • 커리 함수는 실행 시점에 매개 변수로 받은 함수의 인자를 사용하는 함수를 다시 반환한다.
  • 반환되는 함수는 Lexical scope 개념에 의해 커리 함수에 전달된 함수를 기억한다(= 클로저)

커링 대상 함수 만들기

  • 커링 대상 함수의 예시로 자주 사용되는 sum 함수를 간단하게 구현한다.
function sum(num1, num2) {
  return num1 + num2;
}
console.log(sum(10, 20)); // 30

커리 함수 만들기

  • 커링 대상 함수인 sum은 매개 변수로 num1, num2 두 개를 받는다.
  • 커리 함수 또한 두 개의 매개 변수를 각각 받는 함수로 나뉘도록 다음과 같이 구현한다.
function currying(func) {  // --- 1.
  return function(a) {     // --- 2.
    return function(b) {   // --- 3.
      return func(a, b);   // --- 4.
    }
  }
}
  1. 커리 함수는 함수(func)를 매개 변수로 받아 커링 기법을 적용하여야 한다.
  2. 커리 함수는 호출 시점에 새로운 함수 A를 반환하며, 반환되는 함수 A는 매개 변수를 하나 전달 받아야 한다.
    또한, 이 함수 A는 클로저로서 커리 함수가 전달 받은 매개 변수 func를 기억하고 있다.
  3. 커리 함수의 결과로 반환된 함수 A는 호출 시점에 새로운 함수 B를 반환하며, 반환되는 함수 B 역시 매개 변수 a를 필요로 한다.
    마찬가지로, 함수 B 역시 클로저로서 커리 함수가 전달 받은 매개 변수 func와 함수 A가 전달 받은 매개 변수 a를 기억하고 있다.
  4. 함수 A의 결과로 반환 함수 B는 호출 시점에 매개 변수 b를 입력 받아 실행되며, func(a, b)를 비로소 호출한다.
    함수인 func와 변수인 a는 Lexical scope 개념에 의해 식별자 해결이 가능하며, 결과 커링 대상 함수를 실행할 수 있게 된다.
  • 덧붙여, 위 커리 함수는 다음과 같은  화살표 함수로도 작성이 가능하다.
const currying = func => a => b => func(a,b);

/*
function currying(func) {
  return function(a) {
    return function(b) {
      return func(a, b);
    }
  }
}
*/

사용 예시

const log = console.log;

function sum(num1, num2) {
  return num1 + num2;
}
const sum_res = sum(10, 20);
log(`sum: ${sum_res}`); // sum: 30

function currying(func) {
  return function(a) {
    return function(b) {
      return func(a, b);
    }
  }
}

const curried = currying(sum);
/* 
 함수 sum을 커링하고, 반환된 함수를 curried에 할당한다.
 이 때, currying(sum)을 풀어 써보면 아래와 같다.

const curried = function(a) {
  return function(b) {
    return sum(a, b); // Lexical scope에 의해 반환되는 함수의 생성 시점에 func가 기억된다.
  }
}
*/

// 함수 코드 진행

const example1 = curried(20);
/* 
 첫 번째 인자가 구해졌을 때 이를 토대로 func를 기억하는 클로저를 호출할 수 있다.
 이 때, curried(20)을 풀어 써보면 아래와 같다.
 
const example1 = function(b) {
  return sum(20, b); // Lexical scope에 의해 반환되는 함수의 생성 시점에 변수 a가 기억된다.
}
*/

// 함수 코드 진행

const ex1_res = example1(30);
log(`ex1: ${ex1_res}`); // ex1: 50
/*
 두 번째 인자도 구해졌을 때, 이를 토대로 func, a를 기억하는 클로저를 호출할 수 있다.
 이 때, example1(30)을 풀어 써보면 아래와 같다.
 
const ex2_res = sum(20, 30);
*/
  • 다음과 같이 즉시 호출도 가능하다.
const example2 = curried(30)(40);
log(`ex2: ${example2}`); // ex2: 70

 

커링은 왜 사용하는가?

  • 커링은 함수형 프로그래밍에서 로직을 구현하기 위해, 함수와 함수를 조합하는 과정에서 가독성 / 확장성에 이점이 있다고 한다.
  • 그러나 아직 함수형 프로그래밍에 대한 배경이 부족한 탓에 다음 글로 대체하도록 한다.
 

A Beginner's Guide to Currying in Functional JavaScript - SitePoint

M. David Green demonstrates the concept of currying — a useful technique, to partially evaluate functions and make your functional JavaScript more readable.

www.sitepoint.com