<span>Juliaの<!-- .element: style="font-size:60%" --><br></span><span>型システム<!-- .element: style="white-space:nowrap" --></span> 入門 === <!-- .element: style="font-size: 300%" --> <!-- .slide: data-background-color="rgba(102,130,223,0.3)" --> 2019/08/24 JuliaTokai \#03 antimon2(後藤 俊介) Note: Julia の型システム 簡易チュートリアル ---- <!-- .slide: data-background-color="rgba(102,130,223,0.3)" --> ## お品書き + お前誰よ? + 簡単なJuliaの紹介 + 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)** + コミュニティ:**[JuliaTokai](https://juliatokai.connpass.com/)**, **[機械学習名古屋](https://machine-learning.connpass.com/)**, [Python東海](https://connpass.com/series/292/), Ruby東海, … + 言語:**[Julia](https://julialang.org)**, Python, Scala(勉強中), Ruby, … + ![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):new: 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(204,102,51,0.3)" --> [![機械学習名古屋 第21回勉強会 Azure Machine Learning](https://i.imgur.com/SzM2KQs.png) https://machine-learning.connpass.com/event/139028/](https://machine-learning.connpass.com/event/139028/) Note: ちょうど1週間後に開催! --- <!-- .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.2.0(2019/08/20) + LTS v1.0.4(2019/05/16) + pre v1.3.0-rc1(2019/08/18) + 科学技術計算に強い! + 動作が速い!(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 Array{Int64,1}: 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 Array{Int64,1}: 1 4 9 16 25 julia> A = [x+10y for y=1:3, x=1:3] 3×3 Array{Int64,2}: 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 Array{Tuple{Int64,Int64,Int64},1}: (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 Array{Int64,2}: 1 2 3 4 julia> A' # `○'` は転置行列の記法(これも MATLAB/Octave 由来) 2×2 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}: 1 3 2 4 julia> transpose(A) # 正確には転置行列はこっち 2×2 LinearAlgebra.Transpose{Int64,Array{Int64,2}}: 1 3 2 4 ``` <!-- .element: style="font-size:50%" --> Note: Julia では2次元配列が行列の扱いっ あと `○.'` という書式は廃止されました(`transpose(A)` 使ってね)っ ---- <!-- .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 Array{Int64,2}: 4 2 3 10 julia> A * B # matrix multiply 2×2 Array{Int64,2}: 3 12 9 24 julia> A .* B # elementwise multiply 2×2 Array{Int64,2}: 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 Array{Float64,1}: 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)" --> # <small>Julia の</small><br>型システム --- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ## 基本 Note: というか取り敢えずコード例 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### `typeof` ```julia julia> n = 1; julia> typeof(n) # => Int64 julia> typeof("文字列") # => String julia> typeof([1.0, 2.0, 3.0]) # => Array{Float64,1} ``` Note: すべてのオブジェクトは(実行時に)型が決まっていますっ `typeof` 関数(組込関数)はオブジェクトに対する型を返す関数っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### `<:`、`>:` ```julia julia> Int <: Integer # => true julia> Integer >: Int # => true julia> Float64 <: Int # => false julia> Int <: Signed <: Integer <: Real <: Number <: Any # => true ``` <!-- .element: style="font-size=80%" --> Note: `a <: b` は `a` が `b` の subtype(descendant type) のときに `true` となる演算子(組込関数)っ `>:` はその逆(`(b >: a) == (a <: b)`)っ なお同一の型の場合も `true` となる(`(Int <: Int) == true`) ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### `supertype`、`subtypes` ```julia julia> supertype(Int) # => Signed julia> supertype(supertype(Int)) # => Integer julia> subtypes(Integer) # => [Bool, Signed, Unsigned] julia> Base.show_supertypes(Int) # => Int64 <: Signed <: Integer <: Real <: Number <: Any ``` <!-- .element: style="font-size=80%" --> Note: `supertype(T)` は 型 `T` の(直接の)基本型(親)を返す関数。 `subtypes(T)` は 型 `T` の(直接の)派生型(子)をすべて返す関数。 `Base.show_supertypes()` は export されていないので `Base.` 必須。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### `isa` ```julia julia> isa(1, Int) # => true julia> 1 isa Int # => true julia> isa(0x1234, Integer) # => true julia> isa(1.0, Integer) # => false ``` <!-- .element: style="font-size=80%" --> Note: `isa(v, T) == (typeof(v) <: T)` っ 中置記法の演算子としても記述可能っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### 型の定義例 (1) ```julia julia> struct Point x y end; julia> Point <: Any # => true julia> p = Point(2.0, 3.0); julia> typeof(p) # => Point julia> p isa Point # => true ``` <!-- .element: style="font-size=70%" --> Note: っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### 型の定義例 (2) ```julia julia> abstract type AbstractTime end; julia> struct MyTime <: AbstractTime hour::Integer minute::Integer second::Integer end; julia> MyTime <: AbstractTime <: Any # => true ``` <!-- .element: style="font-size=70%" --> Note: 定義例(1) は 型定義時に ` <: Any` を省略していると見なせる(言い換えると、`<: 〜` を省略すると `Any` の派生型になる)。 `mutable struct` で宣言を始めると(今日の本題じゃないので詳細略)っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### 型アノテーション (1) ```julia julia> function time2string(time::AbstractTime)::String "$(gethour(time)):$(getminute(time)):$(getsecond(time))" end; julia> time2string(MyTime(1,23,45)) # => "1:23:45" julia> typeof(ans) # => String ``` <!-- .element: style="font-size=70%" --> Note: 関数定義時、引数と戻り値の型を `x::T` のような書式でアノテーション出来る。 引数の場合は、`x isa T` となる値 `x` のみしか受け付けなくなる。 戻り値の場合は、最終的に戻り値の型チェックを行い、`T` と互換性があれば変換した上で返すが、なければエラーを投げる(実行時チェックであり、静的チェックがされるわけではない)。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### 型アノテーション (2) ```julia julia> function annotationsample(x) n::Int = x n += 1.0 n end; julia> annotationsample(0) # => 1 julia> typeof(ans) # => Int64 ``` <!-- .element: style="font-size=70%" --> Note: 関数本体等のローカルスコープでは、変数にも型アノテーションを付けることが出来る(REPLの地の部分やグローバルスコープではできない)。 それ以降その変数は指定した型であることが保証される。ただし静的チェックではなく、期待した型以外が来た場合は、まず変換を試み、変換できればそのまま、失敗したらエラーとなる(実行時チェック、戻り値の場合と同様)。 --- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ## Nominative Typing Note: ここからが今日の本題っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Nominative Typing + 日本語だと **公称型** + 名前ベースの型システム + 他の多くの静的型付言語でも採用 + Java, C++, C#, Swift, Rust, … Note: つまり **型名** が重要、ということ。 対称的なのは **Structural Typing**(構造型)、Ocaml, Haxe, TypeScript など **Duck Typing** はまた別(Ruby, Python 等) ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Nominal Subtyping + 明示的な宣言により部分型(派生型)を定義 + 例: `struct T <: A 〜 end` + (Julia の場合)構造の継承は行われない Note: 日本語だと、 **公称的部分型** ? なお Haxe は Structural typing だが、クラス継承は Nominal subtyping ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Abstract Type + **抽象型** + `isabstracttype(T) == true` + `abstract type 〜 end` で定義 + オブジェクト(インスタンス)を生成できない型 + (Julia の場合)他の型の **supertype**(基本型)に必ずなる + 例: `Any`, `Integer`, `AbstractString`, … Note: っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Concrete Type + **具象型**(または **具体型**) + `isconcretetype(T) == true` + `struct 〜 end` などで定義 + 全てのオブジェクトは何らかの具象型を持つ + (Julia の場合)決して他の型の supertype(基本型)に**ならない** + 例: `Int64`, `String`, `Date`, … Note: `mutable struct 〜 end` または `primitive type 〜 end` もあります(今日の本題じゃないので詳細略)っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Type Tree + 全ての型は **`Any`** の subtype(descendant type) + 全ての型は何らかの abstract type を1つだけ supertype(親)として持つ + abstract type は複数の型を subtype(子)として持つことができる + 型の親子関係は木構造を形成する Note: っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```graphviz digraph TypeTree { nodesep=1.0 node [fontname=Courier,shape=box,style=filled, fillcolor=gray95] edge [arrowsize=0.8] edge [arrowtail="empty" dir=back] Any -> AbstractString Any -> Number Any -> "Dates.AbstractTime" AbstractString -> String Number -> Real Real -> AbstractFloat AbstractFloat -> Float64 AbstractFloat -> Float32 Real -> Integer Integer -> Signed Signed -> Int32 Signed -> Int64 Integer -> Unsigned Unsigned -> UInt32 Unsigned -> UInt64 "Dates.AbstractTime" -> "Dates.TimeType" "Dates.TimeType" -> "Date" "Dates.TimeType" -> "DateTime" } ``` Note: これは Type Tree のほんの一部ですっ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Point + Julia は Nominative typing + Nominal subtyping で、**単一継承** <!-- .element: style="font-size:150%" --> --- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ## Parametric Type ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Parametric type + **型パラメータ** を取る型 + `Array{T,N}` など + 他言語の Generic type(総称型)に相当 + Java, C#, Scala, … Note: っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> #### Type parameter に指定出来るもの: <!-- .element: style="font-size:80%;text-align:left" --> + 型(`Int`, `String`, `Any`,…) + Symbol値(`:yes` 等) + `isbits()` に渡して `true` が返る値(整数値、実数値、関数等) + それらの tuple(`(Int,1,:a,+)` とか) Note: 型パラメータに指定出来るのは **型だけではない!** ただし parametric type の側で何番目の型パラメータはどんな種類のものが来るのか想定されて設計されている場合が殆どですっ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia julia> Array{Int, 1}; # 第1パラメータは型、第2パラメータは整数値 julia> Val{:+}; # パラメータはSymbol julia> typeof(Val(+)) # => Val{+} # パラメータは関数 ``` Note: `Array` は、型と整数値の2つを型パラメータとしてとる `Val` は、型パラメータとして渡せる任意の値を1つだけとる ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### UnionAll type + Parametric な型で、型パラメータが満たされていないもの + 例:`Array`, `Array{Int}`, … + UnionAll type は abstract type でも concrete type でもない(微妙に重要) Note: `Array{Int, 1}` は UnionAll ではなく concrete type。 あと `Vector{Integer}` は concrete だが `Vector{<:Integer}` は UnionAll ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Parametric subtyping (1) ```julia julia> Array{Int, 1} <: Array{Int} <: Array <: Any # => true julia> all(isa([1], T) for T=( Array{Int, 1}, Array{Int}, Array)) # => true ``` Note: 型パラメータはより後方のものが省略可能で、省略されるとより制約がゆるくなる感じ。 その「制限が厳しくなる方向」に subtyping 関係が構築されるっ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### 型制約 + 型パラメータに制約を持たせること + 例1: `Array{T} where {T <: Integer}` + 例2: `Vector`(⇔ `Array{T, 1} where T`) + 例3: `Array{<:Integer}`(例1の省略形) + 型制約の付いた型は UnionAll type Note: 例1は **上限制約** 型パラメータの supertype(ancestor type)に上限を設けている。`>:` を用いて **下限制約** を設けることも出来る(`〜<:T<:〜` のようにすると両側に制約) 例2は **制約なし** という制約。それを利用して型パラメータの一部を固定している。 例3の省略形は、後で `T` を **型変数** として利用しない場合に有用。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Parametric subtyping (2) ```julia julia> Vector{Int} <: Vector{<:Integer} <: Vector <: Any # => true julia> Vector{Int} <: Vector{Integer} # => false julia> isa.(Ref([1]), (Vector{Int}, Vector{<:Integer}, Vector{Integer}, Vector)) # => (true, true, false, true) ``` Note: 型パラメータに指定した型に継承関係があっても、Parametric type の継承関係とは無関係。 ただし型制約で上限下限等を指定した場合にはそれに合致すれば継承関係が形成される。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```graphviz digraph TypeTree { nodesep=1.0 node [fontname=Courier,shape=box,style=filled, fillcolor=gray95] edge [arrowsize=0.8] edge [arrowtail="empty" dir=back] Any -> AbstractArray // AbstractArray -> "AbstractArray{Integer}" AbstractArray -> Array AbstractArray -> "AbstractArray{<:Integer}" // "AbstractArray{Integer}" -> "Array{Integer}" // "Array" -> "Array{Integer}" "AbstractArray{<:Integer}" -> "Array{<:Integer}" "AbstractArray{Int}" -> "Array{Int}" "Array{<:Integer}" -> "Array{Int}" "Array" -> "Array{<:Integer}" "AbstractArray{<:Integer}" -> "AbstractArray{Int}" AbstractArray -> AbstractVector // AbstractVector -> "AbstractVector{Integer}" // "AbstractArray{Integer}" -> "AbstractVector{Integer}" Array -> Vector AbstractVector -> Vector AbstractVector -> "AbstractVector{<:Integer}" "AbstractArray{<:Integer}" -> "AbstractVector{<:Integer}" "AbstractArray{Int}" -> "AbstractVector{Int}" "Vector{<:Integer}" -> "Vector{Int}" "AbstractVector{<:Integer}" -> "Vector{<:Integer}" "Vector" -> "Vector{<:Integer}" "Array{<:Integer}" -> "Vector{<:Integer}" "AbstractVector{<:Integer}" -> "AbstractVector{Int}" "AbstractVector{Int}" -> "Vector{Int}" "Array{Int}" -> "Vector{Int}" } ``` Note: これは Type Tree のほんの一部ですっ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Point + Parametric type! + Parametric subtyping! <!-- .element: style="font-size:170%" --> --- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> # 型と多重ディスパッチ Note: より正確には、「型派生(subtyping)と多重ディスパッチ」 --- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ## 例:「リスト(配列)」 vs `sum` Note: っ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### `Array` vs `sum` ```julia sum(x::AbstractArray) = reduce(+, x) # sum([1, 2, 4]) == 7 ``` Note: 正確にはもう少し細かいコードになっているが、実質↑これと同値。 つまり、「`x` の各要素を取り出して `+` で和を取る」という定義になっている。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### `Range` vs `sum` ```julia function sum(r::AbstractRange) l = length(r) return l * first(r) + step(r) * ((l-1) * l ÷ 2) end # sum(1:10) == 55 ``` Note: 正確にはもう少し細かいコードになっているが、実質↑これと同値。 つまり、長さ(=要素数)を取得した上で等差数列の和の公式を利用しているだけ。 あと `AbstractRange <: AbstractArray` だが、先に定義した方は呼ばれない。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### 等比数列 vs `sum` Note: まずは等比数列型を定義するところから。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia struct GeometricSequence{T<:Number} <: AbstractArray{T,1} a::T r::T n::Int end ``` Note: 何気に AbstractArray を継承した型の定義例にもなってますっ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> $$ \begin{eqnarray*} S_n = \frac{a(1-r^n)}{1-r} \end{eqnarray*} \\ \Downarrow $$ ```julia sum(seq::GeometricSequence) = seq.a * (1 - seq.r^seq.n) / (1 - seq.r) # sum(GeometricSequence(0.5, 0.5, 5)) == 0.96875 ``` Note: 簡単のためオーバーフローのことは考えてないコードになっていますっ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> #### 整数の等比数列 vs `sum` ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia sum(seq::GeometricSequence{<:Integer}) = seq.a * (1 - seq.r^seq.n) ÷ (1 - seq.r) # sum(GeometricSequence(1, 3, 5)) == 121 ``` Note: この多重定義をしなくても算出は出来るが、結果が `121.0` という実数値(`Float64`)になってしまう。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> #### 有理数の等比数列 vs `sum` ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> $$ a = \frac{c}{b}, r = \frac{q}{p} \\ \Downarrow \\ \begin{eqnarray*} S_n &=& \frac{a(1-r^n)}{1-r} \\ &=& \frac{\frac{c}{b}(1-(\frac{q}{p})^n)}{1-\frac{q}{p}} \\ &=& \frac{c(p^n-q^n)}{bp^{n-1}(p-q)} \end{eqnarray*} \\ \Downarrow $$ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia function sum(seq::GeometricSequence{<:Rational}) c = numerator(seq.a) b = denominator(seq.a) q = numerator(seq.r) p = denominator(seq.r) n = seq.n c*(p^n-q^n)//(b*p^(n-1)*(p-q)) end # sum(GeometricSequence(1//1, 1//2, 6)) == 63//32 ``` Note: この多重定義をしなくても算出は出来るが、除算(`//`)が1回だけになるのでパフォーマンスは圧倒的に良くなる。↓ ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia julia> using BenchmarkTools julia> @btime sum_org(GeometricSequence(1//1, 1//2, 6)) # => 240.141 ns (1 allocation: 32 bytes) # => 63//32 julia> @btime sum(GeometricSequence(1//1, 1//2, 6)) # => 83.133 ns (1 allocation: 32 bytes) # => 63//32 ``` ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Point + 多重定義されている場合、引数に渡る型により近い(基本)型で引数が宣言された関数(メソッド)が呼ばれる + より下方の(派生)型で関数を多重定義することで、うまく型設計すればより正しい計算結果となるようになったり、パフォーマンス改善も見られる --- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ## 補足:特別な型 ※説明は省くので各自見ておいてね <!-- .element: class="fragment" style="text-align:left;font-size:140%" --> ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Tuple types Note: Tuple だけは Julia の型システムで特別扱い ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia julia> typeof((1,"foo",2.5)) # => Tuple{Int64,String,Float64} julia> (1,"foo",2.5) isa Tuple{Integer, AbstractString, AbstractFloat} # => true julia> typeof((a=1,b="foo",c=2.5)) # => NamedTuple{(:a, :b, :c),Tuple{Int64,String,Float64}} ``` ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> + Tuple を値とする型 + 型パラメータいくつでも指定可 + 型パラメータは **共変** + 例えば `Tuple{Int} <: Tuple{Integer} <: Tuple{Any}` が成立 + フィールドを持たない(要素にインデックスで参照する) + `NamedTuple` ならプロパティ名でも参照できる(けれどフィールドではない) Note: 型パラメータに `Vararg{T}` を利用すると、要素数不定の `Tuple` も宣言・利用できる。 あと `NamedTuple` もあるよ。 ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Union type Note: 所謂 **直和型** ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia julia> Int <: Union{Int, String} # => true julia> all(isa(v, Union{Number,String}) for v in (1,"foo",2.5)) # => true ``` ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> + 継承関係を持たない複数の型の **直和型** + `Int <: Union{Int, String}` かつ `String <: Union{Int, String}` + 型パラメータが1つだけならその型自身と等しい(`Union{Int} === Int`) + 型パラメータが継承関係にあれば、より基本型(親)のものと等しい(`Union{Int, Integer} === Integer`) Note: Julia に `Maybe` 型とか `Option` 型 はないが、`Union{T,Nothing}` でエミュレートは出来る(実際 Julia 内部でも使われている) ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> #### Bottom type + `Union{}` のこと。 + 全ての型の subtype となる型(`Union{} <: Int`、`Union{} <: String`) Note: Type Tree で常に葉(末端)になるので Bottom type と言います。対称となる Top type は `Any`。 使い途は知りませんw ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ### Singleton type Note: 他言語で言うところの **特異型** ? ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> ```julia julia> Int isa Type{Int} # => true julia> Int isa Type{Integer} # => false julia> Int isa Type{<:Integer} # => true ``` ---- <!-- .slide: data-background-color="rgba(96,173,81,0.3)" --> + `Type{T}` + ある型(`T`)を唯一の値(インスタンス)として持つ型(メタ型) + 例:`Int isa Type{Int}` + 型制約を付ければ継承関係も表せる(`Int isa Type{<:Integer}`) Note: っ --- <!-- .slide: data-background-color="rgba(170,121,193,0.3)" --> # まとめ ---- <!-- .slide: data-background-color="rgba(170,121,193,0.3)" --> + Subtyping 使いこなせ! + 多重ディスパッチ使いこなせ! + Julia 楽しい! Note: っ ---- <!-- .slide: data-background-color="rgba(170,121,193,0.3)" --> ## 参考リンク + [Types](https://docs.julialang.org/en/v1/manual/types/) - [Julia Documentation](https://docs.julialang.org/en/v1) + [More about types](https://docs.julialang.org/en/v1/devdocs/types/) - [Julia Documentation](https://docs.julialang.org/en/v1) + [18. Subtyping](https://benlauwens.github.io/ThinkJulia.jl/latest/book.html#chap18) - [ThinkJulia.jl](https://benlauwens.github.io/ThinkJulia.jl/latest/book.html) --- <!-- .slide: data-background-color="rgba(170,121,193,0.3)" --> # おまけ ---- <!-- .slide: data-background-color="rgba(44,214,221,0.3)" --> ## 来栖川電算 Note: スポンサー枠っ ---- <!-- .slide: data-background="https://i.imgur.com/5CbxlN7.jpg" data-background-color="rgba(44,214,221,0.3)" --> Note: ![募集中!](https://i.imgur.com/5CbxlN7.jpg) ---- <!-- .slide: data-background-color="rgba(204,102,51,0.3)" --> ## 機械学習名古屋 ---- <!-- .slide: data-background-color="rgba(204,102,51,0.3)" --> [![機械学習名古屋 第21回勉強会 Azure Machine Learning](https://i.imgur.com/SzM2KQs.png) https://machine-learning.connpass.com/event/139028/](https://machine-learning.connpass.com/event/139028/) Note: ちょうど1週間後に開催!(再) --- <!-- .slide: data-background-color="rgba(170,121,193,0.3)" --> ご清聴ありがとうございます。 Note: ご清聴ありがとうございますっ!
{"metaMigratedAt":"2023-06-14T23:21:14.947Z","metaMigratedFrom":"YAML","title":"<span>Juliaの<!-- .element: style=\"font-size:60%\" --><br></span><span>型システム<!-- .element: style=\"white-space:nowrap\" --></span> 入門","breaks":false,"slideOptions":"{\"transition\":\"slide\",\"theme\":\"league\"}","contributors":"[{\"id\":\"80062a4b-8dad-49ac-95bf-848ce0686e9e\",\"add\":25468,\"del\":14535}]"}
    3686 views