嵌套函数中使用的变量是否被视为全局变量?

Are variables used in nested functions considered global?

这是一个愚蠢的问题,如果是这样,我深表歉意。这是给 Julia 的,但我想这个问题不是特定于语言的。

Julia 中建议不要在函数中使用全局变量,但有时我不确定变量是全局变量还是局部变量。我在函数中定义了一个变量,但对于嵌套函数来说是全局变量。例如,在下面,

a=2;
f(x)=a*x;

变量 a 被认为是全局变量。但是,如果我们将这一切包装在另一个函数中,a 是否仍被视为 f 的全局函数?例如,

function g(a)
  f(x)=a*x;
end

我们不使用 a 作为 f 的输入,所以它在这个意义上是全局的,但它仍然只在 g 的范围内定义,所以是局部的从这个意义上说。我不知道。谢谢。

你可以直接检查@DNF评论的确实是这种情况(即变量a被捕获在一个闭包中)。

代码如下:

julia> function g(a)
         f(x)=a*x
       end
g (generic function with 1 method)

julia> v = g(2)
(::var"#f#1"{Int64}) (generic function with 1 method)

julia> dump(v)
f (function of type var"#f#1"{Int64})
  a: Int64 2

在这个例子中你的函数 g returns 一个函数。我将一个 v 变量绑定到返回的函数以便能够检查它。

如果您 dump 绑定到 v 变量的值,您可以看到 a 变量存储在闭包中。

存储在闭包中的变量应该不会影响代码的性能。这是一个典型的模式,例如当以某些参数(在闭包中捕获)为条件对某些函数进行优化时。

如您在这段代码中所见:

julia> @code_warntype v(10)
MethodInstance for (::var"#f#1"{Int64})(::Int64)
  from (::var"#f#1")(x) in Main at REPL[1]:2
Arguments
  #self#::var"#f#1"{Int64}
  x::Int64
Body::Int64
1 ─ %1 = Core.getfield(#self#, :a)::Int64
│   %2 = (%1 * x)::Int64
└──      return %2

一切都是类型稳定的,所以这样的代码很快。

虽然在某些情况下会发生装箱(这种情况应该很少见;当您的函数非常复杂以至于编译器无法证明不需要装箱时会发生这种情况;大多数情况下会发生这种情况如果您为闭包中捕获的变量赋值):

julia> function foo()
           x::Int = 1
           return bar() = (x = 1; x)
       end
foo (generic function with 1 method)

julia> dump(foo())
bar (function of type var"#bar#6")
  x: Core.Box
    contents: Int64 1

julia> @code_warntype foo()()
MethodInstance for (::var"#bar#1")()
  from (::var"#bar#1")() in Main at REPL[1]:3
Arguments
  #self#::var"#bar#1"
Locals
  x::Union{}
Body::Int64
1 ─ %1  = Core.getfield(#self#, :x)::Core.Box
│   %2  = Base.convert(Main.Int, 1)::Core.Const(1)
│   %3  = Core.typeassert(%2, Main.Int)::Core.Const(1)
│         Core.setfield!(%1, :contents, %3)
│   %5  = Core.getfield(#self#, :x)::Core.Box
│   %6  = Core.isdefined(%5, :contents)::Bool
└──       goto #3 if not %6
2 ─       goto #4
3 ─       Core.NewvarNode(:(x))
└──       x
4 ┄ %11 = Core.getfield(%5, :contents)::Any
│   %12 = Core.typeassert(%11, Main.Int)::Int64
└──       return %12