Juliaで役に立つ豆知識

何かJuliaであまり知られていないっぽいけれど便利な機能をここに追記していこうと思います。
どんなトピックスがあるかは、右の目次を見てください。

# 確認環境
Julia 1.4.2
Mac OS 10.14.6

# struct関連

## structの値の初期化にkwdefを使う
Juliaのstructを使うときにあらかじめ値を入れておきたい場合があると思います。
一つの方法は

```julia
struct TestA
    a ::Float64
    b ::Array{Float64,1}

    TestA() = new(0,rand(3))
end
```
とstructの中に定義する方法ですね。

```julia
testA = TestA()
println(testA)
```

```
TestA(0.0, [0.6482247142585289, 0.3035639028122792, 0.697402899461609])
```
同様のことをkwdefを使ってできます。

```julia
Base.@kwdef struct TestAkw
    a ::Float64 = 0
    b ::Array{Float64,1} = rand(3)
end
```
kwdefを使うと、フィールドにそのまま値を入れることができるので、初期値を入れるときに非常に便利です。

## structで定義したArrayは実は値を変えられる

例えば、上で定義したstructを使って、

```julia
Base.@kwdef struct TestAkw
    a ::Float64 = 0
    b ::Array{Float64,1} = rand(3)
end

testAkw = TestAkw()
println(testAkw)
testAkw.b[:] = rand(3)
println(testAkw)
```
ができます。structは変更不可能なはずなのに、bの値を変えられます。ただし、

```julia
testAkw.b = rand(3)
testAkw.b = rand(4)
```

```
ERROR: LoadError: setfield! immutable struct of type TestAkw cannot be changed
Stacktrace:
```
とかはエラーが出ます。これは、定義した配列に代入することは可能でも、新しい配列をフィールドとして定義できないということだと思います。
また、定義ずみの配列にpushすることは可能です。

```julia
push!(testAkw.b,2) 
println(testAkw)
```
は

```
TestAkw(0.0, [0.09444996612558998, 0.030326088326802836, 0.4934630346414506, 2.0])
```
とできます。

## structを配列のように扱う

```julia
Base.@kwdef mutable struct TestB
    a ::Float64 = 0
    b ::Array{Float64,1} = rand(3)
end
```
というstructを定義したとします。このstructを配列のように扱いたければ、

```julia
function Base.setindex!(x::TestB,v,i) 
    x.b[i] = v
end

function Base.getindex(x::TestB,i)
    return x.b[i]
end
```
という関数を定義しておくと、

```julia
testb = TestB()
println(testb)
testb[2] = 3
println(testb)
v = testb[2]
println(v) 
```

