Julia 類型

2021-03-06 14:25 更新

類型

Julia 中,如果類型被省略,則值可以是任意類型。添加類型會顯著提高性能和系統(tǒng)穩(wěn)定性。

Julia 類型系統(tǒng)的特性是,具體類型不能作為具體類型的子類型,所有的具體類型都是最終的,它們可以擁有抽象類型作為父類型。其它高級特性有:

  • 不區(qū)分對象和非對象值:Julia 中的所有值都是一個有類型的對象,這個類型屬于一個單一、全連通類型圖,圖中的每個節(jié)點都是類型。
  • 沒有“編譯時類型”:程序運行時僅有其實際類型,這在面向?qū)ο缶幊陶Z言中被稱為“運行時類型”。
  • 值有類型,變量沒有類型——變量僅僅是綁定了值的名字而已。
  • 抽象類型和具體類型都可以被其它類型和值參數(shù)化。具體來講, 參數(shù)化可以是 符號, 可以是 isbits 返回值為 true 的類型任意值 (本質(zhì)想是講, 這些數(shù) 像整數(shù)或者布爾值一樣, 儲存形式類似于 C 中的數(shù)據(jù)類型或者 struct, 并且 沒有指向其他數(shù)據(jù)的指針), 也可以是元組。如果類型參數(shù)不需要被使用或者 限制, 可以省略不寫。

Julia 的類型系統(tǒng)的設(shè)計旨在有效及具表現(xiàn)力,既清楚直觀又不夸張。許多 Julia 程序員可能永遠不會覺得有必要去明確地指出類型。然而某些程序會因聲明類型變得更清晰,更簡單,更迅速及健壯。

類型聲明

:: 運算符可以用來在程序中給表達式和變量附加類型注釋。這樣做有兩個理由:

  1. 作為斷言,幫助確認(rèn)程序是否正常運行
  2. 給編譯器提供額外類型信息,幫助提升性能

:: 運算符放在表示值的表達式之后時讀作“前者是后者的實例”,它用來斷言左側(cè)表達式是否為右側(cè)表達式的實例。如果右側(cè)是具體類型,此類型應(yīng)該是左側(cè)的實例。如果右側(cè)是抽象類型,左側(cè)應(yīng)是一個具體類型的實例的值,該具體類型是這個抽象類型的子類型。如果類型斷言為假,將拋出異常,否則,返回左值:

    julia> (1+2)::FloatingPoint
    ERROR: type: typeassert: expected FloatingPoint, got Int64

    julia> (1+2)::Int
    3

可以在任何表達式的所在位置做類型斷言。 :: 最常見的用法是作為一個在函數(shù)/方法簽名中的斷言,例如 f(x::Int8) = ... (查看方法)。

:: 運算符跟在表達式上下文中的變量名后時,它聲明變量應(yīng)該是某個類型,有點兒類似于 C 等靜態(tài)語言中的類型聲明。賦給這個變量的值會被 convert 函數(shù)轉(zhuǎn)換為所聲明的類型:

    julia> function foo()
             x::Int8 = 1000
             x
           end
    foo (generic function with 1 method)

    julia> foo()
    -24

    julia> typeof(ans)
    Int8

這個特性用于避免性能陷阱,即給一個變量賦值時意外更改了類型。

“聲明”僅發(fā)生在特定的上下文中:

    x::Int8        # a variable by itself
    local x::Int8  # in a local declaration
    x::Int8 = 10   # as the left-hand side of an assignment

并適用于整個當(dāng)前范圍,甚至在聲明之前。目前,聲明類型不能用于全局范圍,例如在 REPL 中就不可以,因為 Julia 還沒有定型的全局變量。需要注意的是在函數(shù)返回語句中,上述的前兩個表達式計算值,還有就是 :: 是一個類型的斷言不是一個聲明。

抽象類型

抽象類型不能被實例化,它組織了類型等級關(guān)系,方便程序員編程。如,編程時可針對任意整數(shù)類型,而不需指明是哪種具體的整數(shù)類型。

使用 abstract 關(guān)鍵字聲明抽象類型:

    abstract ?name?
    abstract ?name? <: ?supertype?

