Julia独習メモ ＜2＞

Juliaを独習していて、その内容や気になったことをメモ書きとして残しています。

前回まで:

https://qiita.com/gyu-don/items/6b06e14f873393d7bf0f

# 3日目 マニュアル読みの続き
##  [算術演算子と初等関数](https://docs.julialang.org/en/v1/manual/mathematical-operations/)
### 算術演算子、論理演算子、ビット演算子
概ね普通なので目についたものだけ触れる。

- 整数での除算は`x ÷ y`
    - ゼロ方向に丸められる整数除算
    - `fld`, `cld`など、丸め方が異なる整数除算もある
    - `%`は、`÷`に対応する剰余演算子。一方、丸め方の異なる剰余演算子`mod`もある
- `x \ y`というのがあり、`y / x`と同じ意味である    - ただし、試したところ引数の評価順序は、前者が`x`, `y`の順であるのに対し、後者は`y`, `x`の順であった
- `x`の`y`乗は`x^y`
- `√` は単項演算子のように振る舞う。`\sqrt`-tabで入力
- bitwise xorは`⊻`で、`\xor`-tabで入力できる
- bitwise nand `⊼`, bitwise nor `⊽`もそれぞれ、`\nand`, `\nor`とtabで入力できる
- 右シフトは算術シフト。論理右シフトは`>>>`
- `+=` などの演算子も用意されている
- Pythonのような比較演算子の連続にも対応している
    - `1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5`
    - 値の評価順序はundefinedであることがドキュメントに明記されており、副作用を持った項を使うことは推奨されない

これら演算子は、`(+)`のようにすることで、関数の形で呼び出すこともできる。

```jl
julia> (-)(9, 4)
5

julia> (-)(3)
-3
```

### vectrized dot
各演算には、 `.+` や `.^` などのドット演算が自動的に定義され、配列の各要素に対し、演算が行われる。

```jl
julia> [1,2,3] .+ 10
3-element Vector{Int64}:
 11
 12
 13
```

演算子を関数形式で呼び出す記法では、以下のように書く。 `(+).([1, 2, 3], 10)`

関数についても、ドット演算を行える。

```jl
julia> sin.([0, π/2, π, 3π/2, 2π])
5-element Vector{Float64}:
  0.0
  1.0
  1.2246467991473532e-16
 -1.0
 -2.4492935982947064e-16
```

`1.+1`は、`1. + 1`なのか`1 .+ 1`なのか分からないため、このような記法は禁止されている。

演算子の優先順位や結合順序は以下で見られる。

```jl
julia> Base.operator_precedence(:+), Base.operator_precedence(:*)
(11, 12)

julia> Base.operator_associativity(:+), Base.operator_associativity(:*)
(:none, :none)
```

`+`や`*`の結合順序がnoneなのは、`1 + 2 + 3`は`(+)((+)(1, 2), 3)`や`(+)(1, (+)(2, 3))`ではなく、`(+)(1, 2, 3)`と解釈されるかららしい。

## [複素数と有理数](https://docs.julialang.org/en/v1/manual/complex-and-rational-numbers/)
虚数単位は`im`で、定数倍や足し算により、複素数を作ることができる。

```jl
julia> im
im

julia> typeof(im)
Complex{Bool}

julia> 1im
0 + 1im

julia> typeof(ans)
Complex{Int64}

julia> 1.0 + im
1.0 + 1.0im

julia> typeof(ans)
ComplexF64 (alias for Complex{Float64})
```

`complex(1, 2)`のようにしても作ることができる。

`1 // 2` で、有理数 $\frac{1}{2}$が作れる。

## [文字と文字列](https://docs.julialang.org/en/v1/manual/strings/)
`Char`は32ビット整数であり、Unicodeを扱うこともできる。ただし、1コードポイントに対応しないものは扱えない。

