函數 - 基礎篇
撐了好久,終於來到這個章節了,這個章節應該算是本書我個人最喜歡的章節!幾乎每個程式語言都有函數,我們在前面的章節或多或少都有用過一些函數,用最多的大概是把東西印出來的 print()
函數了。但在開始介紹函數之前,我想先問大家一個很基本的問題:你認為「函數(Function)」是什麼?
函數是什麼?
如果你曾經寫過其他程式語言,不知道你是否曾想過這個最最最基本的問題?到底什麼是函數?因為學校老師或是書上說要寫函數就是用 function
或 def
給它寫下去,裡面可能還有什麼參數啦、回傳值什麼的...。
我上課的時候最喜歡問同學們這種「為什麼」的問題,例如「為 什麼要寫函數?」,我通常會得到像是「因為函數可以重複使用」之類的答案。是沒錯啦,函數可以重複使用的確是使用函數的好處之一,但它並不是寫函數的目的。想想看,你是否有寫過一些函數,是根本沒有用第二次的?如果有,那你認為的「因為函數可以重複使用」這個說法就打自己臉了。所以,到底什麼是函數?
大家在國中或高中上數學課的時候有沒有看過這種東西:
f(x) = 3x + 2
當時數學老師會說這個叫做「一元一次方程式」,那個 x
叫做「代數」,如果 x
代 2 進去會得到 8,代 3 進去會得到 11,如果再代一次 2,還是會得到 8。這就是函數,前面那個 f 就是函數 function 的意思,只是大家可能沒意識到罷了(還是不願想起來?)
所以如果要我給「函數」一個定義,我會說:
函數是「輸入值」與「輸出值」之間的對應關係,而函數的名稱就是這個關係的名字。
把這些名詞換成程式語言的用語,輸入值就是「參數(parameter)」,而輸出值就是「回傳值(return value)」。一個好的函數,理想狀況是可以做到函數的輸出值只跟輸入值有關,只要是固定的輸入值,不管執行幾次,它的答案不會飄,都是固定的輸出值,不會因為時間、亂數或環境變數之類的「副作用(Side Effect)」而造成輸出值不同。如果可以做到沒有副作用的函數,我們也會稱這樣的函數叫「純函數(Pure Function)」,不純免錢。
給函數一個好的名字很重要,最好做到一眼就看出來這個函數想做什麼事。之所以會撰寫函數,是因為我們可以透過函數把原本比較繁瑣的流程抽象出來,我們的腦細胞就可以把重點放在怎麼使用這個函數,而不需要關注函數本身實作的細節。命名是電腦科學界的兩大難題之一,有好的名字是很難的,光是命名就能寫一整本書了。
想要再深入了解這方面的主題,可用關鍵字「函數式程式設計(Functional Programming)」再找其他資料研究。
所以,為什麼要使用函數?因為函數可以把某部份的程式碼抽象化,讓程式碼更容易閱讀,說的更白話一點寫函數就是幫某一段程式碼邏輯取個名字,讓我們不用閱讀函數的實作細節,只要看函數的名字就知道這個函數的用途。當然,函數可以重複使用也是一個好處,只是這就不太算寫函數的目的。
函數名稱的命名建議跟變數名稱一樣,規則也差不多,有些不該用或不能用的,例如 print
、input
這種原本就是 Python 的內建函數,用了之後原本的 print()
就不能用了;或是 True
、False
這些保留字,都不能用來當函數名稱。而且,畢竟函數的目的是幫 某一段程式碼取名字,所以函數名稱要有意義,能一看就知道這個函數是做什麼的,如果用 a
、b
、c
這類看不太出來用途的名字,倒不如不要寫函數。
定義函數
在 Python 定義函數,使用的是 def
關鍵字。在 def
後面接著函數的名稱以及一對小括號,最後別忘了加上冒號 :
,寫起來大概像這樣:
def say_hello():
print("Hello")
在 def
底下,就是函數的本體,也就是執行這個函數的時候實際上要做的事。有看到在第二行 print()
前面的留白嗎?那是刻意的,這在程式語言裡叫做「縮排(Indentation)」?如果我故意把縮排拿掉,像這樣:
def say_hello():
print("Hello") # 這會出錯,沒辦法執行