abstract 關(guān)鍵字引入了新的抽象類型,類型名為 ?name? 。類型名后可跟 <: 及已存在的類型,表明新聲明的抽象類型是這個“父”類型的子類型。

如果沒有指明父類型,則父類型默認(rèn)為 Any ——所有對象和類型都是這個抽象類型的子類型。在類型理論中,Any 位于類型圖的頂峰,被稱為“頂”。Julia 也有預(yù)定義的抽象“底”類型,它位于類型圖的最底處,被稱為 None。NoneAny 對立:任何對象都不是 None 的實例,所有的類型都是 None 的父類型。

下面是構(gòu)造 Julia 數(shù)值體系的抽象類型子集的具體例子:

    abstract Number end
    abstract Real     <: Number
    abstract AbstractFloat <: Real
    abstract Integer  <: Real
    abstract Signed   <: Integer
    abstract Unsigned <: Integer

<: 運算符意思為“前者是后者的子類型”,它聲明右側(cè)是左側(cè)新聲明類型的直接父類型。也可以用來判斷左側(cè)是不是右側(cè)的子類型:

    julia> Integer <: Number
    true

    julia> Integer <: FloatingPoint
    false

抽象類型的一個重要用途是為具體的類型提供默認(rèn)實現(xiàn)。舉個簡單的例子:

  function myplus(x, y)
      x + y
  endof

第一點需要注意的是, 上面的參數(shù)聲明等效于 x::Anyy::Any. 當(dāng)這個函數(shù)被調(diào)用時, 例如 myplus(2, 5), Julia 會首先查找參數(shù)類型匹配的 myplus 函數(shù). (關(guān)于多重分派的詳細信息請參考下文.) 如果沒有找到比上面的函數(shù)更相關(guān)的函數(shù), Julia 根據(jù)上面的通用函數(shù)定義并編譯一個 myplus 具體函數(shù), 其參數(shù)為兩個 Int 型變量, 也就是說, Julia 會定義并編譯:

  function myplus(x::Int, y::Int)
      x + y
  end

最后, 調(diào)用這個具體的函數(shù)。

因此, 程序員可以利用抽象類型編寫通用的函數(shù),然后這個通用函數(shù)可以被許多具體的類型組合調(diào)用。也正是由于多重分派,程序員可以精確的控制是調(diào)用更具體的還是通用的函數(shù)。

需要注意的一點是, 編寫面向抽象類型的函數(shù)并不會帶來性能上的損失,因為每次調(diào)用函數(shù)時,根據(jù)不同的參數(shù)組合,函數(shù)總是要重新編譯的。(然而, 如果參數(shù)類型為包含抽象類型的容器是, 會有性能方面的問題;參見下面的關(guān)于性能的提示。)

位類型

位類型是具體類型,它的數(shù)據(jù)是由位構(gòu)成的。整數(shù)和浮點數(shù)都是位類型。標(biāo)準(zhǔn)的位類型是用 Julia 語言本身定義的:

    bitstype 16 Float16 <: FloatingPoint
    bitstype 32 Float32 <: FloatingPoint
    bitstype 64 Float64 <: FloatingPoint

    bitstype 8  Bool <: Integer
    bitstype 32 Char <: Integer

    bitstype 8  Int8     <: Signed
    bitstype 8  Uint8    <: Unsigned
    bitstype 16 Int16    <: Signed
    bitstype 16 Uint16   <: Unsigned
    bitstype 32 Int32    <: Signed
    bitstype 32 Uint32   <: Unsigned
    bitstype 64 Int64    <: Signed
    bitstype 64 Uint64   <: Unsigned
    bitstype 128 Int128  <: Signed
    bitstype 128 Uint128 <: Unsigned

聲明位類型的通用語法是:

    bitstype ?bits? ?name?
    bitstype ?bits? ?name? <: ?supertype?

?bits? 表明類型需要多少空間來存儲,?name? 為新類型的名字。目前,位類型的聲明的位數(shù)只支持 8 的倍數(shù),因此布爾類型也是 8 位的。

Bool, Int8Uint8 類型的聲明是完全相同的,都占用了 8 位內(nèi)存,但它們是互相獨立的。

