鍍金池/ 教程/ Java/ 方法
類型轉(zhuǎn)換和類型提升
方法
嵌入式 Julia
交互
調(diào)用 C 和 Fortran 代碼
類型
代碼性能優(yōu)化
多維數(shù)組
元編程
函數(shù)
簡介
線性代數(shù)
與其它語言的區(qū)別
數(shù)學(xué)運(yùn)算和基本函數(shù)
構(gòu)造函數(shù)
控制流
常見問題
并行計(jì)算
擴(kuò)展包
開發(fā)擴(kuò)展包
開始
字符串
運(yùn)行外部程序
變量的作用域
模塊
網(wǎng)絡(luò)和流
代碼樣式
復(fù)數(shù)和分?jǐn)?shù)
可空類型
整數(shù)和浮點(diǎn)數(shù)
變量
日期和時(shí)間

方法

函數(shù)中說到,函數(shù)是從參數(shù)多元組映射到返回值的對(duì)象,若沒有合適返回值則拋出異常。實(shí)際中常需要對(duì)不同類型的參數(shù)做同樣的運(yùn)算,例如對(duì)整數(shù)做加法、對(duì)浮點(diǎn)數(shù)做加法、對(duì)整數(shù)與浮點(diǎn)數(shù)做加法,它們都是加法。在 Julia 中,它們都屬于同一對(duì)象: + 函數(shù)。

對(duì)同一概念做一系列實(shí)現(xiàn)時(shí),可以逐個(gè)定義特定參數(shù)類型、個(gè)數(shù)所對(duì)應(yīng)的特定函數(shù)行為。方法是對(duì)函數(shù)中某一特定的行為定義。函數(shù)中可以定義多個(gè)方法。對(duì)一個(gè)特定的參數(shù)多元組調(diào)用函數(shù)時(shí),最匹配此參數(shù)多元組的方法被調(diào)用。

函數(shù)調(diào)用時(shí),選取調(diào)用哪個(gè)方法,被稱為重載。 Julia 依據(jù)參數(shù)個(gè)數(shù)、類型來進(jìn)行重載。

定義方法

Julia 的所有標(biāo)準(zhǔn)函數(shù)和運(yùn)算符,如前面提到的 + 函數(shù),都有許多針對(duì)各種參數(shù)類型組合和不同參數(shù)個(gè)數(shù)而定義的方法。

定義函數(shù)時(shí),可以像復(fù)合類型中介紹的那樣,使用 :: 類型斷言運(yùn)算符,選擇性地對(duì)參數(shù)類型進(jìn)行限制:

julia> f(x::Float64, y::Float64) = 2x + y;

此函數(shù)中參數(shù) xy 只能是 Float64 類型:

    julia> f(2.0, 3.0)
    7.0

如果參數(shù)是其它類型,會(huì)引發(fā) “no method” 錯(cuò)誤:

    julia> f(2.0, 3)
    ERROR: `f` has no method matching f(::Float64, ::Int64)

    julia> f(float32(2.0), 3.0)
    ERROR: `f` has no method matching f(::Float32, ::Float64)

    julia> f(2.0, "3.0")
    ERROR: `f` has no method matching f(::Float64, ::ASCIIString)

    julia> f("2.0", "3.0")
    ERROR: `f` has no method matching f(::ASCIIString, ::ASCIIString)

有時(shí)需要寫一些通用方法,這時(shí)應(yīng)聲明參數(shù)為抽象類型:

    julia> f(x::Number, y::Number) = 2x - y;

    julia> f(2.0, 3)
    1.0

要想給一個(gè)函數(shù)定義多個(gè)方法,只需要多次定義這個(gè)函數(shù),每次定義的參數(shù)個(gè)數(shù)和類型需不同。函數(shù)調(diào)用時(shí),最匹配的方法被重載:

    julia> f(2.0, 3.0)
    7.0

    julia> f(2, 3.0)
    1.0

    julia> f(2.0, 3)
    1.0

    julia> f(2, 3)
    1

