前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >6 Julia 类型

6 Julia 类型

作者头像
猫叔Rex
发布2020-06-30 11:52:39
9100
发布2020-06-30 11:52:39
举报
文章被收录于专栏:科学计算

类型

Julia中没有class,也没有子类型的继承关系,所有具体类型都是最终的,并且只有抽象类型可以作为其超类型。Julia中的继承是继承行为,而不是继承结构。

类型声明

声明某个变量的类型,也可以用来断言变量类型是否正确

代码语言:javascript
复制
(2+4)::Float64
>> ERROR: ...
(2+4)::Int64
6

类型声明常用的两个地方在函数中的参数类型和返回类型

代码语言:javascript
复制
function f9(x::Int64)::Float64
    x/10
end
f9(10)
>>1.0

抽象类型

抽象类型不能被实例化,我们前面讲到的Int64/Float64等都是抽象类型的具体类型,抽象类型不能直接使用,类似于C++中的抽象类。 抽象类型的定义方法如下: Julia abstract type «name» end abstract type «name» <: «supertype» end 第二行中的<:表示新声明的抽象类型是后面类型的子类型。 如果没有给出父类型,则默认为Any。 抽象类型有:

代码语言:javascript
复制
abstract type Number end
abstract type Real     <: Number end
abstract type AbstractFloat <: Real end
abstract type Integer  <: Real end
abstract type Signed   <: Integer end
abstract type Unsigned <: Integer end
代码语言:javascript
复制
Integer <: Number
>>true
Integer <: AbstractFloat
>>false

原始类型

原始类型是具体类型,其数据是由简单的位组成。即我们前来讲到的Float64/Int64/Uint64/Uint32等。是可以实例化为对象的。 声明原始类型的语法为:

代码语言:javascript
复制
primitive type «name» «bits» end
primitive type «name» <: «supertype» «bits» end

而标准的原始类型都是在语言本身中定义的:

代码语言:javascript
复制
primitive type Float16 <: AbstractFloat 16 end
primitive type Float32 <: AbstractFloat 32 end
primitive type Float64 <: AbstractFloat 64 end
primitive type Bool <: Integer 8 end
primitive type Char <: AbstractChar 32 end
primitive type Int8    <: Signed   8 end
primitive type UInt8   <: Unsigned 8 end

原始类型的应用

代码语言:javascript
复制
function f10(x::Int64)::Int32
    x + 10
end
a = f10(10)

我们前面说到抽象类型不能被实例化,但如果我们把上面的Int64换成抽象类型Real,发现也是可以正确被赋值的

代码语言:javascript
复制
function f11(x::Real)::Real
    x + 10
end
b = f11(10)

这又是为什么呢?我们可以用typeof()函数查看变量的类型

代码语言:javascript
复制
typeof(a)
>>Int32
typeof(b)
>>Int64

即在使用抽象类型时,Julia会针对每个调用它的参数的具体类型重新编译。 有两点需要说明:

  • 即使我们的参数类型为抽象类型,性能不会有任何损失;但如果函数参数是抽象类型的容器(比如数组,矩阵等),可能存在性能问题
  • 我们前面讲到的Bool、UInt8和Int8都是8 bits,但它们的根本区别是具有不同的超类型:Bool的超类型是Integer,Int8是Signed而UInt8是Unsigned。

类型转换

这个我们在前面讲函数时其实已经提到过

代码语言:javascript
复制
function foo(a, b)
    x::Int8 = a
    y::Int8 = b
    x + y
end

该函数中把Int64类型转成了Int8后再进行计算。也可以像C++中的强制类型转换的用法一样

代码语言:javascript
复制
Int8(10)
convert(Int8, 10)

但这种强转仅限于数字之间,而且不能越界

代码语言:javascript
复制
Int8(1000)
>>error
Int8("10")
>>error
convert(Int8, "10")
>>error

如果我们就是想把字符串转成数字的话,可以用parse函数

代码语言:javascript
复制
parse(Int8, "10")

我们也可以为convert()函数增加一个方法

