【Julia】 基礎: 型

<!-- 【Julia】 基礎: 型 -->
# 0. はじめに

本稿では Julia の型を概説します．

Julia では型は `Core.typeof()` で，要素の型は `Base.eltype()` で見ることができます．

```julia
@show typeof("1"), typeof(1)
# (typeof("1"), typeof(1)) = (String, Int64)
```


```julia
@show eltype(["1", "one", "Eins", "un", "ひ", "一"])
# String
```

型の包含関係は `<:` です．

```
help?> <:
search: <:

  <:(T1, T2)

  Subtype operator: returns true if and only if all values of type T1 are also
  of type T2.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> Float64 <: AbstractFloat
  true
  
  julia> Vector{Int} <: AbstractArray
  true
  
  julia> Matrix{Float64} <: Matrix{AbstractFloat}
  false
```


# 1. 整数型 Integer

<!-- --- -->

```
help?> Integer
search: Integer isinteger

  Integer <: Real

  Abstract supertype for all integers.
```

---

# 1.1 Int64

数値計算する上でまず覚えるとよい型は 64ビット符号付き整数型 `Int64` です．
`Int` はそのエイリアスです．
全部で `Int8 Int64 Int32 Int16 Int128` というビット数の異なる符号付き整数型があります．

```julia
Int64 <: Signed <: Integer# true
Int == Int64# true
Int8 <: Int64# false
typemax(Int64) == 2^63 - 1# true
```

