如何在 julia 中使用数组元素作为迭代器?
How do I use an array element as an iterator in julia?
我正在努力让我的 Julia 代码更易于理解(对物理学家而言),并且我认为如果我可以使用某种向量类型的迭代器会更好。我正在尝试将每个元素用作迭代器。到目前为止我的解决方案是:
kcut=2
= Array{Float64}(undef,3)
Ø = [0, 0, 0]
for [1] in -kcut:kcut, [2] in -kcut:kcut, [3] in -kcut:kcut
if norm()^2 <= kcut^2 && != Ø
println()
end
end
println()
几乎达到了预期的效果。唯一的问题是一旦我完成了 for 循环,我的 M 被重新定义为它在循环中采用的最后一个配置,在这种情况下为 [2,2,2]。当我如下遍历普通变量时,情况似乎并非如此
x=1
for x in 1:10
println(x)
end
println(x)
这告诉我 x 在循环后仍然等于 1。如果可能的话,我想有相同的行为。在我使用它进行迭代之前,还有什么方法可以在不定义 M 的情况下做到这一点?
编辑:我需要 M 输出为数组,这样我就可以在上面做一些线性代数。
您可以直接在 M 上迭代,例如:
julia> using LinearAlgebra: norm
julia> kcut=2;
julia> Ø = [0, 0, 0];
julia> for in Iterators.product(-kcut:kcut, -kcut:kcut, -kcut:kcut)
if norm()^2 <= kcut^2 && != Ø
# is a Tuple now, which should be best in most cases.
#
# If you want arrays instead:
= collect()
println(, "\t", )
end
end
(0, 0, -2) [0, 0, -2]
(-1, -1, -1) [-1, -1, -1]
(0, -1, -1) [0, -1, -1]
(1, -1, -1) [1, -1, -1]
(-1, 0, -1) [-1, 0, -1]
(0, 0, -1) [0, 0, -1]
(1, 0, -1) [1, 0, -1]
(-1, 1, -1) [-1, 1, -1]
...
julia> println()
ERROR: UndefVarError: not defined
Stacktrace:
[1] top-level scope at REPL[5]:1
这样,M只定义在for
循环的范围内。
要了解原始代码的行为,您必须对 Julia 内部执行的代码转换 ("code lowering") 有基本的了解。特别是,for
循环按照 iteration 接口
的规则替换
总而言之,片段如下:
M = [42]
for M[1] in -k:k
println(M[1])
end
与以下行为相同:
M = [42]
next = iterate(-k:k)
while next !== nothing
M[1] = i
println(M[1])
next = iterate(iter, state)
end
你看到 M
必须预先存在,这样 M[1]=i
才不会失败,并且循环体内发生的事情没有理由不会在它之后持续存在。
您可以使用 StaticArrays
执行以下操作以获得 "tuples that can do linear algebra":
julia> using StaticArrays
julia> ⊗(u, v) = (i ⊗ j for i in u for j in v)
⊗ (generic function with 1 method)
julia> ⊗(x::Number, y::Number) = SVector(x, y)
⊗ (generic function with 2 methods)
julia> ⊗(x::SVector{N}, y::Number) where {N} = SVector(x..., y)
⊗ (generic function with 3 methods)
julia> collect((1:3) ⊗ (10:12) ⊗ (100:101))
18-element Array{SArray{Tuple{3},Int64,1,3},1}:
[1, 10, 100]
[1, 10, 101]
[1, 11, 100]
[1, 11, 101]
[1, 12, 100]
[1, 12, 101]
[2, 10, 100]
[2, 10, 101]
[2, 11, 100]
[2, 11, 101]
[2, 12, 100]
[2, 12, 101]
[3, 10, 100]
[3, 10, 101]
[3, 11, 100]
[3, 11, 101]
[3, 12, 100]
[3, 12, 101]
julia> using LinearAlgebra: norm
julia> for M in (1:3) ⊗ (10:12) ⊗ (100:101)
println(norm(M))
end
100.50373127401788
101.49876846543509
100.60815076324582
101.6021653312566
100.72239075796404
101.7152889196113
100.5186549850325
101.51354589413178
100.62305898749054
101.61692772368194
100.73728207570423
101.73003489628813
100.54352291420865
101.5381701627521
100.64790112068906
101.64152694642087
100.7620960480676
101.75460677532
但我不确定这是否值得。理想情况下,SVector
s 和 Number
s 的 \otimes
将构造一个惰性数据结构,它只在迭代时创建适当大小的 SVector
s(而不是像这里那样展开)。现在懒得写了
一个更好的变体(但数学语法略少)是重载 ⨂(spaces...)
以一次完成所有事情:
julia> ⨂(spaces::NTuple{N}) where {N} = (SVector{N}(t) for t in Iterators.product(spaces...))
⨂ (generic function with 1 method)
julia> ⨂(spaces...) = ⨂(spaces)
⨂ (generic function with 2 methods)
julia> collect(⨂(1:3, 10:11))
3×2 Array{SArray{Tuple{2},Int64,1,2},2}:
[1, 10] [1, 11]
[2, 10] [2, 11]
[3, 10] [3, 11]
julia> collect(⨂(1:3, 10:11, 100:101))
3×2×2 Array{SArray{Tuple{3},Int64,1,3},3}:
[:, :, 1] =
[1, 10, 100] [1, 11, 100]
[2, 10, 100] [2, 11, 100]
[3, 10, 100] [3, 11, 100]
[:, :, 2] =
[1, 10, 101] [1, 11, 101]
[2, 10, 101] [2, 11, 101]
[3, 10, 101] [3, 11, 101]
虽然我认为这更合适,但它收集到不同的形状。
我正在努力让我的 Julia 代码更易于理解(对物理学家而言),并且我认为如果我可以使用某种向量类型的迭代器会更好。我正在尝试将每个元素用作迭代器。到目前为止我的解决方案是:
kcut=2
= Array{Float64}(undef,3)
Ø = [0, 0, 0]
for [1] in -kcut:kcut, [2] in -kcut:kcut, [3] in -kcut:kcut
if norm()^2 <= kcut^2 && != Ø
println()
end
end
println()
几乎达到了预期的效果。唯一的问题是一旦我完成了 for 循环,我的 M 被重新定义为它在循环中采用的最后一个配置,在这种情况下为 [2,2,2]。当我如下遍历普通变量时,情况似乎并非如此
x=1
for x in 1:10
println(x)
end
println(x)
这告诉我 x 在循环后仍然等于 1。如果可能的话,我想有相同的行为。在我使用它进行迭代之前,还有什么方法可以在不定义 M 的情况下做到这一点?
编辑:我需要 M 输出为数组,这样我就可以在上面做一些线性代数。
您可以直接在 M 上迭代,例如:
julia> using LinearAlgebra: norm
julia> kcut=2;
julia> Ø = [0, 0, 0];
julia> for in Iterators.product(-kcut:kcut, -kcut:kcut, -kcut:kcut)
if norm()^2 <= kcut^2 && != Ø
# is a Tuple now, which should be best in most cases.
#
# If you want arrays instead:
= collect()
println(, "\t", )
end
end
(0, 0, -2) [0, 0, -2]
(-1, -1, -1) [-1, -1, -1]
(0, -1, -1) [0, -1, -1]
(1, -1, -1) [1, -1, -1]
(-1, 0, -1) [-1, 0, -1]
(0, 0, -1) [0, 0, -1]
(1, 0, -1) [1, 0, -1]
(-1, 1, -1) [-1, 1, -1]
...
julia> println()
ERROR: UndefVarError: not defined
Stacktrace:
[1] top-level scope at REPL[5]:1
这样,M只定义在for
循环的范围内。
要了解原始代码的行为,您必须对 Julia 内部执行的代码转换 ("code lowering") 有基本的了解。特别是,for
循环按照 iteration 接口
总而言之,片段如下:
M = [42]
for M[1] in -k:k
println(M[1])
end
与以下行为相同:
M = [42]
next = iterate(-k:k)
while next !== nothing
M[1] = i
println(M[1])
next = iterate(iter, state)
end
你看到 M
必须预先存在,这样 M[1]=i
才不会失败,并且循环体内发生的事情没有理由不会在它之后持续存在。
您可以使用 StaticArrays
执行以下操作以获得 "tuples that can do linear algebra":
julia> using StaticArrays
julia> ⊗(u, v) = (i ⊗ j for i in u for j in v)
⊗ (generic function with 1 method)
julia> ⊗(x::Number, y::Number) = SVector(x, y)
⊗ (generic function with 2 methods)
julia> ⊗(x::SVector{N}, y::Number) where {N} = SVector(x..., y)
⊗ (generic function with 3 methods)
julia> collect((1:3) ⊗ (10:12) ⊗ (100:101))
18-element Array{SArray{Tuple{3},Int64,1,3},1}:
[1, 10, 100]
[1, 10, 101]
[1, 11, 100]
[1, 11, 101]
[1, 12, 100]
[1, 12, 101]
[2, 10, 100]
[2, 10, 101]
[2, 11, 100]
[2, 11, 101]
[2, 12, 100]
[2, 12, 101]
[3, 10, 100]
[3, 10, 101]
[3, 11, 100]
[3, 11, 101]
[3, 12, 100]
[3, 12, 101]
julia> using LinearAlgebra: norm
julia> for M in (1:3) ⊗ (10:12) ⊗ (100:101)
println(norm(M))
end
100.50373127401788
101.49876846543509
100.60815076324582
101.6021653312566
100.72239075796404
101.7152889196113
100.5186549850325
101.51354589413178
100.62305898749054
101.61692772368194
100.73728207570423
101.73003489628813
100.54352291420865
101.5381701627521
100.64790112068906
101.64152694642087
100.7620960480676
101.75460677532
但我不确定这是否值得。理想情况下,SVector
s 和 Number
s 的 \otimes
将构造一个惰性数据结构,它只在迭代时创建适当大小的 SVector
s(而不是像这里那样展开)。现在懒得写了
一个更好的变体(但数学语法略少)是重载 ⨂(spaces...)
以一次完成所有事情:
julia> ⨂(spaces::NTuple{N}) where {N} = (SVector{N}(t) for t in Iterators.product(spaces...))
⨂ (generic function with 1 method)
julia> ⨂(spaces...) = ⨂(spaces)
⨂ (generic function with 2 methods)
julia> collect(⨂(1:3, 10:11))
3×2 Array{SArray{Tuple{2},Int64,1,2},2}:
[1, 10] [1, 11]
[2, 10] [2, 11]
[3, 10] [3, 11]
julia> collect(⨂(1:3, 10:11, 100:101))
3×2×2 Array{SArray{Tuple{3},Int64,1,3},3}:
[:, :, 1] =
[1, 10, 100] [1, 11, 100]
[2, 10, 100] [2, 11, 100]
[3, 10, 100] [3, 11, 100]
[:, :, 2] =
[1, 10, 101] [1, 11, 101]
[2, 10, 101] [2, 11, 101]
[3, 10, 101] [3, 11, 101]
虽然我认为这更合适,但它收集到不同的形状。