2009-01-07 27 views
10

Tôi chỉ học ruby ​​và cố gắng hiểu phạm vi mã được thực hiện trong các khối. Ví dụ: tôi muốn có thể tạo một khối ảnh hưởng đến phương pháp mà nó được gắn vào, như vậy:ruby: một khối có thể ảnh hưởng đến các biến cục bộ trong một phương thức không?

def test(&block) 
    block.call() if block_given? 
    puts "in test, foo is #{foo}" 
    puts "in test, bar is #{bar}" 
end 

test() { 
    foo="this is foo" 
    bar="this is bar" 
} 

Trong trường hợp này tôi không muốn phải sửa đổi khối nào cả - Tôi muốn có thể viết nó bằng cách sử dụng tham chiếu biến đơn giản và không có tham số. Chỉ bằng cách thay đổi phương pháp 'thử nghiệm' trong ví dụ trên, có thể truy cập các biến được xác định trong khối không?

Một lần nữa, mục tiêu là để lại khối chưa được sửa đổi, nhưng có thể truy cập các biến được tạo từ bên trong 'kiểm tra' sau khi khối thực hiện.

Trả lời

10

Trước hết, block.call() được thực hiện với yield và bạn không cần tham số &block theo cách đó.

Bạn thường không thể làm những gì bạn muốn, các khối bị ràng buộc khi chúng được tạo và bên trong khối bạn có thể thấy các biến cục bộ được xác định tại thời điểm đó; cách dễ nhất để làm những gì bạn muốn, mà không phải là cách bạn sẽ sử dụng khối thường là, là thế này:

def test() 
    foo = yield if block_given? 
    puts "in test, foo is #{foo}" 
end 

test() { 
    foo="this is foo" 
} 

Nhưng đó chỉ là một tác dụng phụ vì foo được "trả lại" bởi khối. Thay vào đó, nếu bạn thực hiện điều này:

def test() 
    foo = yield if block_given? 
    puts "in test, foo is #{foo}" 
end 

test() { 
    foo="this is foo" 
    "ha ha, no foo for you" 
} 

Bạn sẽ nhận thấy rằng nó có điều gì đó khác.

Dưới đây là kỳ diệu:

def test(&block) 
    foo = eval "foo", block.binding 
    puts foo 
    block.call 
    foo = eval "foo", block.binding 
    puts foo 
end 

foo = "before test" 
test() { 
    foo = "after test" 
    "ha ha, no foo for you" 
} 

Đó sẽ loại công việc, nhưng nó phá vỡ nếu bạn loại bỏ foo = "before test"foo trở thành một biến địa phương trong khối và không tồn tại trong các ràng buộc.

Tóm tắt: bạn không thể truy cập các biến cục bộ từ một khối, chỉ những người dân địa phương nơi khối được xác định và giá trị trả lại của khối.

Thậm chí điều này sẽ không làm việc:

def test(&block) 
    eval "foo = 'go fish'", block.binding 
    block.call 
    bar = eval "foo", block.binding 
    puts bar 
end 

foo trong các ràng buộc khác với địa phương trong khối (Tôi không biết điều này, nhờ).

-1
def test(&block) 
    foo = yield 
    puts "in test, foo is #{foo}" 
end 

test { "this is foo" } 

in in test, foo is this is foo

Giá trị sản lượng là giá trị của khối.

Bạn cũng có thể chuyển tham số cho sản lượng, sau đó có thể truy cập bằng khối sử dụng | param, một giá trị khác | lúc bắt đầu của khối.

Ngoài ra, hãy kiểm tra procs.

foo = "this is foo" 
p = Proc.new { "foo is #{foo}" } 
p.call 

Prints "foo is this is foo"

def test(p) 
    p.call 
end 

test p 

Prints "foo is this is foo"

def test2(p) 
    foo = "monkey" 
    p.call 
end 

test2 p 

Prints "foo is this is foo"

+0

Điều này là gây hiểu nhầm, bạn không truy cập vào người dân địa phương trong khối như câu hỏi cho biết, chỉ giá trị trả lại của khối. –

3

Không, một khối không thể ảnh hưởng đến các biến cục bộ tại nơi nó được gọi là.

Các khối trong Ruby là đóng cửa, có nghĩa là chúng nắm bắt phạm vi xung quanh chúng khi chúng được tạo. Các biến được hiển thị khi bạn tạo khối là các biến mà nó thấy. Nếu có foobar ở đầu mã của bạn, bên ngoài bất kỳ phương pháp nào, chặn sẽ thay đổi các mã đó khi được gọi.

2

Bạn có thể làm những gì bạn muốn bằng cách tiết thêm một chút:

class Test 
    def foo(t) 
    @foo = t 
    end 
    def bar(t) 
    @bar = t 
    end 
    def test(&block) 
    self.instance_eval &block if block_given? 
    puts "in test, foo is #{@foo}" 
    puts "in test, bar is #{@bar}" 
    end 
end 

Test.new.test() { 
    foo "this is foo" 
    bar "this is bar" 
} 

Bạn có thể tạo các phương pháp như attr_accessor mà sẽ tạo ra setter apropriate (các foobar phương pháp).

Các vấn đề liên quan