您可以使用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]
这集合成不同的形状,尽管我认为这更合适。