范围有限的值
该页面正在翻译中。 |
范围有限的值表示Julia中动态范围的实现。
!!! 注意"词汇范围与动态范围" 词法范围是Julia中的默认行为。 在区域的词法定义中,变量的区域由程序的词法(文本)结构决定。 动态定义作用域时,变量在程序执行期间绑定到最后一个赋值。
具有有限作用域的值的状态取决于程序的执行路径。 这意味着同一时间单个值可以有多个不同的值。
兼容性:Julia1.11
Julia1.11中出现了有限的范围值。 在Julia1.8及更高版本中,scopedvalues中提供了兼容的实现。jl包。 |
以最简单的形式 'ScopedValue'可以使用默认值创建,然后使用 '与'或 '@with'添加新的动态区域。 新作用域继承父作用域(并递归地从所有外部作用域)的所有值,所提供的有限作用域值优先于以前的定义。
首先,让我们看一个*lexical*域的示例。 'Let’运算符启动一个新的词法域,其中`x`的外部定义被其内部定义所掩盖。
x = 1
let x = 5
@show x # 5
end
@show x # 1
在以下示例中,由于Julia使用了词法作用域,因此函数`f’主体中的变量’x’引用了全局作用域中定义的变量’x`,并且添加作用域`let`不会改变函数`f’观察到的值。
x = 1
f() = @show x
let x = 5
f() # 1
end
f() # 1
现在,使用’ScopedValue',您可以使用*dynamic*范围。
using Base.ScopedValues
x = ScopedValue(1)
f() = @show x[]
with(x=>5) do
f() # 5
end
f() # 1
请注意,`ScopedValue’的观察值取决于程序执行路径。
使用`const`变量来指示具有有限范围的值通常是有意义的,并且可以通过一次调用`with`来设置几个`scopedvalues`的值。
using Base.ScopedValues
f() = @show a[]
g() = @show b[]
const a = ScopedValue(1)
const b = ScopedValue(2)
f() # a[] = 1
g() # b[] = 2
# Добавляем новую динамическую область и задаем значение.
with(a => 3) do
f() # a[] = 3
g() # b[] = 2
with(a => 4, b => 5) do
f() # a[] = 4
g() # b[] = 5
end
f() # a[] = 3
g() # b[] = 2
end
f() # a[] = 1
g() # b[] = 2
'ScopedValues`为’with’提供宏版本。 表达式'@with var=>val expr在变量`var`的新动态区域中计算`expr
,该变量设置为’val'。 +@with var⇒val expr+'等价于'+with(var⇒val)do expr end+'。 但是,'with’需要闭包或具有null参数的函数,这会导致额外的调用帧。 例如,考虑以下函数’f
:
using Base.ScopedValues
const a = ScopedValue(1)
f(x) = a[] + x
要在动态范围内使用值为"2"的"a"运行函数"f`,请使用"with":
with(() -> f(10), a=>2)
但是,这需要将函数`f`包含在具有null参数的函数中。 为了避免出现额外的调用帧,您可以使用宏"@with":
@with a=>2 f(10)
动态区域由任务继承 `任务'在任务创建时。 动态区域*不是*通过`分布式分布。jl’操作。 |
在下面的示例中,我们在开始任务之前打开一个新的动态区域。 父任务和两个子任务同时观察具有有限范围的相同值的独立值。
using Base.ScopedValues
import Base.Threads: @spawn
const scoped_val=ScopedValue(1)
@同步开始
带(scoped_val=>2)
@spawn@show scoped_val[]#2
结束
带(scoped_val=>3)
@spawn@show scoped_val[]#3
结束
@显示scoped_val[]#1
结束
具有有限区域的值在整个区域中是恒定的,但是可以在这样的值中存储可变状态。 请记住,在并行编程的上下文中,有关于全局变量的通常警告。
在有限范围的值中存储对可变状态的引用时也必须小心。 也许,在添加新的动态区域时,您会希望显式地 取消可更改状态的共享访问。
using Base.ScopedValues
import Base.Threads: @spawn
const sval_dict = ScopedValue(Dict())
# Пример неправильного использования изменяемого значения
@sync begin
# `Dict`не является потокобезопасным, приведенное ниже использование является недопустимым
@spawn (sval_dict[][:a] = 3)
@spawn (sval_dict[][:b] = 3)
end
@sync begin
# Если вместо этого мы передадим каждой задаче уникальный словарь,
# то сможем обращаться к словарям без гонок.
with(sval_dict => Dict()) do
@spawn (sval_dict[][:a] = 3)
end
with(sval_dict => Dict()) do
@spawn (sval_dict[][:b] = 3)
end
end
例子:
在下面的示例中,有限范围值用于在web应用程序中实现访问权限验证。 定义查询权限后,将添加一个新的动态范围,并设置一个范围有限的"级别"值。 应用程序的其他部分可以请求具有有限范围的值并接收相应的值。 其他替代方案,如任务本地存储和全局变量,不适合这种分布。 我们唯一的选择是通过整个调用链运行值。
using Base.ScopedValues
const LEVEL = ScopedValue(:GUEST)
function serve(request, response)
level = isAdmin(request) ? :ADMIN : :GUEST
with(LEVEL => level) do
Threads.@spawn handle(request, response)
end
end
function open(connection::Database)
level = LEVEL[]
if level !== :ADMIN
error("Access disallowed")
end
# ... открыть подключение
end
function handle(request, response)
# ...
open(Database(#=...=#))
# ...
end
成语
撤销对可变状态的共享访问
using Base.ScopedValues
import Base.Threads: @spawn
const sval_dict = ScopedValue(Dict())
# Если вы хотите добавить новые значения в словарь, а не заменять
# их, явным образом отмените общий доступ к значениям. В этом примере мы используем `merge`
# для отмены общего доступа к состоянию словаря в родительской области.
@sync begin
with(sval_dict => merge(sval_dict[], Dict(:a => 10))) do
@spawn @show sval_dict[][:a]
end
@spawn sval_dict[][:a] = 3 # Гонка отсутствует, поскольку общий доступ отменен.
end
具有有限作用域的值作为全局变量
要访问受限范围值的含义,受限范围值本身必须在(词法)范围内。 这意味着大多数情况下,您将使用范围有限的值作为永久全局变量。
using Base.ScopedValues
const sval = ScopedValue(1)
实际上,范围有限的值可以被视为函数的隐藏参数。
这并不排除它们作为非全局变量的使用。
using Base.ScopedValues
import Base.Threads: @spawn
function main()
role = ScopedValue(:client)
function launch()
#...
role[]
end
@with role => :server @spawn launch()
launch()
end
但也许在这种情况下,直接将参数传递给函数会更容易。
ScopedValue值太多
如果您为单个模块创建了很多`ScopedValue',那么使用特殊结构来存储它们可能会更好。
using Base.ScopedValues
Base.@kwdef struct Configuration
color::Bool = false
verbose::Bool = false
end
const CONFIG = ScopedValue(Configuration(color=true))
@with CONFIG => Configuration(color=CONFIG[].color, verbose=true) begin
@show CONFIG[].color # true
@show CONFIG[].verbose # true
end
API文档
# '基。ScopedValues。ScopedValue'-Type
ScopedValue(x)
创建一个跨动态区域分发值的容器。 使用方法 'with'创建动态区域并进入。
这些值只能在进入一个新的动态区域时设置,并且它们在该区域的整个生命周期中都是恒定的。
动态区域适用于所有任务。
例子
julia> using Base.ScopedValues;
julia> const sval = ScopedValue(1);
julia> sval[]
1
julia> with(sval => 2) do
sval[]
end
2
julia> sval[]
1
兼容性:Julia1.11
Julia1.11中出现了有限的范围值。 在Julia1.8及更高版本中,scopedvalues中提供了兼容的实现。jl包。 |
# '基。ScopedValues。用'-Function
with(f, (var::ScopedValue{T} => val)...)
使用变量`var`在新的动态区域中执行`f`,该变量已被赋值为`val'。 `val’转换为类型’T'。
例子
julia> using Base.ScopedValues
julia> a = ScopedValue(1);
julia>f(x)=a[]+x;
朱莉娅>f(10)
11
julia>with(a=>2)do
f(10)
结束
12
朱莉娅>f(10)
11
朱莉娅>b=ScopedValue(2);
julia>g(x)=a[]+b[]+x;
julia>with(a=>10,b=>20)do
g(30)
结束
60
julia>with(()->a[]*b[],a=>3,b=>4)
12
# '基。ScopedValues。@with`-Macro
@with (var::ScopedValue{T} => val)... expr
'With’版本作为宏。 表达式'@with var=>val expr在变量`var`的新动态区域中计算`expr
,该变量设置为’val'。 val’转换为类型’T'。 `+@with var⇒val expr+'等价于'+with(var⇒val)do expr end+
,但`@with`避免创建闭包。
例子
julia> using Base.ScopedValues
julia> const a = ScopedValue(1);
julia> f(x) = a[] + x;
julia> @with a=>2 f(10)
12
julia> @with a=>3 begin
x = 100
f(x)
end
103
# '基。isassigned'—Method
isassigned(val::ScopedValue)
检查’ScopedValue’是否已被赋值。
例子
julia> using Base.ScopedValues
julia> a = ScopedValue(1); b = ScopedValue{Int}();
julia> isassigned(a)
true
julia> isassigned(b)
false
# '基。ScopedValues。获取'-Function
get(val::ScopedValue{T})::Union{Nothing, Some{T}}
如果未指定具有有限区域的值并且没有默认值,则返回’nothing'。 否则,它返回'Some{T}'与当前值。
例子
julia> using Base.ScopedValues
julia> a = ScopedValue(42); b = ScopedValue{Int}();
julia> ScopedValues.get(a)
Some(42)
julia> isnothing(ScopedValues.get(b))
true