变量的作用域
变量的可见性区域(或简称:作用域)是代码中可访问变量的部分。 通过限制作用域,可以避免变量名冲突。 意思很简单:两个不同的函数可以有同名的参数’x',这两个参数’x’指的是不同的对象。 可能存在许多其他情况,其中相同的名称在不同的代码块中具有不同的含义。 当具有相同名称的变量具有相同或不同的值时,取决于范围定义规则。 本节将详细讨论它们。
在某些语言结构的帮助下,_blocks由作用域形成,即代码中可以充当某些变量集的作用域的部分。 变量的作用域不能是任意的一组源代码行;作用域必须对应于这些块之一。 朱莉娅有两种主要类型的地区:全球和本地。 第二种类型可以嵌套。 Julia还区分了引入严格区域而不是非严格区域的设计。 机会取决于它https://en.wikipedia.org/wiki/Variable_shadowing [shadowing]同名的全局变量。
视野设计
区域块是使用以下结构创建的。
建筑工程 | 区域类型 | 您可以使用的区域 |
---|---|---|
全球 |
全球 |
|
本地(非严格) |
全球 |
|
本地(非严格) |
全球的,本地的 |
|
本地(严格) |
全球 |
|
本地(严格) |
全球的,本地的 |
朱莉娅使用https://en.wikipedia.org/wiki/Scope_ (computer_science)#Lexical_scope_vs._dynamic_scope[词法区域]。 这意味着函数的作用域不是从调用者的作用域继承的,而是从定义函数的作用域继承的。 例如,在下面的代码中,foo`内部的变量`x’是指模块’Bar`的全局作用域中的’x
:
julia> module Bar
x = 1
foo() = x
end;
而不是在使用’foo’的区域中的’x’上:
julia> import .Bar
julia> x = -1;
julia> Bar.foo()
1
因此,"词法范围"意味着可以仅根据使用它的代码来确定变量在代码的特定位置指的是什么。 程序的执行不影响这一点。 嵌套区域具有可从其嵌套的所有外部区域获得的变量。 反过来,来自内部区域的变量在外部区域中不可用。
全球视区
每个模块形成一个新的全局域,独立于其他模块的全局域-没有全面的全局域。 可以使用运算符将其他模块中的变量输入模块区域 使用和导入,以及通过点符号通过限定名称访问它们。 也就是说,每个模块都是一个所谓的命名空间,以及将名称与值相关联的一流数据结构。
如果顶级表达式包含关键字`local`的变量声明,则此变量在此表达式之外不可用。 表达式内的变量不会影响具有相同名称的全局变量。 一个例子是在顶层的`begin`或`if`块中声明`local x`。
julia> x = 1
begin
local x = 0
@show x
end
@show x;
x = 0
x = 1
请记住,交互式命令提示符(REPL)属于`Main`模块的全局范围。
本地视区
大多数代码块形成新的局部区域(有关完整列表,请参阅 表上)。 如果这样的块在语法上嵌套在另一个局部区域中,则它创建的区域将嵌套在它所属的所有局部区域中。 反过来,这些区域最终嵌套在计算代码的模块的全局区域中。 来自外部区域的变量在它们包含的任何内部区域中都是可见的(也就是说,它们可以被读取和写入),除非具有相同名称的局部变量"掩盖"外部变量。 即使外部局部变量在内部块之后(即在程序文本的后面)声明,也是如此。 当我们说一个变量存在于一个特定的区域时,我们的意思是一个具有这个名称的变量存在于当前区域嵌套的任何区域中,或者存在于当前区域本身。
在某些编程语言中,变量必须先显式声明,然后才能使用。 在Julia中,显式声明也是可能的:如果在任何局部作用域中写入`local x`,则会在其中声明一个新的局部变量,而不管外部作用域中是否已经存在名为`x`的变量。 但是,以这种方式声明每个新变量是乏味的,因此在Julia中,就像在许多其他语言中一样,当为以前未知的变量赋值时,这个变量是隐式声明的。 如果当前作用域是全局的,那么新变量将是全局的。 如果当前区域是局部的,那么新变量将是嵌套最深的局部区域的局部,并且只能在其内部访问,而不能从外部访问。 在为现有局部变量赋值时,始终是局部变量发生变化:您只能通过使用关键字`local`在嵌套作用域中显式声明新的局部变量来隐藏局部变量。 特别是,这适用于在内部函数中分配值的变量,这对于Python开发人员来说可能是意想不到的。 在这种语言中,内部函数中的赋值会导致创建一个新的局部变量,除非该变量被显式声明为非局部变量。
一般来说,这样的机制是很自然的理解,但是,像许多自然现象一样,它有自己的微妙之处,乍一看并不明显。
当表达式’x=<value>`在局部作用域中使用时,它的值在Julia中根据以下规则确定,同时考虑到赋值表达式位于何处以及`x`在这部分代码中已经引用了什么。
-
*一个现有的局部变量:*如果’x’已经是一个局部变量,那么值被分配给这个现有的变量’x'。
-
*严格区域:*如果局部变量’x`不再存在,并且赋值发生在严格范围结构内(即在`let`块内,函数或宏的主体,包含或生成器内),则在赋值范围内创建一个名为`x’的新
-
*非严格区:*如果局部变量`x`不存在yet_并且赋值包括的字段的所有构造都是非严格的(循环,'try’和’catch’块,或’struct’块),则结果取决于全局变量`x`是否被定义。 如果未定义全局变量’x`,则在赋值作用域中创建一个名为`x’的新局部变量。 如果定义了全局变量’x',则赋值被认为是不明确的。 *在非交互contexts_(files,eval)中显示歧义警告,并创建新的局部变量。 *在交互式上下文(REPL,笔记本)中,值被分配给全局变量`x`。
正如您所看到的,在非交互式上下文中,严格和非严格作用域的规则是相同的,除了如果全局变量被隐式局部变量(即,未声明为`local x`)遮蔽,则会显示警告。 在交互式上下文中,为了方便起见,应用更复杂的规则。 它们将在进一步的例子中更详细地讨论。
现在您已经了解了规则,让我们来看看一些示例。 假设每个示例在新的REPL会话中执行,以便在每个代码片段中,值仅分配给该块中的全局变量。
让我们从一个简单明了的情况开始-在严格范围内分配,在这种情况下在函数体内,当具有该名称的局部变量尚不存在时。:
julia> function greet()
x = "hello" # Новая локальная переменная
println(x)
end
greet (generic function with 1 method)
julia> greet()
hello
julia> x # Глобальная
ERROR: UndefVarError: `x` not defined in `Main`
在’greet’函数中,赋值’x="hello"`在函数作用域中创建一个新的局部变量`x'。 在这种情况下,两个事实很重要:赋值发生在局部区域,局部变量’x’还不存在。 由于’x’是局部变量,因此是否存在名为`x’的全局变量并不重要。 在这里,例如,我们在定义和调用`greet`之前执行赋值`x=123'。
julia> x = 123 # Глобальная
123
julia> function greet()
x = "hello" # Новая локальная переменная
println(x)
end
greet (generic function with 1 method)
julia> greet()
hello
julia> x # Глобальная
123
由于`greet`中的变量`x`是本地的,因此调用’greet`不会影响全局变量`x’的值(并且它是否存在并不重要)。 根据严格作用域规则,忽略名为`x`的全局变量的存在:在严格作用域中对变量`x`的值赋值发生在局部级别(除非变量`x`被声明为全局)。
在下一个我们将要考虑的明显情况下,一个名为`x`的局部变量已经存在。 在这种情况下,运算符’x=<value>'总是为这个现有的局部变量`x’赋值。 无论赋值发生在相同的局部作用域中,发生在同一函数主体中的内部局部作用域中,还是嵌套在另一个函数中的函数主体中,都是如此,这也称为https://en.wikipedia.org/wiki/Closure_ (computer_programming)[关闭]。
例如,我们使用`sum_to’函数,它计算从一到`n’的整数之和。
function sum_to(n)
s = 0 # Новая локальная переменная
for i = 1:n
s = s + i # Значение присваивается существующей локальной переменной
end
return s # Та же локальная переменная
end
与前面的例子一样,函数"sum_to"开头的变量"s"的第一次赋值导致在函数体内声明一个新的局部变量"s"。 'For’循环在函数作用域内有自己的内部局部作用域。 在赋值’s=s+i’发生的地方,局部变量’s’已经存在,因此赋值导致现有变量`s`的值发生变化,而不是创建新的局部变量。 您可以通过在REPL中调用`sum_to’来检查这一点。
julia> function sum_to(n)
s = 0 # Новая локальная переменная
for i = 1:n
s = s + i # Значение присваивается существующей локальной переменной
end
return s # Та же локальная переменная
end
sum_to (generic function with 1 method)
julia> sum_to(10)
55
julia> s # Глобальная
ERROR: UndefVarError: `s` not defined in `Main`
由于变量`s`是函数`sum_to`的本地变量,因此调用该函数不会影响全局变量`s`。 此外,您可以看到`for`循环中的表达式`s=s+i`修改了由于初始化`s=0`而创建的相同变量`s`,因为我们得到了从1到10的整数的正确和55。
让我们稍微关注一下’for’循环有自己的作用域的事实,并编写一个稍微更详细的函数版本,我们将其称为`sum_to_def'。 在其中,`s+i`的总和在改变`s`之前存储在变量`t`中。
julia> function sum_to_def(n)
s = 0 # Новая локальная переменная
for i = 1:n
t = s + i # Новая локальная переменная `t`
s = t # Значение присваивается существующей локальной переменной `s`
end
return s, @isdefined(t)
end
sum_to_def (generic function with 1 method)
julia> sum_to_def(10)
(55, false)
这个版本和以前一样返回`s`,但也使用宏`@isdefined`返回一个布尔值,指示函数的外部局部作用域中是否定义了名为`t’的局部变量。 从结果中可以看出,变量’t`没有定义在`for’循环的主体之外。 原因再次在于严格的域规则。:由于变量`t’的值赋值发生在引入严格作用域的函数内部,因此导致在它出现的局部作用域中创建一个新的局部变量`t`,即在循环的主体内部。 即使有一个名为`t`的全局变量,也不会有任何区别-严格范围规则不受全局范围内容的影响。
请注意,for循环体的局部作用域与内部函数的局部作用域没有区别。 这意味着通过将循环主体实现为对内部辅助函数的调用,可以以不同的方式重写此示例。 行为不会改变。
julia> function sum_to_def_closure(n)
function loop_body(i)
t = s + i # Новая локальная переменная `t`
s = t # Значение присваивается той же локальной переменной `s`, что и ниже
end
s = 0 # Новая локальная переменная
for i = 1:n
loop_body(i)
end
return s, @isdefined(t)
end
sum_to_def_closure (generic function with 1 method)
julia> sum_to_def_closure(10)
(55, false)
这个例子说明了几个要点。
-
内部功能区域类似于任何其他嵌套局部区域。 特别是,如果变量在内部函数之外已经是局部的并且在内部函数中被赋值,则外部局部变量被改变。
-
如果外部局部变量的定义位于变量更新的位置之后并不重要,则规则保持不变。 在确定内部局部变量的值之前,分析整个外部局部域并解析其局部变量。
这种方案意味着代码通常可以在不改变其含义的情况下转移到内部函数或从内部函数转移,这简化了在支持闭包的语言中使用许多标准惯用语(参见 做块)。
让我们继续讨论适用lax域规则的一些更令人困惑的情况。 为此,我们将提取’greet`和’sum_to_def’函数的主体到非严格范围的上下文中。 首先,让我们把`greet`函数的主体放在`for’循环中,它形成了一个非严格的作用域,并在REPL中执行它。
julia> for i = 1:3
x = "hello" # Новая локальная переменная
println(x)
end
hello
hello
hello
julia> x
ERROR: UndefVarError: `x` not defined in `Main`
由于在执行`for`循环时未定义全局变量`x`,因此规则的第一个规定适用于非严格范围,并且变量`x`被创建为`for’循环的局部变量。 因此,在循环执行后,全局变量’x’保持未定义。 接下来,让我们将函数`sum_to_def’的主体提取到全局作用域中,并将其参数固定为’n=10'。
s = 0
for i = 1:10
t = s + i
s = t
end
s
@isdefined(t)
这个代码是做什么的? 提示:这是一个诡计多端的问题。 正确的答案是,这一切都取决于具体情况。 如果此代码以交互方式输入,则以与函数体中相同的方式执行。 但是,如果代码是从文件执行的,则会显示歧义警告,并出现错误"未定义变量"。 让我们先检查一下它在REPL中的工作原理。
julia> s = 0 # Глобальная
0
julia> for i = 1:10
t = s + i # Новая локальная переменная `t`
s = t # Значение присваивается глобальной переменной `s`
end
julia> s # Глобальная
55
julia> @isdefined(t) # Глобальная
false
在REPL中,通过确定循环内的赋值是否导致更改全局变量的值或根据是否定义了具有该名称的全局变量创建新的局部变量来模拟函数体中的执行。 如果存在具有该名称的全局变量,则赋值会导致其更改。 如果没有全局变量,则作为赋值的结果创建一个新的局部变量。 此示例演示了这两种情况。
-
没有名为`t`的全局变量,因此,作为赋值`t=s+i`的结果,创建了一个新变量`t`,本地为’for`循环。
-
存在一个名为`s`的全局变量,因此表达式`s=t`为其赋值。
第二个事实解释了为什么全局变量`s`的值会随着循环的执行而改变,第一个是为什么变量`t`在循环执行后仍然未定义。 现在让我们尝试执行相同的代码,就好像它包含在文件中一样。
julia> code = """
s = 0 # Глобальная
for i = 1:10
t = s + i # Новая локальная переменная `t`
s = t # Новая локальная переменная `s` с предупреждением
end
s, # Глобальная
@isdefined(t) # Глобальная
""";
julia> include_string(Main, code)
┌ Warning: Assignment to `s` in soft scope is ambiguous because a global variable by the same name exists: `s` will be treated as a new local. Disambiguate by using `local s` to suppress this warning or `global s` to assign to the existing global variable.
└ @ string:4
ERROR: LoadError: UndefVarError: `s` not defined in local scope
这里我们使用函数 'include_string'计算`代码’作为文件内容。 您也可以将`代码’保存到文件中,然后为该文件调用`include`-结果将是相同的。 正如您所看到的,这种情况下的代码以与REPL中完全不同的方式执行。 让我们弄清楚这是怎么回事。
-
在执行循环之前,定义值为"0"的全局变量"s"。
-
`S=t’的赋值发生在非严格域中-在任何函数或形成严格域的其他构造的主体之外的`for`循环中。
-
因此,该规则的第二条规定适用于非严格域,并且赋值不明确,导致警告。
-
执行继续,并且`s`变量成为`for’循环主体的局部变量。
-
由于变量`s`是`for`循环的局部,因此在计算`t=s+i`时未定义,并且发生错误。
-
这完成了执行,但如果它一直持续到`s`和`@isdefined(t)`,则值`0`和`false’将被返回。
此示例演示了与作用域相关的许多重要方面:在作用域中,每个变量只能有一个值,该值不依赖于表达式的顺序。 表达式`s=t`在循环中的存在使得变量`s`对它是局部的。 这意味着它在表达式`t=s+i’的右侧使用时也是局部的,尽管首先评估此表达式。 有人可能会认为循环第一行中的’s’将是一个全局变量,而第二行中的`s’将是一个局部变量,但这是不可能的,因为这两行在作用域的同一块中,并且在某
了解有关非严格范围的详细信息
我们已经审查了局部区域的所有规则,但在结束本节之前,值得说几句关于为什么非严格区域在交互式和非交互式上下文中的行为不同。 出现了两个明显的问题。
-
为什么每个地方的行为都不一样?
-
为什么每个地方的行为都与文件中的行为不一样? 也许警告可以忽略?
在Julia直到0.6的版本中,所有全局作用域都像现在在REPL中一样工作:当`x=<value>`的赋值发生在循环中(无论是在`try`/`catch`块中还是在`struct`主体中),但在函数体之外(无论 这种方法的优点是直观方便,因为它尽可能类似于函数体内部的行为。 特别是,在调试函数时,它可以轻松地将代码从函数体传输到REPL,反之亦然。 但也有缺点。 首先,这种方法很难解释和理解:它经常混淆用户并引起投诉。 相当公平。 其次,更重要的是,它在编写大规模程序时产生了困难。 在像下面这样的一小段代码中,不难理解发生了什么。
s = 0
for i = 1:10
s += i
end
显然,目标是改变现有的全局变量’s'。 这还能意味着什么? 然而,在现实中,代码并不总是那么简短和清晰。 例如,在真实的程序中,您经常可以找到这个:
x = 123
# гораздо дальше
# возможно, в другом файле
for i = 1:10
x = "hello"
println(x)
end
# гораздо дальше
# возможно, еще в одном файле
# или, может быть, в предыдущем, где `x = 123`
y = x + 234
要弄清楚这里发生了什么并不容易。 由于’x+"hello"`会导致方法错误,因此似乎意图使变量`x’成为`for’循环的局部变量。 然而,基于执行时间和现有方法的值,不可能确定变量的范围。 在Julia的0.6版本中采用的行为特别危险,原因如下:有人可以先写一个"for"循环,它工作正常,但是其他人可以在一个完全不同的地方添加一个新的全局变量,可能在一个不同的文件中,代码的含义发生了变化。 它要么刚刚开始失败,要么更糟糕的是,它被执行,但结果错误。 在精心设计的编程语言中,这种效果https://en.wikipedia.org/wiki/Action_at_a_distance_ (computer_programming)[令人毛骨悚然的远程攻击]不应该被允许。
因此,在Julia1.0中,我们简化了有关作用域的规则:在任何局部作用域中,为尚未成为局部变量的名称赋值会导致创建新的局部变量。 结果,非严格区域的概念已经完全消失,这使得可以防止远程作用的可能负面影响。 我们已经确定并消除了许多错误,这些错误已经成为我们放弃非严格领域的惩罚。 最后,他们可以在他们的桂冠上休息! 但不,事情没那么简单。 毕竟,我现在经常不得不这样做。:
s = 0
for i = 1:10
global s += i
end
你看到"全局"注释了吗? 太可怕了。 当然,这种情况是不能容忍的。 但严重的是,在这样的顶级代码中强制使用"全局"充满了两个主要问题。
-
现在将代码从函数体复制到REPL进行调试并不那么方便-您必须添加`global`注释,然后在将代码传回时将其删除。
-
没有经验的开发人员可以在没有`global`关键字的情况下编写此类代码,并且不明白为什么它不起作用。 返回错误"变量`s’未定义",这并没有说明问题的原因。
从Julia1.5开始,此代码在REPL或Jupyter notebooks等交互式上下文(如Julia0.6)以及文件和其他非交互式上下文中没有"global"注释,会显示这样一个明确的警告。
在非严格范围内为变量`s`赋值是不明确的,因为存在具有相同名称的全局变量。 'S’将被解释为一个新的局部变量。 若要解决歧义,请使用"局部s"来抑制此警告,或使用"全局s"来为现有全局变量赋值。
这解决了两个问题,并保留了编写1.0版本中实现的大规模程序的优点:全局变量对已删除代码的含义没有隐藏的影响;通过复制到REPL进行调试,新手开 局部在非严格区域,发出明确警告。
这种方法的一个重要特征是,在文件中运行而不显示警告的代码将在新的REPL会话中以相同的方式工作。 相反,如果在将REPL会话中的代码保存在文件中后,它的执行方式与REPL中的执行方式不同,则会收到警告。
让块
'Let’运算符创建一个具有严格作用域的块(见上文),并在每次执行时引入新的变量绑定。 变量的值必须立即赋值。
julia> var1 = let x
for i in 1:5
(i == 4) && (x = i; break)
end
x
end
4
虽然分配时可以用新值复盖现有值位置,但"let"始终创建新位置。 这种差异通常不起作用,只有当变量由于关闭而继续存在于其范围之外时才会表现出来。 "Let"语法允许以逗号分隔的变量赋值和名称列表。:
julia> x, y, z = -1, -1, -1;
julia> let x = 1, z
println("x: $x, y: $y") # x — локальная переменная, y — глобальная
println("z: $z") # выдает ошибку, так как переменной z еще не присвоено значение, но она является локальной
end
x: 1, y: -1
ERROR: UndefVarError: `z` not defined in local scope
赋值运算符按顺序求值,运算符的右侧在作用域内求值,然后在左侧引入新变量。 出于这个原因,表达式’let x=x’将是有意义的,因为左侧和右侧部分的`x`是分开存储的不同变量。 下面是一个需要"let"行为的示例。
julia> Fs = Vector{Any}(undef, 2); i = 1;
朱莉娅>而我<=2
Fs[i]=()->i
全局i+=1
结束
朱莉娅>Fs[1]()
3
朱莉娅>Fs[2]()
3
这里创建并保存了两个闭包,返回变量’i'。 但是,它总是相同的变量`i`,因此闭包的工作方式相同。 使用’let`,您可以为’i’创建一个新的绑定。
julia> Fs = Vector{Any}(undef, 2); i = 1;
julia> while i <= 2
let i = i
Fs[i] = ()->i
end
global i += 1
end
julia> Fs[1]()
1
julia> Fs[2]()
2
由于’begin’构造不会引入新的作用域,因此使用不带参数的`let`来简单地引入新的作用域块而不会立即创建新的绑定可能会很有用。
julia> let
local x = 1
let
local x = 2
end
x
end
1
由于’let’引入了一个新的作用域块,因此内部局部变量`x`与外部局部`x`不同。 此特定示例等效于以下代码。
julia> let x = 1
let x = 2
end
x
end
1
循环和夹杂物
在循环和 包含在它们的体内输入的变量在每次迭代时被重新放置在内存中,就好像循环的主体被封闭在一个"let"块中一样。 例如:
julia> Fs = Vector{Any}(undef, 2);
julia>对于j=1:2
Fs[j]=()->j
结束
朱莉娅>Fs[1]()
1
朱莉娅>Fs[2]()
2
'For’循环迭代或包含变量始终是一个新变量。:
julia> function f()
i = 0
for i = 1:3
# пусто
end
return i
end;
julia> f()
0
但是,有时重用现有的局部变量作为迭代变量是有用的。 为此目的使用关键字`outer’很方便。
julia> function f()
i = 0
for outer i = 1:3
# пусто
end
return i
end;
julia> f()
3
常量
变量通常用于为某些不可变值分配名称。 此类变量的值仅分配一次。 您可以使用关键字通知编译器此赋值。 'const'。
julia> const e = 2.71828182845904523536;
julia> const pi = 3.14159265358979323846;
可以在一个"const"语句中声明多个变量。
julia> const a, b = 1, 2
(1, 2)
声明’const’应该只在与全局变量相关的全局作用域中使用。 编译器很难用全局变量优化代码,因为它们的值(甚至类型)可以随时更改。 如果全局变量没有改变,声明’const’就解决了这个性能问题。
情况与局部常量不同。 编译器可以自动判断一个局部变量是否为常量,因此不需要声明局部常量。 此外,还不支持此功能。
特殊的顶级赋值,例如关键字`function`和`struct`执行的赋值,默认情况下是常量。
请注意,'const’只影响变量的绑定;变量可以绑定到可变对象(例如,数组),该对象仍然可以修改。 此外,当尝试为声明为常量的变量赋值时,以下情况是可能的。
-
如果新值的类型与常量的类型不同,则会发生错误。
julia> const x = 1.0
1.0
julia> x = 1
ERROR: invalid redefinition of constant x
-
如果新值和常量的类型相同,则会显示警告。
julia> const y = 1.0
1.0
julia> y = 2.0
WARNING: redefinition of constant y. This may fail, cause incorrect answers, or produce other errors.
2.0
-
如果赋值未更改变量的值,则不显示消息。
julia> const z = 100
100
julia> z = 100
100
最后一条规则适用于不可变对象,即使变量的绑定发生变化,例如:
julia> const s1 = "1"
"1"
julia> s2 = "1"
"1"
julia>指针。([s1,s2],1)
2元素数组{Ptr{UInt8},1}:
Ptr{UInt8} @0x00000000132c9638
Ptr{UInt8} @0x0000000013dd3d18
朱莉娅>s1=s2
"1"
julia>指针。([s1,s2],1)
2元素数组{Ptr{UInt8},1}:
Ptr{UInt8} @0x0000000013dd3d18
Ptr{UInt8} @0x0000000013dd3d18
但是,对于可变对象,警告照常显示。
julia> const a = [1]
1-element Vector{Int64}:
1
julia> a = [1]
WARNING: redefinition of constant a. This may fail, cause incorrect answers, or produce other errors.
1-element Vector{Int64}:
1
请注意:虽然有时可以更改’const’变量的值,但强烈不鼓励这样做。 此功能仅用于交互式工作时的方便。 更改常量可能会导致各种问题或意外结果。 例如,如果一个方法引用了一个常量,并且在更改它之前已经编译过,则可以使用旧值。
julia> const x = 1
1
julia> f() = x
f (generic function with 1 method)
julia> f()
1
julia> x = 2
WARNING: redefinition of constant x. This may fail, cause incorrect answers, or produce other errors.
2
julia> f()
1
类型化全局变量
兼容性:Julia1.8
Julia1.8中增加了对类型化全局变量的支持。 |
全局绑定不仅可以声明为具有常量值,还可以声明为具有常量类型。 这可以在不使用"全局x"语法分配实际值的情况下完成。::T`或当赋值为’x::T=123’时。
julia> x::Float64 = 2.718
2.718
julia> f() = x
f (generic function with 1 method)
julia> Base.return_types(f)
1-element Vector{Any}:
Float64
对于将值赋值给全局变量的任何赋值,Julia首先尝试使用函数将其转换为适当的类型 '转换':
julia> global y::Int
julia> y = 1.0
1.0
julia> y
1
julia> y = 3.14
ERROR: InexactError: Int64(3.14)
Stacktrace:
[...]
类型不必是特定的,但是具有抽象类型的注释通常不会提供太多的性能优势。
为全局变量赋值或设置其类型后,不能更改绑定类型。
julia> x = 1
1
julia> global x::Int
ERROR: cannot set type for global x. It already has a value or is already set to a different type.
Stacktrace:
[...]