英文:
Why does updating a global vector with for-loop and with list comprehension in Julia give different results?
问题
以下是你要翻译的内容:
任务: 我有一个全局向量 vec
,它将在循环中随机更新。我有一个全局向量 vecvec
(向量的向量),它应该存储 vec
的历史记录。
尝试的解决方案(不起作用): 函数 make_vec()
会随机更新 vec
。我将其放在循环中,如下所示。
for i in 1:5
make_vec()
println(vec)
push!(vecvec,vec)
end
运行这个(或者更准确地说,下面的最小工作示例),可以从打印的输出中看到,全局变量 vec
通过 make_vec()
在循环的每一轮中都被更新,正如预期的那样。但是:
问题 1: 那么为什么收集的历史记录 vecvec
中的所有向量都相同?
我可以猜测问题的答案,即 push!
不会推送实际的向量 vec
,而只是对 vec
的引用。因此,当我在循环完成后检查 vecvec
时,所有条目都与 vec
的最后一次更新相同。
但是这带来了另一个问题。
问题 2: 在下面的最小工作示例中,我添加了 make_vec()
的两个替代实现。特别是,make_vec2()
(make_vec3()
类似)与 make_vec()
相同,但使用列表理解替换了 for 循环。为什么用 make_vec2()
替换循环中的 make_vec()
会得到不同的结果(实际上是期望的结果)?
vec = [1,2,3]
vecvec = []
function make_vec()
for i in 1:3
vec[i] = rand(0:9)
end
end
function make2_vec()
global vec = [rand(0:9) for i=1:3]
end
function make3_vec()
local vec3 = [1,2,3]
for i in 1:3
vec3[i] = rand(0:9)
end
global vec = vec3
end
for i in 1:5
make_vec() # 用 make_vec2 和 make_vec3 会得到期望的结果,但不是 make_vec
println(vec)
push!(vecvec,vec)
end
print(vecvec)
我在使用内核 1.8.3 的 Jupyter 笔记本中运行了这个示例。
英文:
I pasted the full MWE below. Since it is a bit long, I first give a break-down.
Task: I have a global vector vec
that is going to be randomly updated in a loop. I have a global vector vecvec
(vector of vectors) that is supposed to store the history of the vec
s.
Attempted Solution (does not work): The function make_vec()
randomly updates vec
. I put this in a loop as follows.
for i in 1:5
make_vec()
println(vec)
push!(vecvec,vec)
end
Running this (or rather the MWE below), one can see from the printed output, that the global variable vec
is updated by make_vec()
in each round of the loop as intended. But:
Question 1: Why then are all vectors in the collected history vecvec
the same?
I can guess an answer to the question, namely that push!
does not push the actual vector vec
but only a reference to vec
. Thus, when I inspect vecvec
after completion of the loop, all entries are identical to the last update of vec
.
But this raises another question.
Question 2: In the MWE below, I add two alternative implementations of make_vec()
. In particular, make_vec2()
(make_vec3()
is similar) is the same as make_vec()
but replaces the for-loop with list comprehension. Why does replacing make_vec()
in the loop by make_vec2()
give a different result (in fact the desired result)?
vec = [1,2,3]
vecvec = []
function make_vec()
for i in 1:3
vec[i] = rand(0:9)
end
end
function make2_vec()
global vec = [rand(0:9) for i=1:3]
end
function make3_vec()
local vec3 = [1,2,3]
for i in 1:3
vec3[i] = rand(0:9)
end
global vec = vec3
end
for i in 1:5
make_vec() # make_vec2 and make_vec3 would give the desired result, but not make_vec
println(vec)
push!(vecvec,vec)
end
print(vecvec)
I run this in a Jupyter notebook with kernel 1.8.3.
答案1
得分: 5
在以下代码中:
for i in 1:3
vec[i] = rand(0:9)
end
你没有改变vec
所绑定的值(在这种情况下是一个数组),而是只改变了数组中存储的元素。这通常称为原地更新。
另一方面:
global vec = [rand(0:9) for i=1:3]
分配了一个新的向量,并将这个新值绑定到了vec
变量上。这意味着你不是更新某个容器中的值,而是替换了整个容器。
同样适用于以下情况:
local vec3 = [1,2,3]
for i in 1:3
vec3[i] = rand(0:9)
end
global vec = vec3
local vec3 = [1,2,3]
分配了一个新容器,而 global vec = vec3
将这个新容器绑定到了 vec
变量上。
这清楚吗?
作为一种附加说明 - 最好不要使用vec
作为变量名,因为vec
是在Base Julia中定义的函数名。
英文:
In
for i in 1:3
vec[i] = rand(0:9)
end
you do not change the value (array in this case) that vec
is bound to. Instead you only change the elements stored in the array.
This is typically called in-place update.
On the other hand:
global vec = [rand(0:9) for i=1:3]
allocates a new vector and binds this new value to vec
variable. This means that instead of updating values in some container you replace the container.
The same with:
local vec3 = [1,2,3]
for i in 1:3
vec3[i] = rand(0:9)
end
global vec = vec3
The local vec3 = [1,2,3]
allocates new container and global vec = vec3
binds this new container to vec
variable.
Is this clear?
As a side note - it is better do not use vec
as variable name as vec
is a function name that is defined in Base Julia.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论