为什么在Julia中使用for循环和列表推导来更新全局向量会得到不同的结果?

huangapple go评论59阅读模式
英文:

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 vecs.

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.

huangapple
  • 本文由 发表于 2023年1月9日 09:06:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75052389.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定