復(fù)合類型

復(fù)合類型也被稱為記錄、結(jié)構(gòu)、或者對象。復(fù)合類型是變量名域的集合。它是 Julia 中最常用的自定義類型。在 Julia 中,所有的值都是對象,但函數(shù)并不與它們所操作的對象綁定。Julia 重載時,根據(jù)函數(shù) 所有參數(shù)的類型,而不僅僅是第一個參數(shù)的類型,來選取調(diào)用哪個方法(詳見 :方法 )。

使用 type 關(guān)鍵字來定義復(fù)合類型:

    julia> type Foo
             bar
             baz::Int
             qux::Float64
           end

構(gòu)建復(fù)合類型 Foo 的對象:

    julia> foo = Foo("Hello, world.", 23, 1.5)
    Foo("Hello, world.",23,1.5)

    julia> typeof(foo)
    Foo (constructor with 2 methods)

當(dāng)一個類型像函數(shù)一樣被調(diào)用時,它可以被叫做類型構(gòu)造函數(shù)(constructor)。每個類型有兩種構(gòu)造函數(shù)是自動被生成的(它們被叫做默認(rèn)構(gòu)造函數(shù))。第一種是當(dāng)傳給構(gòu)造函數(shù)的參數(shù)和這個類型的字段類型不一一匹配時,構(gòu)造函數(shù)會把它的參數(shù)傳給 convert 函數(shù),并且轉(zhuǎn)換到這個類型相應(yīng)的字段類型。第二種是當(dāng)傳給構(gòu)造函數(shù)的每個參數(shù)和這個類型的字段類型都一一相同時,構(gòu)造函數(shù)直接生成類型。要自動生成兩種默認(rèn)構(gòu)造函數(shù)的原因是:為了防止用戶在聲明別的新變量的時候不小心把構(gòu)造函數(shù)給覆蓋掉。

由于沒有約束 bar 的類型,它可以被賦任意值;但是 baz 必須能被轉(zhuǎn)換為 Int

    julia> Foo((), 23.5, 1)
    ERROR: InexactError()
     in Foo at no file

你可以用 names 這個函數(shù)來獲取類型的所有字段。

    julia> names(foo)
    3-element Array{Symbol,1}:
     :bar
     :baz
     :qux

獲取復(fù)合對象域的值:

    julia> foo.bar
    "Hello, world."

    julia> foo.baz
    23

    julia> foo.qux
    1.5

修改復(fù)合對象域的值:

    julia> foo.qux = 2
    2.0

    julia> foo.bar = 1//2
    1//2

沒有域的復(fù)合類型是單態(tài)類型,這種類型只能有一個實例:

    type NoFields
    end

    julia> is(NoFields(), NoFields())
    true

is 函數(shù)驗證 NoFields 的“兩個”實例是否為同一個。有關(guān)單態(tài)類型,后面會詳細講。

有關(guān)復(fù)合類型如何實例化,需要 參數(shù)化類型方法這兩個背景知識。將在構(gòu)造函數(shù)中詳細介紹構(gòu)造實例。

不可變復(fù)合類型

可以使用關(guān)鍵詞 immutable 替代 type 來定義 不可變 復(fù)合類型:

    immutable Complex
      real::Float64
      imag::Float64
    end

這種類型和其他復(fù)合類型類似,除了它們的實例不能被更改。不可變復(fù)合類型具有以下幾種優(yōu)勢:

  • 它們在一些情況下更高效。像上面 Complex 例子里的類型就被有效地封裝到數(shù)組里,而且有些時候編譯器能夠避免完整地分配不可變對象。
  • 不會與類型的構(gòu)造函數(shù)提供的不變量沖突。
  • 用不可變對象的代碼不容易被侵入。

一個不可變對象可以包含可變對象,比如數(shù)組,域。那些被包含的可變對象仍然保持可變;只有不可變對象自己的域不能變得指向別的對象。

