如何在第一次调用 Julia 函数时执行一些代码?
How to execute some code the first time a Julia function is called?
我有一个特定的用例,我希望函数在第一次调用时基本上提供警告以告诉用户一些信息。除了使用全局计数器并跟踪函数被调用的次数之外,我不确定如何检查它。关于特定 Julia 语法的任何想法可以让我检查该函数是否是第一次调用?
这里有一些关于在您可以选择重写函数体时如何执行此操作的想法。 所有这些也可以通过编写一个执行相应转换的相当简单的宏来实现。好吧,如果你想让顶级和本地定义正常工作,这并不是那么简单。
(非)选项 1
从概念上讲,您可以使用生成的函数来执行此操作,并且在您尝试时它会大部分起作用:
julia> @generated function dostuff(x)
@warn "You really shouldn't do stuff!"
return :(2x + 1)
end
dostuff (generic function with 1 method)
julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[1]:2
3
julia> dostuff(1)
3
但是:不要。编译器可以自由选择何时调用“生成器”,并引用the docs:这些副作用发生的确切时间、频率或次数是不确定的.不是个好主意。
此外,@warn
是否会使用生成函数中允许的打印函数是值得怀疑的。在早期的 Julias 中,使用 println
而不是 Core.println
有时会在生成的函数中出错,因为前者修改了事件循环。
选项 2
所以为了更好的东西。您可以通过将函数定义为 let-bound 变量的闭包来做类似的事情,而不是使用全局计数器的想法:
julia> let isfirstcall = Threads.Atomic{Bool}(true)
global function dostuff(x)
if Threads.atomic_xchg!(isfirstcall, false)
@warn "You really shouldn't do stuff!"
end
return 2x + 1
end
end
dostuff (generic function with 1 method)
julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[16]:4
3
julia> dostuff(1)
3
julia> isfirstcall
ERROR: UndefVarError: isfirstcall not defined
我在这里选择使用原子只是为了 atomic_xchg!
的乐趣,但如果线程不是问题,那么普通的布尔值也可以。
选项 3
此外,虽然可以避免,但如果操作得当,全局变量也不会太糟糕。这意味着:使它成为 const
Ref
。并且(可选,但在这种情况下推荐)使用 var
字符串为其指定用户通常无法访问的名称:
julia> const var"##isfirstcall" = Ref(true)
julia> function dostuff(x)
if var"##isfirstcall"[]
@warn "You really shouldn't do stuff!"
var"##isfirstcall"[] = false
end
return 2x + 1
end
dostuff (generic function with 1 method)
julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[22]:3
3
julia> dostuff(1)
3
使用日志记录宏的 maxlog
功能:
julia> function warnfirst(x, y)
@warn "This is the first time you called this" maxlog=1
return 2x + y
end
warnfirst (generic function with 1 method)
julia> warnfirst(1, 2)
┌ Warning: This is the first time you called this
└ @ Main REPL[1]:2
4
julia> warnfirst(1, 2)
4
julia> warnfirst(1.0, 2.0) # what about different specializations?
4.0
我有一个特定的用例,我希望函数在第一次调用时基本上提供警告以告诉用户一些信息。除了使用全局计数器并跟踪函数被调用的次数之外,我不确定如何检查它。关于特定 Julia 语法的任何想法可以让我检查该函数是否是第一次调用?
这里有一些关于在您可以选择重写函数体时如何执行此操作的想法。 所有这些也可以通过编写一个执行相应转换的相当简单的宏来实现。好吧,如果你想让顶级和本地定义正常工作,这并不是那么简单。
(非)选项 1
从概念上讲,您可以使用生成的函数来执行此操作,并且在您尝试时它会大部分起作用:
julia> @generated function dostuff(x)
@warn "You really shouldn't do stuff!"
return :(2x + 1)
end
dostuff (generic function with 1 method)
julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[1]:2
3
julia> dostuff(1)
3
但是:不要。编译器可以自由选择何时调用“生成器”,并引用the docs:这些副作用发生的确切时间、频率或次数是不确定的.不是个好主意。
此外,@warn
是否会使用生成函数中允许的打印函数是值得怀疑的。在早期的 Julias 中,使用 println
而不是 Core.println
有时会在生成的函数中出错,因为前者修改了事件循环。
选项 2
所以为了更好的东西。您可以通过将函数定义为 let-bound 变量的闭包来做类似的事情,而不是使用全局计数器的想法:
julia> let isfirstcall = Threads.Atomic{Bool}(true)
global function dostuff(x)
if Threads.atomic_xchg!(isfirstcall, false)
@warn "You really shouldn't do stuff!"
end
return 2x + 1
end
end
dostuff (generic function with 1 method)
julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[16]:4
3
julia> dostuff(1)
3
julia> isfirstcall
ERROR: UndefVarError: isfirstcall not defined
我在这里选择使用原子只是为了 atomic_xchg!
的乐趣,但如果线程不是问题,那么普通的布尔值也可以。
选项 3
此外,虽然可以避免,但如果操作得当,全局变量也不会太糟糕。这意味着:使它成为 const
Ref
。并且(可选,但在这种情况下推荐)使用 var
字符串为其指定用户通常无法访问的名称:
julia> const var"##isfirstcall" = Ref(true)
julia> function dostuff(x)
if var"##isfirstcall"[]
@warn "You really shouldn't do stuff!"
var"##isfirstcall"[] = false
end
return 2x + 1
end
dostuff (generic function with 1 method)
julia> dostuff(1)
┌ Warning: You really shouldn't do stuff!
└ @ Main REPL[22]:3
3
julia> dostuff(1)
3
使用日志记录宏的 maxlog
功能:
julia> function warnfirst(x, y)
@warn "This is the first time you called this" maxlog=1
return 2x + y
end
warnfirst (generic function with 1 method)
julia> warnfirst(1, 2)
┌ Warning: This is the first time you called this
└ @ Main REPL[1]:2
4
julia> warnfirst(1, 2)
4
julia> warnfirst(1.0, 2.0) # what about different specializations?
4.0