Julia 学习笔记——6. 函数进阶和宏
函数的参数类型
Julia 允许给函数的(一部分或全部)参数加上类型约束,例如
1
2
3function add2(x::Int64, y::Int64)
return x + y
end
正常使用例如 1
2add2(1, 3) # 4
add2(Int64(1), Int64(10)) # 11
注意 Julia 不会对参数进行自动的类型转换,即使转换是安全无损的,例如
1
add2(Int32(1), Int32(2)) # error
使用过于具体的参数类型约束通常不是合适的选择,可以使用更抽象的类型约束,例如使用一般的整数类型
1
2
3function add3(x::Integer, y::Integer)
return x + y
end
此时可以支持更多种类的参数进行调用,例如 1
2add3(Int32(1), Int32(10)) # 11
add3(Int32(1), 10) # 11
注意到这里允许两个参数是不同的整数类型,Julia 提供了
where 关键字为泛型编程增加额外约束(类似 C++ 的 concepts 和
requires),例如要求两个参数的类型一致 1
2
3function add4(x::T, y::T) where {T <: Integer}
return x + y
end
where有各种灵活的语法,这里不作展开。
多态分发
Julia 允许对参数列表不同的同名函数提供不同的具体实现,例如
1
2f(x::Int32) = Int32
f(x::Int64) = Int64
这里根据参数的不同类型返回对应的类型。Julia 得到了 f
的多种实现 1
f (generic function with 2 methods)
Julia 会智能地在实际使用中匹配不同的实现 1
2
3
4
5julia> f(10)
Int64
julia> f(Int32(10))
Int32
这种机制被称为多态分发(动态分派):Julia 在调用函数时会根据形参列表(不含参数默认值)匹配最合适的实现。
注意:Julia 完全依赖形参列表来区分同名函数,如果新加上的一个实现和已有的某个实现的形参列表完全一致,那么会进行覆盖,并不会增加实现的数量。
这里的动态分派与 C++ 的函数重载非常相似,只不过 C++ 的重载决议发生在编译期,而 Julia 的多态分发发生在运行期。
使用 methods() 函数可以查询到 f
几种实现的更具体信息:例如刚刚在REPL中的定义的函数 f
1
2
3
4
5
6julia> methods(f)
# 2 methods for generic function "f" from Main:
[1] f(x::Int64)
@ REPL[2]:1
[2] f(x::Int32)
@ REPL[1]:1
或者 methods() 这个内置函数自身的几种实现在源码中的位置
1
2
3
4
5
6
7
8
9
10
11
12julia> methods(methods)
# 5 methods for generic function "methods" from Base:
[1] methods(f)
@ runtime_internals.jl:1377
[2] methods(f, t, mod::Module)
@ runtime_internals.jl:1365
[3] methods(f, mod::Union{Nothing, AbstractSet{Module}, Module, AbstractArray{Module}})
@ runtime_internals.jl:1377
[4] methods(f, t)
@ runtime_internals.jl:1358
[5] methods(f, t, mod::Union{Nothing, AbstractSet{Module}, Tuple{Module}, AbstractArray{Module}})
@ runtime_internals.jl:1358