理解不可變復(fù)合變量的一個有用的辦法是每個實例都是和特定域的值相關(guān)聯(lián)的 - 這些域的值就能告訴你關(guān)于這個對象的一切。相反地,一個可變的對象就如同一個小的容器可能包含了各種各樣的值,所以它不能從它的域的值確定出這個對象。在決定是否把一個類型定義為不變的時候,先問問是否兩個實例包含相同的域的值就被認(rèn)為是相同,或者它們會獨立地改變。如果它們被認(rèn)為是相同的,那么這個類型就該被定義成不可變的。

再次強調(diào)下, Julia 中不可變類型有兩個重要的特性:

  • 不可變復(fù)合類型的數(shù)據(jù)在傳遞時會被拷貝 (在賦值時是這樣, 在調(diào)用函數(shù)時也是這樣), 相對的, 可變類型的數(shù)據(jù)是以引用的方式互相傳遞.
  • 不可變復(fù)合類型內(nèi)的域不可改變.

對于有著 C/C++ 背景的讀者, 需要仔細想下為什么這兩個特性是息息相關(guān)的。設(shè)想下,如果這兩個特性是分開的,也就是說,如果數(shù)據(jù)在傳遞時是拷貝的, 然而數(shù)據(jù)內(nèi)部的變量可以被改變, 那么將很難界定某段代碼的實際作用。舉個例子,假設(shè) x 是某個函數(shù)的參數(shù), 同時假設(shè)函數(shù)改變了參數(shù)中的一個域:x.isprocessed = true。根據(jù) x 是值傳遞或者引用傳遞, 在調(diào)用完函數(shù)是, 原來 x 的值有可能沒有改變, 也有可能改變. 為了防止出現(xiàn)這種不確定效應(yīng), Julia 限定如果參數(shù)是值傳遞, 其內(nèi)部域的值不可改變。

被聲明類型

以上的三種類型是緊密相關(guān)的。它們有相同的特性:

  • 明確地被聲明
  • 有名字
  • 有明確的父類
  • 可以有參數(shù)

正因有共有的特性,這些類型內(nèi)在地表達為同一種概念的實例,DataType,是以下類型之一:

    julia> typeof(Real)
    DataType

    julia> typeof(Int)
    DataType

DataType 既可以抽象也可以具體。如果是具體的,它會擁有既定的大小,存儲安排和(可選的)名域。所以一個位類型是一個大小非零的 DataType,但沒有名域。一個復(fù)合類型是一個可能擁有名域也可以為空集(大小為零)的 DataType 。

在這個系統(tǒng)里的每一個具體的值都是某個 DataType 的實例,或者一個多元組。

多元組類型

多元組的類型是類型的多元組:

    julia> typeof((1,"foo",2.5))
    (Int64,ASCIIString,Float64)

類型多元組可以在任何需要類型的地方使用:

    julia> (1,"foo",2.5) :: (Int64,String,Any)
    (1,"foo",2.5)

    julia> (1,"foo",2.5) :: (Int64,String,Float32)
    ERROR: type: typeassert: expected (Int64,String,Float32), got (Int64,ASCIIString,Float64)

如果類型多元組中有非類型出現(xiàn),會報錯:

    julia> (1,"foo",2.5) :: (Int64,String,3)
    ERROR: type: typeassert: expected Type{T<:Top}, got (DataType,DataType,Int64)

注意,空多元組 () 的類型是其本身:

    julia> typeof(())
    ()

多元組類型是關(guān)于它的組成類型是協(xié)變的,一個多元組是另一個多元組的子類型意味著對應(yīng)的第一個多元組的各元素的類型是第二個多元組對應(yīng)元素類型的子類型。比如:

    julia> (Int,String) <: (Real,Any)
    true

    julia> (Int,String) <: (Real,Real)
    false

    julia> (Int,String) <: (Real,)
    false

直觀地看,這就像一個函數(shù)的各個參數(shù)的類型必須是函數(shù)簽名的子類型(當(dāng)簽名匹配的時候)。

類型共用體

類型共用體是特殊的抽象類型,使用 Union 函數(shù)來聲明:

    julia> IntOrString = Union(Int,String)
    Union(String,Int64)

    julia> 1 :: IntOrString
    1

    julia> "Hello!" :: IntOrString
    "Hello!"

    julia> 1.0 :: IntOrString
    ERROR: type: typeassert: expected Union(String,Int64), got Float64

