如何在第一次调用 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