现在,快速匿名函数是julia固有的,我还是必须使用装饰器,还是自动实现该装饰器。另外,当我将一个函数作为参数传递给另一个函数时,可以静态键入它吗?我该怎么做才能提高运行速度。
绝对不再需要FastAnonymous。您可以通过以下方式自己进行验证:
julia> @noinline g(f, x) = f(x) # prevent inlining so you know it's general
g (generic function with 1 method)
julia> h1(x) = g(identity, x)
h1 (generic function with 1 method)
julia> h2(x) = g(sin, x)
h2 (generic function with 1 method)
julia> @code_warntype h1(1)
Variables
#self#::Core.Compiler.Const(h1, false)
x::Int64
Body::Int64
1 ? %1 = Main.g(Main.identity, x)::Int64
??? return %1
julia> @code_warntype h2(1)
Variables
#self#::Core.Compiler.Const(h2, false)
x::Int64
Body::Float64
1 ? %1 = Main.g(Main.sin, x)::Float64
??? return %1
julia> h3(x) = g(z->"I'm a string", x)
h3 (generic function with 1 method)
julia> @code_warntype h3(1)
Variables
#self#::Core.Compiler.Const(h3, false)
x::Int64
#9::getfield(Main, Symbol("##9#10"))
Body::String
1 ? (#9 = %new(Main.:(##9#10)))
? %2 = #9::Core.Compiler.Const(getfield(Main, Symbol("##9#10"))(), false)
? %3 = Main.g(%2, x)::Core.Compiler.Const("I'm a string", false)
??? return %3
Run Code Online (Sandbox Code Playgroud)
在每种情况下,Julia都知道返回类型,这就要求它“了解”函数参数正在做什么。此外:
julia> m = first(methods(g))
g(f, x) in Main at REPL[1]:1
julia> m.specializations
Core.TypeMapEntry(Core.TypeMapEntry(Core.TypeMapEntry(nothing, Tuple{typeof(g),typeof(identity),Int64}, nothing, svec(), 1, -1, MethodInstance for g(::typeof(identity), ::Int64), true, true, false), Tuple{typeof(g),typeof(sin),Int64}, nothing, svec(), 1, -1, MethodInstance for g(::typeof(sin), ::Int64), true, true, false), Tuple{typeof(g),getfield(Main, Symbol("##9#10")),Int64}, nothing, svec(), 1, -1, MethodInstance for g(::getfield(Main, Symbol("##9#10")), ::Int64), true, true, false)
Run Code Online (Sandbox Code Playgroud)
这有点难以理解,但是如果仔细看,您会发现它g已经为3种输入进行了编译:
Tuple{typeof(identity), Int64}Tuple{typeof(sin), Int64}Tuple{getfield(Main, Symbol("##9#10")),Int64}(由于g与诸如关键字参数处理的内部实现之类的事情有关的原因,编译后的版本也将其自身作为一个额外的参数,但让我们暂时忽略它。)最后一个是实现匿名类型的类型的生成名称。功能。这说明每个函数都有自己的类型,这就是为什么将函数作为参数进行传递的原因。
对于专家而言,还有其他因素可以发挥作用:由于类型推断会遇到无法解决的暂停问题,因此在某些情况下,推断会决定这一切都变得太复杂并“尽早”中止。在这种情况下(相对较少),可以帮助强制编译器专门针对特定参数。在我们的示例中,这意味着声明g为
@noinline g(f::F, x) where F = f(x)
Run Code Online (Sandbox Code Playgroud)
而不是
@noinline g(f, x) = f(x)
Run Code Online (Sandbox Code Playgroud)
::F通常这是不必要的,并且看起来毫无用处,但是您可以将其用作编译器提示,以增加用于推断结果的工作量。我不建议默认情况下这样做(这会使您的代码更难阅读),但是如果您发现奇怪的性能问题,则可以尝试一下。