我们可以在julia中将一个函数作为另一个函数的参数传递吗?它是如何工作的?这是否意味着输入函数先于调用函数运行,还是仅在调用函数专门调用输入函数时才调用输入函数?
朱莉娅最大的特点之一是,您可以自己找出这些问题的答案。与其进行实验然后观察表面上的行为,不如直接询问朱莉娅在幕后做过的事情,是获得答案的更方便,更简洁的方法。让我们从其他两个答案中借用这些示例,并通过@code_lowered以下方法询问Julia发生了什么:
julia> f() = println("hello world")
julia> g(any_func::Function) = any_func()
#Me: hi, Julia, what happened here?
julia> @code_lowered g(f)
#Julia: hi there, this is the lowered code, anything else you want to know?
CodeInfo(:(begin
nothing
return (any_func)()
end))
#Me: it looks like function `g` just returned `(any_func)()` and did nothing else,
# is it equvienlent to write `f()`?
julia> @code_typed g(f)
#Julia: yes, the codes looks the same after type inference stage:
CodeInfo(:(begin
$(Expr(:inbounds, false))
# meta: location REPL[1] f 1
# meta: location coreio.jl println 5
SSAValue(0) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
# meta: pop location
# meta: pop location
$(Expr(:inbounds, :pop))
return (Base.print)(SSAValue(0), "hello world", $(QuoteNode('\n')))::Void
end))=>Void
julia> @code_typed f()
CodeInfo(:(begin
$(Expr(:inbounds, false))
# meta: location coreio.jl println 5
SSAValue(0) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
# meta: pop location
$(Expr(:inbounds, :pop))
return (Base.print)(SSAValue(0), "hello world", $(QuoteNode('\n')))::Void
end))=>Void
Run Code Online (Sandbox Code Playgroud)
这是否意味着输入功能先于调用功能运行?
在这种特殊情况下,很难回答,g编译器在编译时对调用函数进行了优化,因此g在运行时没有。:P让我们添加一些额外的内容到g:
julia> g(any_func::Function) = (println("I don't wanna be optimized out!"); any_func())
g (generic function with 1 method)
julia> @code_lowered g(f)
CodeInfo(:(begin
nothing
(Main.println)("I don't wanna be optimized out!")
return (any_func)()
end))
julia> @code_typed g(f)
CodeInfo(:(begin
$(Expr(:inbounds, false))
# meta: location coreio.jl println 5
SSAValue(0) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
# meta: pop location
$(Expr(:inbounds, :pop))
(Base.print)(SSAValue(0), "I don't wanna be optimized out!", $(QuoteNode('\n')))::Void
$(Expr(:inbounds, false))
# meta: location REPL[2] f 1
# meta: location coreio.jl println 5
SSAValue(1) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
# meta: pop location
# meta: pop location
$(Expr(:inbounds, :pop))
return (Base.print)(SSAValue(1), "hello world", $(QuoteNode('\n')))::Void
end))=>Void
Run Code Online (Sandbox Code Playgroud)
此示例显示的内容g恰好在之前运行f,因此答案为是。遵循相同的模式,很容易查看Liso的示例:
julia> f = x->x+1
julia> fun(f::Function, a) = f(a)
# nothing new here, `f(9)` was called before `fun`
# and the return value was passed to `fun` with a binding name `a`.
julia> @code_lowered fun(f, f(9))
CodeInfo(:(begin
nothing
return (f)(a)
end))
# to verify your second question:
julia> foo(f, x) = x
foo (generic function with 1 method)
julia> @code_lowered foo(f, 1)
CodeInfo(:(begin
nothing
return x
end))
Run Code Online (Sandbox Code Playgroud)
输入函数仅在调用函数专门调用它时才被调用吗?
因此,是的,上面的示例表明,如果f调用函数未调用foo,则将直接对其进行优化。
与其他语言不同,Julia不仅是一个对用户不透明的神奇黑匣子,有时打开箱子并自学成才是有效的。顺便说一句,Julia中还有两个阶段(@ code_llvm,@ code_native),您可能需要转储这些低级代码以进行一些高级调查,请参阅这篇文章中Stefan的出色答案:@ code_native,@和有什么区别? Julia中的code_typed和@code_llvm?有关更多详细信息。
这些函数之间有什么不同:g1()= f(),g2(f :: Function)= f()。两者的结果都相同,所以它们之间有什么不同?
julia> @code_lowered g1()
CodeInfo(:(begin
nothing
return (Main.f)()
end))
julia> @code_lowered g2(f)
CodeInfo(:(begin
nothing
return (f)()
end))
Run Code Online (Sandbox Code Playgroud)
降低的代码告诉g1()总是返回Main.f(),这里Main.f是f中Main的模块,但g2返回f()这里f是你传递给它的功能。为了清楚起见,我们可以定义g2为:
julia> g2(argumentf::Function) = argumentf()
g2 (generic function with 1 method)
julia> @code_lowered g2(f)
CodeInfo(:(begin
nothing
return (argumentf)()
end))
Run Code Online (Sandbox Code Playgroud)
g2是“将一个函数作为另一个函数的参数传递”,g1可以视为的别名Main.f。你能理解这个吗?@红色