2012-04-18 1 views
3

define_method는 다음과 같은 동작 전시 :루비 - define_method 및 폐쇄

class TestClass 
    def exec_block(&block) ; yield ; end 
end 
TestClass.new.send(:exec_block) do ; puts self ; end 
# -> main 
TestClass.send(:define_method, :bing) do ; puts self ; end 
TestClass.new.bing 
# -> <TestClass:...> 

는 내가 이해하지 못하는 것은이 define_method에 전달 된 블록이 폐쇄되어있을 가능성이다. 따라서 적어도 의 값을 main으로 캡처해야합니다 (내 이해에 따라) exec_block으로 전화 할 때 표시해야합니다.

블록이 메서드의 본문이된다는 것을 알고 있지만 동작의 이유를 이해하지 못합니다. 블록이 다른 방법으로 사용될 때 블록이 다른 것으로 평가되는 이유는 무엇입니까?

다른 방법으로 define_method의 블록 동작을 어떻게 재현 할 수 있습니까? 즉 exec_block으로`main '대신에 <TestClass:...>을 어떻게 출력 할 수 있습니까?

+0

그러면 exec_block의 동작을 어떻게 설명 할 수 있습니까? 'self'가 포착되지 않았다면'main'을 출력해야합니다. 또는 나는 무엇인가 놓치 느냐? – Norswap

+0

죄송합니다, 나는 tought의 반대를 썼습니다. 사실'print'는'main'을 출력하고 캡처되지 않았다면'TestClass : ...>'를 출력해야합니다. – Norswap

+0

니스! 그러나 만약 자신이 동적으로 바인드되어 있다면,'TestClass'는'exec_block'의 경우를 출력하지 않아야합니까? 블록은'yield whitin'TestClass'에 의해 호출되기 때문에. 사실 Ruby가 블록의 정의를 읽을 때 어떤 일이 발생하는지에 대해서는 명확하지 않습니다. 예를 들어'self'에 관해 저장된 정보가 저장되어 있습니까? (또한 답안에 귀하의 의견을 요약한다면, 나는 그것을 받아 들일 것입니다.) – Norswap

답변

5

self는 다른 변수와 같은 폐쇄에 의해 캡처됩니다. 그러나 BasicObject#instance_eval 및 alikes 동적 self 변수를 다시 바인딩

class A 
    def exec_block(&block) 
    block.call 
    end 
end 

class B 
    def exec_indirect(&block) 
    A.new.exec_block(&block) 
    end 
end 

block = proc { p self } 
a = A.new; b = B.new 

a.exec_block(&block) # => main 
b.exec_indirect(&block) # => main 

:

가 문맥을 설정하기 위해

가 가변 self 설정되어 우리는 Proc 주위 다른 구현 객체를 전달하여 그 검증 할 OBJ에 코드 OBJ의 인스턴스 변수에 액세스 코드를 제공하는 동안 실행

블록이 지정

경우,이 방법 체로서 사용된다 차례로 516,

Module#define_method 연결된 블록을 실행 instance_eval를 이용한다. 이 블록은 instance_eval [...을 사용하여 평가됩니다.

class A 
    def exec_block(&block) 
    instance_eval(&block) 
    end 
end 

block = proc { p self } 
A.new.exec_block(&block) # => #<A:0x00000001bb9828> 

instance_eval이 유일한 방법이 될 것으로 보인다 사용하여 이전에 언급 한 바와 같이 :]

가 관찰 : 당신의 exec_blockinstance_eval를 사용하는

A.send(:define_method, :foo, &block) 
a.foo     # => #<A:0x00000001717040> 
a.instance_eval(&block) # => #<A:0x00000001717040> 

를 지식으로, 당신은 지금 다시 작성할 수 있습니다 수정 된 컨텍스트를 사용하여 Proc 인스턴스를 실행하십시오. Ruby에서는 implement dynamic binding까지 사용할 수 있습니다.

0

먼저 블록을 yield 대신 명시 적으로 메서드에 전달하면 block.call을 사용할 수 있습니다. 두 번째 일 - exec_block 방법 내부 instance_eval(&block)yield를 교체하고 당신이 마법을 볼 수 있습니다)

조금 더 설명을 - 첫번째 예에서 블록은 main 객체를 가리키는 self 변수와 함께 로컬 범위를 잡는다.

두 번째 예제 (define_method)에서 블록은 새로운 메서드 본문으로 처리되며 instance_eval을 사용하여 개체의 범위 내에서 평가됩니다. 자세한 내용은 확인할 수 있습니다 :

+1

그래서 여러분이 말하는 것은 블록이 정의 될 때'self '의 의미가 고정되어 있지 않다는 것입니다. 그렇지 않으면 'self'는 어휘 적으로 범위가 지정되지 않습니다. 내가 맞습니까? – Norswap

0

http://apidock.com/ruby/Module/define_method은 니클라스 B의 의견에 영감 :

class TestClass 
    def exec_block(&block) ; yield ; end 
end 
s = self 

TestClass.new.send(:exec_block) do ; puts s ; end 
# -> main 

TestClass.send(:define_method, :bing) do ; puts s ; end 
TestClass.new.bing 
# -> main