Почему это назначение внутри цикла терпит неудачу в Julia 0.7 и 1.0?
(k, a, b, a1, b1) = (BigInt(2), BigInt(4), BigInt(1), BigInt(12), BigInt(4))
while k <= BigInt(4)
(p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
end
Этот код компилируется и запускается в Julia 0.6, но в 1.0 производит ERROR: UndefVarError: k not defined
,
Что-то изменилось между версиями? Что не так с этим кодом в Julia 1.0?
2 ответа
Ответ от shadowtalker правильный. Тем не менее, есть одна важная проблема с этим кодом, которая стоит добавить еще несколько объяснений (и это было слишком долго для комментария).
Соответствующие правила определения области в Юлии для этого случая следующие (и если вы хотите прочитать подробности, вы можете найти их здесь https://docs.julialang.org/en/latest/manual/variables-and-scoping/):
while
блок вводит новую локальную область видимости;- локальная область видимости наследует переменные из глобальной области видимости только для чтения (если только они
global
ключевое слово, как объяснено shadowtalker); - локальная область видимости наследует переменные от локальной области видимости для чтения и записи, если они не удовлетворены
local
ключевое слово.
Теперь - почему это важно? Причина в том, что ваш код будет вести себя по-разному, если вы запускаете его в глобальной области (например, в Julia REPL), и по-разному, если вы используете его в локальной области (например, внутри функции).
Если вы запустите его в глобальной области видимости, то то, что показал shadowtalker, необходимо. Однако, если это происходит, например, внутри функции, вам не нужно ничего менять. Например, эта функция будет работать правильно:
function f()
(k, a, b, a1, b1) = (BigInt(2), BigInt(4), BigInt(1), BigInt(12), BigInt(4))
while k <= BigInt(4)
(p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
end
end
также без создания функции, если вы, например, используете let
блок в глобальной области видимости, код будет работать так, как задумано:
let
k, a, b, a1, b1 = BigInt(2), BigInt(4), BigInt(1), BigInt(12), BigInt(4)
while k <= BigInt(4)
(p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
end
k, a, b, a1, b1
end
так как let
создает локальную область (я добавил заявление в конце let
блок, чтобы заставить его оценить значения введенных переменных, чтобы вы могли проверить их).
Это значительное изменение по сравнению с Julia 0.6 и более ранними версиями, где Julia делала разницу между жесткой и мягкой локальной областью видимости (см. Здесь https://docs.julialang.org/en/release-0.6/manual/variables-and-scoping/), но это различие пропало - все локальные области действия теперь ведут себя одинаково. Это существенное изменение, и на практике это означает, что вы можете ожидать, что код, который работает правильно в некоторой локальной области (функция в большинстве случаев) изменит свое поведение, если вы скопируете его в глобальную область. Из моего опыта использования let
Блоки, как показано выше, это самый простой способ решить эту проблему.
Да, что-то изменилось. Я думаю, что это происходит:
Назначение p
, q
, а также k
внутри цикла действительно создает новые p
, q
, а также k
привязки в новой локальной области. Тогда, потому что k
также используется на правой стороне, Юлия пытается использовать местный k
и терпит неудачу.
Чтобы получить ожидаемую семантику (мутировать существующую p
, q
, а также k
определено на верхнем уровне), сначала нужно объявить их "глобальными":
while k <= BigInt(4)
global p, q, k
(p, q, k) = (k*k, BigInt(2)*k+BigInt(1), k+BigInt(1))
end
редактирование: спасибо пользователю AnAverageHuman на Freenode за это.