在講method的定義之前,我們先來聊聊多型(polymorphism)
多型,大家第一個會想到的大多都是在物件導向風格裏面,讓subclass繼承superclass的method,method會依據不同的subclass有不同的行為。
但是polymorphism擁有更廣泛的意思,我們把焦點聚焦在最後一段:method會依據不同的subclass有不同的行為。
也就是說,polymorphism不只是單單放在物件導向的繼承上,只要符合同樣的function會依據不同型別而有不同行為就算。
若是依據維基百科的定義:
Polymorphism is the provision of a single interface to entities of different types.
多型為不同型別的實體提供了單一介面。
看到這裡大家想到什麼了呢?
對!在C++裏面函式多載(function overloading)跟運算子多載(operator overloading)也是一種polymorphism!
說到底,polymorphism就是為了要在同樣的介面上提供不同的實作,由這個思考點出發,大家就不會被印象中物件導向的那個polymorphism束縛了。
ad hoc是個拉丁字,指的是為了某個目的特別設置的,那什麼是特設的多型呢?
像是function overloading跟operator overloading就是一種ad hoc polymorphism
也就是依據傳入參數的型別組合,決定要使用哪一個function,而為不同參數的型別組合定義不同行為是特設的,而不是源自於內建的型別系統
然而,operator overloading其實是function overloading的特例!
怎麼說呢?我們可以把operator寫成function的樣子:1 + 2 ≡ +(1, 2) ≡ add(1, 2)
這樣是不是覺得一樣了呢
所以ad hoc polymorphism指的就是function overloading這回事了
這跟ad hoc polymorphism的行為相對,ad hoc polymorphism會依據不同的型別組合去決定要用哪一個function
但是parametric polymorphism不考慮型別,而是提供一種行為框架,直接定義一個function,然後依據使用時傳入的型別做操作
像是C++中的template就有這樣的味道,但是他定義的是data type而不是function
泛型(generic programming),就是parametric polymorphism的一種表現方式
在其他語言中有這樣的generic functions就是parametric polymorphism的精隨了
或稱為subtype polymorphism,這個就是大家在物件導向語言中熟悉的polymorphism了
也就是subclass繼承了superclass的method介面,但是實作是依據subclass的method內容
所以我們可以把subclass當成superclass的子型別(subtype)
介紹完了不同的polymorphism,來講講Julia中舉足輕重的特性,multiple dispatch
那什麼是dispatch呢?dispatch的意思是分派、分配,也就是說語言要如何決定要執行哪一個method的實作內容
在Julia中,如果有很多個相同名字的methods的話,要決定用哪一個呢?
通常我們在其他語言裡面,像是Python
class Foo:
...
def double(x):
return 2*x
class Bar:
...
def double(x):
return str(x)*2
一旦我們定義了class,基本上就會依據class的種類來決定要用哪個版本的method
這稱為dynamic dispatch
Julia會依據參數的數量跟method中所有型別種類決定要挑哪一個method出來執行
function double(obj::Foo, x)
return 2*x
end
function double(obj::Bar, x)
return string(x)*2
end
使用method中所有型別種類決定要執行哪一個method,這樣的方法稱為multiple dispatch
g(x::Float64, y) = 2x + y
g(x, y::Float64) = x + 2y
這樣的定義會造成語意不清,g(Float64, Float64)要執行哪一條呢?
避免語意不清,由精確到廣義的定義順序是很棒的方式
g(x::Float64, y::Float64) = 2x + 2y
g(x::Float64, y) = 2x + y
g(x, y::Float64) = x + 2y
Julia會依照定義的順序將使用者所呼叫的函式進行對映,一個蘿蔔一個坑。
如此一來我們可以很優雅的定義細部的規則,進而避免掉多重條件判斷,像是在其他語言裡的switch。
這個特質或許更接近functional language中的pattern matching。
參數化是Julia中重要的特質。
我們可以寫個函式來確認兩個參數型別是否相同。
一般都會寫成這樣:
function same_type(x, y)
if typeof(x) == typeof(y)
return true
else
return false
end
end
我們可以動點手腳
function same_type{T}(x::T, y::T)
return true
end
這個函式在把x
跟y
兩個參數讀進來的同時,會去把他們的型別設為T
,所以他會去對照兩個是不是同樣的。
所以我們可以不做任何判斷就把結果丟出去。
function same_type(x, y)
return false
end
讓我們補上反面的判斷。編譯器會先檢查前面相同型別的,接著如果不適合就會落到後面的函式。
當然我們也可以加入一些限制,像是我只檢查T
要是Number
的子類別
function same_type{T<:Number}(x::T, y::T)
return true
end
今天的教學就到這邊拉~~~