2013-05-15 39 views
7

Nếu tôi có:Phạm vi của một biến địa phương trong một khối

2.times do 
    i ||= 1 
    print "#{i} " 
    i += 1 
    print "#{i} " 
end 

tôi nhận được 1 2 1 2, trong khi tôi đã mong 1 2 2 3. Tại sao i mất nhiệm vụ khi vòng lặp bắt đầu lại? Nó hoạt động như mong đợi nếu nhiệm vụ xảy ra bên ngoài vòng lặp, vì vậy tôi đoán nó phải làm với phạm vi, nhưng tôi đã không nhận ra các vòng có phạm vi riêng của họ. Ai đó có thể làm rõ?

Cập nhật: Cảm ơn sự giúp đỡ về điều này. Một phần của sự nhầm lẫn của tôi xuất phát từ việc đến với Ruby từ Python, mà không có phạm vi khối (tôi nghĩ).

+0

là thế này cho các mục đích sư phạm? bởi vì loại mã này hoàn toàn không đơn điệu ... – tokland

Trả lời

9

Nhìn vào mã bên dưới:

2.times do 
    p defined? i 
    i ||= 1 
    p defined? i 
    p "#{i} " 
    i += 1 
    p "#{i} " 
end 

Output:

nil 
"local-variable" 
"1 " 
"2 " 
nil 
"local-variable" 
"1 " 
"2 " 

Điều đó có nghĩa trong mỗi lần lặp một phạm vi mới được tạo ra, và i được biết là chỉ phạm vi đó; được chứng minh bởi nil"local-variable".

Bây giờ i được tạo ra bên ngoài của block, và xem đầu ra (không nil đến):

i = nil 
2.times do 
    p defined? i 
    i ||= 1 
    p defined? i 
    p "#{i} " 
    i += 1 
    p "#{i} " 
end 

Output:

"local-variable" 
"local-variable" 
"1 " 
"2 " 
"local-variable" 
"local-variable" 
"2 " 
"3 " 

Để biết về nhiều ||= trông What Ruby’s ||= (Double Pipe/Or Equals) Really Does

+0

Điều đó làm cho nó trở nên độc đáo, cảm ơn! – ivan

3

Nó không phải là "vòng lặp" có phạm vi. Đó là khối. Có, một khối là phạm vi cục bộ.

Nếu bạn không muốn biến được hiểu là cục bộ đối với khối, nó cần tồn tại trước bên ngoài khối. Ngay cả chỉ cần thiết lập i đến nil trong một dòng trước sẽ làm điều này.

(Nhưng sự mong đợi của bạn về 1 2 3 4 sẽ vẫn không khá được đáp ứng ...!)

+0

Thực ra, ngay cả khi 'nếu sai thì i = nil end' sẽ là đủ, nhưng chính xác * tại sao * đó có lẽ sẽ quá khó hiểu để giải thích trong ngữ cảnh này. –

10

Tôi không biết những gì mong đợi của bạn được dựa trên. Nếu bạn nghĩ những gì tôi nghĩ rằng bạn nghĩ, nó phải là 1 2 2 3. Bạn có thể đạt được điều đó bằng cách khai báo biến số i bên ngoài khối.

i = nil 

2.times do 
    i ||= 1 
    print "#{i} " 
    i += 1 
    print "#{i} " 
end 

Sau đó, khối đóng trên biến đó (đóng) và sử dụng nó. Nếu không đóng cửa, i là địa phương để chặn và là mới mỗi lần.

+0

bạn nói đúng, tôi đã nhầm lẫn những kỳ vọng của riêng tôi :) – ivan

2

Bạn có thể vui chơi với nó. Ví dụ: bạn muốn truy cập phạm vi bên trong khối.

block = -> do 
    x = "Hello from inside a block" 
    binding # return the binding 
end 

p defined? x    #=> nil 
x = eval "x", block.call #=> #<Binding:0x007fce799c7dc8> 
p defined? x    #=> "local-variable" 
p x      #=> "Hello from inside a block" 

Điều này rất quan trọng vì nó cho phép nhà phát triển cơ bản thổi đi việc đóng gói một khối và nên thận trọng.

1

Câu trả lời đơn giản là bạn khôi phục biến số i trên mỗi lần lặp và được đặt lại về giá trị của một biến.

0

Vòng lặp Ruby như x.times tạo phạm vi địa phương để các biến giới thiệu trong vòng lặp này là cục bộ và sẽ bị hủy sau khi đạt đến cuối khối. Đó là lý do tại sao bạn không nhận được kết quả mong đợi của bạn. Trong ruby ​​nếu bạn muốn vòng lặp với phạm vi địa phương, bạn có thể sử dụng vòng lặp for hoặc while không tạo phạm vi cục bộ nhưng biến cục bộ của bạn i sẽ vẫn có thể truy cập được sau vòng lặp.

for j in (1..2) 
    i ||= 1 
    print "#{i} " 
    i += 1 
    print "#{i} " 
end 

in 1 2 2 3 như kết quả mong đợi của bạn. Bây giờ nếu chúng ta chạy trên vòng lặp lão hóa nó sẽ cho kết quả 3 4 4 5. Để biết thông tin thêm whilefor làm việc như nhau (phạm vi ít & in 1 2 2 3) mặt khác each, x.upto(y)x.times làm việc như nhau (tạo phạm vi địa phương và in 1 2 1 2)

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