2014-07-25 36 views
7

Tại sao là each vòng lặp ưu tiên hơn vòng lặp for trong Ruby? Có sự khác biệt về độ phức tạp thời gian hay chúng chỉ khác nhau về cú pháp?Cách ưa thích để lặp trong Ruby là gì?

+0

Điều này sẽ bị đóng dưới dạng trùng lặp chứ không phải dựa trên ý kiến. Câu thứ hai đưa ra một tiêu chí rõ ràng cho những gì "ưa thích" trong câu đầu tiên có nghĩa là, IMO. Tôi chắc chắn có một bản sao ở đâu đó, mặc dù. –

+0

Điều này không cần phải đóng cửa. SO không phải là Wikipedia. Chúng tôi có thể có hai hoặc bảy chủ đề thảo luận các câu hỏi liên quan chặt chẽ. Với tất cả các do tuyên bố rõ ràng tôn trọng tình trạng thâm niên rất cao của bạn ở đây. –

Trả lời

2

Một câu hỏi thú vị. Có một số cách lặp trong Ruby. Tôi đã lưu ý rằng có một nguyên tắc thiết kế trong Ruby, rằng khi có nhiều cách làm giống nhau, thường có những khác biệt tinh tế giữa chúng, và mỗi trường hợp đều có cách sử dụng riêng của nó, vấn đề riêng của nó mà nó giải quyết. Vì vậy, cuối cùng bạn cần phải có khả năng viết (và không chỉ để đọc) tất cả chúng.

Đối với câu hỏi về vòng lặp for, điều này tương tự như my earlier question whethe for loop is a trap.

Về cơ bản có 2 cách rõ ràng chính của vòng lặp, một là bởi lặp (hay tổng quát hơn, khối), chẳng hạn như

[1, 2, 3].each { |e| puts e * 10 } 
[1, 2, 3].map { |e| e * 10) 
# etc., see Array and Enumerable documentation for more iterator methods. 

Kết nối với cách này của iterating là lớp Enumerator, mà bạn nên cố gắng hiểu.

Cách khác là lặp Pascal-ish theo while, untilfor vòng lặp.

for y in [1, 2, 3] 
    puts y 
end 

x = 0 
while x < 3 
    puts x; x += 1 
end 

# same for until loop 

Giống như ifunless, whileuntil có dạng đuôi của họ, chẳng hạn như

a = 'alligator' 
a.chop! until a.chars.last == 'g' 
#=> 'allig' 

Thứ ba cách rất quan trọng của vòng lặp là vòng lặp ngầm, hoặc vòng lặp bằng đệ quy. Ruby là cực kỳ dễ uốn, tất cả các lớp đều có thể sửa đổi được, các móc có thể được thiết lập cho các sự kiện khác nhau, và điều này có thể được khai thác để tạo ra các cách lặp lạ thường nhất. Các khả năng rất bất tận đến mức tôi thậm chí không biết bắt đầu nói về chúng ở đâu. Có lẽ một nơi tốt là blog by Yusuke Endoh, một nghệ sĩ nổi tiếng làm việc với mã Ruby là tài liệu nghệ thuật của ông về sự lựa chọn.

Để chứng minh những gì tôi muốn nói, hãy xem xét vòng lặp này

class Object 
    def method_missing sym 
    s = sym.to_s 
    if s.chars.last == 'g' then s else eval s.chop end 
    end 
end 

alligator 
#=> "allig" 
1

Bên cạnh các vấn đề dễ đọc, các for lặp loop trong Ruby đất trong khi each làm nó từ mã nguồn gốc, vì vậy về nguyên tắc each nên hiệu quả hơn khi lặp lại tất cả các phần tử trong một mảng.

Vòng với nhau:

arr.each {|x| puts x} 

Vòng với cho:

for i in 0..arr.length 
    puts arr[i] 
end 

Trong trường hợp each chúng tôi chỉ đi qua một khối mã đến một phương pháp thực hiện trong mã nguồn gốc của máy (mã nhanh) , trong khi trong trường hợp for, tất cả các mã phải được hiểu và chạy có tính đến tất cả sự phức tạp của ngôn ngữ Ruby.

Tuy nhiên for linh hoạt hơn và cho phép bạn lặp lại theo nhiều cách phức tạp hơn each, ví dụ: lặp lại với một bước nhất định.

EDIT

