Julia v0.4 新機能&変更点まとめ
待ちに待ったJuliaの新バージョンv0.4がリリース直前です!
現在はRC1RC2が利用可能ですので、新しいもの好きの方はダウンロードして試すことができます。
Releases · JuliaLang/julia · GitHub
v0.4はかなり長い間開発されてきましたので、新機能が盛りだくさんになっています。 すべての変更点を紹介することはできませんので、完全なリストは本家のリリースノートを参照してください。 注目度の高いものから順に見て行きましょう。
ドキュメント機能とパッケージのコンパイル機能追加!
パッケージを作る人にもユーザにも嬉しい機能追加がこの2つです。 ドキュメント機能は今まで標準ライブラリにしかなかったドキュメントが、パッケージ作成者や他のユーザも追加できるようになりました。 また、パッケージのコンパイル機能では、サードパーティーのパッケージが初回の実行時に自動的にコンパイルされるようになり、パッケージの読み込み時間がかなり短縮されるようになりました。
ドキュメント機能
ドキュメント機能では、ドキュメントをメソッドや型の定義の部分に書くことができます。
例えば、以下のようにPoint
型と、distance
というユークリッド距離を求める関数を書いたとしましょう。
""" Point type. This is a type to represent a point in 3D Cartesian coordinate system. Fields: * `x`: coordinate of X axis. * `y`: coordinate of Y axis. * `z`: coordinate of Z axis. """ type Point x::Float64 y::Float64 z::Float64 end "Enclidean distance from the origin." function distance(p::Point) sqrt(p.x^2 + p.y^2 + p.z^2) end
ドキュメントはMarkdown形式で記述でき、型やメソッドの直前に文字列リテラルとして記述します。
こうしたドキュメントは、JuliaのREPLからもヘルプ機能(?
コマンド)で参照できます。
パッケージのコンパイル機能
パッケージのコンパイル機能では、パッケージの作者がモジュールがコンパイル可能であることを明示的に示すことによって、初回の利用時に自動的にコンパイルされます。 また、パッケージに変更があった場合には、それを検出して次の利用時に自動的にコンパイルしますので、古いコードが利用されるなどの心配はありません。
パッケージの作者は、__precompile__()
関数をモジュール定義の直前に呼び出すだけでこの新機能に対応できます。
ただし、互換性の問題からVERSION >= v"0.4.0-dev+6521" && __precompile__()
と書くことが推奨されています。
私は、バージョン番号を覚えるのが面倒なので、isdefined(Base, :__precompile__) && __precompile__()
と書いています。
実例はこちらsrc/DocOpt.jlを参照してください。
実際に試してみると、DocOpt.jlのような小さなライブラリでも、読み込み時間が半分以下に短縮されています。
~/v/julia (master|…) $ julia3 -e 'tic(); using DocOpt; toc()' elapsed time: 1.093200985 seconds ~/v/julia (master|…) $ julia4 -e 'tic(); using DocOpt; toc()' elapsed time: 0.430147529 seconds
- Module initialization and precompilation: http://julia.readthedocs.org/en/release-0.4/manual/modules/#module-initialization-and-precompilation
言語機能の強化
v0.4には言語機能の強化がいくつか入りましたので、いくつか重要なものをかいつまんで紹介します。
関数呼び出し構文のオーバーロード
call
というメソッドを定義することで、関数呼び出しの構文をオーバーロードすることができます。
これは、x(...)
という構文がcall(x, ...)
というように変換されますので、対応するcall
メソッドを定義するだけで利用できます。
この機能は特にコンストラクタを定義する際に便利で、例えば配列の生成がVector(Int, 10)
ではなくVector{Int}(10)
のように呼べるようになりました。
これは、call{T}(::Type{Vector{T}}, m::Integer) = ...
というメソッドが定義されているためです。
関数の生成機能 (@generated
)
少しわかりにくい機能なのですが、極めて強力な新機能のひとつに生成関数 (generated function) というものがあります。訳語は適当です。 これは、メタプログラミングのひとつで、引数の型の組み合わせから式を生成するというものです。 以下の様な対応表を考えてみると、少し分かりやすいかもしれません。
機能 | ドメイン → コドメイン |
---|---|
関数 | 値 → 値 |
マクロ | 式 → 式 |
生成関数 | 型 → 式 |
生成関数の式への展開は初回の呼び出し時のみ起こり、それ以降は普通の関数のように使えます。
より詳しい仕組みはJuliaCon 2015のJake Bolewski氏のトークを参照してください。
Jake Bolewski: Staged programming in Julia - YouTube
- Generated functions: http://julia.readthedocs.org/en/release-0.4/manual/metaprogramming/#generated-functions
コンストラクタとしてのconvert
メソッド
コンストラクタの呼び出しが、convert
へとフォールバックするようになりました。
ですので、以下の例を見ると分かるのですが、ある型にconvert
を定義することでコンストラクタのように振る舞うことができます。
convert
の方がコンストラクタより多機能ですので、引数がひとつであり、かつある値の型変換という意味を伴う場合にはconvert
を定義するほうが良いかもしれません。
type Foo x Foo(x::Int) = new(x) end Base.convert(::Type{Foo}, x::Integer) = Foo(convert(Int, x)) @show Foo(1) # ok both on v0.3 and v0.4 @show Foo(0x01) # ok on v0.4, but not on v0.3
- Constructors, Call, and Conversion: http://julia.readthedocs.org/en/release-0.4/manual/constructors/#constructors-call-and-conversion
性能・機能向上など
標準ライブラリや処理系の最適化も進められています。 いくつか重要そうなものを挙げておきましょう。
新しい世代別GCの導入
一新されたGCにより、一回のGCの時間が短縮され遅延が緩和されるようになりました。
SubArray
SubArray
の設計が見直され、様々な操作がより効率的になりました。
これは、先ほど紹介した新機能の生成関数によるところでもあります。
より正確なコード行情報
今までかなりいい加減だったスタックトレースなどのコード行の情報がより正確になりました。 デバッグやパフォーマンスチューニングなどで非常に重要な情報ですのでありがたいです。
その他新機能
言語機能には直接関与しませんが、様々な新機能がv0.4で導入されました。 Juliaをより多目的に使えるようになる機能です。
Nullable{T}
型
値が存在しないことを示す型Nullable{T}
が導入されました。
これはHaskellのMaybe
などに対応するもので、計算の失敗を示す返り値として用いられる事が多いです。
例えば、v0.4で導入されたtryparse
は文字列をうまくパースで来たときには中身のあるNullable
を、失敗した時には空のNullable
を返します。
julia> tryparse(Int, "123") Nullable(123) julia> tryparse(Int, "notint") Nullable{Int64}()
日付や時刻の型
DateTime
やDate
など、日付や時刻を表す型が追加され、様々な演算が定義されています。
例えば、現在時刻はnow
関数で取得できます。
julia> now() 2015-09-13T13:41:42 julia> typeof(now()) DateTime
明示的な関数のインライン化
関数のインライン化は以前からありましたが、新しく導入された@inline
マクロを使うことでコンパイラに関数のインライン化を強制できます。
くり返し呼ばれる関数では@inline
を使うことでパフォーマンスが向上することがあります。
使い方は簡単で、関数定義の直前に@inline
とつけるだけです。
@inline function getindex(x::T, i::Integer) ... end
その他変更
構文や名前の変更で重要なものをまとめておきます。
辞書の構文
v0.3では["one" => 1, "two" => 2]
のように辞書を書いていたのですが、この構文が非推奨になりました。
v0.4ではDict("one" => 1, "two" => 2)
のようにコンストラクタ呼び出しを使います。
Union
、Tuple
の構文
v0.3ではUnion(T1, T2)
、Tuple(T1, T2)
のように書いていたのですが、一貫性のためそれぞれUnion{T1, T2}
、Tuple{T1, T2}
という構文に変更されました。
型に関しては一貫して{ }
を使うようです。
型名の変更
Uint
やUint32
などの名前がそれぞれUInt
, UInt32
という名前に変更になりました。
前の名称を使っても特に警告はありませんが、将来的にはすべて後者に置き換えられると思いますので、UInt
やUInt32
などと書きましょう。
また、String
がAbstractString
に変更されました。
int
やfloat
などの関数が非推奨
v0.3では、文字列や型の変換にint
やfloat
関数を使っていたのですが、これが非推奨となりました。
文字列をパースしてInt
型に変換する際にはparse(Int, str)
を、別の数値型からInt
にする際はInt(x)
を利用してください。