Переменная, определенная вне цикла while, не определена внутри? - PullRequest
0 голосов
/ 02 сентября 2018

Я пытаюсь написать решатель Ньютона-Рафсона в Джулии. Метод Ньютона-Рафсона показан на этом изображении.

Newton-Raphson solver equation

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 Ответ

0 голосов
/ 02 сентября 2018

Сначала позвольте мне дать решение:

Есть три возможных подхода

Подход 1. Добавьте 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).

Теперь причина, почему вы должны это сделать.

В Julia 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 аналогична

...