對(duì)非數(shù)值的值,或參數(shù)個(gè)數(shù)少于 2,f 是未定義的,調(diào)用它會(huì)返回 “no method” 錯(cuò)誤:

    julia> f("foo", 3)
    ERROR: `f` has no method matching f(::ASCIIString, ::Int64)

    julia> f()
    ERROR: `f` has no method matching f()

在交互式會(huì)話中輸入函數(shù)對(duì)象本身,可以看到函數(shù)所存在的方法:

    julia> f
    f (generic function with 2 methods)

這個(gè)輸出告訴我們,f 是一個(gè)含有兩個(gè)方法的函數(shù)對(duì)象。要找出這些方法的簽名,可以通過使用 methods 函數(shù)來實(shí)現(xiàn):

    julia> methods(f)
    # 2 methods for generic function "f":
    f(x::Float64,y::Float64) at none:1
    f(x::Number,y::Number) at none:1

這表明,f 有兩個(gè)方法,一個(gè)以兩個(gè) Float64 類型作為參數(shù)和另一個(gè)則以一個(gè) Number 類型作為參數(shù)。它也指示了定義方法的文件和行數(shù):因?yàn)檫@些方法在 REPL 中定義,我們得到明顯行數(shù)值:none:1。

定義類型時(shí)如果沒使用 ::,則方法參數(shù)的類型默認(rèn)為 Any。對(duì) f 定義一個(gè)總括匹配的方法:

    julia> f(x,y) = println("Whoa there, Nelly.");

    julia> f("foo", 1)
    Whoa there, Nelly.

總括匹配的方法,是重載時(shí)的最后選擇。