不含任何類型的類型共用體,是“底”類型 None

    julia> Union()
    None

抽象類型 None 是所有其它類型的子類型,且沒有實例。零參的 Union 調(diào)用,將返回?zé)o實例的類型 None

參數(shù)化類型

Julia 的類型系統(tǒng)支持參數(shù)化:類型可以引入?yún)?shù),這樣類型聲明為每種可能的參數(shù)組合聲明一個新類型。

所有被聲明的類型(DataType 的變體)都可以使用同樣的語法來參數(shù)化。我們將按照如下順序來討論:參數(shù)化符合類型、參數(shù)化抽象類型、參數(shù)化位類型。

參數(shù)化復(fù)合類型

    abstract Pointy{T}
    type Point{T} <: Pointy{T}
      x::T
      y::T
    end

類型參數(shù)跟在類型名后,用花括號括起來:

    type Point{T}
      x::T
      y::T
    end

這個聲明定義了新參數(shù)化類型 Point{T} ,它有兩個 T 類型的“坐標(biāo)軸”。參數(shù)化類型可以是任何類型(也可以是整數(shù),此例中我們用的是類型)。具體類型 Point{Float64} 等價于將 Point 中的 T 替換為 Float64 后的類型。上例實際上聲明了許多種類型:Point{Float64}, Point{String}, Point{Int64} 等等,因此,現(xiàn)在每個都是可以使用的具體類型:

    julia> Point{Float64}
    Point{Float64} (constructor with 1 method)

    julia> Point{String}
    Point{String} (constructor with 1 method)

Point 本身也是個有效的類型對象:

    julia> Point
    Point{T} (constructor with 1 method)

Point 在這兒是一個抽象類型,它包含所有如 Point{Float64}, Point{String} 之類的具體實例:

    julia> Point{Float64} <: Point
    true

    julia> Point{String} <: Point
    true

其它類型則不是其子類型:

    julia> Float64 <: Point
    false

    julia> String <: Point
    false

Point 不同 T 值所聲明的具體類型之間,不能互相作為子類型:

    julia> Point{Float64} <: Point{Int64}
    false

    julia> Point{Float64} <: Point{Real}
    false

這一點非常重要:

雖然 Float64 <: Real, Point{Float64} <: Point{Real} 不成立!

換句話說,Julia 的類型參數(shù)是 不相關(guān) 的。盡管 Point{Float64} 的實例按照概念來說,應(yīng)該是 Point{Real} 的實例,但兩者在內(nèi)存中的表示上有區(qū)別:

  • Point{Float64} 的實例可以簡便、有效地表示 64 位數(shù)對兒
  • Point{Real} 的實例可以表示任意 Real 實例的數(shù)對兒。由于 Real 的實例可以為任意大小、任意結(jié)構(gòu),因此 Point{Real} 實際上表示指向 Real 對象的指針對兒

上述區(qū)別在數(shù)組中更明顯: Array{Float64} 可以在一塊連續(xù)內(nèi)存中存儲 64 位浮點數(shù),而 Array{Real} 則保存指向每個 Real 對象的指針數(shù)組。而每個 Real 對象的大小,可能比 64 位浮點數(shù)的大。

構(gòu)造函數(shù)中將介紹如何給復(fù)合類型自定義構(gòu)造方法,但如果沒有特殊構(gòu)造聲明時,默認(rèn)有兩種構(gòu)造新復(fù)合對象的方法:一種是明確指明構(gòu)造方法的類型參數(shù);另一種是由對象構(gòu)造方法的參數(shù)來隱含類型參數(shù)。

指明構(gòu)造方法的類型參數(shù):

    julia> Point{Float64}(1.0,2.0)
    Point{Float64}(1.0,2.0)

    julia> typeof(ans)
    Point{Float64} (constructor with 1 method)

參數(shù)個數(shù)應(yīng)與構(gòu)造函數(shù)相匹配:

    julia> Point{Float64}(1.0)
    ERROR: no method Point{Float64}(Float64)

    julia> Point{Float64}(1.0,2.0,3.0)
    ERROR: no method Point{Float64}(Float64, Float64, Float64)

