클로저란 무엇인가 프로그래밍

클로저에 대한 자료를 찾다가 마땅한 게 잘 안 보여서 영문위키백과를 번역해봤습니다


클로저 Closure (computer science)


컴퓨터과학에서 클로저란 자체의 환경을 가진 함수이다 이 환경에는 적어도 하나의 결합변수가 존재한다 클로저는 ML이나 리스프와 같은 프로그래밍언에서 처음으로 쓰였다


클로저는 여러 차례의 실행에서도 그들의 결합변수의 상태를 유지한다


클로저란 용어는 흔히 익명함수를 가리키는 것으로 오해된다 아마도 대부분의 언어에서 클로저를 만들기 위해 익명함수를 구현함으로써 프로그래머들에게 두 개의 개념이 동시에  소개됐기 때문일 것이다 그러나 이들은 다른 개념이다  


클로저의 개념은 1960년대에 개발돼 Scheme 프로그래밍언어에서 처음으로 완전히 구현됐다 그로부터 많은 언어가 클로저를 지원하도록 설계됐다


함수객체도 때때로 클로저로 불린다


클로저와 1급함수


클로저는 전형적으로 함수가 1급함수인 언어들에서 나타난다 즉, 그들 언어에서 함수들은 함수의 인자로서 넘겨질 수 있고 함수호출의 반환값으로 사용되며, 보다 단순한 문자열이나 정수처럼 변수이름에 결합되거나 할 수 있다 예를 들면 다음 Scheme 함수를 보라:


; Return a list of all books with at least THRESHOLD copies sold.

(define (best-selling-books threshold)

 (filter

   (lambda (book) (>= (book-sales book) threshold))

   book-list))

이 예제에서 람다식 (lambda (book) (>= (book-sales book) threshold)) 는 함수 best-selling-books의 내부에 존재한다 이 람다식이 실행될 때 Scheme은 이 람다의 코드와, 람다식 내부의 자유변수인 변수 threshold에의 참조로 이뤄진 클로저를 생성한다   


이 클로저는 함수 filter로 넘겨지고 이 함수는 클로저를 반복적으로 호출해 어느 책이 결과 리스트에 더해지고 어느 책이 제외되는지 판단한다 클로자 자체가 변수 threshold의 참조를 가졌기에 time filter가 매번 호출할 때마다 그 변수를 사용할 수 있다 함수 filter 자체는 완전히 별개의 파일에서 정의됐을 수 있다


이번에는 클로저를 지원하는 다른 유명 언어인 ECMAScript(JavaScript)로 재작성한 같은 예제이다


// Return a list of all books with at least 'threshold' copies sold.

function bestSellingBooks(threshold) {

 return bookList.filter(

     function(book) { return book.sales >= threshold; }

   );

}


키워드 function이 lambda 대신에 쓰이고 매서드 Array.filter가 전역함수 filter 대신 쓰였지만 구조와 코드의 효과는 동일하다 함수가 클로저를 생성해 그것을 반환할 수도 있다 다음 예제에서 함수가 함수를 결과값으로 반환한다


Scheme:


; Return a function that approximates the derivative of f

; using an interval of dx, which should be appropriately small.

(define (derivative f dx)

 (lambda (x) (/ (- (f (+ x dx)) (f x)) dx)))


ECMAScript:


// Return a function that approximates the derivative of f

// using an interval of dx, which should be appropriately small.

function derivative(f, dx) {

 return function(x) {

   return (f(x + dx) - f(x)) / dx;

 };

}


이 경우 클로저는 그것을 생성한 함수의 범위 밖에서 살아남기 때문에 변수 f와 dx는 함수 derivative가 반환된 뒤에도 살아남아을 수  있다 클로저가 없는 언어들에서 지역변수의 수명은 변수를 선언한 범위의 실행과 일치한다 클로저를 가진 언어들에서 변수들은 현재의 어떤 클로저라도 그들을 참조하고 있는 한, 반드시 계속 존재해야 한다 이것이 몇 종류의 가비지컬렉션을 구현하는 가장 일반적 방법이다  


이것이 늘 명확한 것은 아니지만 클로저가 익명함수로 만들어질 필요는 없다 예를 들어 파이썬 프로그래밍언어는 익명함수에 대한 지원이 매우 제한적이지만 클로저는 완전히 지원하고 있다 예를 들어 위의 ECMAScript의 예제는 파이썬으로 다음과 같이 구현될 수 있다


# Return a function that approximates the derivative of f

# using an interval of dx, which should be appropriately small.

def derivative(f, dx):

   def gradient(x):

       return (f(x + dx) - f(x)) / dx

   return gradient


이 예제에서 gradient라는 이름의 함수는 함수 f 및 dx와 함께 클로저를 형성한다 이 클로저는 derivative라는 이름의 바깥 함수에 의해 반환된다 사실, 파이썬의 클로저는 종종 이름이 있는 함수를 써서 생성되며 람다식의 제한 때문에 다른 언어들의 익명함수와 똑같은 역할을 하기도 한다   


클로저의 사용


클로저의 용도는 많은데,


  • 소프트웨어라이브러리의 설계자들은 중요한 함수에 클로저를 인자로 넘김으로써 사용자가 행동을 원하는 대로 바꿀 수 있도로 허용할 수 있다 예를 들어, 값들을 정렬하는 함수는 유저가 설정한 기준에 따라서 정렬된 값들을 비교하는 클로저를 인자로 받을 수 있다

  • 클로저는 값을 구하는 것을 지연시키기 때문에, 즉 호출되기 전에는 아무 것도 안하기 때문에, 클로저는 제어구조를 정의하는 데에 쓰일 수 있다 예를 들면, 스몰토크의 분기(if/then/else)와 반복(while/for)를 포함하는 모든 표준적 제어구조는 클로저를 받아들이는 매서드를 가진 객체들을 써서 정의된다 사용자는 또한 그들 나름의 제어구조를 쉽게 정의할 수 있다   

  • (assignment를 허용하는 언어들에서) 같은 환경으로 둘러싸인 복수의 함수는 그 환경을 바꿈으로써 비공개적으로 통신할 수 있다


Scheme에서


(define foo #f)

(define bar #f)


(let ((secret-message "none"))

 (set! foo (lambda (msg) (set! secret-message msg)))

 (set! bar (lambda () secret-message)))


(display (bar)) ; prints "none"

(newline)

(foo "meet me by the docks at midnight")

(display (bar)) ; prints "meet me by the docks at midnight"


  • Closures can be used to implement object systems

  • 클로저로 객체시스템을 구현할 수도 있다


어떤 발표자들은 지역환경과 결합되는 어떠한 데이타구조라도 클로저로 부르지만 이 용어는 일반적으로 그러한 함수들을 특별히 지칭한다  



덧글

댓글 입력 영역