重載是 Julia 最強(qiáng)大最核心的特性。核心運(yùn)算一般都有好幾十種方法:

    julia> methods(+)
    # 125 methods for generic function "+":
    +(x::Bool) at bool.jl:36
    +(x::Bool,y::Bool) at bool.jl:39
    +(y::FloatingPoint,x::Bool) at bool.jl:49
    +(A::BitArray{N},B::BitArray{N}) at bitarray.jl:848
    +(A::Union(DenseArray{Bool,N},SubArray{Bool,N,A<:DenseArray{T,N},I<:(Union(Range{Int64},Int64)...,)}),B::Union(DenseArray{Bool,N},SubArray{Bool,N,A<:DenseArray{T,N},I<:(Union(Range{Int64},Int64)...,)})) at array.jl:797
    +{S,T}(A::Union(SubArray{S,N,A<:DenseArray{T,N},I<:(Union(Range{Int64},Int64)...,)},DenseArray{S,N}),B::Union(SubArray{T,N,A<:DenseArray{T,N},I<:(Union(Range{Int64},Int64)...,)},DenseArray{T,N})) at array.jl:719
    +{T<:Union(Int16,Int32,Int8)}(x::T<:Union(Int16,Int32,Int8),y::T<:Union(Int16,Int32,Int8)) at int.jl:16
    +{T<:Union(Uint32,Uint16,Uint8)}(x::T<:Union(Uint32,Uint16,Uint8),y::T<:Union(Uint32,Uint16,Uint8)) at int.jl:20
    +(x::Int64,y::Int64) at int.jl:33
    +(x::Uint64,y::Uint64) at int.jl:34
    +(x::Int128,y::Int128) at int.jl:35
    +(x::Uint128,y::Uint128) at int.jl:36
    +(x::Float32,y::Float32) at float.jl:119
    +(x::Float64,y::Float64) at float.jl:120
    +(z::Complex{T<:Real},w::Complex{T<:Real}) at complex.jl:110
    +(x::Real,z::Complex{T<:Real}) at complex.jl:120
    +(z::Complex{T<:Real},x::Real) at complex.jl:121
    +(x::Rational{T<:Integer},y::Rational{T<:Integer}) at rational.jl:113
    +(x::Char,y::Char) at char.jl:23
    +(x::Char,y::Integer) at char.jl:26
    +(x::Integer,y::Char) at char.jl:27
    +(a::Float16,b::Float16) at float16.jl:132
    +(x::BigInt,y::BigInt) at gmp.jl:194
    +(a::BigInt,b::BigInt,c::BigInt) at gmp.jl:217
    +(a::BigInt,b::BigInt,c::BigInt,d::BigInt) at gmp.jl:223
    +(a::BigInt,b::BigInt,c::BigInt,d::BigInt,e::BigInt) at gmp.jl:230
    +(x::BigInt,c::Uint64) at gmp.jl:242
    +(c::Uint64,x::BigInt) at gmp.jl:246
    +(c::Union(Uint32,Uint16,Uint8,Uint64),x::BigInt) at gmp.jl:247
    +(x::BigInt,c::Union(Uint32,Uint16,Uint8,Uint64)) at gmp.jl:248
    +(x::BigInt,c::Union(Int64,Int16,Int32,Int8)) at gmp.jl:249
    +(c::Union(Int64,Int16,Int32,Int8),x::BigInt) at gmp.jl:250
    +(x::BigFloat,c::Uint64) at mpfr.jl:147
    +(c::Uint64,x::BigFloat) at mpfr.jl:151
    +(c::Union(Uint32,Uint16,Uint8,Uint64),x::BigFloat) at mpfr.jl:152
    +(x::BigFloat,c::Union(Uint32,Uint16,Uint8,Uint64)) at mpfr.jl:153
    +(x::BigFloat,c::Int64) at mpfr.jl:157
    +(c::Int64,x::BigFloat) at mpfr.jl:161
    +(x::BigFloat,c::Union(Int64,Int16,Int32,Int8)) at mpfr.jl:162
    +(c::Union(Int64,Int16,Int32,Int8),x::BigFloat) at mpfr.jl:163
    +(x::BigFloat,c::Float64) at mpfr.jl:167
    +(c::Float64,x::BigFloat) at mpfr.jl:171
    +(c::Float32,x::BigFloat) at mpfr.jl:172
    +(x::BigFloat,c::Float32) at mpfr.jl:173
    +(x::BigFloat,c::BigInt) at mpfr.jl:177
    +(c::BigInt,x::BigFloat) at mpfr.jl:181
    +(x::BigFloat,y::BigFloat) at mpfr.jl:328
    +(a::BigFloat,b::BigFloat,c::BigFloat) at mpfr.jl:339
    +(a::BigFloat,b::BigFloat,c::BigFloat,d::BigFloat) at mpfr.jl:345
    +(a::BigFloat,b::BigFloat,c::BigFloat,d::BigFloat,e::BigFloat) at mpfr.jl:352
    +(x::MathConst{sym},y::MathConst{sym}) at constants.jl:23
    +{T<:Number}(x::T<:Number,y::T<:Number) at promotion.jl:188
    +{T<:FloatingPoint}(x::Bool,y::T<:FloatingPoint) at bool.jl:46
    +(x::Number,y::Number) at promotion.jl:158
    +(x::Integer,y::Ptr{T}) at pointer.jl:68
    +(x::Bool,A::AbstractArray{Bool,N}) at array.jl:767
    +(x::Number) at operators.jl:71
    +(r1::OrdinalRange{T,S},r2::OrdinalRange{T,S}) at operators.jl:325
    +{T<:FloatingPoint}(r1::FloatRange{T<:FloatingPoint},r2::FloatRange{T<:FloatingPoint}) at operators.jl:331
    +(r1::FloatRange{T<:FloatingPoint},r2::FloatRange{T<:FloatingPoint}) at operators.jl:348
    +(r1::FloatRange{T<:FloatingPoint},r2::OrdinalRange{T,S}) at operators.jl:349
    +(r1::OrdinalRange{T,S},r2::FloatRange{T<:FloatingPoint}) at operators.jl:350
    +(x::Ptr{T},y::Integer) at pointer.jl:66
    +{S,T<:Real}(A::Union(SubArray{S,N,A<:DenseArray{T,N},I<:(Union(Range{Int64},Int64)...,)},DenseArray{S,N}),B::Range{T<:Real}) at array.jl:727
    +{S<:Real,T}(A::Range{S<:Real},B::Union(SubArray{T,N,A<:DenseArray{T,N},I<:(Union(Range{Int64},Int64)...,)},DenseArray{T,N})) at array.jl:736
    +(A::AbstractArray{Bool,N},x::Bool) at array.jl:766
    +{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti},B::SparseMatrixCSC{Tv,Ti}) at sparse/sparsematrix.jl:530
    +{TvA,TiA,TvB,TiB}(A::SparseMatrixCSC{TvA,TiA},B::SparseMatrixCSC{TvB,TiB}) at sparse/sparsematrix.jl:522
    +(A::SparseMatrixCSC{Tv,Ti<:Integer},B::Array{T,N}) at sparse/sparsematrix.jl:621
    +(A::Array{T,N},B::SparseMatrixCSC{Tv,Ti<:Integer}) at sparse/sparsematrix.jl:623
    +(A::SymTridiagonal{T},B::SymTridiagonal{T}) at linalg/tridiag.jl:45
    +(A::Tridiagonal{T},B::Tridiagonal{T}) at linalg/tridiag.jl:207
    +(A::Tridiagonal{T},B::SymTridiagonal{T}) at linalg/special.jl:99
    +(A::SymTridiagonal{T},B::Tridiagonal{T}) at linalg/special.jl:98
    +{T,MT,uplo}(A::Triangular{T,MT,uplo,IsUnit},B::Triangular{T,MT,uplo,IsUnit}) at linalg/triangular.jl:10
    +{T,MT,uplo1,uplo2}(A::Triangular{T,MT,uplo1,IsUnit},B::Triangular{T,MT,uplo2,IsUnit}) at linalg/triangular.jl:11
    +(Da::Diagonal{T},Db::Diagonal{T}) at linalg/diagonal.jl:44
    +(A::Bidiagonal{T},B::Bidiagonal{T}) at linalg/bidiag.jl:92
    +{T}(B::BitArray{2},J::UniformScaling{T}) at linalg/uniformscaling.jl:26
    +(A::Diagonal{T},B::Bidiagonal{T}) at linalg/special.jl:89
    +(A::Bidiagonal{T},B::Diagonal{T}) at linalg/special.jl:90
    +(A::Diagonal{T},B::Tridiagonal{T}) at linalg/special.jl:89
    +(A::Tridiagonal{T},B::Diagonal{T}) at linalg/special.jl:90
    +(A::Diagonal{T},B::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit}) at linalg/special.jl:89
    +(A::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit},B::Diagonal{T}) at linalg/special.jl:90
    +(A::Diagonal{T},B::Array{T,2}) at linalg/special.jl:89
    +(A::Array{T,2},B::Diagonal{T}) at linalg/special.jl:90
    +(A::Bidiagonal{T},B::Tridiagonal{T}) at linalg/special.jl:89
    +(A::Tridiagonal{T},B::Bidiagonal{T}) at linalg/special.jl:90
    +(A::Bidiagonal{T},B::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit}) at linalg/special.jl:89
    +(A::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit},B::Bidiagonal{T}) at linalg/special.jl:90
    +(A::Bidiagonal{T},B::Array{T,2}) at linalg/special.jl:89
    +(A::Array{T,2},B::Bidiagonal{T}) at linalg/special.jl:90
    +(A::Tridiagonal{T},B::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit}) at linalg/special.jl:89
    +(A::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit},B::Tridiagonal{T}) at linalg/special.jl:90
    +(A::Tridiagonal{T},B::Array{T,2}) at linalg/special.jl:89
    +(A::Array{T,2},B::Tridiagonal{T}) at linalg/special.jl:90
    +(A::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit},B::Array{T,2}) at linalg/special.jl:89
    +(A::Array{T,2},B::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit}) at linalg/special.jl:90
    +(A::SymTridiagonal{T},B::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit}) at linalg/special.jl:98
    +(A::Triangular{T,S<:AbstractArray{T,2},UpLo,IsUnit},B::SymTridiagonal{T}) at linalg/special.jl:99
    +(A::SymTridiagonal{T},B::Array{T,2}) at linalg/special.jl:98
    +(A::Array{T,2},B::SymTridiagonal{T}) at linalg/special.jl:99
    +(A::Diagonal{T},B::SymTridiagonal{T}) at linalg/special.jl:107
    +(A::SymTridiagonal{T},B::Diagonal{T}) at linalg/special.jl:108
    +(A::Bidiagonal{T},B::SymTridiagonal{T}) at linalg/special.jl:107
    +(A::SymTridiagonal{T},B::Bidiagonal{T}) at linalg/special.jl:108
    +{T<:Number}(x::AbstractArray{T<:Number,N}) at abstractarray.jl:358
    +(A::AbstractArray{T,N},x::Number) at array.jl:770
    +(x::Number,A::AbstractArray{T,N}) at array.jl:771
    +(J1::UniformScaling{T<:Number},J2::UniformScaling{T<:Number}) at linalg/uniformscaling.jl:25
    +(J::UniformScaling{T<:Number},B::BitArray{2}) at linalg/uniformscaling.jl:27
    +(J::UniformScaling{T<:Number},A::AbstractArray{T,2}) at linalg/uniformscaling.jl:28
    +(J::UniformScaling{T<:Number},x::Number) at linalg/uniformscaling.jl:29
    +(x::Number,J::UniformScaling{T<:Number}) at linalg/uniformscaling.jl:30
    +{TA,TJ}(A::AbstractArray{TA,2},J::UniformScaling{TJ}) at linalg/uniformscaling.jl:33
    +{T}(a::HierarchicalValue{T},b::HierarchicalValue{T}) at pkg/resolve/versionweight.jl:19
    +(a::VWPreBuildItem,b::VWPreBuildItem) at pkg/resolve/versionweight.jl:82
    +(a::VWPreBuild,b::VWPreBuild) at pkg/resolve/versionweight.jl:120
    +(a::VersionWeight,b::VersionWeight) at pkg/resolve/versionweight.jl:164
    +(a::FieldValue,b::FieldValue) at pkg/resolve/fieldvalue.jl:41
    +(a::Vec2,b::Vec2) at graphics.jl:60
    +(bb1::BoundingBox,bb2::BoundingBox) at graphics.jl:123
    +(a,b,c) at operators.jl:82
    +(a,b,c,xs...) at operators.jl:83

