2010-07-20 29 views
150

Tôi vừa có một câu hỏi nhanh về các vòng lặp trong Ruby. Có sự khác biệt nào giữa hai cách lặp lại này thông qua một bộ sưu tập?"cho" và "mỗi" trong Ruby

# way 1 
@collection.each do |item| 
    # do whatever 
end 

# way 2 
for item in @collection 
    # do whatever 
end 

Chỉ cần tự hỏi nếu chúng giống hệt nhau hoặc nếu có thể có sự khác biệt tinh tế (có thể khi @collection là không).

Trả lời

249

Đây là sự khác biệt duy:

mỗi:

irb> [1,2,3].each { |x| } 
    => [1, 2, 3] 
irb> x 
NameError: undefined local variable or method `x' for main:Object 
    from (irb):2 
    from :0 

cho:

irb> for x in [1,2,3]; end 
    => [1, 2, 3] 
irb> x 
    => 3 

Với for vòng lặp, biến iterator vẫn sống sau khi khối là làm xong. Với vòng lặp each, nó không, trừ khi nó đã được định nghĩa là một biến cục bộ trước khi vòng lặp bắt đầu.

Ngoài ra, for chỉ là đường cú pháp cho phương pháp each.

Khi @collectionnil cả hai vòng ném một ngoại lệ:

Ngoại lệ: biến địa phương không xác định hoặc phương pháp '@collection' cho chính: Object

25

ví dụ đầu tiên của bạn,

@collection.each do |item| 
    # do whatever 
end 

is more idiomatic. Trong khi Ruby hỗ trợ các cấu trúc vòng lặp như forwhile, thì cú pháp khối thường được ưu tiên.

Một khác biệt tinh tế là bất kỳ biến nào bạn khai báo trong vòng lặp for sẽ có sẵn bên ngoài vòng lặp, trong khi các biến trong khối lặp có hiệu quả riêng tư.

0

Theo như tôi biết, việc sử dụng các khối thay vì cấu trúc điều khiển trong ngôn ngữ là thành ngữ hơn.

2

Có vẻ như không có sự khác biệt, for sử dụng each bên dưới.

$ irb 
>> for x in nil 
>> puts x 
>> end 
NoMethodError: undefined method `each' for nil:NilClass 
    from (irb):1 
>> nil.each {|x| puts x} 
NoMethodError: undefined method `each' for nil:NilClass 
    from (irb):4 

Giống như Bayard nói, mỗi thành ngữ đều thành ngữ hơn. Nó ẩn nhiều hơn từ bạn và không yêu cầu các tính năng ngôn ngữ đặc biệt. mỗi Telemachus của Comment

for .. in .. đặt iterator ngoài phạm vi của vòng lặp, vì vậy

for a in [1,2] 
    puts a 
end 

a xác định sau khi vòng lặp kết thúc. Trường hợp là each thì không. Đó là một lý do khác có lợi cho việc sử dụng each, bởi vì biến tạm thời sống một thời gian ngắn hơn.

+1

Có _is_ một sự khác biệt nhỏ (như yjerem, ChristopheD và Bayard đề cập) liên quan đến phạm vi biến. – Telemachus

+0

Không chính xác, 'for' không sử dụng' each' bên dưới. Xem các câu trả lời khác. – akuhn

+0

@akuhn Để biết thêm chi tiết, vui lòng xem [câu hỏi] này (http://stackoverflow.com/q/39839809/5101493) và cả hai câu trả lời tuyệt vời của nó. –

41

Xem "The Evils of the For Loop" để có giải thích tốt (có một khác biệt nhỏ khi xem xét phạm vi biến).

Sử dụng eachconsidered more idiomatic sử dụng Ruby.

+0

@zachlatta: Cảm ơn bạn đã thông báo. Tôi sẽ chỉnh sửa liên kết để trỏ đến biến thể webarchive.org của bài viết! – ChristopheD

+1

http://graysoftinc.com/early-steps/the-evils-of-the-for-loop là liên kết mới, giờ trang web của JEG2 đã trực tuyến trở lại. – pnomolos

5

Một khác nhau hơn ..

number = ["one", "two", "three"] 
=> ["one", "two", "three"] 

loop1 = [] 
loop2 = [] 

number.each do |c| 
    loop1 << Proc.new { puts c } 
end 
=> ["one", "two", "three"] 

for c in number 
    loop2 << Proc.new { puts c } 
end 
=> ["one", "two", "three"] 

loop1[1].call 
two 
=> nil 

loop2[1].call 
three 
=> nil 

nguồn: http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-ruby

để biết rõ hơn: http://www.ruby-forum.com/topic/179264#784884

+0

'loop1' và' loop2' không được xác định. –

+0

Đã cập nhật câu trả lời! Cảm ơn @ DarekNędza –

0

Không bao giờ sử dụng for nó có thể gây ra lỗi.

Sự khác biệt là tinh tế nhưng có thể gây ra lỗi rất lớn!

Đừng để bị lừa, đây không phải là về các vấn đề về mã hoặc thành ngữ. Đây là vấn đề tránh các lỗi gần như không thể tháo gỡ trong mã sản xuất. Việc thực hiện của Ruby for có một lỗ hổng nghiêm trọng và không nên được sử dụng. Luôn sử dụng các vòng each, không bao giờ sử dụng vòng lặp for.

Dưới đây là một ví dụ nơi for giới thiệu một lỗi,

class Library 
    def initialize 
    @ary = [] 
    end 
    def method_with_block(&block) 
    @ary << block 
    end 
    def method_that_uses_these_blocks 
    @ary.map(&:call) 
    end 
end 

lib = Library.new 

for n in %w{foo bar quz} 
    lib.method_with_block { n } 
end 

puts lib.method_that_uses_these_blocks 

Prints

quz 
quz 
quz 

Sử dụng %w{foo bar quz}.each { |n| ... } in

foo 
bar 
quz 

Tại sao?

Trong vòng lặp for biến số n được xác định một lần và chỉ và sau đó một định nghĩa được sử dụng cho tất cả các lần lặp lại. Do đó, mỗi khối tham chiếu đến cùng một số n có giá trị là quz vào thời điểm kết thúc vòng lặp. Lỗi!

Vòng lặp each biến mới n được xác định cho mỗi lần lặp, ví dụ trên biến n được xác định ba lần riêng biệt. Do đó, mỗi khối tham chiếu đến một số n riêng biệt với các giá trị chính xác.

+0

Bạn có nghĩa là 'phương thức', không phải' hàm'. –

+0

@ericduminil bắt tốt, quá nhiều, haha. – akuhn

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