day*_*ter 5 loops for-loop julia
这是一个真正的初学者问题......
在for循环中,何时评估循环参数?
在这里,循环永远运行,因此c每次循环开始时显然都会被"检查":
c= [1]
for i in c
push!(c, i)
@show c
end
c = [1,1]
c = [1,1,1]
c = [1,1,1,1]
...
Run Code Online (Sandbox Code Playgroud)
但是这个循环只评估一次:
c= [1]
for i in 1:length(c)
push!(c, i)
@show c
end
c = [1,1]
Run Code Online (Sandbox Code Playgroud)
这看起来像是评估enumerate(c)每个循环:
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]
...
Run Code Online (Sandbox Code Playgroud)
但这个循环显然不会:
c= [1]
for i in eachindex(c)
push!(c, i)
@show c
end
c = [1,1]
Run Code Online (Sandbox Code Playgroud)
这样做:
c= [1]
foreach(a -> (push!(c, a); @show c), c)
c = [1,1]
c = [1,1,1]
c = [1,1,1,1]
...
Run Code Online (Sandbox Code Playgroud)
正如我所说,这是一个真正的初学者问题.但我错过了一般模式吗?
我相信主要的一点是你的各种循环在两种不同类型的对象上调用Julia的迭代器接口:
数组对象c本身
一个AbstractUnitRange对象(或其子类型之一)
当你循环时for i in c,朱莉娅不知道有多大c.所有Julia需要的是迭代的当前状态(它已达到的索引)以及下一个要访问的索引c应该是什么.它可以测试它是否完成迭代,c如果下一个索引将超出范围.
从Julia的迭代器接口文档中复制,这样的循环基本归结为:
state = start(c)
while !done(c, state)
(i, state) = next(c, state)
# body
end
Run Code Online (Sandbox Code Playgroud)
如果你追加到c循环体中,总会有一个下一个索引要访问(while !done(c, state)即将永远true).阵列c可以增长直到你的内存已满.
循环使用enumerate和foreach都以c相同的方式依赖于数组的迭代器接口,因此c在这些循环期间修改时会看到类似的行为.
另一方面,循环使用for i in 1:length(c)和for i in eachindex(c)不迭代c自身,但支持迭代器接口的不同对象.
关键点是这些对象是在迭代开始之前创建的,并且c在循环体中进行修改时不会受到影响.
在第一种情况下,length(c)计算然后1:length(c)创建为UnitRange类型的对象.在您的例子是从1开始,在1停止,所以我们push!要c迭代期间只有一次.
在第二种情况下,调用eachindex([1])返回一个Base.OneTo对象; 就像一个UnitRange对象,除了保证从1开始.在你的示例Base.OneTo(1)中创建并从1开始并在1处停止.
这两个对象都是子类型,AbstractUnitRange最终是子类型AbstractArray.迭代器接口允许您按顺序访问这些对象持有的值.