2008-11-02 3 views
36

클로저 샘플을 보았습니다. - What is a 'Closure'?클로저를 언제 사용합니까?

클로저를 사용하는 간단한 예제는 누구에게 제공 할 수 있습니까?

특히 폐쇄가 의미있는 시나리오는 무엇입니까?

언어가 클로저를 지원하지 않는다고 가정하면 어떻게 비슷한 효과를 얻을 수 있습니까?

아무에게도 불쾌감을주지 않으려면 C#, python, javascript, ruby ​​등의 언어로 코드 샘플을 게시하십시오.
죄송합니다. 기능 언어를 아직 이해하지 못했습니다.

+0

저는 폐쇄 패턴을 통해 간결한 방식으로 구현할 수있는 디자인 패턴에 대한 링크를 살펴볼 수 있다고 생각합니다. [클로저 디자인 패턴 게시] (http://arturoherrero.com/2012/04/25/closure -design-patterns /) – Sudarshan

+0

이와 같은 질문이 끝나면 커뮤니티에 불행합니다. 이것은 매우 합리적인 질문이며 많은 학습 폐쇄 - 어려운 주제 -가 가질 것입니다. 실제 사용 사례를 이해하는 것은 어려운 주제에 대한 학습 곡선을 가속화하는 좋은 방법입니다. 고맙게도 닫혀지기 전에 많은 위대한 해답이있었습니다. – Matt

답변

30

클로저는 단순히 훌륭한 도구입니다. 언제 그들을 사용합니까? 당신이 좋아할 때마다 ... 이미 말했듯이, 대안은 수업을 작성하는 것입니다; 예를 들어 pre C# 2.0에서는 매개 변수화 된 스레드를 만드는 것이 진짜 어려움이었습니다.

string name = // blah 
int value = // blah 
new Thread((ThreadStart)delegate { DoWork(name, value);}); // or inline if short 

는 이름과 값으로 클래스를 만드는

또는 마찬가지로 목록 검색과 (람다를 사용하는 것을 비교 : C# 2.0 당신도`ParameterizedThreadStart '그냥 할 필요가 없습니다 이 시간) : - :

internal sealed class PersonFinder 
{ 
    public PersonFinder(int minAge, string region) 
    { 
     this.minAge = minAge; 
     this.region = region; 
    } 
    private readonly int minAge; 
    private readonly string region; 
    public bool IsMatch(Person person) 
    { 
     return person.Age > minAge && person.Region == region; 
    } 
} 
... 
Person person = list.Find(new PersonFinder(minAge,region).IsMatch); 
다시

Person person = list.Find(x=>x.Age > minAge && x.Region == region); 

이 대안은 두 가지 속성을 가진 클래스와 메서드를 작성하는 것입니다

이것은 입니다.은 컴파일러가 보닛에서 수행하는 것과 비슷합니다 (실제로는 개인 읽기 전용이 아닌 공용 읽기/쓰기 필드를 사용합니다).

C# 캡처가있는 가장 큰주의 사항은 범위를 보는 것입니다. 예를 들면 :

 for(int i = 0 ; i < 10 ; i++) { 
      ThreadPool.QueueUserWorkItem(delegate 
      { 
       Console.WriteLine(i); 
      }); 
     } 

이 내가 각각에 사용되는 변수 때문에, 당신은 무엇을 기대 인쇄되지 않을 수 있습니다. 반복의 조합을 볼 수 있습니다 - 심지어 10 10. C#의 캡처 된 변수를 신중하게 파악해야합니다.

 for(int i = 0 ; i < 10 ; i++) { 
      int j = i; 
      ThreadPool.QueueUserWorkItem(delegate 
      { 
       Console.WriteLine(j); 
      }); 
     } 

여기서 각 j는 개별적으로 캡처됩니다 (즉, 컴파일러에서 생성 된 다른 클래스 인스턴스).

Jon Skeet은 C# 및 Java 클로저를 포함하는 좋은 블로그 항목을 가지고 있습니다 here; 자세한 내용은 자신의 책 C# in Depth을 참조하십시오.이 책에는 전체 장이 있습니다.

+0

약간 주제에서 벗어나지 만, "Person person = list.Find (x => x.Age> minAge && x.Region == region);" 큰 소리로 읽으면? –

+3

"변수 person (Person 유형의)에 list.Find : x가 minAge보다 크고 x.Region이 x"x.Age "로 지정됩니다. 아마 약식 줄지 모르지만 : "... with list.Find 나이가 minAge 및 Region보다 큰 지역을 지역으로 검색하십시오." 그러나 "가는 것"은 "=>"의 일반적인 해석입니다. –

+0

이와 관련하여 http://stackoverflow.com/questions/1962539/using-closures-to-keep-track-of-a-variable-good-ide-or-dirty-trick에 답변하시는 것이 좋습니다. – RCIX

11

일반적으로 클로저가없는 경우 클로저의 환경에 해당하는 클래스를 전달하고 전달해야하는 클래스를 정의해야합니다.

(defun make-adder (how-much) 
    (lambda (x) 
    (+ x how-much))) 

및 사용 :

예를 들어, 리스프와 같은 언어로, 하나는 thusly 히 인수에 사전 정의 된 금액을 추가 (폐쇄을 통해 환경) 함수를 반환하는 함수를 정의 할 수 있습니다 같은 :

public class Adder { 
    private int howMuch; 

    public Adder(int h) { 
    howMuch = h; 
    } 

    public int doAdd(int x) { 
    return x + howMuch; 
    } 
} 
,536 : 폐쇄하지 않고 언어에서

cl-user(2): (make-adder 5) 
#<Interpreted Closure (:internal make-adder) @ #x10009ef272> 
cl-user(3): (funcall * 3)  ; calls the function you just made with the argument '3'. 
8 

, 당신은 이런 식으로 뭔가를 할 것입니다

다음이처럼 사용

폐쇄 암시 그것으로 환경을 전달

Adder addFive = new Adder(5); 
int addedFive = addFive.doAdd(3); 
// addedFive is now 8. 
; 실행 부분 (람다) 내부에서 그 환경을 완벽하게 참조 할 수 있습니다. 클로저가 없으면 해당 환경을 명시 적으로 만들어야합니다.

클로저를 사용할 때 설명해야 할 내용은 항상입니다. 대부분의 인스턴스가 클래스를 인스턴스화하여 계산의 다른 부분에서 일부 상태를 가져 와서 다른 곳에 적용하는 경우 인스턴스를 지원하는 언어의 클로저로 우아하게 대체됩니다.

클로저를 사용하여 객체 시스템을 구현할 수 있습니다.

+0

답변 해 주셔서 감사합니다. 클로저를 사용하는 것이 효과적인 코드 샘플을 설명해 주시겠습니까? – shahkalpesh

1

루아와 파이썬에서는 "코딩 만 할 때"하는 것이 매우 자연스러운 일입니다. 매개 변수가 아닌 것을 참조 할 때 닫히기 때문에 당신이 닫는 것입니다. (따라서 대부분은 예제로는 매우 흐릿해질 것입니다.)

구체적인 경우에는 단계가 (실행 취소(), 다시 실행()) 클로저 쌍인 실행 취소/다시 실행 시스템을 상상해보십시오. 더 성가신 방법은 다음 중 하나 일 수 있습니다. (a) unredoable 클래스가 보편적으로 dorky 인수를 갖는 특별한 메소드를 갖도록하거나, (b) UnReDoOperation umpteen 시간을 서브 클래스 화하십시오.

또 다른 구체적인 예는 무한리스트입니다. 일반화 된 컨테이너로 작업하는 대신 다음 요소를 검색하는 함수를 사용하십시오. (iterators의 힘의 일부입니다.)이 경우에는 상태의 조금만 유지하거나 (다음 정수, 전체 목록이 음수가 아닌 정수 또는 이와 유사한 경우) 또는 실제 컨테이너. 어느 쪽이든, 그것은 외부에있는 것을 참조하는 함수입니다.(무한리스트의 경우 상태 변수는 클로저 변수 여야합니다. 그렇지 않으면 모든 호출에 대해 깨끗해야하기 때문입니다)

+0

답변 해 주셔서 감사합니다. 클로저를 사용하는 것이 효과적인 코드 샘플을 설명해 주시겠습니까? – shahkalpesh

22

"모든 시간"에 대한 이전 답변에 동의합니다. 함수 언어 나 람다와 클로저가 공통적 인 언어로 프로그래밍 할 때는주의를 기울이지 않고도 사용합니다. 그것은 "기능을위한 시나리오는 무엇입니까?" 또는 "루프의 시나리오는 무엇입니까?" 이것은 원래의 질문을 벙어리하게 만드는 것이 아니라 오히려 특정 시나리오의 측면에서 정의하지 않은 언어 구문이 있음을 지적하는 것입니다. 당신은 모든 것을 위해 항상 그것들을 사용합니다. 그것은 제 2의 천성입니다.

Qc를 나 자신의 학생, 안톤와 걷고 있었다 유서 깊은 마스터 :

이것은 어떻게 든 연상시킨다. 을 원하면 Anton가 "Master, 나는 개체가 매우 좋은 것으로 들었습니다 - 이 사실입니까?" Qc Na는 그의 학생 을 조심스럽게 바라보고 대답했다. "어리석은 학생 - 물건은 단지 가난한 사람 남자의 종결"입니다.

Chastised, Anton는 그의 주인에게서 떠나고 그의 세포, 닫는 공부에 의지에 돌려 보냈다. 그는 신중하게 "Lambda : The Ultimate ..."시리즈의 종이와 사촌을 읽고, 클로저 기반 객체 시스템을 가진 Scheme 인터프리터를 구현했습니다. 그는 을 많이 배웠으며 그의 진행 상황을 주인에게 알리는 것을 고대했습니다. Qc를 나 안톤 와 그의 다음 거리에

는 말 그의 주인을 인상 시도 "마스터, 나는 이 문제를 연구 부지런히, 그리고 이제 객체가 진정으로 가난한 사람의 폐쇄 있습니다 을 이해합니다." Qc Na는 Anton의 막대기로 " 을 배울 때 대답하겠습니까?"클로저가 가난한 사람 사람의 물건이라고 말하면서 응답했습니다. 그 순간, 안톤 이 밝혀졌습니다.

(http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html)

+0

니스. 함수 프로그래밍이 다르므로 (상태 관리의 의미에서) 클로저를 사용하는 것이 자연 스럽습니다. 함수 프로그래밍에 대한 배경 지식이별로 없습니다. 나는이 개념을 도입 한 C#을 사용한다. 한 가지 예가 여전히 인정 될 것입니다. – shahkalpesh

+0

당신이 준 유추를 보면 (FP는 객체의 개념을 가지고 있지 않기 때문에), 상태는 함수에서 다른 함수로 전달되어 전체 응용 프로그램의 상태가 관리됩니다. 맞습니까? – shahkalpesh

+0

순수 FP에서는 일반적으로 상태에 전혀 신경 쓰지 않습니다. 당신이 필요로 할 때, 당신은 그것을 명시 적으로 만들고 그것을 전달합니다. –

14

클로저를 사용하는 가장 간단한 예는 무엇인가라는 태닝입니다. 기본적으로 두 개의 인자 인 ab을 호출 할 때 함께 덧붙인 함수 인 f()을 가정 해 봅시다.

def f(a, b): 
    return a + b 

을하지만 우리는 한 번에 하나 개의 인수로 f()를 호출 할 것인지, 인수를 위해,의 말을 보자 : 그래서, 파이썬, 우리는 가지고있다. 따라서 f(2, 3) 대신 f(2)(3)이 필요합니다. 이것은과 같이 수행 할 수 있습니다 : 우리가 f(2)를 호출 할 때

이제
def f(a): 
    def g(b): # Function-within-a-function 
     return a + b # The value of a is present in the scope of g() 
    return g # f() returns a one-argument function g() 

는, 우리는 새로운 기능, g()를 얻을; 이 새로운 함수는 범위의 변수f()이므로, 이상의 변수를 닫습니다. 따라서 클로저라는 용어가 사용됩니다.우리가 g(3)를 호출 할 때, (f의 정의에 의해 바인딩) 변수 a2 + 3 => 5

이 여러 시나리오에서 유용 반환 g()에 의해 액세스 할 수 있습니다. 나는 많은 인수를 받아,하지만 그들 중 몇이 나에게 유용했다 기능을 가지고 예를 들어, 나는 그렇게 같은 일반적인 기능을 작성할 수

def many_arguments(a, b, c, d, e, f, g, h, i): 
    return # SOMETHING 

def curry(function, **curry_args): 
    # call is a closure which closes over the environment of curry. 
    def call(*call_args): 
     # Call the function with both the curry args and the call args, returning 
     # the result. 
     return function(*call_args, **curry_args) 
    # Return the closure. 
    return call 

useful_function = curry(many_arguments, a=1, b=2, c=3, d=4, e=5, f=6) 

useful_function

이제 함수 인 경우에만 9 대신 3 개의 인수가 필요합니다. 자신을 반복하지 않아도되고 일반 솔루션을 만들었습니다. 다른 많은 인수 함수를 작성하면 curry 도구를 다시 사용할 수 있습니다.

3

다음은 Python의 표준 라이브러리 인 inspect.py의 예제입니다. 현재 읽음

매개 변수로 변환 함수와 조인 함수가 있으며 목록과 튜플을 반복적으로 탐색합니다. 재귀는 map()을 사용하여 구현됩니다. 첫 번째 매개 변수는 함수입니다. 이 코드는 Python에서 클로저에 대한 지원보다 먼저 사용되므로 재귀 호출로 변환 및 조인을 전달하려면 두 개의 기본 인수가 필요합니다.당신의 언어를 가질 때, 그리고 바인딩 방법 - 폐쇄, 이것은 당신이 상태를 전달하는 개체를 사용할 수 있습니다 당신은 일반적으로 너무 자주 클로저를 사용하지 않는, OO 언어에서

def strseq(object, convert, join=joinseq): 
    """Recursively walk a sequence, stringifying each element.""" 
    if type(object) in (list, tuple): 
     return join(map(lambda o: strseq(o, convert, join), object)) 
    else: 
     return convert(object) 

을 읽습니다. 파이썬이 클로저를 가지지 않았을 때 사람들은 파이썬이 객체로 클로저를 에뮬레이션하는 반면 Lisp은 클로저로 객체를 에뮬레이트한다고 말했습니다. IDLE (ClassBrowser.py)의 예로서 : 여기

class ClassBrowser: # shortened 
    def close(self, event=None): 
     self.top.destroy() 
     self.node.destroy() 
    def init(self, flist): 
     top.bind("<Escape>", self.close) 

는 self.close 탈출 누를 때 호출 파라미터리스 콜백이다. 그러나 close 구현에는 매개 변수 (self, self.top, self.node)가 필요합니다. 파이썬 바인딩 방법을 가지고 있지 않은 경우, 당신은 여기

class ClassBrowser: 
    def close(self, event=None): 
     self.top.destroy() 
     self.node.destroy() 
    def init(self, flist): 
     top.bind("<Escape>", lambda:self.close()) 

를 작성할 수, 람다는 "자기"가 아니라 매개 변수에서,하지만 문맥에서 얻을 것이다. 내가 거기에 하스켈 더가 사용하는,하지만 난 단지 자바 스크립트 클로저를 사용하는 즐거움을 했어, 그리고 자바 스크립트에게있어 Closure

0

이 문서에서는 폐쇄 실제로 유용한 경우의 두 가지 예를 포함 나는 그 점을별로 보지 못한다. 나의 첫 번째 본능은 "오, 안돼, 다시는 안된다"고 소리 지르는 것이었다. 클로저가 (어쨌든 자바 스크립트에서) 구현 된 방법을 읽은 후에는 지금 나에게별로 나쁘지 않고 구현이 다소 우아 해 보입니다.

하지만 "클로저"가 개념을 설명하는 데 가장 좋은 단어는 아니라는 것을 깨달았습니다. 나는 그것이 "비행 범위 (flying scope)"라는 것이 더 바람직하다고 생각한다.

1

이전 답변 중 하나 인 메모는 사용자가 거의 알지 못하는 사이에 사용하는 경우가 많습니다.

중요한 점은 UI 컨텍스트에 대한 액세스를 허용하면서 코드 재사용을 얻기 위해 UI 이벤트 처리를 설정하는 데 매우 일반적으로 사용된다는 것입니다.

function setColor(button, color) { 

     button.addEventListener("click", function() 
     { 
      button.style.backgroundColor = color; 
     }, false); 
} 

window.onload = function() { 
     setColor(document.getElementById("StartButton"), "green"); 
     setColor(document.getElementById("StopButton"), "red"); 
} 

참고 :이 폐쇄가 실제로 생성되지 않는다는 것을 주목할 필요가 정확성을 위해 여기에 클릭 이벤트에 대한 익명의 핸들러 함수를 정의하는 방법의 예는 setColor() 함수의 buttoncolor 매개 변수를 포함하는 폐쇄 만듭니다이다 setColor() 함수가 종료 될 때까지