자바스크립트로 커링하기

21247 단어 webdevjavascript
medium에서 나를 찾아라

Currying은 함수로 작업할 때 고급 기술이며 여러 프로그래밍 언어에서 사용됩니다.

여러 인수를 취하는 함수를 일련의 중첩 함수로 분해하면 카레가 생깁니다. 각 중첩 함수는 함수에 대한 다음 인수를 가질 것으로 예상합니다.

카레 함수는 각 호출에 대해 모든 인수가 수신될 때까지 매번 새 함수를 반환합니다. 이 인수는 클로저를 통한 커링의 수명 동안 지속될 수 있으며 모두 최종 기능을 실행하는 데 사용됩니다.

매우 기본적인 예는 다음과 같습니다.

function combineWords(word) {
  return function(anotherWord) {
    return function(andAnotherWord) {
      return `${word} ${anotherWord} ${andAnotherWord}`
    }
  }
}

이를 사용하려면 마지막 함수에 도달할 때까지 함수를 여러 번 호출할 수 있습니다.

const result = combineWords('hello,')('good')('morning')
console.log(result)

// result: 'hello, good morning'

그래서 무슨 일이 일어나고 있는지는 combineWords 커리 함수이고 (분명히) 단어가 주어질 때까지 기다렸다가 시리즈의 다음 함수를 실행합니다. 'wow!'combineWords 변수에 바인딩하고 재사용하여 'wow!'로 시작하는 다른 인사말을 만들 수 있습니다.

let greet = combineWords('wow!')
greet = greet('nice')

console.log(greet('jacket'))
console.log(greet('shoes'))
console.log(greet('eyes'))
console.log(greet('socks'))
console.log(greet('hat'))
console.log(greet('glasses'))
console.log(greet('finger nails'))
console.log(greet('PS3'))
console.log(greet('pet'))

/*
result:
  "wow! nice jacket"
  "wow! nice shoes"
  "wow! nice eyes"
  "wow! nice socks"
  "wow! nice hat"
  "wow! nice glasses"
  "wow! nice finger nails"
  "wow! nice PS3"
  "wow! nice pet"
*/

개념이 이해하기 조금 어렵다면 다음과 같이 읽어보십시오.

The mother is expecting all 4 eggs (arguments) before cooking and her 4 children will each carry one to her, one at a time.



function Egg() {...}

// the curry func
function prepareCooking(cook) {
  return function(egg1) {
    return function(egg2) {
      return function(egg3) {
        return function(egg4) {
          return cook(egg1, egg2, egg3, egg4)
        }
      }
    }
  }
}

const cook = function(...eggs) {
  api.turnOnStove()
  api.putEggsOnTop(...eggs)
  api.pourSalt()
  api.serve()
  console.log('served children')
  return 'served'
}

const start = prepareCooking(cook)

let collect = start(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())
collect = collect(new Egg()) // this steps into the last function witih argument "egg4" which will invoke the callback passed to "prepareCooking"

// result:  console.log --> "served children"
// collect === 'served'
cook 콜백이 호출되려면 4개의 모든 에그가 차례로 전달되어야 하며, 각각은 호출을 기다리는 다음 함수를 미리 채웁니다.

세 번째 달걀에서 멈출 경우:

let collect = start(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())

그런 다음 예상되는 마지막 함수egg4에 아직 도달하지 않았으므로 collect 값은 해당 함수입니다.

function prepareCooking(cook) {
  return function(egg1) {
    return function(egg2) {
      return function(egg3) {
        // HERE
        return function(egg4) {
          return cook(egg1, egg2, egg3, egg4)
        }
      }
    }
  }
}

카레를 완성하려면 마지막 달걀을 모으십시오.

let collect = start(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())

// collect === 'served'

이제 각 중첩 함수가 카레 함수 내 외부 범위에 대한 모든 액세스 권한을 갖는다는 것을 아는 것이 중요합니다. 이를 알면 각 중첩 함수 사이에 사용자 정의 논리를 제공하여 특정 상황에 맞출 수 있습니다. 그러나 카레는 카레로 남겨두는 것이 가장 좋습니다.

좀 더 발전된 카레 기능은 다음과 같습니다. (ES5 버전과 ES6 버전을 제공하겠습니다. ES5 구문을 보여주는 오래된 자습서가 많이 있기 때문입니다. 최신 JavaScript 개발자)

ES5



function curry(fn) {
  return function curried() {
    const args = Array.prototype.slice.call(arguments)
    const done = args.length >= fn.length
    if (done) {
      return fn.apply(this, args)
    } else {
      return function() {
        const args2 = Array.prototype.slice.call(arguments)
        return curried.apply(this, args.concat(args2))
      }
    }
  }
}

...와 같다:

ES6



const curry = (fn) => {
  return function curried(...args) {
    const done = args.length >= fn.length
    if (done) {
      return fn.apply(this, args)
    } else {
      return (...args2) => curried.apply(this, [...args, ...args2])
    }
  }
}

이 예를 더 자세히 설명하겠습니다.
curry(fn)를 호출하면 호출 시 다음 인수를 기다리는 내부curried 함수가 반환됩니다. 이제 이 내부 함수를 호출하면 두 가지 조건이 평가됩니다.
  • 호출자가 fn의 모든 인수를 충족하기에 충분한 인수를 전달했습니까?
  • 아니면 fn 필요한 인수가 아직 누락되어 있습니까?

  • 숫자 1인 경우 필요한 모든 인수fn를 선언했으며 카레는 fn 호출을 반환하고 수신된 모든 인수를 전달하여 종료됩니다(기본적으로 현재 일반적으로 fn 호출).

    그러나 숫자 2인 경우 카레는 계속 진행되어야 하며 curried 의 인수를 충족할 때까지 더 많은 인수를 계속 수신할 수 있도록 어떻게든 내부 fn 함수로 돌아가야 합니다. 코드return (...args2) => curried.apply(this, [...args, ...args2])는 지금까지 노출된 모든 인수를 누적하고 이 경우 카레를 계속하는 데 사용합니다.

    한 가지 중요한 규칙이 있습니다.

    The function that is to be invoked before waiting for all the arguments to be collected must have a fixed number of arguments. This means that the function cannot have parameters spreaded (ex: fn(...args))



    전:

    const curry = (fn) => {
      return function curried(...args) {
        const done = args.length >= fn.length
        if (done) {
          return fn.apply(this, args)
        } else {
          return (...args2) => curried.apply(this, [...args, ...args2])
        }
      }
    }
    
    // This is invalid because it uses ...args.  The curry does not understand where to stop
    function func(...args) {
      //
    }
    
    const currying = curry(func)
    

    결론



    카레를 만드는 것은 다른 고급 기술을 조합해야 하기 때문에 카레를 만드는 것이 흥미로운 기술이라고 생각합니다. 관련된 클로저, 고차 함수 및 재귀가 있습니다.

    그리고 이상으로 이번 포스팅을 마칩니다. 소중한 것을 발견하셨기를 바라며 앞으로도 많은 관심 부탁드립니다!

    medium에서 나를 찾아라

    좋은 웹페이지 즐겨찾기