在while循环之外定义的变量不在内部定义吗?

时间:2018-09-02 02:21:03

标签: while-loop scope julia

我正在尝试用Julia写一个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是不确定的。

1 个答案:

答案 0 :(得分: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,而只更改数组的一个元素(所以{{ 1}}指向内存中的同一地址,Julia始终将其视为全局变量。

另外,您可能想阅读Julia手册中的https://docs.julialang.org/en/latest/manual/variables-and-scoping/或这个问题的答案Julia Variable scope相似