2009-07-01 3 views
2

의 직접 코드에 가자 :블록 내에서 변수를 변경할 수 있습니까?

여기
#!/usr/bin/ruby 
require 'tk' 


class Epg 

def initialize 
    @var = "bad"  
    @cvs = nil 
    @items_demo = TkRoot.new() {title "EPG"} 
    TkFrame.new(@items_demo) {|cf| 
      @var = "good" 
      @cvs = TkCanvas.new(cf) {|c|} 
     puts "@cvs 1 is #{@cvs}" 
     puts "@var 1 is #{@var}" 
    }.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes') 

    puts "@cvs 2 is #{@cvs}" 
    puts "@var 2 is #{@var}" 

end #initialize 

def test 
    @var = "bad" 
    puts " @var 3 :#{@var}" 
    (1..3).each {|x| @var="good"} 
    puts " @var 4 :#{@var}" 
end 
end 

e= Epg.new 
e.test 

는 출력 :

@cvs 1 is #<Tk::Canvas:0xb7cecb08> 
@var 1 is good 
@cvs 2 is 
@var 2 is bad   #@var has NOT been changed by the code in the block 
@var 3 :bad 
@var 4 :good   #@var has been changed by the code in the block 

우리는 여기에 다른 동작을 볼 이유는 무엇입니까?

답변

4

당신은 지역 변수의 설정과 현재 self 모두에 걸쳐 폐쇄로 블록 생각할 수 있습니다.

Ruby에서 항상은 무엇이든지간에 로컬 변수에 액세스 할 수 있습니다. self은 현재 개체와 인스턴스 변수에 인스턴스 메서드를 캡슐화합니다. 다음

class Table 
    def initialize(legs) 
    @legs = legs 
    end 

    def with_legs 
    yield @legs 
    end 
end 

그리고 :

루비의 블록을 의미하여
def some_calling_method 
    name = "Yehuda" 
    Table.new(4) {|legs| puts "#{name} gnaws off one of the #{legs} legs" } 
end 

, 당신은 name도 방법을 보지 않고, 블록 내부에 사용할 수 있음을 확신 할 수

다음 코드를 고려 너는 부르고있어.

그러나 다음을 고려 이때

class Person 
    def initialize(name) 
    @name = name 
    end 

    def gnaw 
    Table.new(4).with_legs do |legs| 
     puts "#{@name} gnaws off one of the #{legs} legs" 
    end 
    end 
end 

Person.new("Yehuda").gnaw 

, 우리는 블럭 내부에서 @name 인스턴스 변수에 액세스한다. 이 경우에는 훌륭하게 작동하지만 보장 할 수는 없습니다. 우리는 약간 다른 테이블을 구현하는 경우 :

class Table 
    def initialize(legs) 
    @legs = legs 
    end 

    def with_legs(&block) 
    self.instance_eval(&block) 
    end 
end 

효과적으로, 우리가되어 무슨 말을하는지 "는 다른 자기의 맥락에서 블록을 평가합니다." 이 경우 테이블의 컨텍스트에서 블록을 평가합니다. 왜 그걸 할거야?

이제
class Leg 
    attr_accessor :number 
    def initialize(number) 
    @number = number 
    end 
end 

class Table 
    def initialize(legs) 
    @legs = legs 
    end 

    def with_leg(&block) 
    Leg.new(rand(@legs).instance_eval(&block) 
    end 
end 

, 당신이 할 수 있습니다 :

class Person 
    def initialize(name) 
    @name = name 
    end 

    def gnaw 
    Table.new(4).with_leg do 
     puts "I'm gnawing off one of leg #{number}" 
    end 
    end 
end 

당신이 블록의 내부 사람 개체에 대한 액세스를 원하는 경우에, 당신은해야 할 것 :

class Person 
    def initialize(name) 
    @name = name 
    end 

    def gnaw 
    my_name = name 
    Table.new(4).with_leg do 
     puts "#{my_name} is gnawing off one of leg #{number}" 
    end 
    end 
end 

당신이 할 수 instance_eval을 사용하면 블록 내부의 멀리있는 객체의 메소드에 액세스하는 것이 더 간단하고 간단해질 수 있지만 self을 액세스 할 수 없게 만드는 비용이 듭니다. 이 기술은 대개 DSL에서 사용되며, 여러 가지 방법이 블록에 주입되지만 자기 자신은 그다지 중요하지 않습니다.

이것은 Tk에서 발생합니다. 그들은 자신의 self을 블로킹하기 위해 instance_eval을 사용하여 self을 지우고 있습니다.

+0

고마워, 카츠. 네가 한 모든 말을 소화 할 시간이 필요해. – pierrotlefou

4

설명은 TkFrame.new가 instance_eval을 사용하므로 @var = "good"이라는 할당은 TkFrame의 인스턴스 변수를 변경합니다. 이 밖으로 시도 :

 
class A 
    def initialize(&b) 
    instance_eval(&b) 
    end 
end 

class B 
    def initialize 
    @x = 10 
    @a = A.new do 
     @x = 20 
    end 
    end 
end 

p B.new 

이것은 당신이 볼 것입니다 :

 
#<B:0x10141314 @x=10, @a=#<A:0x10141300 @x=20>> 
+0

오타가 있었습니까? 그것은 "할당 @var ="good "_did not_ TkFrame의 인스턴스 변수를 변경"해야합니까? 귀하의 예에서는 @x가 여전히 10이지만 20이 아닙니다. – pierrotlefou

+0

아니요, 오타가 없습니다. –

+0

YSE.the 할당 @var = "good"은 TkFrame의 인스턴스 변수를 변경하지만 Epg의 intance 변수는 변경할 수 없습니다. – pierrotlefou

관련 문제