重載和靈活的參數(shù)化類型系統(tǒng)一起,使得 Julia 可以抽象表達(dá)高級(jí)算法,不需關(guān)注實(shí)現(xiàn)的具體細(xì)節(jié),生成有效率、運(yùn)行時(shí)專用的代碼。

方法歧義

函數(shù)方法的適用范圍可能會(huì)重疊:

    julia> g(x::Float64, y) = 2x + y;

    julia> g(x, y::Float64) = x + 2y;
    Warning: New definition 
        g(Any,Float64) at none:1
    is ambiguous with: 
        g(Float64,Any) at none:1.
    To fix, define 
        g(Float64,Float64)
    before the new definition.

    julia> g(2.0, 3)
    7.0

    julia> g(2, 3.0)
    8.0

    julia> g(2.0, 3.0)
    7.0

此處 g(2.0, 3.0) 既可以調(diào)用 g(Float64, Any),也可以調(diào)用 g(Any, Float64),兩種方法沒有優(yōu)先級(jí)。遇到這種情況,Julia 會(huì)警告定義含糊,但仍會(huì)任選一個(gè)方法來繼續(xù)執(zhí)行。應(yīng)避免含糊的方法:

    julia> g(x::Float64, y::Float64) = 2x + 2y;

    julia> g(x::Float64, y) = 2x + y;

    julia> g(x, y::Float64) = x + 2y;

    julia> g(2.0, 3)
    7.0

    julia> g(2, 3.0)
    8.0

    julia> g(2.0, 3.0)
    10.0

