LoginSignup
0
0

【メモ】なぜパラメトリックタイプを使うのか

Posted at

そもそもなぜパラメトリックタイプを使うのか? 

  • 型を後から柔軟に指定できるから。

  • 例) 最初に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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0