Переменная, определенная вне цикла while, не определена внутри?
Я пытаюсь написать решатель Ньютона-Рафсона в Джулии. Метод Ньютона-Рафсона показан на этом изображении.
f(x) = x^2.5 - 3x^1.5 - 10
fprime(x) = 2.5x^1.5 - 4.5x^0.5
x = zeros(1000)
x[1] = 10
δ = 1 # a relatively large number compared to what we want the error to be
iter = 1
while δ > 1e-6
x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
iter += 1
δ = abs(x[iter] - x[iter + 1])
if iter == 100
break
end
end
println("The solution is ")
show(x[iter])
Однако, когда я запускаю код, я получаю сообщение об ошибке iter
не определен, хотя я определил его непосредственно перед началом цикла. Есть ли какая-то проблема с областью видимости, которую я полностью пропускаю?
ERROR: LoadError: UndefVarError: iter not defined
Stacktrace:
[1] top-level scope at /Users/natemcintosh/Documents/Julia/Learning_julia.jl:11 [inlined]
[2] top-level scope at ./none:0
[3] include_string(::Module, ::String, ::String) at ./loading.jl:1002
[4] (::getfield(Atom, Symbol("##120#125")){String,String,Module})() at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:120
[5] withpath(::getfield(Atom, Symbol("##120#125")){String,String,Module}, ::String) at /Users/natemcintosh/.julia/packages/CodeTools/8CjYJ/src/utils.jl:30
[6] withpath at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:46 [inlined]
[7] #119 at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:117 [inlined]
[8] hideprompt(::getfield(Atom, Symbol("##119#124")){String,String,Module}) at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/repl.jl:76
[9] macro expansion at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:116 [inlined]
[10] (::getfield(Atom, Symbol("##118#123")){Dict{String,Any}})() at ./task.jl:85
in expression starting at /Users/natemcintosh/Documents/Julia/Learning_julia.jl:10
Я пробовал печатать x
в начале while
цикл и он знает, что x
есть, но думает iter
не определено
1 ответ
Сначала позвольте мне дать решение:
Есть три возможных подхода
Подход 1. Prepend global
до iter += 1
и изменить его на global iter += 1
и все будет работать (однако обратите внимание на комментарий ниже о δ
- потому что он не будет работать правильно, если вы не подготовите global
до δ = abs(x[iter] - x[iter + 1])
т.е. код будет работать, но будет давать неправильные результаты - подходы 2 и 3 не имеют этой проблемы).
Подход 2. Оберните ваш код внутри функции следующим образом:
f(x) = x^2.5 - 3x^1.5 - 10
fprime(x) = 2.5x^1.5 - 4.5x^0.5
function sol(f, fprime)
x = zeros(1000)
x[1] = 10
δ = 1 # a relatively large number compared to what we want the error to be
iter = 1
while δ > 1e-6
x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
iter += 1
δ = abs(x[iter] - x[iter + 1])
if iter == 100
break
end
end
println("The solution is ")
show(x[iter])
end
sol(f, fprime) # now we call it
Решение 3. Оберните ваш код в let
заблокировать, изменив строку function sol(f, fprime)
в решении 2 просто сказать let
(вам не нужно звонить sol
затем).
Теперь причина, почему вы должны это сделать.
У Юлии 1,0 while
вводит новую сферу Правила области видимости в Julia 1.0 таковы, что каждая переменная, которая назначается внутри while
Цикл считается локальной переменной (это изменилось, потому что Julia 0.6 различает жесткую и мягкую локальную область видимости, в Julia 1.0 это различие исчезло - все локальные области одинаковы).
В вашем коде вы присваиваете значения двум переменным: iter
а также δ
внутри петли. Это означает, что Джулия рассматривает их как локальные, поэтому вы не можете получить доступ к их значению до того, как им будет присвоено значение внутри цикла.
Вы хотите прочитать iter
в соответствии x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
но присвойте ему значение только в следующей строке.
Что касается δ
дело сложнее. Вы присваиваете ему значение, но оно используется в условии цикла while δ > 1e-6
, Однако это условие действует на переменные, определенные во внешней области видимости (глобальные в исходном случае). Так что все будет работать, но условие while δ > 1e-6
всегда буду видеть, что δ
равно 1
как это выглядит на значение переменной за пределами цикла. Так что это условие никогда не сработает (и вы всегда будете запускать 100 итераций). В итоге, код, который делает то, что вы хотите (хотя, если вы не исправили δ
задание вы бы не получили предупреждение)
f(x) = x^2.5 - 3x^1.5 - 10
fprime(x) = 2.5x^1.5 - 4.5x^0.5
x = zeros(1000)
x[1] = 10
δ = 1 # a relatively large number compared to what we want the error to be
iter = 1
while δ > 1e-6
x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
global iter += 1
global δ = abs(x[iter] - x[iter + 1])
if iter == 100
break
end
end
println("The solution is ")
show(x[iter])
Наконец, обратите внимание, что линия x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
работает нормально, даже если в нем есть присваивание, потому что вы не перепривязываете переменную x
в нем, но изменить только один элемент массива (так x
указывает на один и тот же адрес в памяти, и Юлия все время обрабатывает его как глобальную переменную).
Также вы можете прочитать этот https://docs.julialang.org/en/latest/manual/variables-and-scoping/ в руководстве по Julia или ответ на этот вопрос. Область действия Julia Variable похожа