# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .jl
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Julia 0.6.2
#     language: julia
#     name: julia-0.6
# ---

# + [markdown] slideshow={"slide_type": "slide"}
# # Introduction to Julia Programming IV
#
# ### Methods
#
# **by <NAME>**

# + [markdown] slideshow={"slide_type": "slide"}
# # Outline
#
# * define methods
# * method ambiguity
# * parametric method
# * design patterns with parametric methods
# * empty generic function
# * method design

# + [markdown] slideshow={"slide_type": "slide"}
# ## 前情提要

# + [markdown] slideshow={"slide_type": "fragment"}
# 程式設計中，設計相對應的Type來搭配演算法使用是很重要的。
#
# Type的設計除了資料結構本身也包含可以對資料結構做的操作。
#
# 今天要講的就是這些method。

# + [markdown] slideshow={"slide_type": "slide"}
# ## Define methods

# + slideshow={"slide_type": "fragment"}
f(x::Float64, y::Float64) = 2x + y

# + slideshow={"slide_type": "fragment"}
f(2.0, 3.0)

# + [markdown] slideshow={"slide_type": "fragment"}
# 看起來不就跟function一樣嗎？

# + [markdown] slideshow={"slide_type": "slide"}
# ### Function與method的差別
# -

f(x::Number, y::Number) = 2x - y
f(2.0, 3)

# + [markdown] slideshow={"slide_type": "fragment"}
# 你會發現Julia幫`f(Float64, Int64)`這樣的組合挑選了`f(Number, Number)`這個method

# + slideshow={"slide_type": "fragment"}
methods(f)  # 你可以查詢目前這個函式名稱有多少種實作

# + [markdown] slideshow={"slide_type": "fragment"}
# 在 Julia，function 指的是`f`，這個**介面**
#
# Method 指的則是`f(x::Number, y::Number) = 2x - y`，這個**實作**

# + [markdown] slideshow={"slide_type": "slide"}
# ## 介面與實作
# -

# #### *介面*
#
# `fly`
#
# #### *實作*
#
# `fly(bird::Bird) = println("Bird flies.")`
#
# `fly(airplane::Airplane) = println("Airplane flies.")`

# + [markdown] slideshow={"slide_type": "fragment"}
# 程式語言，如同人類的自然語言一樣，對應不同的**情境**，同一個詞有不同意思。
#
# 最多變的會是**行為**
#
# 為了對應不同的情境，可能有不同版本的實作

# + [markdown] slideshow={"slide_type": "slide"}
# ## 對程式語言來說，他要如何決定要使用哪一個function的實作版本？

# + [markdown] slideshow={"slide_type": "slide"}
# ### *Multiple dispatch（多重分派）*
# -

# <img src="/pics/multiple_dispatch.svg" style="width:300px;"/>

# + [markdown] slideshow={"slide_type": "fragment"}
# 在Julia中，如果有很多個相同名字的methods的話，要決定用哪一個呢？
#
# Julia會依據**參數的數量**跟**method中所有型別種類**決定要挑哪一個method出來執行
#
# 使用**method中所有型別種類**決定要執行哪一個method，這樣的方法稱為multiple dispatch

# + [markdown] slideshow={"slide_type": "slide"}
# ## Method ambiguity
# -

g(x::Float64, y) = 2x + y

g(x, y::Float64) = x + 2y

# + slideshow={"slide_type": "fragment"}
g(3.0, 4.0)

# + [markdown] slideshow={"slide_type": "fragment"}
# 這樣的定義會造成語意不清，`g(Float64, Float64)`要執行哪一條呢

# + [markdown] slideshow={"slide_type": "slide"}
# ### 由精確到廣義的定義順序是很棒的方式
# -

g(x::Float64, y::Float64) = 2x + 2y
g(x::Float64, y) = 2x + y
g(x, y::Float64) = x + 2y

# + slideshow={"slide_type": "fragment"}
g(2.0, 3)

# + slideshow={"slide_type": "fragment"}
g(2, 3.0)

# + slideshow={"slide_type": "fragment"}
g(2.0, 3.0)

# + [markdown] slideshow={"slide_type": "slide"}
# ## Parametric method
# -

# 聰明的設計讓multiple dispatch替你"回答問題"

# + slideshow={"slide_type": "fragment"}
same_type(x::T, y::T) where {T} = true
same_type(x, y) = false

# + slideshow={"slide_type": "fragment"}
same_type(1, 2)  # 兩者型別相同

