JavaScript의 자동 커링

JavaScript의 자동 커링



이 게시물에서는 JavaScript의 자동 커링을 살펴봅니다.

이 시리즈의 네 번째 기사입니다. 커리 함수는 자동 커리 단항 함수, 가변 함수, 무한 커리 함수 등과 같이 다양한 복잡성을 가질 수 있다고 언급한 이전 기사를 따릅니다.

이 게시물은 순전히 단항 구현으로 자동 커링을 다룰 것입니다.

자동 커링, 도대체 뭐야?



자동 커링은 고급 커링 구현 기술입니다. 모든 함수가 처음부터 단항이기 때문에 다중 함수는 컴파일러에 의해 커링되어야 하는 Haskell의 내장 기능입니다. 따라서 다중 함수는 항상 Haskell에서 자동 커링됩니다.

그러나 JavaScript에서는 함수가 단항으로 제한되지 않습니다. 흥미롭고 재미 있기 때문에 우리는 그것을 위해 물건을 카레하고 있습니다. 문자 그대로 필요하지 않기 때문에 JavaScript에는 자동 커링이 없습니다. 기록을 위해 JavaScript에서 커링을 실제 의미에서 "커링"이라고 부르지 않을 수도 있습니다. 다음 기사의 중간쯤에 우리는 우리가 단지 장난을 치고 있다는 것을 알게 될 것입니다.

우리는 커링을 직접 구현하고 싶습니다. 분명히 우리 함수가 컴파일러에 의해 자동으로 커링되어야 한다는 의미에서 자동 커링되지는 않을 것입니다. 오히려 우리는 코드를 런타임으로 이동하여 우리 자신의 코드로 변환하고 있습니다. 즉, 전달된 각 다중 함수에 대한 커링 함수를 수동으로 정의하는 대신 래퍼와 몇 가지 도우미를 사용하여 해당 함수에 대한 변환 작업을 자동화하고 있습니다.

재귀와 함께 일반 JavaScript 함수 구성을 사용하여 이를 수행할 수 있습니다. 그리고 이후 게시물에서 Function.prototype.call , Function.prototype.apply , Function.prototype.bind 와 같은 기본 JavaScript 메서드를 사용하여 동일한 변환을 달성할 수 있음을 볼 수 있습니다.

Currying 프로세스 자동화



이전 게시물에서 중첩 클로저를 활용하여 인수의 누산기 역할을 하는 래퍼 함수를 ​​사용하는 것을 보았습니다. accumulator 함수는 커링에서 핵심적인 역할을 합니다. 우리는 모든 커링 변환에 하나가 필요합니다.

그러나 자동 커링에서는 중첩 함수 시스템을 사용하지 않습니다. 대신 재귀를 사용합니다. 전달된 인수의 수를 추적하고 다중 함수의 인수 수와 비교하고 비교를 사용하여 누산기를 재귀적으로 반환하여 더 많은 인수를 계속 수락해야 하는지 여부를 결정합니다. 인수가 충분하면 누산기를 종료하고 커링할 함수에 대한 호출을 반환합니다.

계획
핵심 아이디어는 한 번에 하나의 인수만 수락하도록 허용하는 것입니다. unarity는 커링의 본질이기 때문입니다. 그런 다음 계속해서 배열에 인수를 저장합니다. 배열의 사본은 재귀 스택의 현재 실행 컨텍스트에서 항상 액세스할 수 있어야 합니다.

예시



다음 예제에서는 한 번에 하나의 인수만 허용하도록 누산기를 정의합니다.

function curry(f) {
  function curried(args) {
    if (args.length >= f.length) return f(...args);
    return accumulator;

    function accumulator(a) {
      return curried([...args, a]);
    };
  };

  return curried([]);
};


accumulator() 도우미 함수 내에서 curried()를 선언하여 args 배열에 대한 액세스 권한을 부여하여 업데이트된args이 연속적인 재귀 호출에 전달될 수 있도록 합니다.

또한 args의 매개변수에서 curry()를 배열로 사용하고 있으며 배열...args에서 확산된 목록이 아닙니다. 그리고 curried() 의 첫 번째 호출에 빈 배열을 전달합니다.

이제 런타임에 createMessage() 함수를 curry()로 커링하고 사용하려고 합니다.

function createMessage(greeting, name, message) {
  return `${greeting}, ${name}! ${message}`;
};

const curriedCreateMessage = curry(createMessage);

console.log(curriedCreateMessage('Hi')('Haskell')('Whadup?')); // Hi, Haskell! Whadup?
console.log(curriedCreateMessage('Hi', 'Hello')('Haskell', 'Hasikell')('Whadup?', `What's up`));
// Hi, Haskell! Whadup?



위의 두 번째log는 각 호출에서 두 개의 인수가 전달되었음에도 불구하고 추가 인수를 무시하고 첫 번째와 동일한 문자열Hi, Haskell! Whadup?을 반환합니다.

다음과 같이 curry() 함수를 작성할 수 있습니다.

function curry(f) {
  return function curried(...args) {
    if (args.length >= f.length) return f(...args);
    return accumulator;

    function accumulator(a) {
      return curried(...args, a);
    };
  };
};


그러나 전체 단항 함수를 반환하는 데에는 문제가 있습니다. 가변 커링에 대한 다음 기사에서 이에 대해 다룰 것입니다. 하지만 문제가 무엇입니까?

의견에 의견을 자유롭게 공유하십시오.

좋은 웹페이지 즐겨찾기