りんごがでている

何か役に立つことを書きます

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からもヘルプ機能(?コマンド)で参照できます。

f:id:bicycle1885:20150913124935p:plain

パッケージのコンパイル機能

パッケージのコンパイル機能では、パッケージの作者がモジュールがコンパイル可能であることを明示的に示すことによって、初回の利用時に自動的にコンパイルされます。 また、パッケージに変更があった場合には、それを検出して次の利用時に自動的にコンパイルしますので、古いコードが利用されるなどの心配はありません。

パッケージの作者は、__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

言語機能の強化

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

コンストラクタとしての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

性能・機能向上など

標準ライブラリや処理系の最適化も進められています。 いくつか重要そうなものを挙げておきましょう。

新しい世代別GCの導入

一新されたGCにより、一回のGCの時間が短縮され遅延が緩和されるようになりました。

SubArray

SubArrayの設計が見直され、様々な操作がより効率的になりました。 これは、先ほど紹介した新機能の生成関数によるところでもあります。

より正確なコード行情報

今までかなりいい加減だったスタックトレースなどのコード行の情報がより正確になりました。 デバッグやパフォーマンスチューニングなどで非常に重要な情報ですのでありがたいです。

その他新機能

言語機能には直接関与しませんが、様々な新機能がv0.4で導入されました。 Juliaをより多目的に使えるようになる機能です。

Nullable{T}

値が存在しないことを示す型Nullable{T}が導入されました。 これはHaskellMaybeなどに対応するもので、計算の失敗を示す返り値として用いられる事が多いです。 例えば、v0.4で導入されたtryparseは文字列をうまくパースで来たときには中身のあるNullableを、失敗した時には空のNullableを返します。

julia> tryparse(Int, "123")
Nullable(123)

julia> tryparse(Int, "notint")
Nullable{Int64}()

日付や時刻の型

DateTimeDateなど、日付や時刻を表す型が追加され、様々な演算が定義されています。 例えば、現在時刻は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)のようにコンストラクタ呼び出しを使います。

UnionTupleの構文

v0.3ではUnion(T1, T2)Tuple(T1, T2)のように書いていたのですが、一貫性のためそれぞれUnion{T1, T2}Tuple{T1, T2}という構文に変更されました。 型に関しては一貫して{ }を使うようです。

型名の変更

UintUint32などの名前がそれぞれUInt, UInt32という名前に変更になりました。 前の名称を使っても特に警告はありませんが、将来的にはすべて後者に置き換えられると思いますので、UIntUInt32などと書きましょう。

また、StringAbstractStringに変更されました。

intfloatなどの関数が非推奨

v0.3では、文字列や型の変換にintfloat関数を使っていたのですが、これが非推奨となりました。 文字列をパースしてInt型に変換する際にはparse(Int, str)を、別の数値型からIntにする際はInt(x)を利用してください。