→ [Integers and Floating-Point Numbers · The Julia Language](https://docs.julialang.org/en/v1/manual/integers-and-floating-point-numbers/)


# 1.2 BigInt

BigInt 型は桁数の制限がほとんどないので大きな整数の計算に使えます．



```julia
#%% BigInt
BigInt <: Signed <: Integer# true
Int64 <: BigInt# false
# ERROR: MethodError: no method matching typemax(::Type{BigInt})
# typemax(BigInt)
2^63# -9223372036854775808
big(2)^63# 9223372036854775808
googol = big(10)^100# 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
```


Exponent やメモリのオーバーフローには気をつけましょう．

```julia
try
    googolplex = big(10)^googol
catch y
    if isa(y, OverflowError)
        @error "To think googolplex might be absurd!"
    end
end
println("Googolplex is not Googleplex.")
```

```
┌ Error: To think googolplex might be absurd!
└ @ Main ~/.. 
Googolplex is not Googleplex.
```

→ [BigFloats and BigInts](https://docs.julialang.org/en/v1/base/numbers/#Base.GMP.BigInt-Tuple{Any})

# 1.3 ブール型 Bool

Bool 型はプログラミング一般において最も重要な概念のひとつです．
Julia では `true` or `false` の2値です．

<!-- --- -->

```
help?> Bool
search: Bool BoundsError

  Bool <: Integer

  Boolean type, containing the values true and false.

  Bool is a kind of number: false is numerically equal to 0 and true is
  numerically equal to 1. Moreover, false acts as a multiplicative "strong
  zero":

  julia> false == 0
  true
  
  julia> true == 1
  true
  
  julia> 0 * NaN
  NaN
  
  julia> false * NaN
  0.0

  See also: digits, iszero, NaN.

```

---

2元 Boole 束の演算を計算することができる他，整数としての計算もできます．
`==` が返す値のタイプも `Bool` ですので極論これが分かっていないとコーディングできません．

```julia
Bool <: Integer# true
typeof(true)# Bool
true & false# false
true | false# true
true + false# 1
typeof(true + 0)# Int64
################
if 1 == 1 && 0 == 1
    println("Zero equals one.")
else
    @show 0 == 1
end
# 0 == 1 = false
```


# 2. 浮動小数点数型 AbstractFloat

<!-- --- -->

```
Float64 <: AbstractFloat

  64-bit floating point number type (IEEE 754 standard).

  Binary format: 1 sign, 11 exponent, 52 fraction bits.
```

---

# 2.1 Float64

Julia で倍精度浮動小数点数を表す型は `Float64` です．

浮動小数点数型 には `Float64 Float32 Float16` があります．

`float` で浮動小数点数型に変換できます．
詳しくは `help?> float` (REPL で `?`) にあります．
`0.0` の表記などほかは C/C++ に似ています．

```julia
float(1)# 1.0
typeof(float(1))# Float64
typeof(1 / 2)# Float64
typeof(0.0)# Float64
typeof(1 + 0.0)# Float64
floatmax()# 1.7976931348623157e308
```

いわゆる最大値は `typemax` ではなく `floatmax` で出力します．

```
help?> floatmax
search: floatmax floatmin

  floatmax(T = Float64)

  Return the largest finite number representable by the floating-point type T.

  See also: typemax, floatmin, eps.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> floatmax(Float16)
  Float16(6.55e4)
  
  julia> floatmax(Float32)
  3.4028235f38
  
  julia> floatmax()
  1.7976931348623157e308
  
  julia> typemax(Float64)
  Inf
```

# 2.2 BigFloat

Bigfloat を使うと桁数が増えます．

```julia
BigFloat(pi)# 3.141592653589793238462643383279502884197169399375105820974944592307816406286198
BigFloat(1.0)# 1.0
BigFloat(1.25)# 1.25
BigFloat(1.1)# 1.100000000000000088817841970012523233890533447265625
BigFloat("1.1")# 1.100000000000000000000000000000000000000000000000000000000000000000000000000003
```

→ [BigFloats and BigInts](https://docs.julialang.org/en/v1/base/numbers/#Base.MPFR.BigFloat-Tuple{Any,%20RoundingMode})

# 3. 有理数型 Rational

<!-- --- -->

有理数型 `Rational` は主に浮動小数点数型にせずに分数計算したいときに用いられ，数値計算では非常に重要な意味を持っています．

---

Python とくに Sympy で計算したことがある人は任意精度計算（および有理数計算）の意義はおわかりのことと思います．

除算 `//` を使います．
`div()` と `/` は有理数体の演算ではありません．
`div()` は整数の除算で `/` は浮動小数点演算として解釈されます．

```julia
typeof(1 // 2)# Rational{Int64}
typeof(1 // (big(5)^31 * 31))# Rational{BigInt}

div(1,2), typeof(div(1,2))# (0, Int64)
div(-3,2)# -1
typeof(1 / 2)# Float64
```

```
help?> div
search: div divrem DivideError splitdrive code_native @code_native

  div(x, y)
  ÷(x, y)

  The quotient from Euclidean (integer) division. Generally equivalent to a
  mathematical operation x/y without a fractional part.
...
```

```
help?> /
search: / //

  /(x, y)

  Right division operator: multiplication of x by the inverse of y on the
  right. Gives floating-point results for integer arguments.
...
```

# 4. 複素数型 Complex


Julia では虚数単位 $i$ は `im` です．[^imagPy]

[^imagPy]: Python では `1j` です．

```julia
typeof(im)# Complex{Bool}
typeof(1im)# Complex{Int64}
typeof(1.0im)# ComplexF64 (alias for Complex{Float64})
complex(1)# 1 + 0im
complex(1,1)# 1 + 1im
(1 + 1im)^2#0 + 2im
```

`Complex` 型は `Real` から構成されるという観点が取られており かつ `Real` 型は包含していません．

```julia
Real <: Complex# false
```

```
help?> Complex
search: Complex complex ComplexF64 ComplexF32 ComplexF16 precompile

  Complex{T<:Real} <: Number

  Complex number type with real and imaginary part of type T.

  ComplexF16, ComplexF32 and ComplexF64 are aliases for Complex{Float16},
  Complex{Float32} and Complex{Float64} respectively.

  See also: Real, complex, real.
```

# 5. 文字列型 String

`String` 型の「空（くう）文字」は `string()` で与えることができます．
文字列への変換も `string()` です．
文字列の結合は `*` です．`+` では行えません．

```julia
string()# ""
string(1)# "1"
"Albert" * " Einstein"# "Albert Einstein"
# ERROR: MethodError: no method matching +(::String, ::String)
# "Albert" + " Einstein"
```

* 文字列は添字を取れます．

```julia
"Bill Evans"[end]
# 's': ASCII/Unicode U+0073 (category Ll: Letter, lowercase)
```


* 繰り返しは `repeat()` か `^` です．
 
```julia
repeat("So what? ", 4)
"So what? " ^ 4
# "So what? So what? So what? So what? "
```

* 応用してみましょう．

```julia
DRA = "Don't run away"
line1 = DRA * " now... "
line2 = (DRA * ", ")^3 * DRA * "!"
line1 * line2
# "Don't run away now... Don't run away, Don't run away, Don't run away, Don't run away!"
```

* 文字列は for 文の宣言で使えます．

```julia
for Rhapsody in "Blue"
    print(Rhapsody)
    if Rhapsody == "Blue"[end]
        break
    end
    print("✰")
end
# B✰l✰u✰e
```

* プロットなどで $\LaTeX$ 式の数式が出したければ `LaTeXStrings` パッケージを宣言して文字列の前に `L` と書きます．

```julia
using LaTeXStrings
L"x \\mathrm{[m]}"# L"$x \\mathrm{[m]}$"
```

# 6. 集合型 Set

空集合は以下で書きます．小文字の `s` では書けません．

```julia
Set([])# Set{Any}()
```

要素の追加は `push!()` です．[^push]<!-- 後述の `append!()` は使えません． -->
`!` は破壊的変更の印です．

[^push]: 古いページでは `push!()` は遅いと書かれているときもありますが現在のバージョン `Julia Version 1.8.5` では自分で変なことを考えるよりも `for` 文と `push!()` の方が速いコードもあります．

```julia
begin
    ∅ = Set{Int64}([])
    @show isempty(∅)
    push!(∅, 1)
    @show ∅
end
```

出力: 

```
isempty(∅) = true
∅ = Set([1])
Set{Int64} with 1 element:
  1
```

* 合併は `union()` です．

```julia
println(union(Set([1,2,3]), Set([3,4,5])))
# Set([5, 4, 2, 3, 1])
```


# 7. Array 型

Set と異なり Array は順序が全て決まっています．
空列は以下です．

```julia
[]# Any[]
Any[]# Any[]
```

# 7.1 ベクトル型 Vector

Julia では `Vector` は `Array{T,1}` のエイリアスです．

数学的にはベクトルの成分表示に対応しています．
Julia の `Vector` はそれに加えて Python の `list` や `np.array` のようにも使います．

```
help?> Vector
search: Vector BitVector DenseVector StridedVector AbstractVector

  Vector{T} <: AbstractVector{T}

  One-dimensional dense array with elements of type T, often used to represent
  a mathematical vector. Alias for Array{T,1}.
```

* 空のベクトルは以下のように指定します．

```julia
Vector{Float64}()# Float64[]
Float64[]# Float64[]
```

型指定（型注釈）では `::Vector{Float64}` などと書きます．

```julia
julia> typeof(Vector{Float64}())
Vector{Float64} (alias for Array{Float64, 1})
```

* 要素の型の変換が行えます．

```julia
[1,2,3]
#=
3-element Vector{Int64}:
 1
 2
 3
=#
v = Vector{Float64}([1,2,3])
#=
3-element Vector{Float64}:
 1.0
 2.0
 3.0
=#
```


* 要素の追加は `push!()` で行います．

```julia
println(push!(Vector{Float64}([1,2,3]), 4.1))
# [1.0, 2.0, 3.0, 4.1]
```

* `append!()` は要素の追加ではなく配列の結合です．

```julia
println(append!([1,2,3], [3,4,5]))
# [1, 2, 3, 3, 4, 5]
```

他の関数については  [Collections and Data Structures · The Julia Language](https://docs.julialang.org/en/v1/base/collections/#Dequeues) をご覧下さい．


# 7.2 行列型 Matrix


Julia では `Matrix` は `Array{T,2}` のエイリアスです．

行列は数学的には2次元的な数列なのでこれは正しいです．[^sequence]

[^sequence]: 数列は写像として定義する場合とその値として定義する場合があります．

```
help?> Matrix
search: Matrix BitMatrix DenseMatrix StridedMatrix AbstractMatrix

  Matrix{T} <: AbstractMatrix{T}

  Two-dimensional dense array with elements of type T, often used to represent
  a mathematical matrix. Alias for Array{T,2}.
```

計算の具体例です．

```julia
A = [1 2; 3 4im]
```

```
2×2 Matrix{Complex{Int64}}:
 1+0im  2+0im
 3+0im  0+4im
```

* エルミート共役

```julia
A'
```

```
2×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
 1+0im  3+0im
 2+0im  0-4im
```

* 行列の代数演算でエルミート行列を作る

```julia
(A + A') / 2
```

```
2×2 Matrix{ComplexF64}:
 1.0+0.0im  2.5+0.0im
 2.5+0.0im  0.0+0.0im
```

# 8. 辞書型 Dict

<!-- --- -->

```
help?> Dict
search: Dict TypeDict IdDict WeakKeyDict SubtypeDict AbstractDict MakeTypeDict

  Dict([itr])

  Dict{K,V}() constructs a hash table with keys of type K and values of type
  V. Keys are compared with isequal and hashed with hash.

  Given a single iterable argument, constructs a Dict whose key-value pairs
  are taken from 2-tuples (key,value) generated by the argument.
```

---

# 8.1 辞書を使う
辞書型の宣言を Julia で行うには `Dict()` を使います．

対応の始め，いわば辞典の単語を key といい，対応の終わり，いわば単語の意味を value といいます．
比喩の部分は個々の事例ごとに「人の名前と試験の点数」などになったりします．
`Array` と異なるのはこの対応の関係です．

辞書への追加は模式的には `Dictionary["key"] = value` のように行えます．[^dict1] [^dict2]

[^dict1]: [Julia早引きノート［15］辞書(dictionary) - Qiita](https://qiita.com/ttabata/items/77a4f1d02b51d067bfb7)

[^dict2]: [Julia言語のBase.Dictコレクションで連想配列を扱う方法](https://segakuin.com/julia/base/dict.html)

`push!()` でもできます．


```julia
begin
    D = Dict{String, String}([])
    D["author"] = "Aristotle"
    push!(D, "A" => "Any")
    @show D
end
# D = Dict("A" => "Any", "author" => "Aristotle")
```

辞書は類を作るのに役立ちます．
例えば本当の辞書や索引のように 頭文字のアルファベットによる類別を書いてみましょう．

シングルクォーテーションマーク `'` は特別なタイプ `Char` を用意します．[^alpha1] [^alpha2]

[^alpha1]: [How to create strings combining all letters? - General Usage - Julia Programming Language](https://discourse.julialang.org/t/how-to-create-strings-combining-all-letters/49550/2)

[^alpha2]: [Generate all words of a given size from a fixed alphabet - General Usage - Julia Programming Language](https://discourse.julialang.org/t/generate-all-words-of-a-given-size-from-a-fixed-alphabet/20949)

```julia
Dict('A' => Set(["Alain Aspect", "Antonín Dvořák"]))
# Dict{Char, Set{String}} with 1 entry:...
```

# 8.2 辞書の応用

次にこの手法を応用してみましょう．
類のタイプを `Set` から `Vector` に変え，更に内包の `for` のベクトルから辞書を作ってみます．

```julia
Dict([(alphabet, Vector{Type}([])) for alphabet in 'A':'Z'])
# Dict{Char, Vector{Type}} with 26 entries:...
```

このようなやり方で，型のすべてのサブタイプを格納する辞書を作ってみましょう．

```julia
#%% application of a dictionary
function nextsubtype!(T, V)# recurrence
    push!(V, T)
    for t in subtypes(T)
        nextsubtype(t, V)
    end
    # ref. https://en.wikibooks.org/wiki/Introducing_Julia/Types
end
function allsubtypes(T)# vector of types
    V = Vector{Type}()
    nextsubtype!(T, V)
    return V
end
function SubtypeDict(T)
    X = allsubtypes(T)# vector of types
    S = Set([string(x)[1] for x in X])# alphabets
    DictOfTypes = Dict([(s, Vector{Type}([])) for s in S])
    for x in X
        push!(DictOfTypes[string(x)[1]], x)
    end
    return DictOfTypes
end
T = Number
_ans = SubtypeDict(T)
display(_ans)

open("SubtypeDict" * string(T) * ".txt", "w") do f
    write(f, repr("text/plain", _ans))
end
```

`display` の結果は text データとして保存すれば環境に依存せず全体をそのまま見ることができます．[^repr1] [^repr2]

[^repr1]: [I/O and Network · The Julia Language](https://docs.julialang.org/en/v1/base/io-network/#Base.repr-Tuple{MIME,%20Any})

[^repr2]: [Strings · The Julia Language](https://docs.julialang.org/en/v1/base/strings/#Base.repr-Tuple{Any})



```
Dict{Char, Vector{Type}} with 9 entries:
  'B' => [BigFloat, Bool, BigInt]
  'F' => [Float16, Float32, Float64]
  'U' => [Unsigned, UInt128, UInt16, UInt32, UInt64, UInt8]
  'A' => [AbstractFloat, AbstractIrrational]
  'I' => [Irrational, Integer, Int128, Int16, Int32, Int64, Int8]
  'R' => [Real, Rational]
  'S' => [Signed]
  'N' => [Number]
  'C' => [Complex]
```




ソートしたい場合は以下のようにします．[^sortdict]

[^sortdict]: [How do I sort a dictionary by its keys? - New to Julia - Julia Programming Language](https://discourse.julialang.org/t/how-do-i-sort-a-dictionary-by-its-keys/37848)

```julia
sort(collect(_ans), by = x->x[1])
```

```
9-element Vector{Pair{Char, Vector{Type}}}:
 'A' => [AbstractFloat, AbstractIrrational]
 'B' => [BigFloat, Bool, BigInt]
 'C' => [Complex]
 'F' => [Float16, Float32, Float64]
 'I' => [Irrational, Integer, Int128, Int16, Int32, Int64, Int8]
 'N' => [Number]
 'R' => [Real, Rational]
 'S' => [Signed]
 'U' => [Unsigned, UInt128, UInt16, UInt32, UInt64, UInt8]
```

`_ans` は引き継がれています．

# 9. 型をもっと学ぶ*


# 9.1 Abstract Types*

型の包含関係を見て Julia の理解を含めましょう．
例えば `Rational` は `Real` の直下にあってサブタイプを持ちません．

```julia
supertype(Rational)# Real
subtypes(Rational)# Type[]
```

`Real` は `Number` の直下にあります．

```julia
subtypes(Number)# 2-element Vector{Any}:
```

```
2-element Vector{Any}:
 Complex
 Real
```



すべてのタイプの上には `Any` があります．

```julia
subtypes(Any)# 611-element Vector{Any}: (Default)
supertype(Any)# Any 
```

# 9.2 型のグラフを書く**

型の全貌をグラフにしてみましょう．
`Any` の自己ループを除けばグラフは木 (tree) になるはずで再帰的に書いています．

→ [Github に入れた typetree.jl](https://github.com/Krypf/20230320Qiita/blob/main/typetree.jl)

主要部は以下です．

```julia:typetree.jl
function showtypetree(T, io, level=0, tab=8)
    ladder = "ᴸ" * repeat("-", 3)
    ...
    println(T)
    write(io, string(T) * "\n")
    for t in subtypes(T)
        showtypetree(t, io, level+1)# recurrence
    end
    # https://en.wikibooks.org/wiki/Introducing_Julia/Types
end
```

2行目は鍵型の字は Unicode の中から中央の L を選びました．[^centerL]

[^centerL]: [Unicode Input · The Julia Language](https://docs.julialang.org/en/v1/manual/unicode-input/)

* 結果を保存したレポジトリです．
→ [Krypf/20230320Qiita: Julia basics](https://github.com/Krypf/20230320Qiita)

* 9割は `Function` のサブタイプです．`using` などでパッケージを入れるともっと増えます．


`Number` の例を挙げましょう．

```text
Number
	ᴸ---Complex
	ᴸ---Real
		ᴸ---AbstractFloat
			ᴸ---BigFloat
			ᴸ---Float16
			ᴸ---Float32
			ᴸ---Float64
		ᴸ---AbstractIrrational
			ᴸ---Irrational
		ᴸ---Integer
			ᴸ---Bool
			ᴸ---Signed
				ᴸ---BigInt
				ᴸ---Int128
				ᴸ---Int16
				ᴸ---Int32
				ᴸ---Int64
				ᴸ---Int8
			ᴸ---Unsigned
				ᴸ---UInt128
				ᴸ---UInt16
				ᴸ---UInt32
				ᴸ---UInt64
				ᴸ---UInt8
		ᴸ---Rational

```

# 10. おわりに

<!-- --- -->

Julia における型を学びました．
Julia は Python と同じく動的型付け言語です．しかし Python とは異なり Julia では TypeError に対処する機会が多くあります．
従って型の理解は必ず身につけて置かなければなりません．

---

<!-- [^end1] -->
<!-- [^end1]:  -->
この制約は一見枷のようにも思えます．でもこうした枠 (組み) が却ってコードの方向性や速度に寄与し決定することもあるのではないでしょうか．
譬えれば言語にはこんな特性があります:[^endpoke] [^endbrowser]

[^endpoke]: 言語特性の譬え話は昔からあります．Fortran は第3世代最強の呼び声高いプテラ (かせきポケモン) です．[18のプログラミング言語をポケモンに例えてみた！ | TechAcademyマガジン](https://magazine.techacademy.jp/magazine/10770#ta-toc-6)

[^endbrowser]: 譬え話で観るブラウザ戦争．[納得？5つのブラウザを乗り物で表現すると…:らばQ](http://labaq.com/archives/51474865.html)

* C/C++ (/C#), レールの上をひた走る鉄道．速い．でも同じ街の中を移動するのには必要ない．
* Python, バスや自家用車といった車．既製品 (パッケージ) 多数．拡張性◎．鉄道よりは遅い．
* MATLAB, ヘリコプター．構造 (構文) は簡単．操縦 (ファイル管理) が面倒．特定の導線 (線型代数) では高速．ヘリポートが必要．(客として)簡単に乗れない．
* Julia, (自家用) 飛行機．海外に行くにはとても速い．滑走路が必要．国内移動程度なら C/C++ で足りることもある．用途を選ぶ．空港 (パッケージ) がない地域も多い．
<!-- * 小回りは効かないが飛んでしまえば自由．法律を守りながら，高度も悠々10000 ft や 30000 ft． -->

このように，Julia は C 系言語よりは制約はある種少ない上に，Python より激烈に速く，(比喩を離れて) 実際上は Python/MATLAB からの移行の学習コストはほぼ0に等しいわけです．

また C 系の (言語で食べてきた) プログラマ（エンジニア）が喧伝するほどに静的型付けの絶対性に固執する必要もないと僕は思いますし，だからこそ型をある時は堅固ある時は柔軟に扱える動的型付け言語 Julia は高速な数値計算ツールとして最強なのです．

<!-- --- -->

:::note info

つまり Julia は，プログラムの実行速度の観点からは Python と (ここが大きい)，構文の簡明さの観点からは C/C++ と，アクセシビリティの観点からは MATLAB と，互いに補い合える関係にあるということなのです．

:::

---


(参考)

* [Types · The Julia Language](https://docs.julialang.org/en/v1/manual/types/)
* [More about types · The Julia Language](https://docs.julialang.org/en/v1/devdocs/types/)
* [型 · The Julia Language](https://mnru.github.io/julia-doc-ja-v1.0/manual/types.html)
* [Introducing Julia/Types - Wikibooks, open books for an open world](https://en.wikibooks.org/wiki/Introducing_Julia/Types)
* [Diagram with all Julia types? - General Usage - Julia Programming Language](https://discourse.julialang.org/t/diagram-with-all-julia-types/5018)
* Julia で時間を扱う標準ライブラリ [Dates · The Julia Language](https://docs.julialang.org/en/v1/stdlib/Dates/)