【问题标题】:Add 2d tuple to a 2d array in Julia将二维元组添加到 Julia 中的二维数组
【发布时间】:2022-01-21 01:47:22
【问题描述】:

如何在 Julia 中将二维元组添加到二维矩阵?

    t1 = ((10,20),(30,40)); #2d immutable tuple 
    a = [1 2;3 4] #2d matrix 
    a .+ t1 

抛出错误:

MethodError: no method matching +(::Int64, ::Tuple{Int64, Int64})
Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
  +(::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:87
  +(::Integer, ::Ptr) at pointer.jl:161
  ...
Stacktrace:
 [1] _broadcast_getindex_evalf
   @ .\broadcast.jl:648 [inlined]
 [2] _broadcast_getindex
   @ .\broadcast.jl:621 [inlined]
 [3] getindex
   @ .\broadcast.jl:575 [inlined]
 [4] copy
   @ .\broadcast.jl:922 [inlined]
 [5] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2}, Nothing, typeof(+), Tuple{Matrix{Int64}, Tuple{Tuple{Int64, Int64}, Tuple{Int64, Int64}}}})
   @ Base.Broadcast .\broadcast.jl:883
 [6] top-level scope
   @ REPL[15]:1

是否存在向量/矩阵相加方法? 显然,我可以使用 for 循环逐个元素添加。

【问题讨论】:

    标签: matrix tuples julia arithmetic-expressions


    【解决方案1】:

    啊,所以这里的问题是,当您将t1 称为“二维元组”时,实际上不是;它是一个嵌套元组,一个 tuple-of-tuples,因此无法与您的二维数组(实际上 一个二维对象而不仅仅是一个数组的数组)。

    如果你想将一个二维数组添加到一个可以像Tuple 一样堆栈分配的不可变对象,同时又是真正的二维,那么你可以使用StaticArrays.jl 包,它提供了不可变的@ 987654324@类型:

    julia> a = [1 2; 3 4]
    2×2 Matrix{Int64}:
     1  2
     3  4
    
    julia> using StaticArrays
    
    julia> t1 = SA[10 20; 30 40]
    2×2 SMatrix{2, 2, Int64, 4} with indices SOneTo(2)×SOneTo(2):
     10  20
     30  40
    
    julia> a + t1
    2×2 SMatrix{2, 2, Int64, 4} with indices SOneTo(2)×SOneTo(2):
     11  22
     33  44
    

    但是,如果出于某种原因您真的想坚持使用 嵌套 而不是多维,那么让我们也让数组嵌套

    julia> t1 = ((10,20),(30,40)) #tuple-of-tuples
    ((10, 20), (30, 40))
    
    julia> a = [[1,2],[3,4]] #vector-of-vectors
    2-element Vector{Vector{Int64}}:
     [1, 2]
     [3, 4]
    

    在这种情况下,您可以通过递归轻松解决

    add(a,b) = a + b
    add(a::Vector, b::NTuple{N}) where N = [add(a[i],b[i]) for i in 1:N]
    add(a::NTuple{N}, b::Vector) where N = [add(a[i],b[i]) for i in 1:N]
    
    julia> add(a,t1)
    2-element Vector{Vector{Int64}}:
     [11, 22]
     [33, 44]
    

    这种方法也可以扩展到任意深度嵌套的对象:

    julia> a = [[[1,2],[3,4]],[[5,6],[7,8]]]
    2-element Vector{Vector{Vector{Int64}}}:
     [[1, 2], [3, 4]]
     [[5, 6], [7, 8]]
    
    julia> t1 = (((10,20),(30,40)),((50,60),(70,80)))
    (((10, 20), (30, 40)), ((50, 60), (70, 80)))
    
    julia> add(a,t1)
    2-element Vector{Vector{Vector{Int64}}}:
     [[11, 22], [33, 44]]
     [[55, 66], [77, 88]]
    

    不幸的是,真正的多维数组与在不支持真正多维数组的语言中使用向量的向量的常见技巧之间存在广泛的语义混淆。

    【讨论】:

      【解决方案2】:

      @cbk 已经解释了嵌套数组如何不等同于多维结构。但是,如果您的结构已经嵌套,您当然可以将添加内容编写为嵌套广播。从(我相信)Julia 1.6 开始,就有了独立的“广播运算符”的语法,这是可能的:

      julia> t1 = ((10,20),(30,40))
      ((10, 20), (30, 40))
      
      julia> a = [[1,2],[3,4]]
      2-element Vector{Vector{Int64}}:
       [1, 2]
       [3, 4]
      
      julia> broadcast(.+, a, t1)
      2-element Vector{Vector{Int64}}:
       [11, 22]
       [33, 44]
      

      如果aMatrix,您可以在eachrow 上执行相同操作:

      julia> a = [1 2;3 4]
      2×2 Matrix{Int64}:
       1  2
       3  4
      
      julia> broadcast(.+, eachrow(a), t1)
      2-element Vector{Vector{Int64}}:
       [11, 22]
       [33, 44]
      

      (在这种特定情况下,map 等价于broadcast。)

      【讨论】:

      • 哦,太好了!我们可能会注意到,尽管在这种方法中 OP 仍然必须将 a 转换为向量的向量,否则您会得到不同的答案; julia&gt; broadcast(.+, a, t1) 2×2 Matrix{Tuple{Int64, Int64}}: (11, 21) (12, 22) (33, 43) (34, 44)
      • 啊,当然了。我从您的答案中复制了a,但是从它已经是向量向量的部分:D
      • 你也可以写成(.+).(t1, a)(.+).(t1, eachrow(a))。 (我以为我会发现这样写更直观,但事实证明我没有;broadcast 调用版本看起来更清晰。至少它们看起来像一些可爱的古怪表情符号!)
      • add_k(k) = foldr((f, _) -&gt; Broadcast.BroadcastFunction(f), 1:k, init=+) 用于一般的K 级嵌套。
      猜你喜欢
      • 2019-07-23
      • 2012-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-22
      • 2018-07-22
      • 1970-01-01
      • 1970-01-01
      • 2014-10-03
      相关资源
      最近更新 更多