Skip to content

자바스크립트에서의 익명재귀

KIM HAE YEON edited this page Aug 26, 2019 · 2 revisions

자바스크립에서의 익명 재귀

원문 : https://dev.to/simov/anonymous-recursion-in-javascript

(
  (
    (f) => f(f)
  )
  (
    (f) =>
      (l) => {
        console.log(l)
        if (l.length) f(f)(l.slice(1))
        console.log(l)
      }
  )
)
(
  [1, 2, 3]
)

그렇습니다. 그런 것이 있으며, 공유하는 것이 흥미로운 예라고 생각했습니다. 특징 : 클로저, 즉시실행함수, 화살표 함수, 함수형프로그래밍, and 익명재귀.

브라우저 콘솔에서 위 예제를 복사 / 붙여 넣기 할 수 있습니다. 출력은 다음과 같습니다.

[ 1, 2, 3 ]
[ 2, 3 ]
[ 3 ]
[]
[]
[ 3 ]
[ 2, 3 ]
[ 1, 2, 3 ]

함수형 프로그래밍에 대해 말하자면, Scheme의 유사한 예제(JavaScript가 영향을 받은 언어 중 하나)가 다음과 같습니다.

(
  (
    (lambda (f) (f f))
    (lambda (f)
      (lambda (l)
        (print l)
        (if (not (null? l)) ((f f) (cdr l)))
        (print l)
      )
    )
  )
  '(1 2 3)
)

언윈드

다른 많은 프로그래밍 언어에서와 마찬가지로 함수를 호출하는 것은 이름 뒤에 괄호 ()를 추가함으로써 이루어집니다.

function foo () { return 'hey' }
foo()

JavaScript에서는 괄호 안에 있는 식을 모두 포함할 수 있습니다.:

('hey', 2+5, 'dev.to')

위 스니펫의 결과는 'dev.to'입니다. 그 이유는 자바스크립트가 마지막 표현식을 결과값으로 반환하기 때문입니다.

익명(lambda) 괄호 '()' 하나를 싸는 것은 익명 함수 자체라는 뜻입니다.

(function () { return 'hey' })

익명함수에는 이름이 없기 때문에 그 자체가 그다지 유용하지 않으며, 초기화 중에 즉시 부르지 않으면 참고할 수 없을 것입니다.

보통 함수처럼 뒤에 괄호 '()'를 붙여서 호출할 수 있습니다.:

(function () { return 'hey' })()

화살표 함수도 마찬가지입니다.:

(() => 'hey')()

다시 익명함수 뒤에 괄호 '()'를 붙이는 것은 즉시실행함수라고도 합니다.

클로저

closure은 함수와 해당 함수가 선언된 렉시컬 환경의 결합입니다. 화살표함수와 결합하면 다음과 같이 정의할 수 있습니다.:

var foo = (hi) => (dev) => hi + ' ' + dev

브라우저의 콘솔에서 위의 기능을 호출하면 'hey dev.to'가 출력됩니다.:

foo('hey')('dev.to')

감싸진 안쪽 함수의 바깥 스코프에서 hi 인자에 접근할 수 있다는 점에 유의합니다.

위의 코드와 동일합니다.:

function foo (hi) {
  return function (dev) { return hi + ' ' + dev }
}

그리고 즉시 실행 함수는 :

(
  (hi) =>
    (
      (dev) => `${hi} ${dev}`
    )
    ('dev.to')
)
('hey')

먼저 'hey' 매개변수는 'hi' 가 먼저 가장 바깥 함수의 스코프에 전달된됩니다. 그러면 그 함수는 먼저 판별되어야 할 또 다른 즉시 실행 함수를 반환합니다. 그리고 나서 'dev.to' 파라미터 는 가장 안쪽 함수에 대한 'dev'의 인자로 전달되며, 이 함수는 최종 결과인 'hey dev.to'를 반환합니다. (First the hey parameter is passed to the outermost scope to the above function as hi argument. Then that function returns yet another self-executing function that needs to be evaluated first. The dev.to parameter is then passed as the dev argument to the innermost function, and that function return the final result: 'hey dev.to'.)

좀 더 자세한 설명

위 즉시 실행 함수의 약간 변형된 버전은 다음과 같습니다.:

(
  (
    (dev) =>
      (hi) => `${hi} ${dev}`
  )
  ('dev.to')
)
('hey')

먼저 hey 파라미터는 가장 바깥 스코프에 대한 인자로 전달되지만, 함수 대신에 우리는 먼저 판별해야 할 또 다른 표현식이 있습니다. 그래서 'dev.to' 파라미터는 'dev'가 먼저 내부 즉시 실행 함수에 전달되고 또 다른 함수를 반환합니다. 마지막 기능은 가장 바깥 스코프를 만족시켜 hey 파라미터를 받는 것입니다.

즉시 실행 함수클로저는상태를 초기화하고 캡슐화하는 데 사용되며, 이것이 다음 예에서 사용할 것이라는 점에 유의해야 합니다.

익명 재귀

초기의 예시로 돌아가서 이번에는 주석을 달았습니다.:

(
  (
    (f) => f(f) // 3.
  )
  (
    (f) => // 2.
      (l) => { // 4.
        console.log(l)
        if (l.length) f(f)(l.slice(1))
        console.log(l)
      }
  )
)
(
  [1, 2, 3] // 1.
)
  1. 입력 배열 '[1, 2, 3]이 가장 바깥쪽 스코프로 전달됩니다.
  2. 위의 함수에 대한 인수로 이 전체 함수가 전달됩니다.
  3. 이 함수는 아래 함수를 인자 F로 받아 그 자체로 부릅니다.
  4. `2. 3에서 불려지고 있다.가장 외부 스코프를 만족시켜 입력배열을 l 인자로 전달하는 4 함수를 반환하는 결과를 낳습니다.

이 모든 이유는 입력 배열 l을 전달하는 재귀 함수 내부의 f 함수를 참조하기 위함입니다. 그래야 우리가 그것을 부를 수 있습니다.:

f(f)(l.slice(1))

f는 클로저이기 때문에 입력배열에서 작동하는 가장 안쪽 함수에 접근하기 위해 그 자체로 호출하면 된다는 점에 유의하세요.

설명을 위해 첫 번째 console.log(l) 문은 반복적인 top-down을 나타내고, 두 번째 문장은 반복적인 bot-up을 나타냅니다.

결론

이 기사를 재미있게 보고 그 글에서 새로운 것을 배웠기를 바랍니다. 클로저, 즉시 실행 함수, 함수형 프로그래밍 패턴은 블랙매직은 아닙니다. 이해하기 쉽고 가지고 놀기 재미있는 간단한 원칙을 따름니다.

언제 그것들을 사용할지, 그렇지 않을지에 대한 감각을 개발해야 한다고 말해지고 있습니다. 코드를 유지하기가 더 어려워진다면, 아마 그것을 약간 리팩토링 하는 것이 좋을 것입니다.

그럼에도 불구하고 이러한 기본 기법을 이해하는 것은 깨끗하고 우아한 해결책을 만들고 수평을 맞추는 데 중추적인 역할을 합니다.

해피코딩!

Clone this wiki locally