Tôi không đi qua đó bạn có thể bước qua một loạt bằng cách sử dụng phương pháp bước() trước khi gọi mỗi(), do đó sự linh hoạt Tôi tuyên bố cho for vòng lặp thực sự là phi lý.

+1

Bạn có thể đưa ra ví dụ về một vòng lặp phức tạp trong đó 'for' phù hợp hơn' mỗi'? – Stefan

+0

Vâng, tôi đã nghĩ rằng thực sự bước có thể được thực hiện bằng cách sử dụng phương thức step() và sau đó gọi mỗi(). Trong trường hợp này 'for' vẫn là một cách khủng khiếp để lặp: D – Claudix

+0

BTW, vòng lặp' for' tương đương sẽ là 'cho x trong arr; đặt x; end' – Stefan

4
  • Biến tham khảo một mục trong lần lặp là tạm thời và không có ý nghĩa bên ngoài của phiên. Nó là tốt hơn nếu nó được ẩn từ bên ngoài của iteration. Với các vòng lặp bên ngoài, biến đó nằm bên ngoài khối lặp. Trong các trường hợp sau, e chỉ hữu ích trong phạm vi do ... end, nhưng được ngăn cách với khối và được viết bên ngoài khối đó; nó không dễ dàng với một lập trình viên:

    for e in [:foo, :bar] do 
        ... 
    end 
    

    Với biến lặp nội bộ, biến khối được xác định ngay bên trong khối, nơi nó được sử dụng. Dễ đọc hơn:

    [:foo, :bar].each do |e| 
        ... 
    end 
    
  • Sự cố hiển thị này không chỉ dành cho lập trình viên. đối với tầm nhìn theo nghĩa của phạm vi với, biến cho một iterator bên ngoài là bên ngoài truy cập của lặp:

    for e in [:foo] do; end 
    e # => :foo 
    

    trong khi ở iterator nội bộ, một biến khối là vô hình từ bên ngoài:

    [:foo].each do |e|; end 
    e # => undefined local variable or method `e' 
    

    Sau này là tốt hơn từ quan điểm của đóng gói.

  • Khi bạn muốn tổ các vòng, thứ tự của các biến sẽ hơi ngược với vòng lặp bên ngoài:

    for a in [[:foo, :bar]] do 
        for e in a do 
        ... 
        end 
    end 
    

    nhưng với lặp nội bộ, trật tự là đơn giản hơn:

    [[:foo, :bar]].each do |a| 
        a.each do |e| 
        ... 
        end 
    end 
    
  • Với các trình vòng lặp bên ngoài, bạn chỉ có thể sử dụng cú pháp Ruby mã hóa cứng, và bạn cũng phải nhớ kết hợp giữa từ khóa và phương thức được gọi nội bộ (for gọi each), nhưng đối với các trình vòng lặp nội bộ, bạn có thể định nghĩa của riêng bạn, cho phép sự linh hoạt.

7

Có, đây là hai cách lặp lại khác nhau, Nhưng hy vọng tính toán này sẽ giúp.

require 'benchmark' 

a = Array(1..100000000) 
sum = 0 
Benchmark.realtime { 
    a.each { |x| sum += x } 
} 

này có 5,866932 giây

a = Array(1..100000000) 
sum = 0 
Benchmark.realtime { 
    for x in a 
    sum += x 
    end 
} 

này có 6,146521 giây.

Mặc dù đây không phải là cách đúng để thực hiện điểm chuẩn, cũng có một số ràng buộc khác.Nhưng trên một máy duy nhất, mỗi máy dường như nhanh hơn một chút.

+1

+1 để minh họa 'Điểm chuẩn'. –

+0

Bạn có chắc chắn rằng các điểm chuẩn đó có âm thanh thống kê không? Vì 'for' desugars to' each', tôi không mong đợi có sự khác biệt. Nếu có, 'for' sẽ nhanh hơn một chút vì nó không cần phân bổ một phạm vi cục bộ mới vì các biến được khai báo bên trong một vòng lặp' for' gây ô nhiễm phạm vi bên ngoài, trong khi khối được truyền tới 'mỗi' có phạm vi riêng của nó . –

+0

@ JörgWMittag, cái gì? Có 'for' thực sự desugar để' mỗi'? Và, btw., Tôi cũng hy vọng 'for' sẽ nhanh hơn một chút vì cùng một lý do. Tôi giải thích sự quan sát ngược lại với các nhà phát triển Ruby chú ý nhiều hơn đến việc tối ưu hóa 'mỗi' hơn' for'. –

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