Julia 中矩阵集的奇怪行为

strange behaviour of Set of Matrices in Julia

有一组矩阵,当遍历它们并修改它们时,我无法从集合中删除它们:

julia> grids = Set([[1 2;3 4],[3 4;5 6]])
Set{Matrix{Int64}} with 2 elements:
  [1 2; 3 4]
  [3 4; 5 6]

julia> for g in grids
           g[g .== 3] .= 0
           println(g)
           println(grids)
           delete!(grids,g)
       end
[1 2; 0 4]
Set([[1 2; 0 4], [3 4; 5 6]])
[0 4; 5 6]
Set([[1 2; 0 4], [0 4; 5 6]])

julia> println(grids)
Set([[1 2; 0 4], [0 4; 5 6]])

但是手动删除有效:

julia> grids = Set([[1 2;3 4],[3 4;5 6]])
Set{Matrix{Int64}} with 2 elements:
  [1 2; 3 4]
  [3 4; 5 6]

julia> delete!(grids, [1 2;3 4])
Set{Matrix{Int64}} with 1 element:
  [3 4; 5 6]

请问我错过了什么?我假设循环中的 g 表现得像指向某个矩阵的指针,但事实并非如此。显然,我在这里遗漏了一些 Julia 概念......

在这种情况下,您很可能想使用 IdDict 而不是 Set

julia> grids = IdDict([[1 2;3 4],[3 4;5 6]] .=> nothing)
IdDict{Matrix{Int64}, Nothing} with 2 entries:
  [1 2; 3 4] => nothing
  [3 4; 5 6] => nothing

julia> for g in keys(grids)
                  g[g .== 3] .= 0
                  println(g)
                  println(grids)
                  delete!(grids,g)
              end
[1 2; 0 4]
IdDict([1 2; 0 4] => nothing, [3 4; 5 6] => nothing)
[0 4; 5 6]
IdDict([0 4; 5 6] => nothing)

julia> grids
IdDict{Matrix{Int64}, Nothing}()

区别在于IdDict查找矩阵恒等式,而Set查找矩阵内容。

请注意,尽管 IdDict 可以将两个具有相同内容的矩阵存储为单独的条目,如果它们没有存储在相同的内存位置(使用 === 进行测试):

julia> IdDict([1;;] => nothing, [1;;] => nothing)
IdDict{Matrix{Int64}, Nothing} with 2 entries:
  [1;;] => nothing
  [1;;] => nothing

所以你需要选择你想要什么样的行为。

或者你应该在改变它之前从 Set 中删除元素:

julia> grids = Set([[1 2;3 4],[3 4;5 6]])
Set{Matrix{Int64}} with 2 elements:
  [1 2; 3 4]
  [3 4; 5 6]

julia> for g in grids
                  println(grids)
                  delete!(grids,g)
                  g[g .== 3] .= 0
                  println(g)
              end
Set([[1 2; 3 4], [3 4; 5 6]])
[1 2; 0 4]
Set([[3 4; 5 6]])
[0 4; 5 6]

julia> grids
Set{Matrix{Int64}}()

要进一步扩展问题,请参阅:

julia> a = [1]
1-element Vector{Int64}:
 1

julia> x = Set([a])
Set{Vector{Int64}} with 1 element:
  [1]

julia> a in x
true

julia> a[1] = 2
2

julia> x
Set{Vector{Int64}} with 1 element:
  [2]

julia> a in x
false

所以尽管 x 显示它存储 a a 的查找失败,因为 Set 根据 hash 值执行查找 [在 a 时计算的 =22=] 存储在 x 中,如果您更改 a 的内容,它的 hash 也会更改。

简而言之:

  1. 您可以将可变值存储在 Set 中,但不要为了查找而改变它们;
  2. 或者,您可以使用 IdDict,它使用对象标识而不是对象值进行查找。