iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 6
0

Polymorphism

在講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 polymorphism

ad hoc是個拉丁字,指的是為了某個目的特別設置的,那什麼是特設的多型呢?
像是function overloadingoperator overloading就是一種ad hoc polymorphism
也就是依據傳入參數的型別組合,決定要使用哪一個function,而為不同參數的型別組合定義不同行為是特設的,而不是源自於內建的型別系統
然而,operator overloading其實是function overloading的特例!
怎麼說呢?我們可以把operator寫成function的樣子:
1 + 2 ≡ +(1, 2) ≡ add(1, 2)
這樣是不是覺得一樣了呢
所以ad hoc polymorphism指的就是function overloading這回事了

Parametric polymorphism

這跟ad hoc polymorphism的行為相對,ad hoc polymorphism會依據不同的型別組合去決定要用哪一個function
但是parametric polymorphism不考慮型別,而是提供一種行為框架,直接定義一個function,然後依據使用時傳入的型別做操作
像是C++中的template就有這樣的味道,但是他定義的是data type而不是function
泛型(generic programming),就是parametric polymorphism的一種表現方式
在其他語言中有這樣的generic functions就是parametric polymorphism的精隨了

Subtyping

或稱為subtype polymorphism,這個就是大家在物件導向語言中熟悉的polymorphism了
也就是subclass繼承了superclass的method介面,但是實作是依據subclass的method內容
所以我們可以把subclass當成superclass的子型別(subtype)

Multiple dispatch

介紹完了不同的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。

參數化method

參數化是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

這個函式在把xy兩個參數讀進來的同時,會去把他們的型別設為T,所以他會去對照兩個是不是同樣的。
所以我們可以不做任何判斷就把結果丟出去。

function same_type(x, y)
    return false
end

讓我們補上反面的判斷。編譯器會先檢查前面相同型別的,接著如果不適合就會落到後面的函式。

當然我們也可以加入一些限制,像是我只檢查T要是Number的子類別

function same_type{T<:Number}(x::T, y::T)
    return true
end

今天的教學就到這邊拉~~~


上一篇
[Day 05] Julia的型別系統
下一篇
[Day 07] Julia的物件導向世界
系列文
Julia語言—從入門到專案31

尚未有邦友留言

立即登入留言