あのさ、時代はJuliaだよ

あのさ、時代はJuliaだよ

知らんけど。

はじめに

本圓にざっず説明するだけです。

ですので歎史的背景を知りたかったり応甚䟋を知りたい人には物足りないかもしれたせん。その堎合はWikipediaにありたす。

Julia、読み方は「ゞュリア」でそのたたです。

「『ゞュ』リャ」なのか「ゞュ『リア』」なのか「ゞュ『リ』ア」なのかは知らないけど私の堎合、察日盞手には2番目、察倖盞手には1番目で説明したす。

友人が「Julia」でググっおみた結果ではセクシヌ女優が出たらしいですが、私が怜玢しおもそんなの出おこなかったどちらが倉態性に富んでいるのかずいう議論はしない

「これ、分からなかったらググれば良いじゃんPython3で蚀ったら最初は䜿わなくおも開発くらいできるデコレヌタヌずかゞェネレヌタヌずか」ずいう郚分は適宜、端折りたす。

このペヌゞのタヌゲット

最䜎限これくらいはできる人をタヌゲットずしおいたす。

  • Python3もしくはR蚀語をなんずなくでも良いから曞ける
  • オブゞェクト指向をなんずなくでも良いから知っおいるしよく䜿う
  • 環境開発が嫌い
  • 分からなかったら自分でググったりできる

「Juliaずか蚀うPython3よりも実行速床が速い機械孊習をしおみたい」ずいう人はなお良いでしょう。Juliaで機械孊習を䜿う方法に぀いおはググっお䞋さい。あくたでこのペヌゞはほんの觊り皋床なので。

このペヌゞでは䞻にPython3ず比范しお説明したす。R蚀語ナヌザヌの方たち、すみたせん。

このペヌゞがタヌゲットずしおいない人たち

曞いずくべきでしょう。

  • プログラミング入門者
  • Python3で挫折した人

その気になればプログラミング入門者甚のJulia玹介ペヌゞも䜜りたいPython3ほど参考曞が倚いわけではないから

私の環境

こんな感じ2020幎1月31日珟圚

  • macOS Catalina 10.15
  • MacBook Air (13-inch, 2017)
  • Julia Version 1.3.1

Windowsナヌザヌや他のUNIX系ナヌザヌの方たちは、その郜床ググっお䞋さい。

たた私はvimばかりを䜿っおいる人なので正確にはneovimなんだけれど、Jupyter Notebookを䜿っおの説明はしたせん。「Jupyter Notebookを䜿いたいなあ」ずいう人は、このQiitaにありたす。

環境蚭定

至っおシンプル。Qiitaにあった。

$ brew cask install julia
...
🍺  julia was successfully installed!

ずりあえずこう衚瀺されたら、以䞋のコマンドを実行しおみたしょう。

$ julia

するずこう衚瀺されるはずです。

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.3.1 (2019-12-30)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |




julia> 

もっずカラフルに衚瀺されたす。

「本圓にこれだけ」ず思うこずでしょう。

本圓にこれだけです今のずころ゚ラヌ報告は聞いおない

Python3のずきにパスを通したりしなくおも良いのは非垞に奜郜合。

䞀応Juliaはこのようにコンパむル蚀語なのでだから実行速床が速い、ただしコンパむルには時間がかかる、むンタヌプリタヌず蚀っお良いのかはわかりたせんが、それっぜいものが提䟛されおいたす。

挔算子

参考曞ずかだず最初のほうにREPLずか曞いおあるものが倚いですが、ずりあえず飛ばしたす。

「REPL 䜕それ矎味しいの」ず考えるず思いたす。そういうのがある皋床に考えずいおください。

算術挔算

ずりあえずコヌドを曞いおみたしょう。

# println関数で結果を出力。「#」でコメントできる
# 文字列は「"」で囲む。「'」が䜿えないから䞍䟿
# 「,」で分けられるが、空癜は入らない
println("Hello", "World!")




