Julia イテレーションプロトコル 入門
===
<!-- .element: style="font-size: 300%" -->
<!-- .slide: data-background-color="rgba(102,130,223,0.3)" -->
2021/02/13 JuliaTokai \#09
antimon2(後藤 俊介)
Note:
Julia の イテレーション機能で遊ぼう!
----
<!-- .slide: data-background-color="rgba(102,130,223,0.3)" -->
## お品書き
+ お前誰よ?
+ 簡単なJuliaの紹介
+ イテレーションプロトコル
---
<!-- .slide: data-background-color="rgba(102,130,223,0.3)" -->
# お前誰よ?
----
<!-- .slide: data-background-color="rgba(102,130,223,0.3)" -->
## 自己紹介
+ 名前:後藤 俊介
+ 所属:**[有限会社 来栖川電算](https://www.kurusugawa.jp)**
+ コミュニティ:**:star2:[JuliaTokai](https://juliatokai.connpass.com/)**, :star2:[機械学習名古屋](https://machine-learning.connpass.com/), :star:[Python東海](https://connpass.com/series/292/), …
+ 言語:**[Julia](https://julialang.org)**, Python, Scala(勉強中), …
+ ![Twitter](https://i.imgur.com/HqouMIg.png)<!-- .element: class="plain" style="vertical-align:middle;background:transparent" --> [@antimon2](https://twitter.com/antimon2) / ![Facebook](https://i.imgur.com/01nPd37.png)<!-- .element: class="plain" style="vertical-align:middle;background:transparent" --> [antimon2](https://www.facebook.com/antimon2)
+ ![Github](https://i.imgur.com/yBKtii5.png)<!-- .element: class="plain" style="vertical-align:middle;background:transparent" --> [antimon2](https://github.com/antimon2/) / ![Qiita](https://i.imgur.com/FxHMi64.png)<!-- .element: class="plain" style="vertical-align:middle;background:transparent" --> [@antimon2](http://qiita.com/antimon2) / [<i class="fa fa-file-text"><!-- .element style="font-size:120%" --></i> @antimon2](https://hackmd.io/@antimon2)
Note:
今日は(も?)大っぴらに Julia の話ができるっ
----
<!-- .slide: data-background-color="rgba(44,214,221,0.3)" -->
[![有限会社来栖川電算](https://i.imgur.com/8Kuhfel.png)
https://www.kurusugawa.jp](https://www.kurusugawa.jp)
Note:
普段は Python で仕事してますっ
---
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
# <small>簡単な</small><br>Julia の紹介
Note:
Julia の紹介っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
[![Julia](https://raw.githubusercontent.com/JuliaLang/julia-logo-graphics/master/images/julia-logo-color.svg?sanitize=true)<!-- .element: style="background:white;width:80%" -->](https://julialang.org)
Note:
実は何気にロゴマイナーチェンジしてますっ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
## Julia とは?(1)
+ [The Julia Language](https://julialang.org)
+ 最新 v1.5.3(2020/11/09)
+ LTS v1.0.5(2019/09/09)
+ DEV v1.6.0-rc1(2021/02/06) → 次のLTS!
+ 科学技術計算に強い!
+ 動作が速い!(LLVM JIT コンパイル)
Note:
ググるときはなるべく [julialang](https://www.google.co.jp/search?q=julialang) で!
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
## Julia とは?(2)
> + Rのように中身がぐちゃぐちゃでなく、
> + Rubyのように遅くなく、
> + Lispのように原始的またはエレファントでなく、
> + Prologのように変態的なところはなく、
> + Javaのように硬すぎることはなく、
> + Haskellのように抽象的すぎない
>
> ほどよい言語である
<!-- .element: style="font-size:66%" -->
引用元:http://www.slideshare.net/Nikoriks/julia-28059489/8
<!-- .element: style="font-size:71%" -->
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
## Julia とは?(3)
> + C のように高速だけど、
Ruby のような動的型付言語である
> + Lisp のようにプログラムと同等に扱えるマクロがあって、しかも
Matlab のような直感的な数式表現もできる
> + Python のように総合的なプログラミングができて、
R のように統計処理も得意で、
Perl のように文字列処理もできて、
Matlab のように線形代数もできて、
shell のように複数のプログラムを組み合わせることもできる
> + 超初心者にも習得は簡単で、
超上級者の満足にも応えられる
> + インタラクティブにも動作して、コンパイルもできる
<!-- .element: style="font-size:50%" -->
([Why We Created Julia](http://julialang.org/blog/2012/02/why-we-created-julia) から抜粋・私訳)
<!-- .element: style="font-size:71%" -->
Note:
いろんな言語の「いいとこどり」言語!ってことでっ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
## 要するに
<!-- .element: style="font-size:300%" -->
+ 動的言語なのに速い!
+ 文法も覚えやすい!
+ 数値計算に強い!
<!-- .element: style="font-size:180%" -->
Note:
機械学習とかにも持って来いっ!
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
## 主な機能
<!-- .element: style="font-size:280%" -->
+ [多重ディスパッチ](https://docs.julialang.org/en/v1/manual/methods/)
+ [動的型システム](https://docs.julialang.org/en/v1/manual/types/)
+ [並行・並列処理](https://docs.julialang.org/en/v1/manual/parallel-computing/)、コルーチン
+ [組込パッケージマネージャ](https://docs.julialang.org/en/v1/stdlib/Pkg/)
<!-- .element: style="font-size:160%" -->
Note:
っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
## 文法・関数
Note:
以降、ほぼ過去スライドからのコピペ。すっ飛ばして先へ進んで戴いてもOKっ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 基本的な演算
```julia
julia> 1 + 2 - 3 * 4 # 四則演算(除算以外)
-9
julia> 7 / 5 # `整数 / 整数` の結果は浮動小数
1.4
julia> 7 ÷ 5 # `整数 ÷ 整数` の結果は整数
1
julia> 2 ^ 10 # 冪乗は `^`
1024
julia> 123 & 234 | 345 # 論理積 / 論理和
376
julia> 123 ⊻ 234 # 排他的論理和(==`xor(123, 234)`)
145
```
<!-- .element: style="font-size:46%" -->
Note:
整数同士の除算は実数になりますっ
整数除算演算子 `÷` が別に存在します(Python の `//` 相当)っ
また冪乗も(`**` ではなく)`^` ですっ
`⊻` は `\xor`+<kbd>Tab</kbd> または `\veebar`+<kbd>Tab</kbd> で変換できますっ
ちなみに先ほどの `÷` も `\div`+<kbd>Tab</kbd>で(基本的に ${\rm \TeX}$ の書式)っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 配列
```julia
julia> a = [1, 2, 3, 4, 5]
5-element Vector{Int64}:
1
2
3
4
5
julia> a[1] # Julia は 1-origin
1
julia> println(a[2:3]) # 範囲指定は両端含む
[2, 3]
```
<!-- .element: style="font-size:50%" -->
Note:
1-origin であることに注意すればあとは普通の配列っ
あと `a:b` は範囲(`Range`)の記法。両端を含む(Ruby の `a..b` と同じ)っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 配列の内包表記 (1)
```julia
julia> a = [n^2 for n=1:5]
5-element Vector{Int64}:
1
4
9
16
25
julia> A = [x+10y for y=1:3, x=1:3]
3×3 Matrix{Int64}:
11 12 13
21 22 23
31 32 33
```
<!-- .element: style="font-size:50%" -->
Note:
内包表記の記法は Python に類似っ
かつ、`for` にカンマ区切りで複数のイテレータを渡すことで2次元以上の配列も作成可能っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 配列の内包表記 (2)
```julia
julia> [(a,b,c) for c=1:15,b=1:15,a=1:15 if a^2+a*b+b^2==c^2]
6-element Vector{Tuple{Int64, Int64, Int64}}:
(3, 5, 7)
(5, 3, 7)
(6, 10, 14)
(7, 8, 13)
(8, 7, 13)
(10, 6, 14)
```
Note:
Python と同様に `if` で条件を指定することも可能っ
あと Python と同様、`[○ for ○=○]` を `(○ for ○=○)` と書くと配列ではなくて `Generator` が返りますっ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### ベクトル
```julia
julia> x = [1., 2., 3.]; y = [3., 1., 2.];
julia> x + y # `x .+ y` と書いても同じ(elementwise operation)
[4., 3., 5.]
julia> x .* y # これは `x * y` と書くとNG
[3., 2., 6.]
julia> using LinearAlgebra
julia> x ⋅ y # 内積(dot積、`dot(x, y)` と書いても同じ)
11.0
julia> x × y # 外積(cross積、`cross(x, y)` と書いても同じ)
[1., 7., -5.]
```
<!-- .element: style="font-size:50%" -->
Note:
Julia では実は1次元配列がベクトルの扱いっ
`⋅` は `\cdot`+<kbd>Tab</kbd>、`×` は `\times`+<kbd>Tab</kbd>(これらを利用するには `using LinearAlgebra` 必要)っ
あとこれらや先ほどの `÷` や `⊻` などのように、ASCIIの範囲を超えたUnicode文字の演算子(そのほとんどが $\TeX$ 由来)が Julia にはたくさんあります(他には例えば比較演算子の `≤` `≥` や、集合の要素 `∈` や包含関係 `⊆` などなど)
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 行列
```julia
julia> A = [1 2; 3 4] # この記法は MATLAB/Octave 由来
2×2 Matrix{Int64}:
1 2
3 4
julia> A' # `○'` は転置行列(正確には随伴行列)の記法(これも MATLAB/Octave 由来)
2×2 adjoint(::Matrix{Int64}) with eltype Int64:
1 3
2 4
julia> A'ᵀ # ← ≥ v"1.6", v"1.5" までは `transpose(A)` # 正確には転置行列はこっち
2×2 transpose(::Matrix{Int64}) with eltype Int64:
1 3
2 4
```
<!-- .element: style="font-size:50%" -->
Note:
Julia では2次元配列が行列の扱いっ
v1.6 から転置を表す表記として `○'ᵀ` と書けるようになりましたっ
`'\^T` + <kbd>TAB</kbd> で入力できますっ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 行列の演算
```julia
julia> A = [1 2; 3 4]; B = [3 0; 0 6];
julia> A + B # A .+ B でも同様
2×2 Matrix{Int64}:
4 2
3 10
julia> A * B # matrix multiply
2×2 Matrix{Int64}:
3 12
9 24
julia> A .* B # elementwise multiply
2×2 Matrix{Int64}:
3 0
0 24
```
<!-- .element: style="font-size:48%" -->
Note:
行列は `*` で通常の行列積になりますっこれ便利っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### ブロードキャスト
```julia
julia> sin(0.1)
0.09983341664682815
julia> sin.([0.1, 0.2, 0.3, 0.4])
4-element Vector{Float64}:
0.0998334
0.198669
0.29552
0.389418
julia> [0.1, 0.2, 0.3, 0.4] .^ 2
# => [0.01, 0.04, 0.09, 0.16]
```
<!-- .element: style="font-size:50%" -->
Note:
関数名と `(` の間に `.` を置くと、普通の関数を配列に拡張してくれる(ブロードキャスト)っ
`.^` のように演算子の前に `.` を書いても同様(先ほど出た `.+` `.*` もブロードキャスト)っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 関数定義
```julia
julia> f(x) = x^2 + 2x - 1
f (generic function with 1 method)
julia> f(1)
2
julia> f.(1:5)
# => [2, 7, 14, 23, 34]
```
Note:
数学のように直感的な記述で関数を定義可能っ
`2x` は `2*x` の省略形、曖昧さがなければリテラルと他の識別子が続く場合などに勝手に乗算と解釈してくれるっ
またユーザ定義関数も `.` をつけて自動的にブロードキャスト対応っ
----
<!-- .slide: data-background-color="rgba(213,99,92,0.3)" -->
### 有理数・複素数
```julia
julia> 1//2 == 0.5
true
julia> 1//2 - 1//3
1//6
julia> 1im ^ 2 == -1
true
julia> (1.0 + 0.5im) * (2.0 - 3.0im)
3.5 - 2.0im
```
Note:
有理数・複素数を標準サポート。
`//` は有理数除算(結果は有理数)
`im` は虚数単位。
どちらも四則演算も普通に書けますっ
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
# イテレーションプロトコル
Note:
Julia でイテレータを自作しよう!
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## イテレーションプロトコル?
+ [Iteration - Interfaces - Julia Documentation](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-iteration)
+ イテレーション(反復処理)をするための『約束事(=プロトコル)』
Note:
実は、公式マニュアルには「Iteration Protocol」って言葉は一度しか出てきませんっ
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## Lv. 1
Note:
レベル1っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
お題:平方数
```julia
struct Squares
count::Int
end
```
Note:
公式マニュアルにも載ってる例でお茶を濁すっ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
目標
```julia
for n in Squares(5)
println(n)
end
# => 1
# => 4
# => 9
# => 16
# => 25
```
Note:
1~(指定した数)の2乗までの平方数を返すっ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
function Base.iterate(sq::Squares, state::Int = 1)
state > sq.count && return nothing
(state ^ 2, state + 1)
end
```
Note:
これだけ!
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> for n in Squares(10)
println(n)
end
1
4
9
16
25
36
49
64
81
100
```
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> 225 ∈ Squares(20)
true
julia> 50 ∉ Squares(10)
true
```
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> using Statistics
julia> mean(Squares(100))
3383.5
julia> std(Squares(100))
3024.355854282583
```
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point
+ `Base.iterate()` を実装(多重定義)すると、『反復処理』できるようになる!
+ `for`式(の `in`節)に渡せる!
+ `in`(`∈`)演算子が使える!
+ その他 `iterate()` 関数に依存している関数に渡すことができる!
Note:
っ
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### `Base.iterate()`
Note:
`iterate()` 関数について詳細っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
+ `iterate(iter)`
+ 引数:
+ iter:イテレーション対象のオブジェクト
+ 戻り値:最初の要素と『初期状態オブジェクト』からなる Tuple、または `nothing`(列挙すべき要素が1つもない場合)
Note:
`iterate(iter)`(引数を1つだけ取るメソッド)っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
+ `iterate(iter, state)`
+ 引数:
+ iter:イテレーション対象のオブジェクト
+ state:一つ前の『状態オブジェクト』
+ 戻り値:次の要素と次の『状態オブジェクト』からなる Tuple、または `nothing`(すべて列挙し終えた場合)
Note:
`iterate(iter, state)`(引数を2つ取るメソッド)っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
for v in iter
println(v)
end
```
Note:
このコードは…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
_next = iterate(iter)
while _next !== nothing
(v, _stat) = _next
println(v)
_next = iterate(iter, _stat)
end
```
Note:
このコードと等価(糖衣構文)っ
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## Lv. 2
Note:
レベル2っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
お題:平方数(引き続き)
```julia
struct Squares
count::Int
end
function Base.iterate(sq::Squares, state::Int = 1)
state > sq.count && return nothing
(state ^ 2, state + 1)
end
```
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
目標
```julia
collect(Squares(5))
# => [1, 4, 9, 16, 25]
# 現状だと以下のエラーが発生:
# @> ERROR: MethodError: no method matching length(::Squares)
# Closest candidates are:
# length(::Core.MethodTable) at reflection.jl:957
# length(::CompositeException) at task.jl:41
# length(::Cmd) at process.jl:638
# ...
```
Note:
`collect()` や `map()` に渡して配列にして欲しいっ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# Base.IteratorSize(Squares)
# => Base.HasLength()
Base.length(sq::Squares) = sq.count
```
Note:
`length()` 関数にメソッド定義っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# Base.IteratorEltype(Squares)
# => Base.HasEltype()
Base.eltype(::Type{Squares}) = Int
```
Note:
`eltype()` 関数にメソッド定義っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> collect(Squares(7))
7-element Vector{Int64}:
1
4
9
16
25
36
49
```
Note:
すると `collect()` 関数に渡せるっ
他に `map()` 関数や 配列の内包表記の `in` 節などもっ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point
+ `Base.length(iter)` を実装 → `collect(iter)` に渡せる!
+ `Base.eltype(IterType)` を実装 → `collect(iter)` で要素の型を特定できる!
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
`Base.length(iter)` が未実装だと…
```julia
collect(Squares(7))
# @> ERROR: MethodError: no method matching length(::Squares)
# Closest candidates are:
# length(::Core.MethodTable) at reflection.jl:957
# length(::CompositeException) at task.jl:41
# length(::Cmd) at process.jl:638
# ...
```
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
`Base.eltype(IterType)` が未実装だと…
```julia
collect(Squares(7))
# => 7-element Vector{Any}:
# 1
# 4
# 9
# 16
# 25
# 36
# 49
```
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### `Base.IteratorSize()`
+ `Base.IteratorSize(IterType)` は以下のいずれかを返す必要あり:
+ `Base.HasLength()`(デフォルト)
+ `Base.HasShape{N}()`
+ `Base.IsInfinite()`
+ `Base.SizeUnknown()`
Note:
`Base.IteratorSize()` について詳細っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
#### `Base.HasLength()`
+ 長さが有限で固定の場合
+ `Base.length()` も実装する必要あり(デフォルト:未実装)
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
#### `Base.HasShape{N}()`
+ 長さが分かっていて、かつ配列と互換性のある『次元数』の概念がある場合
+ `Base.length()`も実装する必要あり(デフォルト:未実装)
+ `Base.size()`も実装する必要あり(デフォルト:未実装)
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
#### `Base.IsInfinite()`
+ 無限列挙する場合
+ (追加で実装が必要な関数なし)
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
#### `Base.SizeUnknown()`
+ 列挙するサイズが不定の場合
+ 「実行する度に長さが変わる可能性がある場合」など
+ (追加で実装が必要な関数なし)
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### `Base.IteratorEltype()`
+ `Base.IteratorEltype(IterType)` は以下のいずれかを返す必要あり:
+ `Base.HasEltype()`(デフォルト)
+ `Base.EltypeUnknown()`
Note:
`Base.IteratorEltype()` について詳細っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
#### `Base.HasEltype()`
+ 列挙する各要素の型が定まっている場合
+ `Base.eltype()` も実装する必要あり(デフォルト:`Any` を返す実装)
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
#### `Base.EltypeUnknown()`
+ 列挙する各要素の型が不定の場合
+ (追加で実装が必要な関数なし)
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## Lv. 3
Note:
レベル3っ
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
+ (余裕があれば `IsInfinite()` や `SizeUnknown()` の例を挙げる)
+ 過去記事:
+ [Julia で遊ぼう フィボナッチ数列 - Qiita](https://qiita.com/antimon2/items/ff31a74758eb4b63604e)
---
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
# まとめ
----
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
+ Iteration 意外と難しくない!
+ 使いこなせるともっと楽しくなる!
+ Julia 楽しい!
Note:
っ
---
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
# リンク
----
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
## 実験Note
+ [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gist/antimon2/87de53d948babc1b8e6c9e6f0513cdb4/HEAD?filepath=IterationProtocolSample.jl.ipynb)
----
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
## 参考リンク
+ [Julia Documentation](https://docs.julialang.org/en/v1/)
+ [Iteration - Interfaces - Manual](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-iteration)
+ [Iteration - Collections and Data Structures - Base (API Referense)](https://docs.julialang.org/en/v1/base/collections/#lib-collections-iteration)
---
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
ご清聴ありがとうございます。
Note:
ご清聴ありがとうございますっ!
{"metaMigratedAt":"2023-06-15T19:46:20.295Z","metaMigratedFrom":"YAML","title":"Julia イテレーションプロトコル 入門","breaks":"true","slideOptions":"{\"transition\":\"slide\",\"theme\":\"league\"}","contributors":"[{\"id\":\"80062a4b-8dad-49ac-95bf-848ce0686e9e\",\"add\":8153,\"del\":14162}]"}