```
TestB(0.0, [0.20274453089015654, 0.3365997667642584, 0.04122776256227012])
TestB(0.0, [0.20274453089015654, 3.0, 0.04122776256227012])
3.0
```
のようにtestb[2]のような形でフィールドのbに入れられます。
[自分で作ったtypeをベクトルとみなす in Julia](https://qiita.com/cometscome_phys/items/643f56d365c095884b89)
や
[Juliaの多重ディスパッチで遊んでみる：2次元1次元配列](https://qiita.com/cometscome_phys/items/cabc23aa102e9e689c55)
も参照すると良いと思います。

## structが持っているフィールド名を辞書型に格納する
あるstructの中身がわからないとして、その中身を取り出す方法があります。例えば、

```julia
function make_parametersdict(p::T) where T <: Any
    pnames = fieldnames(T)
    pdict = Dict()
    for i=1:length(pnames)
        pdict[String(pnames[i])] = getfield(p,pnames[i])
    end
    return pdict,pnames
end
```
という関数を定義しておくと、中身のわからないstruct pに対して```make_parametersdict(p)```すれば、フィールド名とその値がセットになったものを取り出すことができます。
例えば、

```julia
Base.@kwdef mutable struct TestB
    a ::Float64 = 0
    b ::Array{Float64,1} = rand(3)
end

function make_parametersdict(p::T) where T <: Any
    pnames = fieldnames(T)
    pdict = Dict()
    for i=1:length(pnames)
        pdict[String(pnames[i])] = getfield(p,pnames[i])
    end
    return pdict,pnames
end

testB = TestB()
pdict,pnames = make_parametersdict(testB)
display(pdict)
```
をすると、

```
Dict{Any,Any} with 2 entries:
  "b" => [0.421301, 0.967549, 0.64976]
  "a" => 0.0     
```
が得られます。

## structが持っているフィールド名と同じ名前の変数を用意する
上と組み合わせて、

```julia
Base.@kwdef mutable struct TestB
    a ::Float64 = 0
    b ::Array{Float64,1} = rand(3)
end

function make_parametersdict(p::T) where T <: Any
    pnames = fieldnames(T)
    pdict = Dict()
    for i=1:length(pnames)
        pdict[String(pnames[i])] = getfield(p,pnames[i])
    end
    return pdict,pnames
end

testB = TestB()
pdict,pnames = make_parametersdict(testB)

for i=1:length(pnames)
    eval(Expr(:(=), pnames[i], pdict[String(pnames[i])]))
end

println("a = ", a)
println("b = ", b)
```
とします。出力結果は、

```
a = 0.0
b = [0.48592393007162893, 0.492182409060139, 0.609400886449827]
```
となります。aやbはあらわには定義していないはずなのに、ちゃんと定義されています。
これは、

```
eval(Expr(:(=), pnames[i], pdict[String(pnames[i])]))
```
がポイントで、```pnames[i]```がaあるいはbを表しています。
aあるいはbに```pdict[String(pnames[i])]```を代入するという意味の数式Exprを組み立て、それをevalで評価しています。
これを応用すると、

```julia
function loading()
    p = TestB()
    pdict,pnames = make_parametersdict(p)
    
    
    for i=1:length(pnames)
        eval(Expr(:(=), pnames[i], pdict[String(pnames[i])]))
    end

    if length(ARGS) ≥ 1
        include(pwd()*"/"*ARGS[1])
    end

    for i=1:length(pnames)
        pdict[String(pnames[i])] = eval(pnames[i])
        setfield!(p, pnames[i], eval(pnames[i]));
    end

    display(p)
    println("\n")
    
end

loading()
```

として、file.jlの中身を

```julia
a = 3.3
```
とすれば、

```
julia testj.jl file.jl
TestB(3.3, [0.44783084199216416, 0.7498487813060268, 0.361759560432239])
```
のように、structに対して、外部から与えられない値はそのまま、与えられた値は変更する、ということが可能です。


# 配列関連

## collectで配列を取り出す


``1:10``みたいなものから配列を作るには、

```
a = 1:10
b = collect(a)
```
とすればよいです。出力は

```
10-element Array{Int64,1}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
```
となります。なお、2飛ばしなどは

```
a = 1:2:10
b = collect(a)
```
で作れます。

```
5-element Array{Int64,1}:
 1
 3
 5
 7
 9
```

値を取り出したいときは

```julia
a = 1:2:10
println(a[1])
println(a[2])
```
のような感じで簡単にできます。




## StaticArraysは速い
例えばこのようなコードがあったとします。

```julia
mutable struct TestB
    U::Array{Array{Float64,1},1} 
    TestB(n) = new(Array{Array{Float64,1},1}(undef,n))
end
n = 1000000
testB = TestB(n)

function loop1(testB)
    for i=1:length(testB.U)
        testB.U[i] = rand(4)
    end
end

function loop2!(testB)
    for i=1:length(testB.U)
        testB.U[i] = rand(4)
    end
end

println("loop1")
@time loop1(testB)
@time loop1(testB)
@time loop1(testB)
println("loop2!")

@time loop2!(testB)
@time loop2!(testB)
@time loop2!(testB)
```
これは、配列の配列であるUの中身を更新しています。
出力結果は例えば、

```
loop1
  0.122696 seconds (1.03 M allocations: 108.415 MiB, 36.20% gc time)
  0.097994 seconds (1000.00 k allocations: 106.812 MiB, 37.45% gc time)
  0.150192 seconds (1000.00 k allocations: 106.812 MiB, 58.67% gc time)
loop2!
  0.160977 seconds (1.03 M allocations: 108.294 MiB, 59.41% gc time)
  0.106944 seconds (1000.00 k allocations: 106.812 MiB, 46.52% gc time)
  0.108966 seconds (1000.00 k allocations: 106.812 MiB, 47.72% gc time)
```
となります。

これをStaticArraysを用いて、

```julia
mutable struct TestBst
    U::Array{SVector{4,Float64},1} 
    TestBst(n) = new(Array{SVector{4,Float64},1}(undef,n))
end
testBst = TestBst(n)

println("loop1")
@time loop1(testBst)
@time loop1(testBst)
@time loop1(testBst)

println("loop2!")
@time loop2!(testBst)
@time loop2!(testBst)
@time loop2!(testBst)
```
とすると、

```
loop1
  0.303754 seconds (651.65 k allocations: 44.886 MiB, 9.10% gc time)
  0.000873 seconds (10.00 k allocations: 1.068 MiB)
  0.000544 seconds (10.00 k allocations: 1.068 MiB)
loop2!
  0.009977 seconds (39.01 k allocations: 2.593 MiB)
  0.000544 seconds (10.00 k allocations: 1.068 MiB)
  0.000540 seconds (10.00 k allocations: 1.068 MiB)
```
と速くなります。
StaticArraysとは
https://github.com/JuliaArrays/StaticArrays.jl
これで、
長さ一定の配列に制限することで速度が上がります。
二種類があって、Sがつくものは長さ一定かつ値も一定です。Mがつくものは値は変化しても良いものです。
Mの方は

```julia
mutable struct TestBstm
    U::Array{MVector{4,Float64},1} 
    TestBstm(n) = new(Array{MVector{4,Float64},1}(undef,n))
end
testBstm = TestBstm(n)

println("loop1")
@time loop1(testBstm)
@time loop1(testBstm)
@time loop1(testBstm)

println("loop2!")
@time loop2!(testBstm)
@time loop2!(testBstm)
@time loop2!(testBstm)
```
ですが、これは

```
loop1
  0.099530 seconds (2.04 M allocations: 154.782 MiB, 21.14% gc time)
  0.124582 seconds (2.00 M allocations: 152.588 MiB, 37.75% gc time)
  0.094865 seconds (2.00 M allocations: 152.588 MiB, 29.09% gc time)
loop2!
  0.132371 seconds (2.03 M allocations: 154.112 MiB, 43.11% gc time)
  0.093484 seconds (2.00 M allocations: 152.588 MiB, 31.07% gc time)
  0.124293 seconds (2.00 M allocations: 152.588 MiB, 46.67% gc time)
```
速くなっていません。サイズが小さい配列だからかと思います。Sの方は物理では何らかの場の値を扱うときに使うと良さそうですね。

# 色々
## 発生する乱数を毎回同じにする
計算をデバッグするときとかに、毎回同じ乱数が出てくれると便利なことがあると思います。

```julia
using Random
Random.seed!(123)
testAkw = TestAkw()
println(testAkw)
testAkw = TestAkw()
println(testAkw)
testAkw = TestAkw()
println(testAkw)
```
seed!の中の値を変えると、出てくる乱数が変わります。ここを固定しておけば、何回実行しても同じ乱数が得られます。乱数列を固定している、ということですね。



## エラーを起こしたところで止める
何かエラーが起きた時にメッセージを出して止めたいことがあると思います。
```@assert```はまだ知られていますが、```error```はあまり知られていないと思うので紹介します。


```julia
a = -1
if a < 0
    println("error! a should be positive a = $a")
    exit()
end
```
とすると、

```
error! a should be positive a = -1
```
となり、aが負だとエラーが出て止まります。これが一番素朴な感じですね。

これは、

```julia
a = -1
@assert a < 0 "error! a should be positive a = $a"
```
ともできて、この場合は

```
error! a should be positive a = -1
```
となって止まります。

また、errorという関数を使うと、

```julia
a = -1
if a < 0
    error("error! a should be positive a = $a")
end
```
は

```
ERROR: LoadError: error! a should be positive a = -1
Stacktrace:
```
と止まります。errorを使う場合、どこの呼び出しで止まったかという情報が出てきてソースコードの番号が表示されるので、便利です。

## Plotsの読み込みを高速化する。
[PackageCompiler.jlでPlotsの呼び出しを高速化する2020年7月版](https://qiita.com/cometscome_phys/items/85150cca4e4ff7c1d568)
をやってみてください。

## GoogleColabを使用
手元でJupyter NotebookでJuliaを実行して、ファイルを.ipynbで保存していたとします。
そのファイルをGoogle Colabでも実行してみたいことがありますよね？
そんなときは
[Google ColabでJuliaを使う：元々あるノートブックを使う](https://qiita.com/cometscome_phys/items/1ba6ec181bb0fe1b35d5)
こちらを参考にしてやってみてください。

# データの入出力関係

## 出力あれこれ
printlnの使い方についてまとめます。
たとえば、

```julia
a = rand(3)
b = 4
println(a,"\t",b) #文字列を入れなくても変数でも書き出せるし、何個でも入れられる。\tはTab
println("$a $b") #このように$aなどとすると変数を文字列の中に入れられる
println("$a\t$b") #もちろんTabも入れられる
```
こんなことができます。

$を使えば、このように

```julia
println("$(2*a)\t$b")
println("$(2*a)\t$(sqrt(b))")
```

計算して出力することもできます。
もしたくさんある場合には、printlnではなく改行のないprintも使えます。このように、

```julia
c = rand(1:10,10)
for ci in c
 print(ci,"\t")
end
```
とすると、

```
4	3	3	6	8	7	1	3	4	10
```
こんな感じに1行に並べることも可能です。

もう少しシンプルにも書けて、

```julia
c = rand(1:10,10)
[print("$ci ") for ci in c]
```
でも可能です。

##print_matrixなど
https://twitter.com/LirimyDh/status/1295310732435705856?s=20
で知ったのですが、以下のようなやり方が可能です。

```julia
    a = rand(4,4)
    println("空白")
    Base.print_matrix(stdout,a,""," ","\n")
    println("タブ")
    Base.print_matrix(stdout,a,"","\t","\n")
    println(",")
    Base.print_matrix(stdout,a,"",",","\n")
```
一つ目の値は出力先で、stdoutとすると画面に出力します。次が配列です。
残りの三つですが、一つ目は表示の頭に何か入れる場合です。ここでは空白にしています。
二つ目は値と値の間をどう区切るか、です。三つ目は最後の文字の後にどうするか、です。ここに\nを入れているのは、改行をしたいためです。
出力結果はこうなります。

```
空白
0.6768908174832444  0.3817345277763453 0.42646007837750055 0.17014559504201232
0.06283236014992255 0.8604594763575402 0.9500945643866037  0.7974495350151196
0.9238853594136449  0.6583229009292564 0.43140441711350563 0.8170287950442048
0.06778870328883024 0.4142144775490266 0.6645669648543755  0.334802102829435
タブ
0.6768908174832444 	0.3817345277763453	0.42646007837750055	0.17014559504201232
0.06283236014992255	0.8604594763575402	0.9500945643866037 	0.7974495350151196
0.9238853594136449 	0.6583229009292564	0.43140441711350563	0.8170287950442048
0.06778870328883024	0.4142144775490266	0.6645669648543755 	0.334802102829435
,
0.6768908174832444 ,0.3817345277763453,0.42646007837750055,0.17014559504201232
0.06283236014992255,0.8604594763575402,0.9500945643866037 ,0.7974495350151196
0.9238853594136449 ,0.6583229009292564,0.43140441711350563,0.8170287950442048
0.06778870328883024,0.4142144775490266,0.6645669648543755 ,0.334802102829435
```
Base.と毎回つけるのが面倒な場合には、

```julia
import Base:print_matrix
```
としておけば、``print_matrix``だけでいけます。

## REPLと同じように出力してみたい
Juliaで立ち上げるとREPLモードになり、いろいろできます。この時、

```
julia> a = rand(4,4)
4×4 Array{Float64,2}:
 0.312639  0.317392   0.751933   0.651802
 0.520705  0.0688579  0.0148247  0.57883
 0.833153  0.542301   0.261739   0.933407
 0.225191  0.725512   0.0661146  0.512802
```
のようないい感じな出力をしてくれます。これをファイルを読み込んで実行するときにもやりたい場合には、

```julia:display.jl
a = rand(4,4)
display(a)
```
とします。型情報もついているので、デバッグで配列などを出力したいときに見やすいですね。

## データの保存と読み込み
HDF5が便利です。
[Juliaでのデータの保存と読み込み：HDF5を使ってみる](https://qiita.com/cometscome_phys/items/7ced3362ac64b98129d1)

## テキストデータ読み込みのテンプレート
テキストデータが

```
cat te.dat 
space 3 4.2 3.1 3.4
tab    7   4.9 3.4 3.3
```
だったとします。これを読み込むには、

```julia
filename = "te.dat"
data = readlines(filename) #データ読み込み
num = countlines(filename) #データのラインの数の確認
for i=1:num
  u = split(data[i]) #空白やtabを区切りとして読みこむ
  st = u[1] #文字なら
  println(st)
  it = parse(Int64,u[2]) #整数なら
  println(it)
  dt = parse(Float64,u[3]) #倍精度実数なら
  println(dt)
  dts = parse.(Float64,u[4:end]) #まとめて配列として読み込むなら
  println(dts)
end
```
などとします。