```jl
julia> 'A'
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)

julia> 'あ'
'あ': Unicode U+3042 (category Lo: Letter, other)

julia> '𠮟'
'𠮟': Unicode U+20B9F (category Lo: Letter, other)

julia> '😊'
'😊': Unicode U+1F60A (category So: Symbol, other)

julia> '🇫🇮ERROR: syntax: character literal contains multiple characters
Stacktrace:
 [1] top-level scope
   @ none:1
```

文字列リテラルはダブルクオートや、三重のダブルクオートで書く。
インデックスや、レンジを与えることで、文字や文字列を取り出せる。

```jl
julia> "hello"[1]
'h': ASCII/Unicode U+0068 (category Ll: Letter, lowercase)

julia> "hello"[2:4]
"ell"
```

長い文字列の部分をコピーせずに取り出すには、`@views "hello"[2:4]`のようにするといいらしい。

文字列のインデックスは、何文字目、ではなく、UTF-8で書いたときに何バイト目、を表していて、中途半端な参照をするとエラーが発生する。

```jl
julia> "aあb"[1:1]
"a"

julia> "aあb"[1:2]
"aあ"

julia> "aあb"[1:3]
ERROR: StringIndexError: invalid index [3], valid nearby indices [2]=>'あ', [5]=>'b'
Stacktrace:
 [1] string_index_err(s::String, i::Int64)
   @ Base ./strings/string.jl:12
 [2] getindex(s::String, r::UnitRange{Int64})
   @ Base ./strings/string.jl:278
 [3] top-level scope
   @ REPL[124]:1

julia> "aあb"[1:5]
"aあb"
```

`nextind`/`prevind`を用いて、次/前のインデックスを求めることができる。

```jl
julia> nextind("aあb", 1)
2

julia> nextind("aあb", 2)
5

julia> nextind("aあb", 3)
5

julia> nextind("aあb", 5)
6
```

1文字ずつ取り出すには、こういう方法がある

```jl
julia> s = "aあb"
"aあb"

julia> for i = eachindex(s)
         println(s[i])
       end
a
あ
b

julia> for c in s
         println(c)
       end
a
あ
b
```

文字列リテラル中の"$x"は、変数`x`に置き換えられる。'$'そのものを記述したい場合は"\$"のようにエスケープする。

`r"..."` によって、正規表現を書くことができる。(Pythonのraw文字列にあたるものは`raw"..."`)

```jl
julia> typeof(r"")
Regex

julia> match(r"a.c", "abc")
RegexMatch("abc")
```

replaceは以下のように行う。

```jl
julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"
```

`b"..."` はバイト配列リテラル、`v"0.1"`はバージョン番号リテラル(semver)

