You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用fill()初始化数组数组出现意外结果的原因及优化方案

Julia中fill()初始化数组数组的行为疑问及解决方案

问题场景

我使用fill()初始化两个数组数组:

A = fill(Vector{Float32}(zeros(Float32, 4)), 3);
B = fill(Vector{Float32}(ones(Float32, 4)), 3);

执行原地加法A[1] .+= B[1]后,发现A的所有元素都被修改:

3-element Vector{MVector{4, Float32}}:
 [1.0, 1.0, 1.0, 1.0]
 [1.0, 1.0, 1.0, 1.0]
 [1.0, 1.0, 1.0, 1.0]

改用循环初始化后:

A = Array{Vector{Float32},1}(undef, 3) 

for x in 1:3
    A[x] = zeros(Float32,4)
end

B = Array{Vector{Float32},1}(undef, 3) 

for x in 1:3
    B[x] = ones(Float32,4)
end

执行相同操作后结果符合预期:

3-element Vector{Vector{Float32}}:
 [1.0, 1.0, 1.0, 1.0]
 [0.0, 0.0, 0.0, 0.0]
 [0.0, 0.0, 0.0, 0.0]

问题解答

1. 为何两种初始化方式行为不同?

fill(x, n)的核心是创建一个包含n个同一对象引用的数组。

当你用fill(Vector{Float32}(zeros(Float32, 4)), 3)时,仅生成1个长度为4的Float32向量对象,A数组的3个元素全部指向这个对象。.+=是原地修改操作,直接改变向量本身的内容,而非修改数组元素的引用——所以所有指向该向量的数组元素都会同步看到变化,看起来就像A的所有元素都被修改了。

而循环初始化时,每次迭代调用zeros(Float32,4)都会生成全新的独立向量对象,并赋值给A的对应位置。此时A的3个元素分别指向3个互不关联的向量,修改其中一个自然不会影响其他元素。

2. 用单行方式实现循环初始化的效果?

可以,以下是几种简洁的单行写法:

列表推导式(推荐)

A = [zeros(Float32, 4) for _ in 1:3]
B = [ones(Float32, 4) for _ in 1:3]

列表推导式会为每个循环迭代创建新的向量对象,和手动循环的效果完全一致,语法简洁直观。

map函数

A = map(_ -> zeros(Float32, 4), 1:3)
B = map(_ -> ones(Float32, 4), 1:3)

map会对1:3中的每个元素执行匿名函数,每次调用zeros/ones都会生成新的向量对象。

注意:fill本身无法实现这个效果,因为它的参数是预先创建好的单个对象,无法自动为每个数组元素生成新对象。如果一定要用类似fill的形式,可结合Ref和广播,但这种写法冗余,不推荐:

A = [zeros(Float32,4) for _ in fill(Ref(()), 3)]

内容的提问来源于stack exchange,提问作者thehardyreader

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 05:44:58