Переменная, определенная вне цикла 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 похожа

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