代码语言:javascript
复制
Base.convert(::Type{Int8}, x::String) = parse(Int8, x)
convert(Int8, "10")

类型提升

先看一个简单的例子

代码语言:javascript
复制
1 + 2.0
>>3.0

这个例子中,就是把Int64类型的1转换成了Float64,然后进行加法操作

再来看下面的例子

代码语言:javascript
复制
a = (1, 2.0, Int8(3))
>>(1, 2.0, 3)
b = promote(1, 2.0, Int8(3))
>>(1.0, 2.0, 3.0)

使用promote后,Tuple中的所有元素都提升到了Float64类型

代码语言:javascript
复制
+(1, 2.0)
>>3.0
a = (1, 2.0)
+(a...)
>>3.0

其实+(1,2.0)就相当于+(promote(1, 2.0)...),可以运行@edit +(1+2.0)查看Julia的实现方式。

复合类型

即自定义类型,关键字是struct,Julia中没有class关键字,都用struct代替

代码语言:javascript
复制
struct Foo
    x1
    x2::Int
    x3::Float64
end
foo = Foo("Hello World!", 10, 11.9)
typeof(foo)
>>Foo
foo.x1
>>"Hello World!"

在foo的创建过程中,有两个默认的构造函数会被自动生成,一个可以接受任意参数,如上面的x1,另接受与字段类型完全匹配的参数,如上面的x2,x3。

struct是不可变类型,foo在创建后,内容不可更改。

代码语言:javascript
复制
foo.x2 = 2
>>type Foo is immutable

Stacktrace:
 [1] setproperty!(::Foo, ::Symbol, ::Int64) at .\sysimg.jl:19
 [2] top-level scope at In[15]:1

可变复合类型

代码语言:javascript
复制
mutable struct Foo
    x1
    x2::Int
    x3::Float64
end
foo2 = Foo("Hello World", 10, 12.1)
foo2.x2 = 20

可变的复合类型是建立在堆上的,并具有稳定的内存地址。

DataType

我们上面讨论的抽象类型、原始类型和复合类型,都有以下的共性:

  • 它们都是显式声明的。
  • 它们都具有名称。
  • 它们都已经显式声明超类型。
  • 它们可以有参数。
代码语言:javascript
复制
typeof(Real)
>>DataType

DataType 可以是抽象的或具体的。它如果是具体的,就具有指定的大小、存储布局和字段名称(可选)。因此,原始类型是具有非零大小的 DataType,但没有字段名称。复合类型是具有字段名称或者为空(大小为零)的 DataType。

每一个具体的值在系统里都是某个 DataType 的实例。

Type Unions

类型共用体是一种特殊的抽象类型,具体用法:

代码语言:javascript
复制
IntOrString = Union{Int,AbstractString}
123::IntOrString
>>123
"abc":IntOrString
>>"abc"
12.4::IntOrString
>>TypeError: in typeassert, expected Union{Int64, AbstractString}, got Float64

f(x::IntOrString) = println(x)
f(12)
f("abc")
f(23.4)
f('a')

参数类型

Julia类型系统的一个重要特色就是类型可以支持参数化,我们前面讲到的原始类型、抽象类型和复合类型都支持参数化。类似于C++中的template,但Julia是一种动态语言,在使用参数类型方面优势更加明显。

  1. 复合参数类型
代码语言:javascript
复制
struct Pos{T}
    x::T
    y::T
end
a = Pos{Int64}(1,2)
b = Pos{Float64}(1.1,2.2)
c = Pos(3,4)
d = Pos(3.1,4.2)
typeof(c)
>>Pos{Int64}
typeof(d)
>>Pos{Float64}

每个实例都是Pos的子类型

代码语言:javascript
复制
Pos{Float64} <: Pos
>>true
Pos{Int64} <: Pos
>>true

但不同的T声明的具体类型之间不能互为子类型

代码语言:javascript
复制
Pos{Float64} <: Pos{Real}
>>false

还有一点需要注意,虽然有FLoat64<:Real,但没有Pos{Float64}<:Pos{Real}. 他们的关系如下

image

所以,下面的定义就不能使用

