2013-02-15 2 views
3

내가 더 잘 루비 폐쇄를 이해하려고 노력하고있어 나는 아주 이해하지 못하는이 예제 코드를 건너 온 :이해 루비 폐쇄

def make_counter 
    n = 0 
    return Proc.new { n = n + 1 } 
end 

c = make_counter 
puts c.call # => this outputs 1 
puts c.call # => this outputs 2 

누군가가 나에게 실제로 위의 코드 경우에 발생하는 이해하는 데 도움이 c = make_counter으로 전화합니까? 내 생각에 다음과 같이 생각합니다.

Ruby는 make_counter 메서드를 호출하고 Proc와 연결된 코드 블록이 { n = 1 }이 될 Proc 개체를 반환합니다. 첫 번째 c.call이 실행되면 Proc 개체는 연결된 블록을 실행하고 n = 1을 반환합니다. 그러나 두 번째 c.call이 실행될 때 Proc 개체가 연결된 블록을 여전히 실행하지 않습니다 (여전히 { n = 1 }입니까? 나는 출력이 2로 바뀔 이유를 알지 못한다.

어쩌면 나는 이것을 전혀 이해하지 못할 수도 있고, Ruby에서 실제로 일어나는 일에 대해 설명 할 수 있다면 도움이 될 것이다.

답변

8

make_counter을 호출하면 블록이 평가되지 않습니다. 블록은 c.call을 통해 Proc를 호출 할 때 평가되고 실행됩니다. 따라서 c.call을 실행할 때마다 n = n + 1이라는식이 계산되어 실행됩니다. Proc의 바인딩을 사용하면 n 변수 (로컬 n 변수)가 Proc 클로저 외부에서 처음 선언되었으므로 변수가 범위에 남아있게됩니다. 따라서 n은 반복 할 때마다 계속 증가합니다.

더이 문제를 명확히하기 위해 :

  • 발동을 정의 (또는 람다) 블록이 초기화시 평가되지 않습니다 - 내 코드가 당신이 그것을 볼 그대로 동결된다.
  • 코드는 실제로 '평가'되지만 고정 된 코드를 변경하지는 않습니다. 오히려 Proc의 코드 블록 컨텍스트 내에서 현재 사용되고있는 범위에있는 변수가 있는지 확인합니다. n은 로컬 변수이므로 (이전에 정의 된대로) Proc 내에서 사용되므로 바인딩 내에서 캡처되어 타고 올라옵니다.
  • call 메서드가 Proc에서 호출되면 캡처 된 해당 바인딩 컨텍스트 내에서 '고정 된'코드가 실행됩니다. 다시 전화했을 때 그래서 원래 0으로 할당되어 있던 n가 1로 증가, 같은 n은 2로 다시 증가합니다 등등 ...
+0

"... 먼저 발동 폐쇄 밖에서 선언 "정말 좋은 설명이 아닙니다. Proc 클로저 밖에서 Proc 내의 범위에없는 많은 장소가 있습니다. 그것은 Proc가 만들어 졌을 때'n'이 범위에 있었기 때문입니다. –

+0

@AndrewMarshall, "Proc closure 외부"에 의해, 나는 단순히 범위 안에 있던'n '에 대한 파서의 첫 번째 발생이 Proc 내부에 있지 않다는 것을 의미했습니다. 확실히, 참조가 바인딩에 포함될 수있는 범위에 있어야합니다. - 이것이 확실하다고 가정 했었습니다 ... – PinnyM

+0

죄송합니다.이 작업이 지루할 지 모르겠지만, 내 말로 당신이 말한 것을 설명하게하십시오. 이것이 올바른지 확인하십시오 : 정의에 따르면 코드 블록의 변수 n은 코드 블록 외부의 n에 바인딩됩니다. 이것이 의미하는 것은 Proc와 관련된 코드 블록이 c.call을 통해 평가 될 때 코드 블록은 n = n + 1 (1을 n으로 지정)을 실행하고 1을 반환한다는 것입니다. Proc와 관련된 블록이 실행될 때 두 번째로 다시 n = n + 1을 실행하지만 n은 1이므로이 블록은 2를 반환합니다. 올바르게 설명하겠습니까? – wmock