そもそもなぜパラメトリックタイプを使うのか?
-
型を後から柔軟に指定できるから。
-
例) 最初にintで固定してしまうと、後から型を変更できなくなる。
-
ある程度柔軟さを持って、型を指定したいというときに使う。
-
逆に、何も指定しないと型がガバガバすぎて全ての組み合わせの型を通してしまう。
-
フィールドの型は具体型にする(型の安定性のため)
struct MyStruct{T <: Int}
x::T
y::Float64
end
この場合、xはIntの具体的なサブタイプ(Int64、Int32など)を持つ必要があり、yはFloat64型でなけれならない。
色々なストラクトで遊んでみる
struct Point{T<:Real}
x::T #x,yっていう型はT。TはRealのサブタイプ
y::T
end
Point{Float32}(0, 0).x |> typeof #型パラメータのTの部分は後から変更できる。
Point(0f0, 0f0).x |> typeof #もしくは引数で合わせる。f0はfloat32の意味。
Point{Bool}(0, 0).x |> typeof #boolにすることも可能である。
パラメトリックにしておくと、構造体のフィールドがどういう型になるのかを事前に決められる。
つまり、juliaのコンパイラーをヒントを教えるということになる。
struct Point5{T1, T2}
x::T1 #x,yを違う型にしたい
y::T2
end
begin
@show Point5(1, -2.0).x |> typeof
@show Point5(1, -2.0).y |> typeof
end
struct Point2
x
y
end
Point2("goma", 1) #違う型のも通す。
型指定をした場合のメリット
型指定をした場合はしない場合に比べて、通常は早くなると言われている。
解析
function d(p::Point, q::Point)
sd = (p.x - q.x) ^ 2 + (p.y - q.y) ^ 2
√(sd)
end
let
p = Point(1, -1)
q = Point(1f0, -1f0)
@show p.x
@show Base.getproperty(p, :x)
@code_warntype d(p, q)
end
function dosome()
s = 0.
q = Point(1, 1)
for _ in 1:100000
x = rand()
y = rand()
p = Point(x, y)
s += d(p, q)
end
s
end
@benchmark dosome()
@code_warntype dosome()
初学者向けの便利ツール
#=
struct Point{Tx, Ty}
x::Tx
y::Ty
function Point(x::Tx, y::Ty) where {Tx, Ty}
return new{Tx, Ty}(x, y)
end
end
=#
# ↑と同じ
@concrete struct Point
x
y
end
let
p = Point(1, -1)
@show typeof(p)
q = Point(false, false)
@show q.y typeof(q)
@show Base.getproperty(p, :x)
end
function goma(p::Point{T, T}, q::Point{T, T}) where T<:Real
p.x + q.x + p.y + q.y
end
メモ
次のように、Juliaで構造体のフィールドや配列などのコンテナ系の要素型を具体型で指定することができます。
struct MyStruct{T}
field::T
end
struct MyArray{T, N}
array::Array{T, N}
end
struct_1 = MyStruct{Int}(10)
struct_2 = MyStruct{Float64}(3.14)
array_1 = MyArray{Int, 1}(1:10)
array_2 = MyArray{Float64, 2}(rand(2, 2))
abstract type MyAbstractType end
struct MyStruct{T <: MyAbstractType}
field::T
end
struct MyArray{T <: MyAbstractType, N}
array::Array{T, N}
end
struct MyConcreteType1 <: MyAbstractType
value::Int
end
struct MyConcreteType2 <: MyAbstractType
value::Float64
end
struct_1 = MyStruct{MyConcreteType1}(MyConcreteType1(10))
struct_2 = MyStruct{MyConcreteType2}(MyConcreteType2(3.14))
array_1 = MyArray{MyConcreteType1, 1}(MyConcreteType1.(1:10))
array_2 = MyArray{MyConcreteType2, 2}(MyConcreteType2.(rand(2, 2)))
using BenchmarkTools
struct Point
x
y
end
function d(p::Point)
p.x ^2 + p.y ^2
end
function dosome()
s = 0.
for _ in 1:10000
x = rand()
y = rand()
p = Point(x, y)
s += d(p)
end
s
end