2016-12-11 19 views
5

Đây là câu hỏi mới bắt đầu thực sự ...Trong vòng lặp for, khi nào các tham số vòng lặp được đánh giá?

Trong vòng lặp for, khi nào các thông số vòng lặp được đánh giá?

Ở đây, vòng lặp chạy mãi mãi, vì vậy c rõ ràng 'được kiểm tra' mỗi lần vòng lặp bắt đầu:

c= [1] 
for i in c 
    push!(c, i) 
    @show c 
end 
c = [1,1] 
c = [1,1,1] 
c = [1,1,1,1] 
... 

Nhưng vòng này chỉ đánh giá một lần:

c= [1] 
for i in 1:length(c) 
    push!(c, i) 
    @show c 
end 
c = [1,1] 

này một vẻ giống như nó đánh giá enumerate(c) mỗi vòng lặp:

c= [1] 
for (i, _) in enumerate(c) 
    push!(c, i) 
    @show c 
end 
c = [1,1] 
c = [1,1,1] 
c = [1,1,1,1] 
... 

Nhưng điều này lo op rõ ràng không:

c= [1] 
for i in eachindex(c) 
    push!(c, i) 
    @show c 
end 
c = [1,1] 

Và điều này:

c= [1] 
foreach(a -> (push!(c, a); @show c), c) 
c = [1,1] 
c = [1,1,1] 
c = [1,1,1,1] 
...  

Như tôi đã nói, đó là một câu hỏi mới bắt đầu thực sự. Nhưng tôi thiếu một mô hình chung?

Trả lời

6

tôi tin rằng các điểm chính là vòng lặp khác nhau của bạn được gọi giao diện iterator của Julia trên hai loại khác nhau của đối tượng:

  • đối tượng mảng c tự

  • một đối tượng AbstractUnitRange (hoặc một trong các các loại con của nó)


Khi bạn lặp lại với for i in c, Julia không biết số lượng lớn c là bao nhiêu. Tất cả các nhu cầu của Julia là trạng thái hiện tại của sự lặp lại (chỉ số mà nó đã đạt được) và chỉ số tiếp theo cần truy cập trong số c. Nó có thể kiểm tra nếu nó kết thúc lặp lại trên c nếu chỉ mục tiếp theo này sẽ vượt quá giới hạn.

sao chép từ tài liệu iterator interface Julia, một vòng lặp như vậy về cơ bản nắm tới:

state = start(c) 
while !done(c, state) 
    (i, state) = next(c, state) 
    # body 
end 

Nếu bạn thêm vào c trong cơ thể của vòng lặp, sẽ luôn có một chỉ số tiếp theo đến thăm (tức là while !done(c, state) sẽ luôn là true). Mảng c có thể phát triển cho đến khi bộ nhớ của bạn đầy.

Vòng lặp sử dụng enumerateforeach cả hai đều phụ thuộc vào giao diện trình lặp đến mảng c theo cùng một cách và vì vậy bạn thấy hành vi tương tự khi sửa đổi c trong các vòng lặp này.


Mặt khác, các vòng sử dụng for i in 1:length(c)for i in eachindex(c) không lặp trên c chính nó, nhưng đối tượng khác nhau hỗ trợ giao diện iterator.

Điểm mấu chốt là các đối tượng này được tạo trước khi bắt đầu lặp lại và chúng không bị ảnh hưởng khi sửa đổi c trong phần thân của vòng lặp.

Trong trường hợp đầu tiên, length(c) được tính và sau đó 1:length(c) được tạo làm đối tượng của loại UnitRange. Trong ví dụ của bạn, nó bắt đầu từ 1 và dừng ở 1, vì vậy chúng tôi chỉ push! đến c chỉ một lần trong khi lặp lại.

Trong trường hợp thứ hai, gọi eachindex([1]) trả về đối tượng Base.OneTo; giống như đối tượng UnitRange, ngoại trừ được đảm bảo bắt đầu ở 1. Trong trường hợp mẫu Base.OneTo(1) của bạn được tạo và bắt đầu từ 1 cũng như dừng tại 1.

Cả hai đối tượng này là các kiểu con của AbstractUnitRange và các kiểu con cuối cùng là AbstractArray. Giao diện trình lặp cho phép bạn truy cập các giá trị được giữ bởi các đối tượng này theo thứ tự.

+0

Câu trả lời hay, cảm ơn. Có thể đáng để thêm vào các tài liệu chính thức ... Tôi có thể nhận được vòng để thực hiện một PR một ngày ... :) – daycaster

+0

Vui vì nó đã giúp. Ý tưởng hay: Tôi nghĩ tài liệu của Julia nói chung rất tốt nhưng có thể hữu ích khi nhấn mạnh sự khác biệt trong cách lặp lại khi thích hợp. –

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