{%hackmd _6gkK4EVRrijGnUnS71_YA %}
細かすぎて伝わらないかもしれないJuliaのTips
===
<!-- .element: style="font-size: 300%" -->
<!-- .slide: data-background-color="rgba(102,130,223,0.3)" -->
2022/12/18 JuliaTokai \#13
antimon2(後藤 俊介)
Note:
Julia の基本Tips?
----
<!-- .slide: data-background-color="rgba(102,130,223,0.3)" -->
## お品書き
+ お前誰よ?
+ 簡単なJuliaの紹介
+ Tips集 (?)
---
<!-- .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:[NGK2023S](https://ngk2022s.connpass.com/event/265837/), :star2:[機械学習名古屋](https://machine-learning.connpass.com/), :star:[jl.dev](https://jldev.connpass.com/), [Python東海](https://connpass.com/series/292/), …
+ 言語:**[Julia](https://julialang.org)**, Python, Scala(勉強中), …
+ <!-- .element: class="plain" style="vertical-align:middle;background:transparent" --> [@antimon2](https://twitter.com/antimon2) / <!-- .element: class="plain" style="vertical-align:middle;background:transparent" --> [antimon2](https://www.facebook.com/antimon2)
+ <!-- .element: class="plain" style="vertical-align:middle;background:transparent" --> [antimon2](https://github.com/antimon2/) / <!-- .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://www.kurusugawa.jp](https://www.kurusugawa.jp)
Note:
普段は Python で仕事してますっ
----
<!-- .slide: data-background-color="rgba(44,214,221,0.3)" -->
[
https://ngk2022s.connpass.com/event/265837/](https://ngk2022s.connpass.com/event/265837/)
Note:
MGK2023S のスタッフやってますっ
---
<!-- .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)" -->
[<!-- .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.8.3(2022/11/14)
+ LTS:v1.6.7(2022/07/19)
+ 次期版:v1.9.0-α1(2022/11/15)
+ 科学技術計算に強い!
+ 動作が速い!(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)" -->
### 多次元配列(v1.7以降)
```julia
julia> [1;2;;3;4;;;5;6;;7;8] # 2×2×2 の3次元配列
2×2×2 Array{Int64, 3}:
[:, :, 1] =
1 3
2 4
[:, :, 2] =
5 7
6 8
julia> [1 2;3 4;;;5 6;7 8] # 従来記法との併用も可能(次元の順番に注意)
2×2×2 Array{Int64, 3}:
[:, :, 1] =
1 2
3 4
[:, :, 2] =
5 6
7 8
julia> [1;;;;] # 1×1×1×1 の4次元配列
1×1×1×1 Array{Int64, 4}:
[:, :, 1, 1] =
1
```
<!-- .element: style="font-size:34%" -->
Note:
`;` の数で次元方向の区切ができるっ
これだけでも v1.7 を使うべきと言える仕様拡張っ!
----
<!-- .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)" -->
# Tips集 (?)
Note:
本題!
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## シチュエーション1: `findXXX()` 系関数?
+ 例えば「配列(1次元配列)で同じ要素が3つ続く箇所を見つけてそのインデックスの範囲を返す」はどう実現すれば良い?
+ Julia 標準の `findXXX()` 系の関数って、こういうとき使えるの?
<!-- .element: style="font-size:100%" -->
⇒<!-- .element: class="fragment" data-fragment-index="1" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決法1:`for` 文で回せばOK
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# 同じ要素がN個続く箇所を見つけてそのインデックスの範囲を返す
function findNrepeats(a::AbstractVector, N)
_lastindex = lastindex(a)
for i in eachindex(a) # keys(a) でもOK
r = i:i+N-1
last(r) > _lastindex && break
allequal(a[r]) && return r
end
nothing
end
```
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> a = [0, 7, 7, 5, 0, 0, 0, 1, 1, 2]; # a[5:7] == [0, 0, 0]
julia> findNrepeats(a, 3)
5:7
julia> findNrepeats([3, 1, 4, 1, 5, 9, 2, 6, 5, 3], 2) === nothing
true
```
<!-- .element: style="font-size:50%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決法2:無理矢理 `findXXX()` を利用する形に落とし込む
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# 同じ要素がN個続く箇所を見つけてそのインデックスの範囲を返す ver.2
function findNrepeats_v2(a::AbstractVector, N)
rs = [i:i+N-1 for i in eachindex(a) if i+N-1 ≤ lastindex(a)]
index = findfirst(rs) do r
allequal(a[r])
end
isnothing(index) ? nothing : rs[index]
end
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> a = [0, 7, 7, 5, 0, 0, 0, 1, 1, 2]; # a[5:7] == [0, 0, 0]
julia> findNrepeats_v2(a, 3)
5:7
julia> findNrepeats_v2([3, 1, 4, 1, 5, 9, 2, 6, 5, 3], 2) === nothing
true
```
<!-- .element: style="font-size:50%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### ベンチマーク
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> using BenchmarkTools, Random
julia> N=3;
julia> Random.seed!(1234);
@benchmark findNrepeats(a, $N) setup=(a=rand(0:9, 100))
# 出力例は画像参照
julia> Random.seed!(1234);
@benchmark findNrepeats_v2(a, $N) setup=(a=rand(0:9, 100))
# 出力例は画像参照
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
")
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point1
+ Julia の `findXXX()` は「条件に合致するもののインデックス(の範囲)を返す関数(存在しなければ `nothing`)」という共通仕様
+ 例外:`findmax()` / `findmin()`
+ `for i in eachindex(a)` または `for i in keys(a)`
+ `for i in 1:length(a)` は危険!
Note:
※ Julia の `findXXX()` 系の関数は「条件に合致するもののインデックス(の範囲)を返す関数(存在しなければ `nothing`)」という共通仕様がある(例外:`findmax()`/`findmin()`)ので、`findNrepeats()` もそれに合わせた仕様にしてあります。
※ Julia のインデックスは(標準の1次元配列なら)1-originだけど、全てのコレクションのインデックスがそうとは限らない(設定次第)ので `for i in 1:length(a)` のように書くのは危険!
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point2
+ `findXXX()` 系の関数は正直使いづらいし、必ずしも高パフォーマンスとは限らない。
+ てか `for` ループ速い!
+ あと `allequal()` て関数知ってた?
Note:
※ あと「コレクションの要素が全て同じかどうか」を判定するズバリ `allequal()` て関数が標準であります。便利♪知ってました?
なお「コレクションの要素が全て異なるかどうか」を判定する `allunique()` てのもあります。
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 参考:文字列なら 正規表現+`findfirst()` でOK!
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# 前準備:「同じ文字のN回繰り返し」という正規表現を生成しキャッシュする仕組み
@generated function getNrepeatsRegex(::Val{N}) where {N}
Regex("(.)" * "\\1" ^ (N-1)) # N=3 なら `r"(.)\1\1"` となる
end
# 同じ文字がN個続く箇所を見つけてそのインデックスの範囲を返す
function findNrepeats(s::AbstractString, N)
rex = getNrepeatsRegex(Val(N))
findfirst(rex, s)
end
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> s = "ABC123あああ😁漢字"; # s[7:13] == "あああ"
julia> findNrepeats(s, 3)
7:13
```
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 参考の参考:文字列なら正規表現+`findXXX()` でOK(キャッシュしないバージョン)
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# 同じ要素がN個続く箇所を見つけてそのインデックスの範囲を返す ver.2
function findNrepeats_v2(s::AbstractString, N)
rex = Regex("(.)" * "\\1" ^ (N-1)) # N=3 なら `r"(.)\1\1"`
findfirst(rex, s)
end
```
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> s = "ABC123あああ😁漢字"; # s[7:13] == "あああ"
julia> findNrepeats_v2(s, 3)
7:13
```
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### ベンチマーク
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> using BenchmarkTools, Random
julia> N=3;
julia> Random.seed!(1234);
@benchmark findNrepeats(s, $N) setup=(
s=randstring("123ABCあいう😁漢字", 100))
# 出力例は画像参照
julia> Random.seed!(1234);
@benchmark findNrepeats_v2(s, $N) setup=(
s=randstring("123ABCあいう😁漢字", 100))
# 出力例は画像参照
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
")
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point3
+ `findXXX()` は文字列検索の場合パターン(正規表現、文字の範囲等)も利用OK!
+ ただし毎回正規表現を生成するのは意外とコスト高い(=遅い)
+ `@generated`(生成関数)こういうとき便利♪
Note:
※ `findXXX` は文字列検索の場合第1引数は関数だけではなくパターン(正規表現、文字の範囲等)もOK!
※ ただし毎回正規表現を生成するのは意外とコスト高い(=遅い)ので何らかの方法でキャッシュする必要あり(正規表現リテラルが使用できるなら使用するなど)
※ てかこのサンプルを作ることで `@generated`(生成関数)の使い途を1つ見つけてしまった♪これ基本!
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 補足:`findXXX()`(第1引数に関数を受け取る使い方)
+ ↑はなんで「値で検索」「結果はインデックス」なのか?
+ ⇒「インデックス」または「キー」で参照できるコレクションなら同じI/Fで使えるから!<!-- .element: class="fragment" data-fragment-index="1" -->
Note:
→ 配列や文字列だけじゃなくて、辞書(`Dict`)や名前付きタプル(`NamedTuple`)にも同じI/Fで利用できるから(それらの場合キー(の列)が返る)。
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> D = Dict(["Carol", "Alice", "Ellen", "Bob", "Dave"].=>1:5);
julia> findall(isodd, D) # 値が奇数であるようなエントリーのキーを全て列挙
3-element Vector{String}:
"Carol"
"Dave"
"Ellen"
julia> [D[key] for key in findall(isodd, D)]
3-element Vector{Int64}:
1
5
3
julia> # ※ ↑は結果的に `filter(isodd, collect(values(D)))` と同じ
```
<!-- .element: style="font-size:50%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## シチュエーション2: Y/A `findXXX()` 系関数
+ `findXXX()` 系関数の理念や使い途は分かった。分かったけれどやっぱ使いにくい!
1. インデックスやキーで参照できるコレクションしか扱えない
2. 戻り値がインデックスやキーなのが分かりにくい
+ Julia に「一般のイテレータで使える」「条件に合致する最初の要素を取得(なければ `nothing`)」っていう関数はないの?
<!-- .element: style="font-size:90%" -->
⇒<!-- .element: class="fragment" data-fragment-index="1" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決法1:`Iterators.dropwhile()` と `Base.first()` を組み合わせればOK
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# 第1引数で条件判定して、第2引数のコレクションで最初に合致する要素を返す
# (なければ `nothing`)
function meetfirst_v1(pred::Function, itr)
# NG: `return first(Iterators.dropwhile(!pred, itr))`
itr2 = Iterators.dropwhile(!pred, itr)
try
first(itr2)
catch e
if isa(e, BoundsError) || isa(e, ArgumentError)
return nothing
end
rethrow(e)
end
end
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> a = [314, 159, 265, 358, 979, 323, 846, 264];
julia> meetfirst_v1(n -> n % 11 == 0, a)
979
julia> meetfirst_v1(n -> n % 7 == 0, a) === nothing
true
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> # コラッツ数列を列挙するイテレータ(`Channel`)を返す関数
function collatz(n::Int)
Channel{Int}() do chnl
put!(chnl, n)
while n > 1
n = iseven(n) ? n ÷ 2 : 3n + 1
put!(chnl, n)
end
end
end
collatz (generic function with 1 method)
julia> meetfirst_v1(≥(200), collatz(27))
214
julia> meetfirst_v1(≥(50), collatz(3)) === nothing
true
```
<!-- .element: style="font-size:45%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決法2:パフォーマンス気にしてみる
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
# 第1引数で条件判定して、第2引数のコレクションで最初に合致する要素を返す
# (なければ `nothing`) ver.2
function meetfirst(pred::Function, itr)
itr2 = Iterators.dropwhile(!pred, itr)
next = iterate(itr2)
isnothing(next) ? nothing : first(next) # `first(next)` はタプルの第1要素を取得している
end
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> a = [314, 159, 265, 358, 979, 323, 846, 264];
julia> meetfirst(n -> n % 11 == 0, a)
979
julia> meetfirst(n -> n % 7 == 0, a) === nothing
true
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> # コラッツ数列を列挙するイテレータ(`Channel`)を返す関数
function collatz end; # 長いので略
julia> meetfirst(≥(200), collatz(27))
214
julia> meetfirst(≥(50), collatz(3)) === nothing
true
```
<!-- .element: style="font-size:55%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### ベンチマーク
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> using BenchmarkTools, Random
julia> Random.seed!(1234);
@benchmark meetfirst_v1(≥(200), c) setup=(c=collatz(rand(3:100)))
# 出力例は画像参照
julia> Random.seed!(1234);
@benchmark meetfirst(≥(200), c) setup=(c=collatz(rand(3:100)))
# 出力例は画像参照
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
")
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point1
+ 基本的な発想:`first(Iterators.dropwhile(!pred, itr))`
+ `!《関数》` は `x -> !(《関数》(x))` 相当
+ `first(《空のイテレータ》)` は例外が発生してしまうので適切に対処
Note:
※ 基本的な発想は `first(Iterators.dropwhile(!pred, itr))`
※ `!pred` というのは、`pred()` 関数の結果(`Bool`値 という前提)を否定、つまり `x->!(pred(x))` 相当
※ `first(《空のイテレータ》)` は例外が発生してしまうので適切な対処が必要
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point2
+ `iterate(itr)`:
+ 最初の要素があるとき: `(《要素》, 《状態オブジェクト》)`
+ ないとき: `nothing`
+ Julia の例外処理意外と重いっ!
Note:
※ Julia のイテレーションの仕組み(`iterate()` 関数の使い方・戻り値)を知っていれば、`first()` 関数相当のことを簡単に実現できてしかも例外処理不要!(Julia の例外処理は意外と重い!)
※ (簡単におさらい) `iterate(itr)` は、最初の要素があるときは `(《要素》, 《状態オブジェクト》)`、なければ `nothing`
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## シチュエーション3: 文字列の折り返し
+ 長い文字列はずらーっと横に長くなる…
+ 途中に改行すると改行文字が入っちゃうし…
<!-- .element: style="font-size:100%" -->
⇒<!-- .element: class="fragment" data-fragment-index="1" -->
Note:
+ Julia はフリーインデントだから、長い配列を直書きするときは適宜改行して書けるから良いよね。
+ でも長い文字列はずらーっと横に長くなっちゃうよね…
+ だって Julia の文字列は中に改行を入れられる=途中で改行すると改行文字になっちゃうじゃん…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決:行末に `\` を入れることで折り返しできる(ようになった)よ!(≥v1.7)
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> jgm0 = "寿限無寿限無五劫の擦り切れ海砂利水魚の水行末雲来末風来末食う寝る処に住む処やぶらこうじのぶらこうじパイポ・パイポ・パイポのシューリンガン・シューリンガンのグーリンダイ・グーリンダイのポンポコピーのポンポコナの長久命の長助";
julia> jgm1 = "寿限無寿限無五劫の擦り切れ\
海砂利水魚の水行末雲来末風来末\
食う寝る処に住む処やぶらこうじのぶらこうじ\
パイポ・パイポ・パイポのシューリンガン・\
シューリンガンのグーリンダイ・グーリンダイのポンポコピーのポンポコナの\
長久命の長助";
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> jgm2 = """
寿限無寿限無五劫の擦り切れ海砂利水魚の水行末雲来末風来末\
食う寝る処に住む処やぶらこうじのぶらこうじ\
パイポ・パイポ・パイポのシューリンガン・シューリンガンのグーリンダイ・\
グーリンダイのポンポコピーのポンポコナの\
長久命の長助""";
# ↑ `"""~"""` ならインデントも無視されるのでさらに見やすく!
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> jgm0 == jgm1 == jgm2 # もちろん文字列として同一視できる
true
julia> jgm0 === jgm1 === jgm2 # リテラルとしても同一になる(今回の場合)!
true
```
<!-- .element: style="font-size:55%" -->
Note:
※リテラルとして同一になる(今回の場合)ので `===` で比較しても `true` になる!
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point
+ 文字列の折り返し便利になった!
<!-- .element: style="font-size:2em" -->
Note:
Julia v1.6.x 以前を捨てて良いコードならば。
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 参考:v1.6.x も対象のコードなら…
```julia
julia> jgm0 = "寿限無寿限無五劫の擦り切れ海砂利水魚の水行末雲来末風来末食う寝る処に住む処やぶらこうじのぶらこうじパイポ・パイポ・パイポのシューリンガン・シューリンガンのグーリンダイ・グーリンダイのポンポコピーのポンポコナの長久命の長助";
julia> jgm_oldstyle =
"寿限無寿限無五劫の擦り切れ" *
"海砂利水魚の水行末雲来末風来末" *
"食う寝る処に住む処やぶらこうじのぶらこうじ" *
"パイポ・パイポ・パイポのシューリンガン・" *
"シューリンガンのグーリンダイ・グーリンダイのポンポコピーのポンポコナの" *
"長久命の長助";
julia jgm0 == jgm_oldstyle
true
```
<!-- .element: style="font-size:45%" -->
Note:
※ なお最適化の関係でこれくらいの長さの文字列ならたぶん `jgm0 === jgm_oldstyle` になります。。。
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## シチュエーション4: `OrderedDict` とか `SortedSet` とか
+ Julia の `Dict` や `Set` って順不同ですよね…
+ `OrderdDict` とか `SortedSet` 欲しい…
<!-- .element: style="font-size:100%" -->
⇒<!-- .element: class="fragment" data-fragment-index="1" -->
Note:
+ Julia の `Dict` や `Set` って追加順やキーの序列と列挙順が順不同ですよね…
+ パフォーマンスが理由でそうなっているのだろうとは思うのだけれど、やっぱ所謂 `OrderdDict` とか `SortedSet` 欲しい…ん…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決1:外部パッケージ `DataStructures` にあるからそれ使うのが素直な解決法
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> # 標準の Dict: 順不同
dic = Dict(["Carol", "Alice", "Ellen", "Bob", "Dave"].=>1:5)
Dict{String, Int64} with 5 entries:
"Carol" => 1
"Alice" => 2
"Dave" => 5
"Ellen" => 3
"Bob" => 4
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
(@v1.x) pkg> add DataStructures
# 出力例略
julia> using DataStructures
julia> # OrderedDict: 追加順
odic = OrderedDict(["Carol", "Alice", "Ellen", "Bob", "Dave"].=>1:5)
OrderedDict{String, Int64} with 5 entries:
"Carol" => 1
"Alice" => 2
"Ellen" => 3
"Bob" => 4
"Dave" => 5
```
<!-- .element: style="font-size:45%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> # 標準の Set: 順不同
set = Set(["Carol", "Alice", "Ellen", "Bob", "Dave"])
Set{String} with 5 elements:
"Carol"
"Alice"
"Dave"
"Ellen"
"Bob"
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
(@v1.x) pkg> add DataStructures
# 出力例略
julia> using DataStructures
julia> # SortedSet: 値の昇順
sset = SortedSet(["Carol", "Alice", "Ellen", "Bob", "Dave"])
SortedSet{String, Base.Order.ForwardOrdering} with 5 elements:
"Alice"
"Bob"
"Carol"
"Dave"
"Ellen"
```
<!-- .element: style="font-size:45%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決2:`Vector` でキーの列を別管理する
Note:
列挙時にだけ追加順/ソート順が欲しい場合
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> dkeys = ["Carol", "Alice", "Ellen", "Bob", "Dave"];
julia> dic = Dict(dkeys.=>1:5)
Dict{String, Int64} with 5 entries:
"Carol" => 1
"Alice" => 2
"Dave" => 5
"Ellen" => 3
"Bob" => 4
```
<!-- .element: style="font-size:50%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> # 追加順に列挙
for key in dkeys
println("$(key) => $(dic[key])")
end
Carol => 1
Alice => 2
Ellen => 3
Bob => 4
Dave => 5
```
<!-- .element: style="font-size:45%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> # キーの昇順に列挙
for key in sort(dkeys)
println("$(key) => $(dic[key])")
end
Alice => 2
Bob => 4
Carol => 1
Dave => 5
Ellen => 3
```
<!-- .element: style="font-size:45%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point1
+ `DataStructures.jl` 便利♪
+ `OrderedDict`/`SortedSet` の他に `SortedDict`/`OrderedSet` ももちろんある
+ 他にも有名どころのデータ構造多数用意
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point2
+ キーを `Vector` で別管理するのもお手軽で十分
+ 列挙時にだけ追加順/ソート順が欲しい場合ならば
+ 長さが膨大でなければ
Note:
※ その分メモリを消費するが、列挙時にだけ追加順/ソート順が欲しいならそれで十分
※ ↑の「解決2」でもそんなに困らない(長さが膨大でなければ)
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## シチュエーション5: 無名関数の多重定義
+ `fn = x -> x*x` ⇒ `fn(x, y)` はメソッドエラー(まぁ当然)
+ `fn(x) = x*x` → `fn(x, y) = ~` と多重定義できる
+ 前者の方法で定義した関数(=無名関数)は多重定義できないの?
<!-- .element: style="font-size:100%" -->
⇒<!-- .element: class="fragment" data-fragment-index="1" -->
Note:
+ `fn = x -> x*x` みたいにして関数を定義すると、`fn(x, y)` のような呼び出しは失敗しますよね。多重定義されていないから。
+ `fn(x) = x*x` という定義にすれば良い、そうすれば後で `fn(x, y) = ~` と多重定義できる、それは分かります。
+ では、前者の方法で定義した関数(=無名関数)は多重定義できないの?
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決:できます!
<!-- .element: style="font-size:240%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> fn = x -> x * x
#45 (generic function with 1 method)
julia> # 関数 `fn`(正確には変数 `fn` に格納されている無名関数)の多重定義
(::typeof(fn))(x, y) = x * y
julia> methods(fn)
# 2 methods for anonymous function "#45":
[1] (::var"#45#46")(x) in Main at REPL[121]:1
[2] (::var"#45#46")(x, y) in Main at REPL[122]:2
julia> @show fn(5) fn(5, 8)
fn(5) = 25
fn(5, 8) = 40
40
```
<!-- .element: style="font-size:50%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### Point
+ ほぼ使い途はありません!(重要)
<!-- .element: style="font-size:200%" -->
Note:
※ 重要:できるけれどほぼ使い途はありません!
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
## シチュエーション6: 文字列のインデックス
+ インデックスが 1-origin なのはもう慣れるしかないのでOK
+ 文字列のインデックスが連続じゃないのがどうしても慣れない…
+ 例えば「`s = "123ABCあいう😁漢字"` に対して「`s`の10文字目」と指定して「`'😁'`」を取得したい…。
<!-- .element: style="font-size:100%" -->
⇒<!-- .element: class="fragment" data-fragment-index="1" -->
Note:
+ Juliaの(配列などの)インデックスが 1-origin なのはもう慣れるしかないのでOKです。
+ 文字列(非ASCII文字を含む)のインデックスが連続じゃないのがどうしても慣れないです…。
+ 例えば「`s = "123ABCあいう😁漢字"` に対して「`s`の10文字目」と指定して「`'😁'`」を取得したい…。
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 解決1:`nextind()` という関数が用意されています!
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
help?> nextind
search: nextind IndexCartesian MissingException current_exceptions InterruptException InvalidStateException
nextind(str::AbstractString, i::Integer, n::Integer=1) -> Int
# :《中略》
• Case n > 1
Behaves like applying n times nextind for n==1. The only difference is that if n is so large that applying
nextind would reach ncodeunits(str)+1 then each remaining iteration increases the returned value by 1.
This means that in this case nextind can return a value greater than ncodeunits(str)+1.
# :《以下略》
```
<!-- .element: style="font-size:40%" -->
Note:
…
----
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
```julia
julia> s = "123ABCあいう😁漢字";
julia> nextind(s, 0, 10) # `0` より後で `10` 番目の文字のインデックス
16
julia> s[nextind(s, 0, 10)] # `s` の10文字目
'😁': Unicode U+1F601 (category So: Symbol, other)
```
<!-- .element: style="font-size:40%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 参考1:他の `xxxxind()` 系の関数
```julia
julia> # `thisind()`: 指定したあたりに存在する文字の正しいインデックス番号を返す
julia> s = "123ABCあいう😁漢字";
julia> thisind(s, 9)
7
julia> s[thisind(s, 9)]
'あ': Unicode U+3042 (category Lo: Letter, other)
julia> # `prevind()`:「後ろから何文字目」という指定が可能
julia> s[prevind(s, end+1, 3)] # 後ろから3文字目の文字
'😁': Unicode U+1F601 (category So: Symbol, other)
```
<!-- .element: style="font-size:40%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 参考2:`eachindex()` 関数
```julia
julia> s = "123ABCあいう😁漢字";
julia> # 文字列の全てのインデックスを列挙するなら↓
julia> eachindex(s)
Base.EachStringIndex{String}("123ABCあいう😁漢字")
julia> collect(eachindex(s))
12-element Vector{Int64}:
1
2
3
4
5
6
7
10
13
16
20
23
```
<!-- .element: style="font-size:40%" -->
Note:
…
---
<!-- .slide: data-background-color="rgba(96,173,81,0.3)" -->
### 参考3:`split()` の場合
```julia
julia> s = "123ABCあいう😁漢字";
julia> split(s, "")[10]
"😁"
```
<!-- .element: style="font-size:40%" -->
+ 結果は文字ではなく文字列
+ 一時的に配列が生成される
Note:
※似たようなことはできるが以下が異なる:
+ 結果は文字(`Char`)ではなく文字列(`SubString{String}`)になる
+ 一時的に文字列(部分文字列)の配列が生成されるのでパフォーマンスは決して良くない(使い方次第)
---
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
# まとめ
----
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
+ Julia 楽しい!
Note:
っ
---
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
# リンク
----
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
## 実験Note
+ [](https://mybinder.org/v2/gist/antimon2/0636c79fbdbcb322b6f4dac3f32c0616/a004c18b1c152219c891211fa0d0081c57fb4cf5?filepath=%E7%B4%B0%E3%81%8B%E3%81%99%E3%81%8E%E3%81%A6%E4%BC%9D%E3%82%8F%E3%82%89%E3%81%AA%E3%81%84%E3%81%8B%E3%82%82%E3%81%97%E3%82%8C%E3%81%AA%E3%81%84Julia%E3%81%AETips.jl.ipynb)
----
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
## 参考リンク
+ [Julia Documentation](https://docs.julialang.org/en/v1/)
+ [Base.findfirst](https://docs.julialang.org/en/v1/base/arrays/#Base.findfirst-Tuple{Any}) (method, Arrays)
+ [Base.findfirst](https://docs.julialang.org/en/v1/base/strings/#Base.findfirst-Tuple{AbstractString,%20AbstractString}) (method, Strings)
+ [Base.Iterators.dropwhile](https://docs.julialang.org/en/v1/base/iterators/#Base.Iterators.dropwhile) (function, Iteration utilities)
+ [Base.first](https://docs.julialang.org/en/v1/base/collections/#Base.first) (function, Collections and Data Structures)
+ [iteration](https://docs.julialang.org/en/v1/base/collections/#lib-collections-iteration) (section, Collections and Data Structures)
+ [Base.nextind](https://docs.julialang.org/en/v1/base/strings/#Base.nextind) (method, Strings)
---
<!-- .slide: data-background-color="rgba(170,121,193,0.3)" -->
ご清聴ありがとうございます。
Note:
ご清聴ありがとうございますっ!
{"metaMigratedAt":"2023-06-17T16:58:19.807Z","metaMigratedFrom":"YAML","title":"細かすぎて伝わらないかもしれないJuliaのTips","breaks":true,"slideOptions":"{\"transition\":\"slide\",\"theme\":\"league\"}","contributors":"[{\"id\":\"80062a4b-8dad-49ac-95bf-848ce0686e9e\",\"add\":39574,\"del\":24378}]"}