對于帶有類型參數(shù)的類型,因為重載構(gòu)造函數(shù)是不可能的,所以只有一種默認(rèn)構(gòu)造函數(shù)被自動生成——這個構(gòu)造函數(shù)接受任何參數(shù)并且把們轉(zhuǎn)換成對應(yīng)的字段類型并賦值

大多數(shù)情況下不需要提供 Point 對象的類型,它可由參數(shù)類型來提供信息。因此,可以不提供 T 的值:

    julia> Point(1.0,2.0)
    Point{Float64}(1.0,2.0)

    julia> typeof(ans)
    Point{Float64} (constructor with 1 method)

    julia> Point(1,2)
    Point{Int64}(1,2)

    julia> typeof(ans)
    Point{Int64} (constructor with 1 method)

上例中,Point 的兩個參數(shù)類型相同,因此 T 可以省略。但當(dāng)參數(shù)類型不同時,會報錯:

    julia> Point(1,2.5)
    ERROR: `Point{T}` has no method matching Point{T}(::Int64, ::Float64)

這種情況其實也可以處理,詳見構(gòu)造函數(shù)

參數(shù)化抽象類型

類似地,參數(shù)化抽象類型聲明一個抽象類型的集合:

    abstract Pointy{T}

對每個類型或整數(shù)值 T,Pointy{T} 都是一個不同的抽象類型。Pointy 的每個實例都是它的子類型:

    julia> Pointy{Int64} <: Pointy
    true

    julia> Pointy{1} <: Pointy
    true

參數(shù)化抽象類型也是不相關(guān)的:


    julia> Pointy{Float64} <: Pointy{Real}
    false

    julia> Pointy{Real} <: Pointy{Float64}
    false

可以如下聲明 Point{T}Pointy{T} 的子類型:

    type Point{T} <: Pointy{T}
      x::T
      y::T
    end

對每個 T,都有 Point{T}Pointy{T} 的子類型:

    julia> Point{Float64} <: Pointy{Float64}
    true

    julia> Point{Real} <: Pointy{Real}
    true

    julia> Point{String} <: Pointy{String}
    true

它們?nèi)匀皇遣幌嚓P(guān)的:

    julia> Point{Float64} <: Pointy{Real}
    false

參數(shù)化抽象類型 Pointy 有什么用呢?假設(shè)我們要構(gòu)造一個坐標(biāo)點的實現(xiàn),點都在對角線 x = y 上,因此我們只需要一個坐標(biāo)軸:


    type DiagPoint{T} <: Pointy{T}
      x::T
    end

Point{Float64}DiagPoint{Float64} 都是 Pointy{Float64} 抽象類型的實現(xiàn),這對其它可選類型 T 也一樣。 Pointy 可以作為它的子類型的公共接口。有關(guān)方法和重載,詳見下一節(jié) :ref:man-methods 。

有時需要對 T 的范圍做限制:

    abstract Pointy{T<:Real}

此時, T 只能是 Real 的子類型:

    julia> Pointy{Float64}
    Pointy{Float64}

    julia> Pointy{Real}
    Pointy{Real}

    julia> Pointy{String}
    ERROR: type: Pointy: in T, expected T<:Real, got Type{String}

    julia> Pointy{1}
    ERROR: type: Pointy: in T, expected T<:Real, got Int64

參數(shù)化復(fù)合類型的類型參數(shù),也可以同樣被限制:

    type Point{T<:Real} <: Pointy{T}
      x::T
      y::T
    end

下面是 Julia 的 Rational 的 immutable 類型是如何定義的,這個類型表示分?jǐn)?shù):

    immutable Rational{T<:Integer} <: Real
      num::T
      den::T
    end

單態(tài)類型

單態(tài)類型是一種特殊的抽象參數(shù)化類型。對每個類型 T ,抽象類型“單態(tài)” Type{T} 的實例為對象 T。來看些例子:

    julia> isa(Float64, Type{Float64})
    true

    julia> isa(Real, Type{Float64})
    false

    julia> isa(Real, Type{Real})
    true

    julia> isa(Float64, Type{Real})
    false

