を使うのに、今一度
で使い方を整理する。
- 前回
- 0. まえがき
前回
注意
参照文献はかなり古い(2019年)ため、現在のバージョンでは動作しない関数などが多いとの評判がある。そこでそういった齟齬があった場合は随時コメントする。なお筆者の環境は、
アプリケーション | バージョン |
---|---|
である。
0. まえがき
は動的かつ高性能なプログラミング言語である。これからは
- の強力なツールやデータ構造、パッケージ群を用いる。
- 数値計算、分散処理、高性能計算を行うレシピを扱う。
- データサイエンス用のプログラムを並列処理やメモリ使用最適化で高速化する方法を扱う。
- メタプログラミングや関数型プログラミングについても扱う。
- データベースの使い方やデータ処理を行う方法なども扱う。
0.1 Juliaの特徴
は「科学技術計算を指向した動的な言語」である。すなわち
- 動的言語の特徴である書きやすさ、試行のしやすさを維持しつつ、
- 性的言語と同程度の性能を得ようとする
言語である。
は以下のような特徴を持つ:
- コンパイルされ、最適化された機械語が実行される実行機構 は従来のコンパイル言語とは異なり、関数が実際に良い出されたときにその場でコンパイルする。このため実際に呼び出されたときの情報を用いることができ、静的な型付き言語よりも有利な条件で機械コードを生成可能である。
- 配列の配列とは異なる多次元配列 は多次元配列をサポートしている。他言語とは異なり、には多次元配列しかない。 他方ではなどとは異なり、インデックスがから始まるオリジンである。 また行列はメモリ上、列方向優先で格納されている。 更にでのリストと同じような内包記法で配列を作成できる。 にはブロードキャストという機能があり、配列の全要素に任意の関数を適用し、結果を再度配列として収集できる。 大規模な配列をコピーしないでメモリを節約しつつ扱えるという機能がある。
- 柔軟な関数の利用 関数はキーワードを用いて定義する(使わなくても定義は可能)。文は不要である。またの文に相当する無名関数も定義できる。 更に取得した資源を確実に解放させるために、ブロック部分を関数として関数名を呼び出す機能がある。 またパイプ演算子(引数 |> 関数名)により複数の処理を連続して行う際に見通しをよくすることができる。
- 構造体と総称関数によるオブジェクト指向プログラミング には++やにあるクラスが無く、構造体と総称関数によりオブジェクト指向的なプログラミングを実現している。 更に、1つの関数名に対して実体がいくつも定義され、引数の型に応じて実行される実体が選択される総称関数が実装されている。 こうして構造体と総称関数を用いることでオブジェクト指向を実現している。
- 共用型 「複数の型のいずれか」を意味する共用型がに用意されている。
0.2. 配列
0.2.1 多次元配列
は多次元配列しかない。
# 配列の例 a = [1, 2] println(a)
0.2.2 1オリジン
のインデックスはから始まる。
######################## ### Juliaは1オリジン ### ######################## a = [1,2,3] try println(a[0]) catch println("インデックスは1から始まります。0はダメです") end
0.2.3 カラムメジャー
では、行列を表現する際の1次元目は列である。
m = [1 2; 3 4] println(m) println("$(m[1]),$(m[2]),$(m[3]),$(m[4])")
0.2.4 配列の内包表記
基本的にはのリストの内包表記と同じ方法で1次元配列を内包表記できる。
list1 = [num for num in collect(1:1:100) if num % 2 == 0] println(list1) # 前置ifはelseを指定するときにあると良さそう: Falseだと自動でnothingを返す list2 = [(if num % 2 ==0 num end) for num in collect(1:1:100)] println(list2)
0.3 関数とその使い方
では関数をキーワードを用いて定義する。
################## ### 関数の定義 ### ################## # 関数の最後の文の値が暗黙の返り値になる。途中で抜ける場合以外はreturn文は不要 function add1(x, y) x + y end println(add1(2,3)) # 簡便型 add2(x, y) = x + y println(add2(2,3)) # 無名関数 (x, y) -> x + y list = map(x -> 2x, 1:3) # 無名関数は他の関数の引数として用いるのが普通 println(list)
0.3.1 doブロック構文
map(x->begin if x < 0 && iseven(x) return 0 elseif x == 0 return 1 else return x end end, [A, B, C]) # doを用いると以下のようにより簡潔に書ける map([A, B, C]) do x if x < 0 && iseven(x) return 0 elseif x == 0 return 1 else return x end end
0.3.2 パイプ演算子
パイプ演算子 |> は、関数と引数の順番を入れ替える。すなわち
- 引数 |> 関数
- 関数(引数)
は等価である。これは複数の処理を連続して行う際に見通しを良くする。
#################### ### パイプ演算子 ### #################### # パイプ演算子の方が見通しが良くなる result1 = unique(sort([1,4,2,3,2])) result2 = [1,4,2,3,2] |> sort |> unique println(result1 == result2) println(result1) println(result2)
0.4 Juliaによる「オブジェクト指向」プログラミング
では構造体と総称関数によってオブジェクト指向的なプログラミングを実現する。
0.4.1 構造体
の構造体はのものとほぼ同じである。
# デフォルトではimmutable struct Coordinate1 x::Float64 y::Float64 end # mutableキーワードを付けるとmutableになる mutable struct Coordinate2 x::Float64 y::Float64 end
0.4.2 総称関数と多重ディスパッチ
総称関数とは、1つの関数名に対して実体がいくつも定義されている関数で、引数の型に応じて実行される実体が選択される関数である。
#################### ### 総称関数の例 ### #################### # メソッド(個々の型に対する関数定義)を定義する double(a::Number) = a * 2 double(a::String) = a ^ 2 println(methods(double)) println(double(2)) println(double("aa")) ############################ ### 構造体と総称関数の例 ### ############################ mutable struct Vector2D x::Float64 y::Float64 end function add(v1::Vector2D, v2::Vector2D) Vector2D(v1.x + v2.x, v1.y + v2.y) end v1 = Vector2D(2, 4) v2 = Vector2D(3, 5) println(add(v1, v2))
0.5 パラメータ化型
0.5.1 パラメータ化形と型パラメータ
では、型を定義する際にその一部を型パラメータとして残しておき、実際に利用する際に具体的な型を指定することができる。
########################## ### 型パラメータの指定 ### ########################## # 型をReal型にする mutable struct Vectors2D{T <:Real} x::T y::T end Vectors2D{Int32}(0,1)
0.5.2 共変と不変
ある型の間にサブタイプの関係がある場合に、これらを用いて作成した何らかの型,にでき得るサブタイプ関係を変位という。
言語要素 | 変位 |
---|---|
タプル | 共変 |
配列 | 非変 |
名前付きタプル | 非変 |
構造体 | 非変 |
############ ### 変位 ### ############ abstract type A end struct B <: A end # BがAのサブタイプであることを B<: Aと書く struct C{T} a::T end # 変位の確認 true_or_false1_1 = (B(),) isa Tuple{A} # タプルか? true_or_false2_1 = [B()] isa Vector{A} # 配列か? true_or_false3_1 = (x = B(),) isa NamedTuple{(:x,),Tuple{A}} # 名前付きタプルか? true_or_false4_1 = C(B()) isa C{A} # 構造体か? println(true_or_false1_1) println(true_or_false2_1) println(true_or_false3_1) println(true_or_false4_1) println("") println("*****************") println("") # <:を用いると共変にできる true_or_false1_2 = (B(),) isa Tuple{A} # タプルか? true_or_false2_2 = [B()] isa Vector{<:A} # 配列か? true_or_false3_2 = (x = B(),) isa NamedTuple{(:x,),Tuple{T}} where T <: A # 名前付きタプルか? true_or_false4_2 = C(B()) isa C{<:A} # 構造体か? println(true_or_false1_2) println(true_or_false2_2) println(true_or_false3_2) println(true_or_false4_2)
0.6 共用型
複数の型のいずれかを意味する共用型()という抽象的な抽象型が存在する。
# 共用型の例 IntOrString = Unoon{Int, String}
0.7 マクロと文字列マクロ
0.7.1 マクロ
################## ### マクロの例 ### ################## import Dates macro mytime(expr) esc(quote now = Dates.now() tmp = $expr println(Dates.now() - now) tmp end ) end @mytime(sum(rand(1000000)))
0.7.2 文字列マクロ
文字列マクロは_(は適当な名称)の形をした名前を持つ特殊なマクロである。
0.8 その他の言語機能
0.8.1 タプル
タプルは任意の型の値を複数保持できる。複数の値を返す関数の返り値を格納するのにも用いられる。
typeof((1, "test")) a, b = f()
配列をタプルに変換するには関数を用いる。また関数を用いることもできる。第1引数にインデックス番号から値を作る関数を、第2引数にタプルの要素数を取る。
ntuple(i -> j, 10)
0.8.2 Symbol
シンボルはのように文字列を表す。こちらの方がメモリ効率が良く、高速である。
0.8.4 ショートカット構文
- 条件||実行文:条件が偽のときのみ実行文が実行される
- 条件 &&実行文:条件が真のときのみ実行文が実行される
0.8.5 文字列補間
$を使って文字列に値を埋め込むことができる。これを文字列補間()という。
x = 1.0 println("x = $x") println("3x = $(3 * x)") # 細かく数値の指定をしたければ、printf,sprintfマクロを用いる using Printf @printf("%0.2f", π)
0.8.6 レンジ
には連続した数字の列を作成する機能が用意されている。開始点と終了点を:で区切る*1。
for i in 1:10 println("i = $(i)") end for i in collect(1:2:10) # collect(引数1,引数2,引数3):引数1から引数2おきに引数3までの配列 println("i = $(i)") end
0.8.7 nothingとmissing
には他言語のやに相当する値として、との2つが用意されている。
いずれも値がない事を意味するが、は値が無いことが異常である場合に、は値が無いことが正常である場合に用いる。このために対して演算を施すとエラーを起こす一方で、に対して演算を施すとが伝搬していくのみである。
######################## ### nothingとmissing ### ######################## err_val1 = nothing err_val2 = missing try println(err_val1 + 1) catch println("$(err_val1)に演算を施すとエラーになります。") end println("$(err_val2)に演算を施すと$(err_val2 + 1)を返します(エラーを起こしません)。")
0.8.8 数式の表現
はプログラムの字面を可能な限り数式に近づけられるように設計されており、任意のをプログラム中から利用できるようになっている。たとえばは\piと入力した後に[Tab]キーを押すことで変換される。
更に演算子にも様々な文字を利用できる。たとえば整数除算は÷、排他的論理和を用いる。
またでは数値リテラルと変数の掛け算の差異に掛け算記号を省略できる。たとえばではなくてと書けばよい。
0.8.9 イテレータ
にもイテレータが存在する。
ユーザーが定義した型でイテレータを定義するには関数を定義する。
Base.iterate(iter [, state]) -> Union{Nothing, Tuple{Any, Any}}
### サンプル x = [5,2,3,1,2,3,1,1] sumx = 0 #iterate関数により最初の要素と次のindexを取得 next = iterate(x) println(next) #iteratorの要素がなくなるまで繰り返す while next !== nothing #現在の要素をi, 次のindexをstateに格納 (i, state) = next sumx += i #次の計算に使用する要素と次の次のindexをnextに格納 next = iterate(x, state) end @show(sumx)
*1:と似た方法である。]