# + slideshow={"slide_type": "fragment"}
same_type(1, 2.0)  # 兩者型別不同

# + [markdown] slideshow={"slide_type": "slide"}
# ### *Example*
# -

concat(v::Vector{T}, x::T) where {T} = [v..., x]

# + slideshow={"slide_type": "fragment"}
concat([1, 2, 3], 4)

# + slideshow={"slide_type": "fragment"}
concat([1, 2, 3], 4.0)

# + [markdown] slideshow={"slide_type": "slide"}
# ### 在method的使用上**加上constraints**
# -

foobar(a, b, x::T) where {T <: Integer} = (a, b, x)  # 限制參數型別

# + slideshow={"slide_type": "fragment"}
foobar(1, 2, 3)

# + slideshow={"slide_type": "fragment"}
foobar(1, 2.0, 3)

# + slideshow={"slide_type": "fragment"}
foobar(1, 2.0, 3.0)

# + [markdown] slideshow={"slide_type": "slide"}
# ## Polymorphism
# -

# 多型，是在物件導向風格裏面很重要的特性，讓subtype可以繼承supertype的method，method會依據不同的subclass有不同的行為
#
# polymorphism擁有更廣泛的意思：**method會依據不同的subclass有不同的行為**
#
# 也就是說，polymorphism不只是單單放在物件導向的繼承上，只要符合**同樣的function會依據不同型別而有不同行為**就算

# + [markdown] slideshow={"slide_type": "slide"}
# 若是依據維基百科的定義：
#
# > *Polymorphism is the provision of a single interface to entities of different types.*
#
# > *多型為不同型別的實體提供了單一介面*
#
# 說到底，polymorphism就是為了要***在同樣的介面上提供不同的實作***。

# + [markdown] slideshow={"slide_type": "slide"}
# ### ***Parametric polymorphism***
# -

# parametric polymorphism不考慮確切的型別，而是提供一種行為框架，直接定義一個function，然後依據使用時傳入的型別做操作
#
# Julia 本身就是採用這樣的方式。
#
# **泛型（generic programming）**，就是parametric polymorphism的一種表現方式
#
# 在其他語言中有這樣的generic functions就是parametric polymorphism的精隨了

# + [markdown] slideshow={"slide_type": "slide"}
# ## Design patterns with parametric methods
# -

# ```julia
# abstract type Animal end
# abstract type Dog <: Animal end
# abstract type Cat <: Animal end
#
# type Labrador <: Dog
#     ...
# end
#
# type GoldenRetriever <: Dog
#     ...
# end
# ```

# + [markdown] slideshow={"slide_type": "slide"}
# ### constraints
# -

# #### subtype

# ```julia
# isanimal(::T) where {T <: Animal} = true
# ```

# + [markdown] slideshow={"slide_type": "slide"}
# #### supertype
# -

# ```julia
# issupertype(::T) where {T >: Labrador} = true
# ```

# + [markdown] slideshow={"slide_type": "slide"}
# #### range
# -

# ```julia
# g(::T) where {Labrador <: T <: Animal} = true
# ```

# + [markdown] slideshow={"slide_type": "slide"}
# ### equivalence
# -

# ```julia
# concat(v::Vector{T}, x::T) where {T} = [v..., x]
# ```

# + [markdown] slideshow={"slide_type": "slide"}
# ### replace if-else by dispatching
# -

# ```julia
# collections = []
# for x in xs
#   if x::String
#     push!(collections, Vector([x]))
#   elseif x::Vector
#     push!(collections, x)
#   end
# end
# ```

# + [markdown] slideshow={"slide_type": "slide"}
# ```julia
# handle!(collections, x::String) = (push!(collections, Vector([x])))
# handle!(collections, x::Vector) = (push!(collections, x))
# ```
# -

# ```julia
# collections = []
# for x in xs
#   handle!(collections, x)
# end
# ```

# + [markdown] slideshow={"slide_type": "slide"}
# ## Empty generic function
# -

# ***有的時候你需要定義method的介面，但不定義實作***
#
# 這樣介面跟實作分離的使用情境時常用在增加程式碼的可讀性上
#
# 你可以這樣寫：

# + slideshow={"slide_type": "fragment"}
function generic  # 沒有參數，作為一個佔位符使用
end

# + slideshow={"slide_type": "fragment"}
methods(generic)

# + slideshow={"slide_type": "slide"}
generic()

# + [markdown] slideshow={"slide_type": "slide"}
# ## Q&A
# -



StarcoderdataJupyterScriptsDedupFiltered