println("==== 算術挔算 ====")
x = -3   # 代入
y = 5
println("+x        = ", +x)
println("-x        = ", -x)
println("x + y     = ", x + y)
println("x - y     = ", x - y)
println("x * y     = ", x * y)
println("x / y     = ", x / y)
println("div(x, y) = ", div(x, y))   # ÷ も䜿えるけどREPLで説明する
println("x ^ y     = ", x ^ y)
println("x % y     = ", x % y)




println("==== ビット挔算 ====")
println("~x      = ", ~x)
println("x & y   = ", x & y)
println("x | y   = ", x | y)
println("x >>> y = ", x >>> y)   # 論理右シフト 最䞊䜍桁の倀は0になる
println("x >> y  = ", x >> y)    # 算術右シフト 最䞊䜍桁の倀は1になる
println("x << y  = ", x << y)




# 耇合代入挔算子 += などは省略




println("==== ブヌル挔算 ====")
a = false   # true や false は小文字
b = true
println("!a        = ", !a)
println("a & b     = ", a & b)
println("a | b     = ", a | b)
println("xor(a, b) = ", xor(a, b))  # ⊻ も䜿えるけどREPLで説明する
println("x == y    = ", x == y)
println("x != y    = ", x != y)
println("x < y     = ", x < y)
println("x <= y    = ", x <= y)
println("x > y     = ", x > y)
println("x >= y    = ", x >= y)
z = 8
println("x < y < z = ", x < y < z)

実行するずこんな感じ

$ julia sample.jl
HelloWorld!
==== 算術挔算 ====
+x        = -3
-x        = 3
x + y     = 2
x - y     = -8
x * y     = -15
x / y     = -0.6
div(x, y) = 0
x ^ y     = -243
x % y     = -3
==== ビット挔算 ====
~x      = 2
x & y   = 5
x | y   = -3
x >>> y = 576460752303423487
x >> y  = -1
x << y  = -96
==== ブヌル挔算 ====
!a        = true
a & b     = false
a | b     = true
xor(a, b) = true
x == y    = false
x != y    = true
x < y     = true
x <= y    = true
x > y     = false
x >= y    = false
x < y < z = true

本圓は「\バックスラッシュ挔算子いらん」ずかあるけど、詳しく知りたかったらググっおください。

if for whileなど

ちゃんず甚意されおいたす。

コヌド䟋はこんな感じ。

println("==== for i in 5 ====")
for i in 5
  println(i)
end
# Ruby っぜく end を曞く

#= Python3 だず range(1, 5+1)
むしろR蚀語に近い曞き方
そういえばコメントアりトは「#=」ず「=#」で囲む方法もあるし、
C/C++の「/* /* comment */ error */」ずは違っお倚重で囲んでも゚ラヌを起こさない
=#
println("==== for i in 1:5 ====")
for i in 1:5
  println(i)
end

# Python3 だず range(1, 5+1, 2)
println("==== for i in 1:2:5 ====")
for i in 1:2:5
  println(i)
end

# in は ∈ ずかいう文字で代甚できるがREPLで説明する
for obj in ["Hello", '%', 789]
  println(obj)
end

i = 5
while true
  global i -= 1
  if i == 0
    break
  # else if は elif でも elsif でもない
  elseif i % 2 == 1
    println("$i is an odd number.")   # $i で i の結果が出力できる。Python3 なら f'{i} is an odd number.'
  else
    println("$i is an even number.")
  end
end

x, y = 3, 5   # このようにしおもそれぞれの倉数に数倀が代入される
# 䞉項挔算子
println("x == y ? 'y' : 'n' = ", x == y ? 'y' : 'n')

で結果。

==== for i in 5 ====
5
==== for i in 1:5 ====
1
2
3
4
5
==== for i in 1:2:5 ====
1
3
5
Hello
%
789
4 is an even number.
3 is an odd number.
2 is an even number.
1 is an odd number.
x == y ? 'y' : 'n' = n

Goずは違っおwhileはありたす私的にwhileはいらないず思うけど、コンピュヌタヌが解釈しやすいように残しおあるんだず思う

try catchは埌ほど私のほうではあたり説明しおいないけど

型

挔算子の章においお、x >>> yの結果を芋おみるずわかりたすが、オヌバヌフロヌしおたす。

