コーディング · プログラミング言語
Julia入門:初心者向けチュートリアル
結論
Juliaは、高速な数値計算・科学技術計算のために設計された、高水準の動的型付け言語です。PythonやMATLABのように読めますが、各関数は最初に呼ばれたときにネイティブコードへコンパイルされるため、ループがCに近い速度で動きます。中心となる考え方は多重ディスパッチで、関数は、すべての引数の型に基づいてメソッドを選びます。juliaupでインストールし、julia hello.jlでファイルを実行します。
Juliaとは何か、誰が使うのか
Juliaは、Jeff Bezanson、Stefan Karpinski、Viral Shah、Alan Edelmanが作り、2012年に公開されました。バージョン1.0は2018年に登場し、1.xシリーズは後方互換性を保っています。Juliaは「2言語問題」、つまり遅くて簡単な言語でプロトタイプを作り、その後で処理の重い部分をCやFortranで書き直すという問題の解決を目指しています。
Juliaは、科学者、エンジニア、データアナリストが、シミュレーション、微分方程式、最適化、統計、機械学習、ハイパフォーマンスコンピューティングに使っています。パッケージのエコシステムは、データフレーム、作図、GPU計算、自動微分をカバーしています。
Juliaのインストールとhello, worldの実行
おすすめのインストーラーはjuliaupです。Windows、macOS、LinuxでJuliaのバージョンをインストール・更新します(julialang.org/downloadsを参照)。インストール後は、juliaでREPLが起動し、julia file.jlでスクリプトが実行されます。
# hello.jl
println("Hello, world!")julia hello.jl
# Hello, world!
julia
# julia> 1 + 2
# 3REPLでは、]でパッケージモードに入り(たとえばadd DataFrames)、?で名前のヘルプ、;でシェルを使えます。Backspaceを押すと通常のプロンプトに戻ります。
変数と基本の型
変数の宣言は不要ですが、どの値にも具体的な型があり、typeofで調べられます。必要なら、関数の引数や構造体のフィールドに型注釈を付けられます。
| 型 | 例 | 補足 |
|---|---|---|
| Int64 | 42 | 64ビットシステムでのデフォルトの整数。オーバーフローすると巻き戻ります |
| Float64 | 3.14, 1e-3 | デフォルトの浮動小数点数 |
| Rational | 1//3 | 正確な分数 |
| String | "hello" | UTF-8。"$name"で式を埋め込めます |
| Char | 'a' | Unicodeの1文字 |
| Bool | true, false | 論理値 |
| Symbol | :name | インターンされた名前。キーやオプションによく使います |
| Nothing | nothing | 「値なし」。結果を持たない関数が返します |
name = "Ana"
age = 30
println("$name is $age, next year $(age + 1)")
# Ana is 30, next year 31
typeof(age) # Int64
7 / 2 # 3.5 (always a float)
7 ÷ 2 # 3 (integer division, also div(7, 2))
2^10 # 1024JuliaはπやαのようなUnicodeの名前を受け付けます。REPLやほとんどのエディターでは、\piと入力してTabを押すとπになります。
制御構文
ブロックはキーワードendで閉じます。インデントは読みやすさのためだけにあります。if、for、whileはほとんどの言語と同じように動き、内包表記で配列を1行で作れます。
function sign_name(n)
if n > 0
"positive"
elseif n < 0
"negative"
else
"zero"
end
end
for i in 1:3
println("$i squared is $(i^2)")
end
squares = [x^2 for x in 1:5 if isodd(x)] # [1, 9, 25]
x = 5
result = x > 3 ? "big" : "small" # "big"1:3は両端を含む範囲です。関数やifブロックの最後の式がその値になるので、sign_name(-2)は明示的なreturnなしで"negative"を返します。
関数
add(x, y) = x + y # one-line definition
function greet(name = "world"; punctuation = "!")
return "Hello, $name$punctuation"
end
greet() # "Hello, world!"
greet("Ana", punctuation = ".") # "Hello, Ana."
square = x -> x^2 # anonymous function
map(square, [1, 2, 3]) # [1, 4, 9]
v = [3, 1, 2]
sort!(v) # v is now [1, 2, 3]位置引数はセミコロンの前、キーワード引数はセミコロンの後に書きます。慣例として、sort!やpush!のように名前が!で終わる関数は引数の1つを変更し、sortは新しい配列を返します。
配列・タプル・辞書
| 構造 | リテラル | 補足 |
|---|---|---|
| Vector | [1, 2, 3] | 1始まりで変更可能。push!で伸ばせます |
| Matrix | [1 2; 3 4] | 空白で列を、;で行を区切ります |
| Tuple | (1, "a") | イミュータブルで、長さは固定 |
| Named tuple | (name = "Ana", age = 30) | フィールドには.nameでアクセスします |
| Dict | Dict("a" => 1) | 変更可能なハッシュマップ |
| Set | Set([1, 2, 2]) | 重複のない値 |
v = [10, 20, 30]
v[1] # 10 (first element)
v[end] # 30
push!(v, 40)
A = [1 2; 3 4]
A * A # matrix product: [7 10; 15 22]
A .* A # element-wise: [1 4; 9 16]
v .+ 1 # broadcasting: [11, 21, 31, 41]
sqrt.([1, 4, 9]) # [1.0, 2.0, 3.0]ドット構文はブロードキャストと呼ばれます。f.(x)はfをすべての要素に適用し、.+や.*は要素ごとに計算します。Juliaは、ドット付きの演算の連なりを1つのループにまとめます。
特徴的な機能:多重ディスパッチ
Juliaの関数は、多くのメソッドを持てます。呼び出すと、Juliaはすべての引数の型に対して最も具体的なメソッドを選びます。これはクラスのメソッドに代わるものです。型はデータを保持し、振る舞いは型に対するメソッドを定義して加えます。別のパッケージからでもそれができます。
abstract type Shape end
struct Circle <: Shape
r::Float64
end
struct Rect <: Shape
w::Float64
h::Float64
end
area(c::Circle) = π * c.r^2
area(r::Rect) = r.w * r.h
shapes = [Circle(1.0), Rect(2.0, 3.0)]
println(round.(area.(shapes), digits = 2)) # [3.14, 6.0]Juliaは、見た引数の型に特化した関数のバージョンをコンパイルするため、関数の最初の呼び出しは(コンパイルを含むので)遅く、以降の呼び出しは高速です。作成後にフィールドを変更する必要がある場合は、mutable structを使います。
エラー処理
Juliaは例外を使います。ArgumentErrorやDomainErrorなどの組み込みの例外型を投げるか、Exceptionのサブタイプとなる構造体として独自のものを定義します。tryparseは、例外を投げずにnothingを返します。
function parse_age(s)
n = tryparse(Int, s)
n === nothing && throw(ArgumentError("not a number: $s"))
n < 0 && throw(DomainError(n, "age must not be negative"))
return n
end
try
parse_age("abc")
catch e
if e isa ArgumentError
println("Error: ", e.msg)
else
rethrow()
end
end
# Error: not a number: abc小さな完成プログラム:単語の出現頻度
text = "the cat and the hat and the bat"
counts = Dict{String,Int}()
for w in split(text)
counts[w] = get(counts, w, 0) + 1
end
top = sort(collect(counts), by = last, rev = true)
for (word, n) in first(top, 2)
println(word, " ", n)
end
# the 3
# and 2splitは文字列を空白で分割し、getは新しい単語に対してデフォルトの0を返し、collectは辞書をword => countのペアのベクターに変え、by = lastは回数で並べ替えます。
次に学ぶこと
- docs.julialang.orgの公式Juliaマニュアル。「Getting Started」から始めます。
- julialang.orgの学習ページ。チュートリアル、書籍、講座が並んでいます。
- コードを最適化する前に、マニュアルの「Performance Tips」の章を読む。
- DataFrames.jl、Plots.jl、DifferentialEquations.jlなどのパッケージを見てみる。
よくある質問
JuliaはPythonより速いですか?
ループや数値計算の多いコードでは、ネイティブコードにコンパイルされるため、Juliaは通常、純粋なPythonよりずっと速くなります。NumPyなどのライブラリを使ったPythonは、ベクトル化された演算なら高速になりえるので、差は処理内容によります。
Juliaはなぜ1から数え始めるのですか?
Juliaは、数学やMATLAB、R、Fortranなどの言語の慣例に従っており、配列の最初の要素の添字は1です。
Juliaのコードの初回実行が遅いのはなぜですか?
Juliaは、各関数を最初に呼ばれたときに、その引数の型に合わせてコンパイルします。以降の呼び出しはコンパイル済みのコードを再利用するので、性能はウォームアップの呼び出しの後か、ベンチマーク用のパッケージで計測してください。
Juliaは無料ですか?
はい。JuliaはMITライセンスのオープンソースで、ほぼすべてのパッケージもオープンソースです。