Почему это назначение внутри цикла терпит неудачу в 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 за это.

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