代码语言:javascript
复制
function norm(p::Pos{Real})
    sqrt(p.x^2 + p.y^2)
end
p1 = Pos{Real}(1,2)
norm(p1)
>>2.23606797749979
p2 = Pos{Int64}(1,2)
norm(p2)
>>error

但我们可以写成这样

代码语言:javascript
复制
function nrom2(p::Pos{<:Real})
    sqrt(p.x^2 + p.y^2)
end
norm(p2)
>>2.23606797749979

复合参数类型也支持多个参数类型

代码语言:javascript
复制
struct Pos1{T1,T2}
    x1::T1
    x2::T2
end
p1 = Pos1{Int64,Float64}(1,2.2)
p1.x1
>>1
p1.x2
>>2.2
  1. 抽象参数类型

由抽象类型而来,顾名思义,就是给抽象类型加了个参数

代码语言:javascript
复制
abstract type Pointy{T} end 

与复合参数类型一样,每个实例都是Pointy的子类型

代码语言:javascript
复制
Pointy{Int64} <: Pointy
>>true

不同的T之间不能互为子类型

代码语言:javascript
复制
Pointy{Float64} <: Pointy{Real}
>>false
  1. 原始参数类型

由原始类型而来,就是给原始类型加了个参数

代码语言:javascript
复制
primitive type Ptr{T} 64 end
Ptr{Float64} <: Ptr
>>true
  1. 元组类型

元组的定义我们再《变量》一节中讲过了,元组的内容不可更改

代码语言:javascript
复制
t1 = (1, 2.8)

就相当于不可变参数类型的struct

代码语言:javascript
复制
struct Tuple2{T1,T2}
    x1::T1
    x2::T2
end
t2 = Tuple2{Int64,Float64}(1, 2.8)

那它们之间有什么区别么?

  • 元组类型可以具有任意数量的参数。
  • 元组类型的参数是协变的:Tuple{Int} 是 Tuple{Any}的子类型。因此,Tuple{Any}被认为是一种抽象类型,且元组类型只有在它们的参数都是具体类型时才是具体类型。
  • 元组没有字段名称; 字段只能通过索引访问。

变参元组类型

元组类型的最后一个参数可以是特殊类型Vararg,表示后面可跟任意多个参数

代码语言:javascript
复制
Tup = Tuple{Float64, Vararg{Int64}}
isa((2.2,), Tup)
>>true
isa((2.2, 3), Tup)
>>true
ias((2.2, 3.1), Tup)
>>false
ias((2.2, 3, 4), Tup)
>>true

单态类型

所谓的单态类型,就是Type{T},它是个抽象类型,且唯一的实例就是对象T。

代码语言:javascript
复制
isa(Int64, Type{Int64})
>>true
isa(Real, Type{Int64})
>>flase
isa(Type{Int64}, Type)
>>true
isa(Type{Real}, Type)
>>true

UnionAll类型

在抽象参数类型中,我们讲到,Pointy是它所有实例Pointy{T})等的超类型,但T的类型是不确定的,那这又是如何工作的呢?这就引入了UnionAll类型,Pointy是一种UnionAll类型,是这种类型某些参数的所有值的类型的迭代并集。

UnionAll类型通常使用关键字where编写

代码语言:javascript
复制
struct Pos{T}
    x::T
    y::T
end
function f1(p::Pos{T} where T<:Real)
    p
end

当有两个类型参数时

代码语言:javascript
复制
struct Pos2{T1,T2}
    x::T1
    y::T2
end
function f2(p::Pos2{T1,T2} where T1<:Int64 where T2<:Float64)
    p
end

where还可以限定T的上下界

代码语言:javascript
复制
function f23(Pos{T} where Int64<:T<:Real)
    p
end
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 傅里叶的猫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类型
    • 类型声明
      • 抽象类型
        • 原始类型
          • 类型转换
          • 类型提升
        • 复合类型
          • 可变复合类型
            • DataType
              • Type Unions
                • 参数类型
                  • 变参元组类型
                    • 单态类型
                      • UnionAll类型
                      相关产品与服务
                      容器服务
                      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档