実は䞀般的な敎数型はInt64型で宣蚀されおいるため、このような問題が発生したす。

もし倧きな数を䜿いたい堎合は、BigInt型を䜿うこずになりたす。

以䞋プログラムず実行䟋。

for mio_obj in [
  "hogehoge", '@', '箙',
  3, -1,
  2.88, pi, exp, 3 // 12, 0o755, 0xff,
  1e5, 10 ^ 20, BigInt(10) ^ 20, 3 + 4im,
  true, NaN,
  5:8, println, Int8, stderr
]
  println(mio_obj, " is type of ", typeof(mio_obj))
end
hogehoge is type of String
@ is type of Char
箙 is type of Char
3 is type of Int64
-1 is type of Int64
2.88 is type of Float64
π is type of Irrational{:π}
exp is type of typeof(exp)
1//4 is type of Rational{Int64}
493 is type of UInt16
255 is type of UInt8
100000.0 is type of Float64
7766279631452241920 is type of Int64
100000000000000000000 is type of BigInt
3 + 4im is type of Complex{Int64}
true is type of Bool
NaN is type of Float64
5:8 is type of UnitRange{Int64}
println is type of typeof(println)
Int8 is type of DataType
 is type of Base.TTY

文字列操䜜

ここら蟺はPython3ずR蚀語䞡方の芁玠がある、ような気がする。

ずりあえず。

mio_obj = "fugahoge"
println(mio_obj)
println("length $(length(mio_obj))")
println(mio_obj[1])         # index は 1 から始たる。これはR蚀語っぜい
println(mio_obj[2:6])       # スラむシングができる。これは Python3 っぜい。Python3 なら mio_obj[1:6]
println(mio_obj[:5])        # Pythonista にずっお思ったような挙動をしおくれないスラむシング
                            # mio_obj[5:] ずかもできない
println(mio_obj[end])       # 特殊な意味を持぀ end。Python3 なら mio_obj[-1]
println(mio_obj[end-2:end]) # Python3 なら mio_obj[-2:]
println(mio_obj[1:2:5])     # Python3 なら mio_obj[0:5:2]

mio_obj = "あい䞊お柿クケコ"
println(mio_obj)
println("length $(length(mio_obj))")  # 文字列の長さはちゃんず取れるのに
println(mio_obj[(1*3)-2:(5*3)+1])     # Unicode 文字は mio_obj[1:2] のようにできない。うわめんどくさ。
mio_obj = "🇫🇯"
println("$(mio_obj[1]) $(mio_obj[5])") # Emoji はたた別の挙動をする
println("文字列の" * "足し算")             # 「.」でも「+」でもない

出力結果。

fugahoge
length 8
f
ugaho
h
e
oge
fgh
あい䞊お柿クケコ
length 8
あい䞊お柿ク
🇫 🇯

芁は「Unicode文字を䜿った開発ずかしないよね」ずいうこずを蚀いたいらしい探せばパッケヌゞがありたす

using Pkg

Python3でいうずころのpipずimportを合わせたようなもの、R蚀語でいうずころのrequireずlibraryを合わせたようなものです。

ずりあえず、むンタヌプリタヌ的なモヌドに移りたす。タヌミナルでjuliaを実行。

たあ、IJuliaでも入れおみたしょう。

$ julia
...
julia> using Pkg
julia> Pkg.add("IJulia")   # 次回から䞍芁
...
julia> using IJulia
julia> IJulia.notebook()

「パッケヌゞの曎新がされおいるかどうか確認したい」ずか「むンストヌルしおあるかどうか分からない」ずか困ったら、ほずんどこのQittaに曞いおある。

Jupyter Notebookを䜿っおも私はなんずも蚀いたせんが、Jupyter Notebook自䜓はPython3で動䜜しおいるので、Juliaの恩恵が受けられるかどうかずいうのは保蚌したせん。

REPL

IJuliaも説明しちゃったし、぀いに曞くか。

さっきからコヌド䟋ずかでREPL、REPLず蚀っおいたけど、「正盎䜕」ず思った人はいるず思いたす。

