サーチ…


構文

  • 不変のMyType。フィールド;フィールド;終わり
  • MyTypeと入力します。フィールド;フィールド;終わり

備考

種類はJuliaのパフォーマンスの鍵です。パフォーマンスの重要なアイデアは、 タイプの安定性です。 タイプの安定性は 、そのタイプの関数が返すタイプが、その引数の値ではなくタイプに依存する場合に発生します。

タイプのディスパッチ

Juliaでは、関数ごとに複数のメソッドを定義できます。同じ関数の3つのメソッドを定義するとします。

foo(x) = 1
foo(x::Number) = 2
foo(x::Int) = 3

使用するメソッド( ディスパッチと呼ばれる)を決定するとき、Juliaは引数の型に一致するより具体的なメソッドを選択します。

julia> foo('one')
1

julia> foo(1.0)
2

julia> foo(1)
3

これは、 多型性を促進する。例えば、 NilCons 2つの不変型を定義して、 リンクリストを簡単に作成できます。これらの名前は、それぞれ、空のリストと空でないリストを記述するために伝統的に使用されます。

abstract LinkedList
immutable Nil <: LinkedList end
immutable Cons <: LinkedList
    first
    rest::LinkedList
end

私たちは空リストをNil()とその他のリストでCons(first, rest)で表します。 firstはリンクリストの最初の要素、 restは残りのすべての要素からなるリンクリストです。例えば、リスト[1, 2, 3]は、

julia> Cons(1, Cons(2, Cons(3, Nil())))
Cons(1,Cons(2,Cons(3,Nil())))

リストは空ですか?

さまざまな異なるコレクションで動作する標準ライブラリのisempty関数を拡張したいとします。

julia> methods(isempty)
# 29 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
...

単に関数のディスパッチ構文を使用し、2つのisemptyメソッドをさらに定義することができます。この関数はBaseモジュールからのものなので、 Base.isemptyとして拡張する必要があります。

Base.isempty(::Nil) = true
Base.isempty(::Cons) = false

ここでは、リストが空であるかどうかを判断するために引数値はまったく必要ありませんでした。その情報を計算するためにタイプだけで十分です。 Juliaは引数の名前を省略することができます。値の使用を必要としない場合は、型名だけを保持します。

私たちのisemptyメソッドが動作することをテストできます:

julia> using Base.Test

julia> @test isempty(Nil())
Test Passed
  Expression: isempty(Nil())

julia> @test !isempty(Cons(1, Cons(2, Cons(3, Nil()))))
Test Passed
  Expression: !(isempty(Cons(1,Cons(2,Cons(3,Nil())))))

実際にisemptyの方法の数は2 isemptyに増えています。

julia> methods(isempty)
# 31 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394

明らかに、リンクされたリストが空であるか否かを決定することは、ほんの一例である。しかし、それはもっと興味深いものにつながります。

リストはどのくらいですか?

標準ライブラリのlength関数は、コレクションの長さや特定のiterableを返します。リンクlengthれたリストのlengthを実装する方法はたくさんあります。特に、 whileループを使用することは、おそらくジュリアでは最も高速でメモリ効率が高くなります。しかし早すぎる最適化は避けなければならないので、リンクリストが効率的である必要はないと考えてみましょう。 length関数を書く最も簡単な方法は何ですか?

Base.length(::Nil) = 0
Base.length(xs::Cons) = 1 + length(xs.rest)

最初の定義は簡単です:空のリストは長さ0です。 2番目の定義は読みやすい:リストの長さを数えるために、最初の要素を数え、残りのリストの長さを数える。このメソッドは、 isemptyテストと同様にテストisemptyます。

julia> @test length(Nil()) == 0
Test Passed
  Expression: length(Nil()) == 0
   Evaluated: 0 == 0

julia> @test length(Cons(1, Cons(2, Cons(3, Nil())))) == 3
Test Passed
  Expression: length(Cons(1,Cons(2,Cons(3,Nil())))) == 3
   Evaluated: 3 == 3

次のステップ

