【问题标题】:How do I use an array element as an iterator in julia?如何在 julia 中使用数组元素作为迭代器?
【发布时间】:2020-03-26 15:30:58
【问题描述】:

我正在努力使我的 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] int。当我迭代如下正常变量时,情况似乎并非如此

x=1
for x in 1:10
    println(x)
end
println(x)

这告诉我在循环之后 x 仍然等于 1。如果可能的话,我希望有同样的行为。在我使用它进行迭代之前,还有什么方法可以在不定义 M 的情况下做到这一点?

编辑:我需要将 M 作为数组输出,这样我就可以对其进行一些线性代数。

【问题讨论】:

  • for (M₁, M₂, M₃) in (-kcut:kcut) ⊗ (-kcut:kcut) ⊗ (-kcut:kctu) 够“物理”吗?因为那时您可以将\otimes 定义为Iterators.product 的同义词。

标签: loops math vector julia readability


【解决方案1】:

您可以直接在 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 内部执行的代码转换(“代码降低”)有基本的了解。特别是,for 循环按照iteration 接口的规则替换

总而言之,一个像下面这样的sn-p:

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 不会失败,并且没有理由为什么循环体内发生的事情不会在它之后持续存在。

【讨论】:

  • 这很酷。不过我应该澄清一下。在循环中,我实际上需要在 M 上进行线性代数,所以如果它作为数组输出会很好。有什么办法可以通过类似的方法实现吗?
  • 由于在这种情况下 M 总是大小为 3,因此使用 3 个元素的元组(在性能方面)会更好。
  • 不过,如果您需要,我还是编辑了答案以包含转换。
  • 这是最“朱利安”的答案。特别是当kcut 的大小很大时,简单地生成整个Array 而不是使用Iterators 会导致内存不足的错误。
【解决方案2】:

您可以使用StaticArrays 执行以下操作以获得“可以进行线性代数的元组”:

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

但我不确定这是否值得。理想情况下,SVectors 和Numbers 的\otimes 将构造一个惰性数据结构,仅在迭代时创建适当大小的SVectors(而不是像这里那样飞溅)。我现在懒得写了。

一个更好的变体(但数学语法略少)是重载 ⨂(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]

这集合成不同的形状,尽管我认为这更合适。

【讨论】:

    猜你喜欢
    • 2016-09-05
    • 1970-01-01
    • 2019-02-17
    • 1970-01-01
    • 2020-11-08
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 2022-11-22
    相关资源
    最近更新 更多