# 4〜6日目 関数とタプル
[Functions](https://docs.julialang.org/en/v1/manual/functions/)を読む。
## 関数
関数は以下のように定義できる。最後に評価された式の値が返される。(明示的に`return`で値を返すこともできる)
値を返したくない場合は、`nothing`を返すとよい(`return nothing`)


```jl
julia> function f(x,y)
           x + y
       end
f (generic function with 1 method)

julia> f(x,y) = x + y
f (generic function with 1 method)
```

無名関数は、以下のように定義できる。

```jl
julia> x -> x^2 + 2x - 1
#1 (generic function with 1 method)

julia> function (x)
           x^2 + 2x - 1
       end
#5 (generic function with 1 method)
```

引数の型や返り値の型は、`::TypeName`でつけることができる。

```jl
f(x::Int64)::Int64 = x
```

定義した関数のvectorize版はドット記法により使うことができる。

```jl
julia> inc(x::Int)::Int = x + 1
inc (generic function with 1 method)

julia> inc.([1, 2, 3])
3-element Vector{Int64}:
 2
 3
 4
```

## `hcat`, `vcat`, `hvcat`など
`[1, 2, 3]` のように書くと、`Vector`になる。

```jl
julia> [1,2,3]
3-element Vector{Int64}:
 1
 2
 3
```

`[1 2 3]` のように、カンマを入れずに書くと、`hcat(1, 2, 3)`と同じ意味で扱われ、横方向にそれらを並べた`Matrix`になる。

```jl
julia> [1 2 3]
1×3 Matrix{Int64}:
 1  2  3

julia> hcat(1, 2, 3)
1×3 Matrix{Int64}:
 1  2  3
```

`[[1, 2] [3, 4] [5, 6]]` とすることで、2x3の`Matrix`となる。

```jl
julia> [[1, 2] [3, 4] [5, 6]]
2×3 Matrix{Int64}:
 1  3  5
 2  4  6
```

`[[1 2] [3 4] [5 6]]` は、全てを横に並べたような形になる。`[[1 2], [3 4], [5 6]]`は、`Matrix`の`Vector`である。

```jl
julia> [[1 2] [3 4] [5 6]]
1×6 Matrix{Int64}:
 1  2  3  4  5  6

julia> [[1 2], [3 4], [5 6]]
3-element Vector{Matrix{Int64}}:
 [1 2]
 [3 4]
 [5 6]
```

`[1; 2; 3]` のように書くと、`vcat(1, 2, 3)` と同じ意味で扱われ、縦方向にそれらを並べる。

```jl
julia> [1; 2; 3]
3-element Vector{Int64}:
 1
 2
 3

julia> [[1; 2]; 3]
3-element Vector{Int64}:
 1
 2
 3
```

`Matrix`を`vcat`で並べると、`Matrix`の`Vector`ではなく`Matrix`となる。

```jl
julia> [[1 2]; [3 4]; [5 6]]
3×2 Matrix{Int64}:
 1  2
 3  4
 5  6
```

`[1 2; 3 4; 5 6]`も、`Matrix`となり、これは`hvcat((2, 2, 2), 1, 2, 3, 4, 5, 6)`の意味である。

```jl
julia> [[1 2]; [3 4]; [5 6]]
3×2 Matrix{Int64}:
 1  2
 3  4
 5  6

julia> hvcat((2, 2, 2), 1, 2, 3, 4, 5, 6)
3×2 Matrix{Int64}:
 1  2
 3  4
 5  6
```

`'`を後置することで、`Array`や`Matrix`の転置複素共役(エルミート共役。実数においては単なる転置)を返す。複素数においては複素共役を返し、実数においては変化しない。~~好みや慣れの問題だと思うが、この記法はどうなのか?~~

```jl
julia> A = [1.0+1im 2+2im; 3+3im 4+4im]
2×2 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im
 3.0+3.0im  4.0+4.0im

julia> A'
2×2 adjoint(::Matrix{ComplexF64}) with eltype ComplexF64:
 1.0-1.0im  3.0-3.0im
 2.0-2.0im  4.0-4.0im

julia> A''
2×2 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im
 3.0+3.0im  4.0+4.0im

julia> A = [1.0+1im 2+2im 3+3im]
1×3 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im  3.0+3.0im

julia> A'
3×1 adjoint(::Matrix{ComplexF64}) with eltype ComplexF64:
 1.0 - 1.0im
 2.0 - 2.0im
 3.0 - 3.0im

julia> A''
1×3 Matrix{ComplexF64}:
 1.0+1.0im  2.0+2.0im  3.0+3.0im

julia> x = 1.0 + 2.0im
1.0 + 2.0im

julia> x'
1.0 - 2.0im

julia> x''
1.0 + 2.0im
```

`A[i]`は`getindex(A, i)`に等しく、`A[i] = x`は`setindex!(A, x, i)`に等しい。stylistic conventionsから、末尾に`!`がつくものは、引数を変更していることを表す。

## タプル
`(1, "x", 3.0)` や `(1,)` 、`()`はタプルである。

```jl
julia> typeof((1, "x", 3.0))
Tuple{Int64, String, Float64}
```

タプルは、`getindex`で参照ができる。`setindex!`で変更はできない。

```jl
julia> t = (1, "x", 3)
(1, "x", 3)

julia> t[1]
1

julia> t[2]
"x"

julia> t[3]
3

julia> t[1] = 2
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, String, Int64}, ::Int64, ::Int64)
Stacktrace:
 [1] top-level scope
   @ REPL[58]:1
```

複数の値を返す関数は、タプルを返す関数として作ることができる。このあたりはPythonと同じ。

以下のように名前付きタプル(named tuple)を作ることができる。名前付きタプルは、インデックスでも名前でも参照することができる。

```jl
julia> x = (a=10, b=20)
(a = 10, b = 20)

julia> x[1]
10

julia> x.a
10
```

分割代入も行える。

左辺の数が足りない場合は、エラーなどにはならない。右辺の数が足りない場合は、`BoundsError`が出される。

```jl
julia> a, b = (1, 2, 3)
(1, 2, 3)

julia> b
2

julia> a, b, c = (1, 2)
ERROR: BoundsError: attempt to access Tuple{Int64, Int64} at index [3]
Stacktrace:
 [1] indexed_iterate(t::Tuple{Int64, Int64}, i::Int64, state::Int64)
   @ Base ./tuple.jl:88
 [2] top-level scope
   @ REPL[71]:1
```

行列を右辺に取ったときの挙動は少し思っていたのと違った。

```jl
julia> a, b = [1 2; 3 4]
2×2 Matrix{Int64}:
 1  2
 3  4

julia> a
1

julia> b
3
```

Julia 1.6以降は、左辺の最後の要素に`...`をつけると、残りの要素が全て代入される。

```jl
julia> a, b... = (1, 2, 3, 4)
(1, 2, 3, 4)

julia> a
1

julia> b
(2, 3, 4)

julia> a, b... = [1, 2, 3, 4]
4-element Vector{Int64}:
 1
 2
 3
 4

julia> a
1

julia> b
3-element Vector{Int64}:
 2
 3
 4

julia> a, b... = 1:4
1:4

julia> a
1

julia> b
3-element Vector{Int64}:
 2
 3
 4

julia> a, b, c... = (1, 2)
(1, 2)

julia> c
()

julia> a, b, c... = [1, 2]
2-element Vector{Int64}:
 1
 2

julia> c
Int64[]

julia> a, b... = [1 2 3; 4 5 6; 7 8 9]
3×3 Matrix{Int64}:
 1  2  3
 4  5  6
 7  8  9

julia> a
1

julia> b
8-element Vector{Int64}:
 4
 7
 2
 5
 8
 3
 6
 9
```

Julia 1.9からは、`...`をどの位置でも使えるようになった。

```jl
julia> a, b..., c = 1:4
1:4

julia> a
1

julia> b
2-element Vector{Int64}:
 2
 3

julia> c
4

julia> a..., b, c = 1:4
1:4

julia> a
2-element Vector{Int64}:
 1
 2

julia> b
3

julia> c
4
```

`f((x..., y)) = x` という記法は面白い。

```jl
julia> f((x..., y)) = x
f (generic function with 1 method)

julia> f(1:4)
3-element Vector{Int64}:
 1
 2
 3

# これは以下と同じ
julia> function f(xs)
         x..., y = xs
         x
       end
f (generic function with 1 method)

julia> f(1:4)
3-element Vector{Int64}:
 1
 2
 3
```

無名関数で、このような記法をするためには、タプルの後ろにカンマが必要。 `((x..., y), ) -> x`

名前付きタプルの分割代入は `(; )`によって行える。

```jl
julia> (; b, a) = (a=1, b=2, c=3)
(a = 1, b = 2, c = 3)

julia> a
1

julia> b
2
```

これらは、関数の第一引数を分割しているが、それとは別で、可変長引数の関数を `f(x, y, z...) = x` のように定義できる。可変長引数では、`...`は最後の引数にしか付けられない。

`f(x, y=1) = x + y`のように、デフォルト値付き引数の関数を定義することができる。

キーワード引数は `f(x; y=1)` や `f(x; options...)` のように定義できる。