要消除 Julia 的警告,應(yīng)先定義清晰的方法。

參數(shù)化方法

構(gòu)造參數(shù)化方法,應(yīng)在方法名與參數(shù)多元組之間,添加類型參數(shù):

    julia> same_type{T}(x::T, y::T) = true;

    julia> same_type(x,y) = false;

這兩個(gè)方法定義了一個(gè)布爾函數(shù),它檢查兩個(gè)參數(shù)是否為同一類型:

    julia> same_type(1, 2)
    true

    julia> same_type(1, 2.0)
    false

    julia> same_type(1.0, 2.0)
    true

    julia> same_type("foo", 2.0)
    false

    julia> same_type("foo", "bar")
    true

    julia> same_type(int32(1), int64(2))
    false

類型參數(shù)可用于函數(shù)定義或函數(shù)體的任何地方:

    julia> myappend{T}(v::Vector{T}, x::T) = [v..., x]
    myappend (generic function with 1 method)

    julia> myappend([1,2,3],4)
    4-element Array{Int64,1}:
     1
     2
     3
     4

    julia> myappend([1,2,3],2.5)
    ERROR: `myappend` has no method matching myappend(::Array{Int64,1}, ::Float64)

    julia> myappend([1.0,2.0,3.0],4.0)
    4-element Array{Float64,1}:
     1.0
     2.0
     3.0
     4.0

    julia> myappend([1.0,2.0,3.0],4)
    ERROR: `myappend` has no method matching myappend(::Array{Float64,1}, ::Int64)