このおもちゃの例は、リンクリストで望まれるすべての機能を実装するのはかなり遠いです。反復インタフェースなど、欠けています。ただし、ディスパッチを使用して短くクリアなコードを書く方法を示しています。

不変の型

最も単純な複合型は不変型です。 タプルのような不変型のインスタンスは値です。それらのフィールドは、作成後に変更することはできません。多くの点で、不変型は、型自体と各フィールドの名前を持つTupleようなものです。

シングルトンタイプ

複合型には、定義上、より単純な型が多数含まれています。ジュリアでは、この数字はゼロにすることができます。つまり、不変型にはフィールドを含めることはできません 。これは、空のタプル()と同等です。

なぜこれが役に立つのでしょうか?このような不変型は、それらのインスタンスが1つしか存在しないため、「シングルトン型」として知られています。そのような型の値は「シングルトン値」として知られています。標準ライブラリBaseは、このようなシングルトンタイプが多数含まれています。ここに簡単なリストがあります:

  • Voidではnothingタイプ。 Void.instance (シングルトンタイプのシングルトン値を取得するための特別な構文)が実際にはnothingことを検証できます。
  • MIME"text/plain"などのメディアタイプは、単一インスタンスのMIME("text/plain")持つシングルトンタイプです。
  • Irrational{:π}Irrational{:e}Irrational{:φ}などのタイプはシングルトンタイプで、シングルトンインスタンスは非合理的な値π = 3.1415926535897...などπ = 3.1415926535897...
  • イテレータサイズの特性Base.HasLengthBase.HasShapeBase.IsInfinite 、およびBase.SizeUnknownはすべてシングルトンタイプです。
0.5.0
  • バージョン0.5以降では、各関数はシングルトン型のシングルトンインスタンスです!他のシングルトン値と同様に、関数sin 、たとえばtypeof(sin).instancetypeof(sin).instanceから復元することができます。

これらは何も含まれていないため、シングルトンタイプは非常に軽量であり、実行時のオーバーヘッドがないようにコンパイラによって最適化されることがよくあります。したがって、それらは形質、特別なタグ値、および専門化したい機能のようなものにとって完璧です。

シングルトンタイプを定義するには、

julia> immutable MySingleton end

シングルトンタイプのカスタム印刷を定義するには、

julia> Base.show(io::IO, ::MySingleton) = print(io, "sing")

シングルトンインスタンスにアクセスするには、

julia> MySingleton.instance
MySingleton()

しばしば、これを定数に代入します:

julia> const sing = MySingleton.instance
MySingleton()

ラッパーの種類

ゼロフィールド不変型が面白く便利な場合は、おそらく1つのフィールド不変型がさらに便利です。そのような型は、基本的なデータをラップし、そのデータへの代替インタフェースを提供するため、一般に「ラッパー型」と呼ばれます。 Baseラッパー型の例はStringです。 MyStringという名前のStringと同様の型を定義します。この型は、バイト( UInt8 )のベクトル(1次元配列 )によってサポートされます。

まず、タイプ定義自体といくつかのカスタマイズされた表示:

immutable MyString <: AbstractString
    data::Vector{UInt8}
end

function Base.show(io::IO, s::MyString)
    print(io, "MyString: ")
    write(io, s.data)
    return
end

今度はMyString型を使用できるようになりました!生のUTF-8データをいくつかフィードして、好きなように表示できます:

julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!

明らかに、この文字列型は、 Base.String型と同じくらい使用可能になる前に、多くの作業を必要とします。

真の複合型

たいていの場合、多くの不変型には複数のフィールドが含まれています。例は標準ライブラリRational{T}型であり、分子のnumフィールドと分母のdenフィールドの2つのfiedがあります。このタイプのデザインをエミュレートするのはかなり簡単です。

immutable MyRational{T}
    num::T
    den::T
    MyRational(n, d) = (g = gcd(n, d); new(n÷g, d÷g))
end
MyRational{T}(n::T, d::T) = MyRational{T}(n, d)

有理数を単純化するコンストラクタを実装しました。

julia> MyRational(10, 6)
MyRational{Int64}(5,3)


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow