在一个未定义的while循环外定义的变量?

问题描述 投票:1回答:1

我正试图在朱莉娅写一个Newton-Raphson求解器。 Newton-Raphson方法如图所示。

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是未定义的。

while-loop scope julia
1个回答
2
投票

首先让我给出解决方案:

有三种可能的方法

方法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.通过更改解决方案2中的行let将代码包装在function sol(f, fprime)块中,简单地说let(您不需要再调用sol)。

现在你必须这样做的原因。

在Julia 1.0中,while引入了一个新的范围。 Julia 1.0中的范围规则是分配给while循环内部的每个变量都被认为是局部变量(这已经改变,因为Julia 0.6区分了硬和软本地范围,在Julia 1.0中这种区别已经消失 - 所有本地范围都是相同)。

在代码中,您将值分配给两个变量:循环内的iterδ。这意味着它们被Julia视为本地,因此在循环内分配值之前,不允许您访问它们的值。

您想要在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指向内存中的同一地址,而Julia将其视为一直是一个全局变量)。

你也许想在Julia手册中阅读这个https://docs.julialang.org/en/latest/manual/variables-and-scoping/或者这个问题的答案Julia Variable scope是类似的

© www.soinside.com 2019 - 2024. All rights reserved.