Julia教程6 Julia类型


在网易云课堂上直接搜索:Julia教程 ,就可以找到,教程的全名是:Julia教程 从入门到进阶

这是国内第一个免费的完整的Julia视频教程,非常适合Julia的入门。有兴趣的朋友可以去学习一下。

教程链接:
Julia教程

欢迎关注微信公众号:Quant_Times

类型

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

类型声明

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

(2+4)::Float64
>> ERROR: ...
(2+4)::Int64
6

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

function f9(x::Int64)::Float64
    x/10
end
f9(10)
>>1.0

抽象类型

抽象类型不能被实例化,我们前面讲到的Int64/Float64等都是抽象类型的具体类型,抽象类型不能直接使用,类似于C++中的抽象类。
抽象类型的定义方法如下:

abstract type «name» end
abstract type «name» <: «supertype» end

第二行中的<:表示新声明的抽象类型是后面类型的子类型。
如果没有给出父类型,则默认为Any。
抽象类型有:

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
Integer <: Number
>>true
Integer <: AbstractFloat
>>false

原始类型

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

primitive type «name» «bits» end
primitive type «name» <: «supertype» «bits» end

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

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

原始类型的应用

function f10(x::Int64)::Int32
    x + 10
end
a = f10(10)

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

function f11(x::Real)::Real
    x + 10
end
b = f11(10)

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

typeof(a)
>>Int32
typeof(b)
>>Int64

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

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

类型转换

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

function foo(a, b)
    x::Int8 = a
    y::Int8 = b
    x + y
end

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

Int8(10)
convert(Int8, 10)

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

Int8(1000)
>>error
Int8("10")
>>error
convert(Int8, "10")
>>error

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

parse(Int8, "10")

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

Base.convert(::Type{Int8}, x::String) = parse(Int8, x)
convert(Int8, "10")

类型提升

先看一个简单的例子

1 + 2.0
>>3.0

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

再来看下面的例子

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类型

+(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代替

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在创建后,内容不可更改。

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

可变复合类型

mutable struct Foo
    x1
    x2::Int
    x3::Float64
end
foo2 = Foo("Hello World", 10, 12.1)
foo2.x2 = 20

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

DataType

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

  • 它们都是显式声明的。
  • 它们都具有名称。
  • 它们都已经显式声明超类型。
  • 它们可以有参数。
typeof(Real)
>>DataType

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

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

Type Unions

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

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. 复合参数类型
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的子类型

Pos{Float64} <: Pos
>>true
Pos{Int64} <: Pos
>>true

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

Pos{Float64} <: Pos{Real}
>>false

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

image

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

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

但我们可以写成这样

function nrom2(p::Pos{<:Real})
    sqrt(p.x^2 + p.y^2)
end
norm(p2)
>>2.23606797749979

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

struct Pos1{T1,T2}
    x1::T1
    x2::T2
end
p1 = Pos1{Int64,Float64}(1,2.2)
p1.x1
>>1
p1.x2
>>2.2
  1. 抽象参数类型

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

abstract type Pointy{T} end 

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

Pointy{Int64} <: Pointy
>>true

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

Pointy{Float64} <: Pointy{Real}
>>false
  1. 原始参数类型

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

primitive type Ptr{T} 64 end
Ptr{Float64} <: Ptr
>>true
  1. 元组类型

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

t1 = (1, 2.8)

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

struct Tuple2{T1,T2}
    x1::T1
    x2::T2
end
t2 = Tuple2{Int64,Float64}(1, 2.8)

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

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

变参元组类型

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

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。

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编写

struct Pos{T}
    x::T
    y::T
end
function f1(p::Pos{T} where T<:Real)
    p
end

当有两个类型参数时

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的上下界

function f23(Pos{T} where Int64<:T<:Real)
    p
end

微信公众号:Quant_Times

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值