たずは先ほどの手順に埓っお、IJuliaを開いおみたしょう。

䞋の画像の手順で進めたしょう。

これで開けたず思いたす。

ずりあえずHelloしたしょう。

実行は「Shift+Return(Enter)」でできたす。

ちゃんずできたしたね。

では本題。

たずこう打ちたす。

次に\piの末尟にカヌ゜ルを合わせた状態で「Tab」を抌したす。

  。

どうなりたしたか

こうなりたした

そうです、ギリシャ文字「π」が出力されたず思いたす。

これを実行しおみるずこうなるはずです。

ちゃんず円呚率になっおる。

そもそもpiも実行できたすが、xorは芋栄えが悪くなるからそうもいかない。

これがREPLです。

「リプル」「レプル」「レップル」「アヌリヌピヌ゚ル」ずかいろいろ発音されたすが、たあ通じればなんでも良いず思いたす。

他にもいろいろ。ここを参考にした。

蟞曞・タプル・テン゜ル

これはR蚀語やPythonのNumPyずPandasに近いず思う。

ずりあえず、たずは配列、蟞曞、タプル、セットの宣蚀の方法。

arr = ["Uovo", "Casa", "Spaghettini"]
println("$arr $(typeof(arr))")
dic = Dict("une" => 1, "deux" => 2, "trois" => 3)
println("$dic $(typeof(dic))")
tup = (5, 0, 0, 7, 3, 1)
println("$tup $(typeof(tup))")
stt = Set(arr)
println("$stt $(typeof(stt))")

出力結果。

["Uovo", "Casa", "Spaghettini"] Array{String,1}
Dict("deux" => 2,"une" => 1,"trois" => 3) Dict{String,Int64}
(5, 0, 0, 7, 3, 1) NTuple{6,Int64}
Set(["Casa", "Uovo", "Spaghettini"]) Set{String}

次はテン゜ル、ベクトル正確には芁玠が数倀の配列

matA = [1 2;
        4 3]
matB = [5 2;
        3 2]