換句話說,僅當(dāng) AB 是同一個對象,且此對象是類型時,isa(A,Type{B}) 才返回真。沒有參數(shù)時,Type 僅是抽象類型,所有的類型都是它的實例,包括單態(tài)類型:

    julia> isa(Type{Float64},Type)
    true

    julia> isa(Float64,Type)
    true

    julia> isa(Real,Type)
    true

只有對象是類型時,才是 Type 的實例:

    julia> isa(1,Type)
    false

    julia> isa("foo",Type)
    false

Julia 中只有類型對象才有單態(tài)類型。

參數(shù)化位類型

可以參數(shù)化地聲明位類型。例如,Julia 中指針被定義為位類型:

    # 32-bit system:
    bitstype 32 Ptr{T}

    # 64-bit system:
    bitstype 64 Ptr{T}

這兒的參數(shù)類型 T 不是用來做類型定義,而是個抽象標(biāo)簽,它定義了一組結(jié)構(gòu)相同的類型,這些類型僅能由類型參數(shù)來區(qū)分。盡管 Ptr{Float64}Ptr{Int64} 的表示是一樣的,它們是不同的類型。所有的特定指針類型,都是 Ptr 類型的子類型:

    julia> Ptr{Float64} <: Ptr
    true

    julia> Ptr{Int64} <: Ptr
    true

類型別名

Julia 提供 typealias 機制來實現(xiàn)類型別名。如,UintUint32Uint64 的類型別名,這取決于系統(tǒng)的指針大?。?/p>

    # 32-bit system:
    julia> Uint
    Uint32

    # 64-bit system:
    julia> Uint
    Uint64

它是通過 base/boot.jl 中的代碼實現(xiàn)的:

    if is(Int,Int64)
        typealias Uint Uint64
    else
        typealias Uint Uint32
    end

對參數(shù)化類型,typealias 提供了簡單的參數(shù)化類型名。Julia 的數(shù)組類型為 Array{T,n} ,其中 T 是元素類型, n 是數(shù)組維度的數(shù)值。為簡單起見,Array{Float64} 可以只指明元素類型而不需指明維度:

    julia> Array{Float64,1} <: Array{Float64} <: Array
    true

``Vector`` 和 ``Matrix`` 對象是如下定義的:

    typealias Vector{T} Array{T,1}
    typealias Matrix{T} Array{T,2}

類型運算

Julia 中,類型本身也是對象,可以對其使用普通的函數(shù)。如 <: 運算符,可以判斷左側(cè)是否是右側(cè)的子類型。

isa 函數(shù)檢測對象是否屬于某個指定的類型:

    julia> isa(1,Int)
    true

    julia> isa(1,FloatingPoint)
    false

typeof 函數(shù)返回參數(shù)的類型。類型也是對象,因此它也有類型:

    julia> typeof(Rational)
    DataType

    julia> typeof(Union(Real,Float64,Rational))
    DataType

    julia> typeof((Rational,None))
    (DataType,UnionType)

類型的類型是什么?它們的類型是 DataType

    julia> typeof(DataType)
    DataType

    julia> typeof(UnionType)
    DataType

讀者也許會注意到,DataType 類似于空多元組(詳見上文 )。因此,遞歸使用 ()DataType 所組成的多元組的類型,是該類型本身:

    julia> typeof(())
    ()

    julia> typeof(DataType)
    DataType

    julia> typeof(((),))
    ((),)

    julia> typeof((DataType,))
    (DataType,)

    julia> typeof(((),DataType))
    ((),DataType)

super 可以指明一些類型的父類型。只有聲明的類型(DataType)才有父類型:

    julia> super(Float64)
    FloatingPoint

    julia> super(Number)
    Any

    julia> super(String)
    Any

    julia> super(Any)
    Any

對其它類型對象(或非類型對象)使用 super ,會引發(fā) “no method” 錯誤:

    julia> super(Union(Float64,Int64))
    ERROR: `super` has no method matching super(::Type{Union(Float64,Int64)})

    julia> super(None)
    ERROR: `super` has no method matching super(::Type{None})

    julia> super((Float64,Int64))
    ERROR: `super` has no method matching super(::Type{(Float64,Int64)})


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號