下例中,方法類型參數(shù) T 被用作返回值:

    julia> mytypeof{T}(x::T) = T
    mytypeof (generic function with 1 method)

    julia> mytypeof(1)
    Int64

    julia> mytypeof(1.0)
    Float64

方法的類型參數(shù)也可以被限制范圍:

    same_type_numeric{T<:Number}(x::T, y::T) = true
    same_type_numeric(x::Number, y::Number) = false

    julia> same_type_numeric(1, 2)
    true

    julia> same_type_numeric(1, 2.0)
    false

    julia> same_type_numeric(1.0, 2.0)
    true

    julia> same_type_numeric("foo", 2.0)
    no method same_type_numeric(ASCIIString,Float64)

    julia> same_type_numeric("foo", "bar")
    no method same_type_numeric(ASCIIString,ASCIIString)

    julia> same_type_numeric(int32(1), int64(2))
    false

same_type_numeric 函數(shù)與 same_type 大致相同,但只應(yīng)用于數(shù)對(duì)兒。

關(guān)于可選參數(shù)和關(guān)鍵字參數(shù)

函數(shù)中曾簡略提到,可選參數(shù)是可由多方法定義語法的實(shí)現(xiàn)。例如:

    f(a=1,b=2) = a+2b

可以翻譯為下面三個(gè)方法:

    f(a,b) = a+2b
    f(a) = f(a,2)
    f() = f(1,2)

關(guān)鍵字參數(shù)則與普通的與位置有關(guān)的參數(shù)不同。它們不用于方法重載。方法重載僅基于位置參數(shù),選取了匹配的方法后,才處理關(guān)鍵字參數(shù)。