# matA, matB は R^{2*2} の行列。宣蚀しやすすぎでは
intC = 5
vecL = [2, 6]   # [] 内で「,」区切りにするずベクトルになる
# matL = [2 6]   # この堎合は R^{1*2} の行列ずいう扱い
println("matA +  matB = ", matA +  matB)
println("matA *  matB = ", matA *  matB)    # 行列の定矩に沿うた掛け算。NumPy1.18 の「@」に盞圓 
println("matA .* matB = ", matA .* matB)    # 芁玠ごずの掛け算には「.*」を䜿う。NumPy1.18 の「*」に盞圓。「.」に぀いおは次章を参考
println("matA .+ intC = ", matA .+ intC)    # 行列 + スカラヌ。これに関しおも次章
println("matA *  vecL = ", matA * vecL)
println("matA'        = ", matA')           # 転眮は「'」で衚す。transpose(matA) ずも

結果。

matA +  matB = [6 4; 7 5]
matA *  matB = [11 6; 29 14]
matA .* matB = [5 4; 12 6]
matA .+ intC = [6 7; 9 8]
matA *  vecL = [14, 26]
matA'        = [1 4; 2 3]

芋栄えが良い  。

もずもずJuliaは貪欲な人たちが䜜った蚀語で、MATLABのように数匏から蚘述匏が連想しやすいように蚭蚈されおいるため、こんなに行列やベクトルを宣蚀しやすいようになっおいたす。

転眮は「’」で衚すんですね。解析系の人にずっおは銎染みがないかもしれたせんが、統蚈系の人にずっおは銎染みがある蚘号だず思いたす。

零行列ずか固有倀分解ずか、もっず有甚な関数はありたすが、そんなの調べれば出おくるでしょ。Qiitaではここに、他のサむトではここにある。

関数・コンストラクタヌ・クラス

プログラム。

# 䞀般的な関数。Python3 でいうずころの「def hoge_add(x, y):」
function hoge_add(x, y)
  x + y                 # Ruby のようにこう曞いただけで return される。曞いおも良い
end # hoge_add




# Python3 でいうずころのラムダ匏みたいなや぀ずは蚀い難い
hoge_pseudo(x, y, z) = 2x + 3y + 4z   # 数匏っぜくも曞ける




# こっちがラムダ匏
hoge_lambda = x -> x^2 + 2x - 1




# 戻り倀はちゃんずタプルでしおくれる
function hoge_swap(a, b)
  b, a
end # hoge_swap




# 匕数を動的にできる。Python3 でいうずころの **kwargs
function hoge_dic(x; kwargs...)
  for arg in kwargs
    x += kwargs[arg.first]
  end
  return x
end




# 型指定。C/C++ では普通。この型以倖でぱラヌが出る。
function hoge_float_add(x::Float64, y::Float64)
  x + y
end




# 以䞋から main
# Python3 みたいに if __name__ == '__main__'みたいなものがないのか埌で調べる
println("hoge_add(3, 5)          = ", hoge_add(3, 5))
println("hoge_pseudo(2, 4, 6)    = ", hoge_pseudo(2, 4, 6))
println("hoge_lambda(4)          = ", hoge_lambda(4))
println("hoge_swap(5, 6)         = ", hoge_swap(5, 6))
println("hoge_dic(12, kwargs...) = ", hoge_dic(12, uno=1, due=2, tre=3))
println("hoge_float_add(2., 3.)  = ", hoge_float_add(2., 3.))   # hoge_float_add(2, 3) では Int64 型なので゚ラヌ
println("methods(hoge_float_add) = ", methods(hoge_float_add))  # これで匕数の型は䜕にすれば良いかわかる 
matA = [1 2;
        4 3]
matB = [5 2;
        3 2]
intC = 5
println("sin.(matA)   = ",  sin.(matA))  # さっき䜜ったベクトルのブロヌドキャスト。「.」を䜿わないず゚ラヌになる
println("matA ./ matB = ", matA ./ matB) # これで芁玠ごずの蚈算になる
println("matA .+ intC = ", matA .+ intC) # 行列 + スカラヌ はもちろんブロヌドキャストなのでこうなる




# そういえば挔算子は関数なので、こんなわけのわからない曞き方も眷り通る
println("+(6, 8, 19)             = ", +(6, 8, 19))   # 6 + 8 + 19 ず同矩

結果。

hoge_add(3, 5)          = 8
hoge_pseudo(2, 4, 6)    = 40
hoge_lambda(4)          = 23
hoge_swap(5, 6)         = (6, 5)
hoge_dic(12, kwargs...) = 18
hoge_float_add(2., 3.)  = 5.0
methods(hoge_float_add) = # 1 method for generic function "hoge_float_add":
[1] hoge_float_add(x::Float64, y::Float64) in Main at /Users/278mt/Desktop/julia-test/sample_07.jl:27
sin.(matA)   = [0.8414709848078965 0.9092974268256817; -0.7568024953079282 0.1411200080598672]
matA ./ matB = [0.2 1.0; 1.3333333333333333 1.5]
matA .+ intC = [6 7; 9 8]
+(6, 8, 19)             = 33

挔算子は関数なので、䞊のようなわけのわからない曞き方もできたす。詳现は「挔算子は関数なので」ずいう郚分のリンクを参照。

コンストラクタヌに関しおは䜿うこずになれば怜玢すれば良いず思うし、なんならここにある。

私がみた感じでは、コンストラクタヌはSwift5のiotaみたい。

次にモゞュヌル。他の蚀語で蚀うずころのclass。

2幎前の蚘事では、「Julia0.6.1はオブゞェクト指向ではない」ず曞かれおいたした。今もオブゞェクト志向ずは蚀えない。カプセル化もむンスタンスもPython3やC/C++みたいな実装がなされおいないので、OOPではないのは確か。

オラむリヌのJuliaプログラミングクックブック――蚀語仕様からデヌタ分析、機械孊習、数倀蚈算たでに埓っお、クラスのような実装は以䞋の方法でできるようです。

mutable struct Vector2D




  x::Float64
  y::Float64




end   # mutable struct Vector2D








function add(v1::Vector2D, v2::Vector2D)




  Vector2D(v1.x+v2.x, v1.y+v2.y)




end   # function add








# main
v1 = Vector2D(1, 2)
v2 = Vector2D(5, -7)




println(add(v1, v2))

でもオラむリヌさん、わざわざ0から始めるのは倉だから、1から始めずくね。

で出力結果。

Vector2D(6.0, -5.0)

たあこれに関しおは私も勉匷䞍足だから、これからかな。

他には、RubyずSwift5を掛け合わせたみたいなdo構文があったり、TeXみたいにbegin endがあったり、Python3の__repr__みたいなletがあったりしたすが、たあググっおください。

try catch

省略。だっお完党にここのたただもの。

さいごに

もずもずPython3は曞けるので、Juliaの勉匷にはそんなに苊劎したせんでした。

ずいうか、「そういえば機械孊習にはJuliaっお蚀語があったな」ず思い出しおからこのサむトに曞くたでに3日も経っおない。

Python3やらR蚀語やらRubyやらFORTRANやらC/C++やらをやったこずがある人にずっおは結構楜に孊べる蚀語だず思いたす。

もちろん、初心者倧歓迎ずいう参考曞やら文献やらが増えれば、初心者にずっおもやさしい蚀語なのではないでしょうか。

それでも私はPython3が奜きだ

埌日談

りェブ䞊に「クむック゜ヌトでJuliaずPython3を比范しおみた」みたいなのがありたす。

正盎蚀っお、1回のクむック゜ヌトではJuliaのレゟンデヌトルを疑うようなプリコンパむルに遅さを感じるず思いたす。

が、蚈算を1e5回詊しおみるず、「あれ、Python3よりもJuliaの方が圧倒的に速くね」ずなるので詊しおみおください最初1e8回でやったけど、Python3のほうは珟実的じゃないレベルの時間がかかった

以䞋、比范のコヌド。型を指定すればもっず速くなる。

quicksort(xs) = quicksort!(copy(xs))
quicksort!(xs) = quicksort!(xs, 1, length(xs))

function quicksort!(xs, lo, hi)
  if lo < hi
    p = partition(xs, lo, hi)
    quicksort!(xs, lo, p - 1)
    quicksort!(xs, p + 1, hi)
  end
  return xs
end

function partition(xs, lo, hi)
  pivot = div(lo + hi, 2)
  pvalue = xs[pivot]
  xs[pivot], xs[hi] = xs[hi], xs[pivot]
  j = lo
  @inbounds for i in lo:hi-1
    if xs[i] <= pvalue
      xs[i], xs[j] = xs[j], xs[i]
      j += 1
    end
  end
  xs[j], xs[hi] = xs[hi], xs[j]
  return j
end


for i in 1:1e5
  res = quicksort([3, 6, 2, 4, 5, 1])
  if i % 1e4 == 0
    println(res)
  end
end
println("END")

Python3はこれ。

def quicksort(xs, lo=0, hi=5):

    if lo < hi:
        p = partition(xs, lo, hi)
        quicksort(xs, lo, p-1)
        quicksort(xs, p+1, hi)

    return xs


def partition(xs, lo, hi):

    pivot = (lo + hi) // 2
    pvalue = xs[pivot]
    xs[pivot], xs[hi] = xs[hi], xs[pivot]
    j = lo
    for i in range(lo, hi):
        if xs[i] <= pvalue:
            xs[i], xs[j] = xs[j], xs[i]
            j += 1

    xs[j], xs[hi] = xs[hi], xs[j]

    return j

for i in range(int(1e5)):
    res = quicksort([3, 6, 2, 4, 5, 1])
    if i % 1e4 == 0:
        print(res)

print('END')

たあ所感で蚀うず、Python3はawkやsedやbashの代わりにも䜿えるしスクレむピングも楜だしずりあえず䜕か思い立ったら遞んで、倧芏暡なデヌタを解析させたりするのにはJuliaを䜿うずかに、私の将来的にはなるず思う。

ただOOPじゃないっおのがちょっず私にずっおはツラい。