Почему обновление глобального вектора с помощью цикла for и понимания списка в Джулии дает разные результаты?

Я вставил полный MWE ниже. Поскольку это немного долго, я сначала даю разбивку.

Задача: у меня есть глобальный вектор vec, который будет случайным образом обновляться в цикле. У меня есть глобальный вектор vecvec (вектор векторов), который должен хранить историю vecs.

Попытка решения (не работает): функция 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.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В

    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.

Спасибо. (И, кстати, также спасибо за примечание, я новичок в Джулии.) Важнейшая часть информации, если я правильно понимаю, заключается в том, что версии 2 и 3 выделяют новый вектор, который затем сохраняется в vecvec (и что vecvec сам не хранит vec, а только ссылки на vec). Что произойдет, если я создам вектор целых чисел? В таком случае этот вектор также хранит ссылки на целые числа или сами целые числа?

Alexander Kurz 10.01.2023 07:17

Это зависит от того, как вы создаете вектор целых чисел. Если это Any[1, 2, 3], то это ссылки (поскольку такой вектор может хранить любое значение — как список в Python), когда вы создаете [1, 2, 3], это сами целые числа. Однако обратите внимание, что целые числа не изменяемы, поэтому вы не можете изменить целое число, даже если у вас есть ссылка на него.

Bogumił Kamiński 10.01.2023 11:40

Другие вопросы по теме