Я вставил полный MWE ниже. Поскольку это немного долго, я сначала даю разбивку.
Задача: у меня есть глобальный вектор vec
, который будет случайным образом обновляться в цикле. У меня есть глобальный вектор vecvec
(вектор векторов), который должен хранить историю vec
s.
Попытка решения (не работает): функция make_vec()
случайным образом обновляет vec
. Я помещаю это в цикл следующим образом.
for i in 1:5
make_vec()
println(vec)
push!(vecvec,vec)
end
Запустив это (или, скорее, MWE ниже), из распечатанного вывода видно, что глобальная переменная vec
обновляется на make_vec()
в каждом раунде цикла, как и предполагалось. Но:
Вопрос 1: Почему тогда все векторы в собранной истории vecvec
одинаковые?
Я могу предположить ответ на вопрос, а именно, что push!
не толкает фактический вектор vec
, а только ссылку на vec
. Таким образом, когда я проверяю vecvec
после завершения цикла, все записи идентичны последнему обновлению vec
.
Но это поднимает другой вопрос.
Вопрос 2: В приведенном ниже MWE я добавляю две альтернативные реализации make_vec()
. В частности, make_vec2()
(make_vec3()
похоже) — это то же самое, что и make_vec()
, но заменяет цикл for на понимание списка. Почему замена make_vec()
в цикле на make_vec2()
дает другой результат (на самом деле желаемый результат)?
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)
Я запускаю это в ноутбуке Jupyter с ядром 1.8.3.
В
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.
Это зависит от того, как вы создаете вектор целых чисел. Если это Any[1, 2, 3]
, то это ссылки (поскольку такой вектор может хранить любое значение — как список в Python), когда вы создаете [1, 2, 3]
, это сами целые числа. Однако обратите внимание, что целые числа не изменяемы, поэтому вы не можете изменить целое число, даже если у вас есть ссылка на него.
Спасибо. (И, кстати, также спасибо за примечание, я новичок в Джулии.) Важнейшая часть информации, если я правильно понимаю, заключается в том, что версии 2 и 3 выделяют новый вектор, который затем сохраняется в
vecvec
(и чтоvecvec
сам не хранитvec
, а только ссылки наvec
). Что произойдет, если я создам вектор целых чисел? В таком случае этот вектор также хранит ссылки на целые числа или сами целые числа?