【超入門】Julia言語の基本文法
本記事では、Julia初心者の方向けに基本文法を説明します。
本記事を読み終えれば基本的な文法は理解することができます!!
もしも間違いがありましたら、本記事のコメント欄または、私のTwtter(努力のガリレオ)にDMしていただけると幸いです。
Julia言語のメリット
Julia言語のメリットは主に以下の三つです。
- Pythonのように書けてCのように速い
- 並列計算もできる
- 数式との親和性の高いコーディングが可能
- 企業からのニーズも高い
最近は、Julia言語を使用しているユーザーも増えてきて、今後ホットなプログラミング言語になると考えられています。
Juliaのインストール
Juliaのインストールに関しては、下記を参考に行ってください。
皆さんにとって、おそらく馴染みのある、Jupyter NotebookまたはJupyter Labで、Juliaを動作させるところまで解説しています。
『Jupyter Notebook』を知らない方のために『Jupyter Notebook』を簡単に説明しておきます。
『Jupyter Notebook』とは、コードを小さいモジュール単位で実行することができたり、文章をMarkdownで記入することができる統合開発環境です。
コードの説明を綺麗に記入したりできるので、とても便利です!!
Juliaの基本文法(超入門)
Juliaの基本的な使い方を簡単にまとめていきます。
具体的には、以下の手順で解説していきます。
- 基本操作
- データ構造
- 制御構文
- 関数定義
より詳細な機能については、Juliaの公式ドキュメントを参考にしてください。
基本操作
まずは、これ以降のデータ構造や制御構文を学ぶために、必要不可欠な基本操作を説明していきます。
具体的には、以下の項目を説明します。
- 出力 : print関数, println関数
- 基本演算
- 文字列
- 型変換
出力 : print関数, println関数
出力を行う際は、『print関数』を使用します。
改行つきの出力を行う際は、『println関数』を使用します。
以下に具体例を表示します。
# 出力
print(123)
print(456)
<出力>
123456
改行出力の具体例も紹介します。
# 改行出力
println(123)
println(456)
<出力>
123
456
以降の説明では、出力結果を見やすくするために、『println関数』を基本的に使用します。
型の確認
Juliaでは、明示的に記述する必要はないですが、全ての値に型があり『typeof関数』を使用して型を確認できます。
typeof(1)
<出力>
Int64
基本演算
Juliaの基本演算は以下のように書きます。
# 加算
println(4 + 3)
# 減算
println(4 - 3)
# 乗算
println(4 * 3)
# 除算
println(4 / 3)
# 除算の余り
println(4 % 3)
# べき乗
println(4 ^ 3)
更新演算子は以下のように定義されています。
# 更新演算子
x = 1
# 加算
println(x += 1)
# 減算
println(x -= 1)
# 乗算
println(x *= 2)
# 除算
println(x /= 2)
論理式は、以下のように使用します。
# 論理式の使い方
# 具体例
a = 2; b = 3
# 不等式
println(a < b)
println(a > b)
# 等式
println(a == b)
# 等式否定
println(a != b)
<出力>
true
false
false
true
文字列
Juliaでは、ダブルクォテーションを使用して文字列を使用します。
*Pythonの場合、シングルクォテーションも使用できますが、Juliaではダブルクォテーションのみ使用できます。
具体的には、以下のように文字列を使用します。
"Hello World!"
<出力>
"Hello World!"
文字列に関する処理を簡単に説明します。
具体例として、文字列を以下のように定義しましょう。
s = "Hello World!"
s_1 = "Hello"
s_2 = "World!"
まずは、文字列のインデキシングについて具体例を使用して説明します。
# 文字列のインデキシング
# 2番目の文字を取り出す
println(s[2])
# 最後の文字を取り出す
println(s[end])
# 2番目から5番目の文字列を取り出す
println(s[2:5])
<出力>
e
!
ello
次に文字列の結合は、以下のように行います。
# 文字列の結合
println(s_1 * s_2)
<出力>
Hello World!
次に文字列に関する関数を紹介します。
# 文字列に関する関数
# length : 文字列の長さを出力
println(length(s))
# split : 文字列の分解, e.g. スペースで分解
println(split(s, " "))
# join : 文字列の連結, e.g. スペースで結合
println(join([s_1, s_2], " "))
# lowercase : 全てを小文字に変換
println(lowercase(s))
# uppercase : 全てを大文字に変換
println(uppercase(s))
<出力>
12
SubString{String}["Hello", "World!"]
Hello World!
hello world!
HELLO WORLD!
また、$記号の後に変数名・コードを記述することで文字列の補完を行うことができます。
# 文字列の補完
println("$s_1 - $s_2")
println("コードの出力の埋め込み : $(2 * 2)")
<出力>
Hello - World!
コードの出力の埋め込み : 4
型変換
型変換を行う際は、『parse関数』を使用します。
また、文字列に変換する場合は、『string関数』が便利です。
下記に具体例を示します。
# 型変換具体例
num1 = "123"
# Int64型に変換
num2 = parse(Int64, num1)
# Float64型に変換
num3 = parse(Float64, num1)
# 文字列に変換
num4 = string(num2)
println(typeof(num1))
println(typeof(num2))
println(typeof(num3))
println(typeof(num4))
<出力>
String
Int64
Float64
String
第一引数に変換したい型、第二引数に変換するオブジェクトを入力してください。
データ構造
次に基本的なデータ構造を紹介していきます。
具体的には、以下を紹介します。
- タプル
- リスト
- 辞書
- 多次元配列
タプル
タブルは、いくつかの値をまとめて扱いたいときに便利なデータ構造です。
Juliaでは、タプルを以下のように定義することができます。
a = (1, 2, 3)
括弧を省略して以下ように定義しても良いです。
a = 1, 2, 3
タプルは、要素を後から更新できない(イミュータブル)なので注意してください。
具体例を以下に示します。
a[1] = 4
<出力>
MethodError: no method matching setindex!(::Tuple{Int64, Int64, Int64}, ::Int64, ::Int64)
このように値を代入しようとすると、エラーが表示されます。
リスト
リストは、値の追加や削除ができるデータ構造です、
Juliaでは、一次元の配列型(Array型, ベクトル)と呼ばれる型をリストとして使用します。
具体的には以下のように定義します。
a = [1, 2, 3]
タプルと異なり要素を後から変更することができます。
a[1] = 4
println(a)
<出力>
[4, 2, 3]
リストに関する操作をまとめておきます。
# push! : 末尾に要素を追加, 例. 末尾に4を追加
push!(a, 4)
# pushfirst! : 先頭に要素を追加, 例. 先頭に0を追加
pushfirst!(a, 0)
# insert! : 指定した要素に値を挿入, 例. 1番目に-1を追加
insert!(a, 1, -1)
# deleteat! : 指定した要素の値を削除, 例. 1番目の要素を削除
deleteat!(a, 1)
# append! : リストの結合, 例. [5, 6, 7]を結合
append!(a, [5, 6, 7])
# pop! : 末尾の要素を取得
println(pop!(a))
# maximum : 最大値を取得
println(maximum(a))
# minimum : 最小値を取得
println(minimum(a))
<出力>
7
6
0
辞書型
辞書型は、『キー』と『値』のペアを格納するデータ構造です。
Juliaでは、辞書型を以下のように定義します。
# 辞書型の定義
dic = Dict("a" => 1, "b" => 2, "c" => 3)
『=>』の左側が『キー』に対応して、右側が『値』に対応します。
*キーの重複は許されないので注意してください。
辞書に関する操作をまとめておきます。
# 要素の取得, 例. Keyのaに対応するValueを取得
println(dic["a"])
# 要素の追加, 例. key: d, value: 4を新たに追加
dic["d"] = 4
println(dic)
# pop! : 要素の削除, 例. key: d, value: 4を削除
pop!(dic, "d")
println(dic)
# merge : 辞書を結合, 例. key: d, e, value: 4, 5の辞書を結合
println(merge(dic, Dict("d" => 4, "e" => 5)))
# haskey : keyの探索, 例. key: aの探索
print(haskey(dic, "a"))
<出力>
1
Dict("c" => 3,"b" => 2,"a" => 1,"d" => 4)
Dict("c" => 3,"b" => 2,"a" => 1)
Dict("c" => 3,"e" => 5,"b" => 2,"a" => 1,"d" => 4)
true
hashkeyの出力は該当するキーが存在すれば、『true』を返します。
多次元配列
Juliaには、多次元配列を扱う『Array{T, N}型』が標準で用意されています。
『T』は、配列の要素の型を表して、『N』は、次元数を表します。
一次元配列に関しては、リスト同様に以下のように定義します。
# 一次元配列の定義, *列ベクトル
println([1, 2, 3])
# 一次元配列の定義, *横ベクトル
println([1 2 3])
# 型指定を行う場合
println(Float64[1, 2, 3])
# 連番を使用する場合, 例. 1~3のベクトル
println(Vector(1:3))
<出力>
[1, 2, 3]
[1.0, 2.0, 3.0]
[1, 2, 3]
カンマで区切った場合は縦ベクトル、半角スペースで区切った場合は横ベクトル(1×Nの行列)となります。
2次元配列は以下のように『; 』を使用して定義します。
# 二次元配列の定義
println([1 2; 3 4])
# 改行を使用してもOK
println([1 2
3 4])
# 型指定
println(Float64[1 2; 3 4])
<出力>
[1 2; 3 4]
[1 2; 3 4]
[1.0 2.0; 3.0 4.0]
二次元配列の場合は、新しい行を作成する際に『,』ではなく、『;』または改行を使用することに注意しましょう。
次に様々な配列の初期化方法を紹介します。
# 値が初期化されていない配列
println(Array{Float64}(undef, 2, 2))
# zeros : 要素が全て0の配列
println(zeros(Float64, 2, 2))
# ones : 要素が全て1の配列
println(ones(Float64, 2, 2))
# fill : 要素が全て指定した値の配列, 例. 要素が全て2
println(fill(2, 2, 2))
# rand : 各要素が一様分布従う配列
println(rand(2, 2))
# randn : 各要素が標準正規分布に従う配列
println(randn(2, 2))
<出力>
[2.300813397e-314 2.225639657e-314; 2.2256393723e-314 2.5411678674e-314]
[0.0 0.0; 0.0 0.0]
[1.0 1.0; 1.0 1.0]
[2 2; 2 2]
[0.45915383796131515 0.9845638621756019; 0.955089260307614 0.5031212782780925]
ここからは、配列操作に関して説明していきます。
インデキシング
まずは、一次元配列のインデキシングを具体例を使って紹介します。
# 一次元配列のインデキシング
# 具体例
vec = Vector(1:5)
println("具体例 : $(vec)")
# 2番目の要素を取り出す
println(vec[2])
# 最初の要素を取り出す
println(vec[begin])
# 最後の要素を取り出す
println(vec[end])
# スライス, 例. 2~4番目の要素を取り出す
println(vec[2:4])
# 2ステップごとに取り出す
println(vec[1:2:end])
# 指定したインデックスの要素を取り出す, 例. インデックス1, 4を取り出す
println(vec[[1, 4]])
# 条件に合う要素を取り出す, 例 3より大きい要素
println(vec[vec .> 3])
<出力>
具体例 : [1, 2, 3, 4, 5]
2
1
5
[2, 3, 4]
[1, 3, 5]
[1, 4]
[4, 5]
Juliaの場合ドット『. 』のついた演算子(eg. .>)は要素ごとの計算を行うことを意味しています。
そのため、条件に合う要素を取り出す際は、ドット『.』付きの演算子を使用します。
次に二次元配列のインデキシングを紹介します。
# 二次元配列のインデキシング
# 具体例
mat = [1 2 3; 4 5 6]
println("具体例 : $(mat)")
# 2行1列目を取り出す
println(mat[2, 1])
# beginを使用
println(mat[begin, begin])
# endを使用
println(mat[end, end])
# 列を取り出す, 例. 2列目
println(mat[:, 2])
# 行を取り出す, 例. 2行目
println(mat[2, :])
# 指定したインデックの要素取り出す, 例. (2, 1), (2, 2)
println(mat[[CartesianIndex(2, 1), CartesianIndex(2, 2)]])
# 一つの行を二次元配列で取り出したいとき, 例. 二行目
println(mat[2:2, :])
# 条件に合う要素を取り出す, 例 2より大きい要素
println(mat[mat .> 2])
<出力>
具体例 : [1 2 3; 4 5 6]
4
1
6
[2, 5]
[4, 5, 6]
[4, 5]
[4 5 6]
[4, 5, 3, 6]
reshape関数の使い方
reshape関数は、配列を指定した形に変換する関数です。
reshape関数を作用したあとの配列は、reshape型と呼ばれる型になり、要素を取り出して代入することができません。
そのため、reshape関数を作用した後は、Array型に直してから要素の変更を行う必要があります。
reshape関数の具体的な使い方を以下に示します。
# reshapeの使い方
# 具体例
mat = [1 2 3; 4 5 6]
println("具体例 : $(mat)")
# reshape : 配列を変換
# 3×2の配列に変換
println(reshape(mat, 3, 2))
# ベクトルを3×3行列に変換
println(reshape(Vector(1:9), 3, 3))
# reshape後の代入
mat2 = reshape(mat, 3, 2)
# Array型に変換してから代入
mat2 = Array(mat2)
mat2[1, 1] = 100
println("代入後 : $mat2")
<出力>
具体例 : [1 2 3; 4 5 6]
[1 5; 4 3; 2 6]
[1 4 7; 2 5 8; 3 6 9]
代入後 : [100 5; 4 3; 2 6]
配列の形状取得
配列の形状を取得するためによく使用する関数を以下にまとめました。
# 配列の形状取得
# 具体例
mat = [1 2 3; 4 5 6]
println("具体例 : $(mat)")
# ndims : 配列の次元
println(ndims(mat))
# size : 配列の形状
println(size(mat))
# length : 配列の要素数を取得
println(length(mat))
# eltype : 要素の型を取得
println(eltype(mat))
# typeof : 配列の型を取得
println(typeof(mat))
<出力>
具体例 : [1 2 3; 4 5 6]
2
(2, 3)
6
Int64
Matrix{Int64}
配列に関する関数
配列に関する関数の中で、よく使用するものを以下にまとめました。
# 配列に関する関数
# 具体例
mat = [1 2 3; 4 5 6]
println("具体例 : $(mat)")
# copy : 配列のコピー, 参照渡しを避ける
mat2 = copy(mat)
mat2[1, 1] = 100
println("代入後 : mat2 = $(mat2), mat1 = $(mat)")
# deepcopy : 配列の配列が入るような配列をコピーするときに使用する
mat3 = deepcopy(mat)
println(mat3)
# transpose or ' : 転置
println(transpose(mat))
println(mat')
<出力>
具体例 : [1 2 3; 4 5 6]
代入後 : mat2 = [100 2 3; 4 5 6], mat1 = [1 2 3; 4 5 6]
[1 2 3; 4 5 6]
[1 4; 2 5; 3 6]
[1 4; 2 5; 3 6]
制御構文
次に、if文, for文, while文による制御構文の書き方を紹介します。
- If文
- For文
- While文
- データ構造のループ
- 少し高度なループ
- リスト内包表記
If文
Juliaでは、if, elseif, elseを使用して以下のようにIf文を記述します。
a = 0
if a < 1
println("a < 1")
elseif a > 10
println("a > 10")
else
println("else")
end
<出力>
a < 1
Juliaの場合、If分のブロックを『end』を使って閉じます。
Pythonと異なりインデントは必要不可欠ではないです。
しかし、可読性を上げるためにもインデントを使用することをおすすめします!
三項演算子
少し高度な分岐処理の書き方として、三項演算子というものが存在します。
具体的には、『a ? b : c』という形(3項演算子)でIf文を書くことができます。
『a ? b : c』の意味は、『aがTrueならb, Falseならcを実行』という意味です。
具体例を以下に示します。
a = 0
# 3項演算子
a<1 ? println("a<1") : println("else")
<出力>
a<1
For文
Juliaでは、For文を以下のように記述します。
a = 0
for i in (0:5)
println("i=", i)
end
<出力>
i=0
i=1
i=2
i=3
i=4
i=5
ここで、『(0:5)』というのは、Range型です(丸括弧を省略してもOKです)
『(start:step:stop)』のstep部分を変更することで間隔を調節することができます。
また、stepを負の値にすれば逆順になります。
continue文は以下のように記述します。
# continue
for i in (1:5)
# 3のときだけ次のループにスキップ
if i==3
continue
end
println("i=", i)
end
<output>
i=1
i=2
i=4
i=5
While文
While文は以下のように記述します。
i = 0
# 条件を満たしている間ループ
while i <= 5
println("i=",i)
i += 1
end
<出力>
i=0
i=1
i=2
i=3
i=4
i=5
Break文は以下のように書くことができます。
# break文
i = 0
while true
println("i=", i)
if i >= 5
break
end
i += 1
end
<出力>
i=0
i=1
i=2
i=3
i=4
i=5
データ構造のループ
まずは、リスト使って以下のようにFor文を記述することができます。
lis = [1, 2, 3]
for i in lis
println("i=", i)
end
<出力>
i=1
i=2
i=3
辞書型に対してFor文を使用する場合、以下のようにキーと値を同時に取り出すことができます。
dic = Dict(1 => "one", 2 => "two", 3 => "three")
for (i,j) in dic
println("key= $i, value= $j")
end
<出力>
key= 2, value= two
key= 3, value= three
key= 1, value= one
少し高度なループ
利用できると便利な構文を紹介していきます。
- enumerate関数
- zip関数
① enumerate関数 : インデックスも同時に取り出す
リストの要素のみならず、インデックスを同時に取得する場合は、『enumerate関数』を使用します。
strs = ["one", "two", "three"]
# enumerate : ループのindexも取得(1から始まる)
for (idx, i) in enumerate(b)
println("enumerateの具体例 : (index = $idx, $i)")
end
<出力>
enumerateの具体例 : (index = 1, one)
enumerateの具体例 : (index = 2, two)
enumerateの具体例 : (index = 3, three)
② zip関数 : 同時にループを回す
二つのリストを同時に取り出す場合、『zip関数』を使用します。
nums = [1, 2, 3]
strs = ["one", "two", "three"]
# zip : 同時にループを回す
for (i, j) in zip(nums, strs)
println("zipの具体例 : ($i, $j)")
end
<出力>
zipの具体例 : (1, one)
zipの具体例 : (2, two)
zipの具体例 : (3, three)
リスト内包表記
Juliaのリスト内包表記は以下のように記述します。
println([i for i in (1:5)])
# 条件を加える場合は末尾にif~を追加
println([i for i in (1:5) if i%2 == 0])
# 辞書型を作成する場合はzip関数を使用する
println(Dict(i for i in zip(["one", "two", "three"], [1, 2, 3])))
<出力>
[1, 2, 3, 4, 5]
[2, 4]
Dict("two" => 2, "one" => 1, "three" => 3)
リスト内包型は、利用できるようになると便利なので習得しておきましょう。
関数定義
次に関数を定義する方法を紹介します。
- 基本的な関数定義
- 1行で関数を定義する方法
- 匿名関数
- 可変長引数
基本的な関数定義
最も基本的な関数の定義方法は、functionで始まり、endで終わるブロック内に記述します。
具体例を以下に示します。
function times1(a, b)
return a * b
end
println(times1(2, 2))
# 入力の型を指定する場合
function times2(a::Int, b::Int)
return a * b
end
関数の戻り値を指定するreturnを省略した場合、関数の定義内の最終行の評価結果が返り値となります。
また、入力の型を制約する場合は、『:: 』を使用します。
異なる型が入力された場合は、エラーが生じます(Pythonの型指定とは異なる点です!)
times2(1, 1.1)
<エラー>
MethodError: no method matching times2(::Int64, ::Float64)
Closest candidates are:
times2(::Int64, ::Int64) at In[20]:6
デフォルトの引数は、以下のように設定することができます。
function times(a, b=100)
return a * b
end
# 指定しない場合はデフォルト値が使われる
println(times(2))
# 指定した場合は指定した値が使われる
println(times(2, 5))
<出力>
200
10
1行で関数を定義する方法
Juliaでは、以下のように1行で関数を定義することができます。
# 1行で記述する方法
times(a, b) = a * b
println(times(2,2))
# 複数の戻り値を使用する場合
times_and_add(a, b) = (a * b, a + b)
println(times_and_add(2,2))
<出力>
4
(4, 4)
関数の定義が、2行以上になる複雑な関数の場合は、前述の『function〜end』の形を使用してください。
関数のブロードキャスト
配列の各要素に関数を適用する方法を説明します。
まずは、通常の関数を定義します。
function double_value(x)
return x*2
end
そして、ドット『.』をつけて関数を適用するだけで、配列の各要素に関数を適用できます。
# .をつけてブロードキャスト
double_value.([1, 2, 3])
<出力>
3-element Vectore{Int64}:
2
4
6
匿名関数
Juliaでは、匿名関数を以下のように定義します。
# 匿名関数
squre = x -> x^2
println(squre(2))
# 複数行のとき
squre_plus_one = x -> begin
y = x * x
y + 1
end
println(squre_plus_one(2))
<出力>
4
5
複雑な場合は、『begin〜end』ブロックを使用して記述します。
匿名関数は、主に関数自体を他の関数の引数として与えたい場合に使用します。
例えば、『map関数』と呼ばれる関数を用いることで配列の各要素に引数として与えた関数を適用することができます。
map(x -> x^2, [1, 2, 3])
<出力>
3-element Vector{Int64}:
1
4
9
可変長引数
Juliaでは、『… 』を使用することで可変長引数を設定することができます。
# ...に好きなだけ引数を入れられる
function product(x ...)
a = 1
for i in (1:length(x))
a *= x[i]
end
return a
end
println("2 × 3 × 4 = $(product(2, 3, 4))")
<出力>
2 × 3 × 4 = 24
多重ディスパッチ
Juliaでは、多重ディスパッチという機能がサポートされています。
多重ディスパッチは、関数に複数のメソッドを持たせて、引数の型に応じて実行させるメソッドを変えることができる機能です。
具体例を以下に示します。
function example(x :: Int64)
return "Int64"
end
function example(x :: Float64)
return "Float64"
end
function example(x :: String)
return "String"
end
この例は、exampleという関数に三つのメソッドを付加したものです。
動作を確認してみます。
println(example(2))
println(example(0.2))
println(example("hello"))
<output>
Int64
Float64
String
このように引数の型に応じて、返す値を変えることができます。
REPLについて
REPLは、JuliaをJupyterではなく、ターミナルから使用するアプリケーションです。
簡単な使用方法を紹介しておきます。
REPLの終了
Ctrl + Dまたは、『exit()』と入力することで、REPLを終了することができます。
REPLのモード
主に、REPLは4つのモードがあります。
各モードとモードを変更するためのキーを紹介します。
モード | キー |
---|---|
Juliaモード | Del キー |
パッケージ管理モード : パッケージを管理 | ] キー |
ヘルプモード : 関数を入力するとヘルプを表示 | ? キー |
シェルモード : シェルを使用できる | ; キー |
Juliaを実装する際のTips
最後にJuliaを実装する際のTipsを紹介します。
出力を表示しない
セミコロンを末尾につけることで出力表示を回避することができます。
具体例を以下に示します。
a = 1;
b = 1;
これを実行しても、出力は表示されません。
LaTeX記法の使用
Juliaでは、LaTex記法を使用してギリシャ文字等を使用することができます。
具体的には、バックスラッシュ『\』の次に数式記号名を入力して、Tabキーを押すと数式記号が表示されます。
例えば、アルファを入力する場合は、『\alpha』と入力して、Tabキーを押すと、『α』に変換できます。
使用できる数式一覧は下記を参考にしてください。
参考資料
本記事を作成するために使用した参考資料を紹介します。
参考文献
参考文献を紹介します。
1から始める Juliaプログラミング
とても良い本です。
Julia愛が伝わってきます。
参考URL
本記事を作成するために利用した参考資料を紹介します。
The Julia Programming Language
Julia言語の体系的なレクチャー動画です。
全ては見ていませんが、参考になります。
まとめ
本記事では、Juliaの基本文法を紹介しました。
当然、本記事では紹介しきれなかった項目もたくさんあるので、各で勉強を進めてください。
本記事が皆様の役に立てたら幸いです…
Pythonを学習するのに効率的なサービスを紹介していきます。
まず最初におすすめするのは、Udemyです。
Udemyは、Pythonに特化した授業がたくさんあり、どの授業も良質です。
また、セール中は1500円定義で利用することができ、コスパも最強です。
下記の記事では、実際に私が15個以上の講義を受講して特におすすめだった講義を紹介しています。
他のPythonに特化したオンライン・オフラインスクールも下記の記事でまとめています。
自分の学習スタイルに合わせて最適なものを選びましょう。
また、私がPythonを学ぶ際に使用した本を全て暴露しているので参考にしてください。