03 Function

前面講到,datetime module 裡面的now()today() 都是那個模組的功能,其實我們也可以自己寫功能
這些功能就叫 function
function 就是有 input (輸入) 就會給出 output (輸出) 的東西(跟數學一樣吧)

Syntax and Usage

function 用 def 來創造,創造出來之後,要用時再呼叫
下面這是一個最簡單的function

def main():
    print("this is a simple function")

main()

上面 def 是指宣告建立一個 function
main() 是指這個 function 的名稱叫 main,
括號內是這個 function 可以用的參數,現在沒有東西。
print 那一行是 main() 所執行的事情
最下面 main()呼叫這個 function
也就是說,如果沒有被呼叫,那麼main()裡面的東西就永遠不會跑



例如

def print1():
    print("1")

def print2():
    print("2")

def print3():
    print("3")

print1()
print2()
print3()

也可以把 function 的內容改成其他東西試試看唷


關於程式,還有一個重點叫「要寫可以重複使用的code」
例如一個打招呼的程式,要怎麼寫呢?

print("Hi")

嗎?
那現在如果要加名字呢?

確實我們也可以使用不同的變數
例如以下

name1 = "Iriii"
name2 = "Lilia"
name3 = "Tom"
print("hi")
print(name1)
print("hi")
print(name2)
print("hi")
print(name3)

但這樣重複性太高了,我們目的是要寫可以重複多次使用的東西
所以這時候 function 就很好用了


剛剛提到括號內的東西是那個 function 可以用的

def printName(name):
    print("Hi")
    print(name)


printName("Iriii")
printName("Lilia")
printName("Tom")

這樣就可以印出和上面相同的效果囉


第一行的 name 是一個可以代入的東西,function 裡有出現 name 的時候,
都是指呼叫 function 的那個當下 填入的東西




現在用上面的 printName() 和一個 for loop 寫出一個要求三個使用者輸入名字,
然後會跟輸入名字打招呼的程式吧
(例如我輸入LILIA,程式會說 Welcome LILIA!,然後接著繼續要我輸入其他名字)


Scope

現在來看看下面的code
猜猜看會跑出什麼呢?

def printAnyThing():
    print(a)

def main():
    a = "sdfghjk"
    print(a)
    printAnyThing()


main()

咦咦咦咦怎麼回事,怎麼出現
NameError: name 'a' is not defined


我們要來講一個很重要的概念囉
就是 scope!!!



Scope是什麼呢?


簡單來說,在記憶體的認知裡,每個東西都有自己的壽命
試想如果所有東西都一直佔新的記憶體,應該會像開太多程式一樣卡吧w


電腦決定東西在記憶體中存活的時間就是靠scope
(Google說scope的中文是範圍)
一旦出了那個東西屬於的scope,那個變數就會被忘記


Scope一般可以用縮排判斷


例如 for loop 裡,for i in range(10)i 就是一個變數
這個 i 在跑完這個 for loop 的時候就會被忘記


上面例子裡,a 屬於 main() 裡面,所以一旦出了他的 scope,就會不存在了
那有沒有可以用在所有scope裡的變數呢?


有!


就是最外層創立的變數!


這些在最外層(不用縮排)的變數叫: Global variable
而屬於自己的 scope 的則叫: Local variable


Local variable 如果和 Global variable 撞名,
這時候系統會假裝自己忘記 Global variable 存在,而去使用 Local 的那個
這個叫 shadowing
可以允許發生,但非常不建議



上面那個範例裡,還用了 main() 把主要的程式碼都包起來
事實上,這是接下來我們都要用的寫法喔



把所有主要驅動程式的 code 放在 main() 裡面有很多好處
除了可以減少不必要的記憶體浪費,也能讓整個程式架構更清楚


簡單來說,最外面那層可以放的東西只有

  • import module
  • Global variable (BAD)
  • 各種 def function()
  • def main() 必要!
  • main() 必要!
  • 之後會學的class 等


現在我們的程式總算有該有的樣子了:)))

Return Statement

現在讀讀看下面程式,會有error嗎? 為什麼?

a = "sdfghjk"
def printAnyThing():
    print(a)

def main():
    print(a)
    printAnyThing()


main()

那這個呢?

def myFunc():

    a = 50
    print(a)

def main():
    myFunc()
    print(a)

main()

NameError: name 'a' is not defined



現在來講關於 function 最後一個重點吧


既然所有 function 裡的東西在那個 function 的 scope 結束時都會消失
那總得有個方法留下資料吧


每一個 function 都需要一個 return
用法就像下面

def myFunc():
    a = 50
    return a

def main():
    
    print(myFunc())

main()

一看到 return ,這個 function 會立刻結束在那一行
並回傳 return 的唯一內容
這個值會成為那個 function 唯一留下的東西



嘗試理解下面 code 吧
會印出什麼呢?

def myFunc(a, b):
    ans = a + b
    return ans

def main():
    
    print(myFunc(3, 4))

main()




現在寫一個猜數字的遊戲吧

要能

  • 向使用者拿一個範圍
  • 隨機產生一個範圍內的數字
  • 讓使用者一直猜直到猜對

記住要有以下要素

  • 如果使用者輸入的不是 int 要求重新輸入

    (提示: while loop)
  • 產生出來的亂數必須包含使用者輸入的上限
  • 要有 main()
  • 決定數字是否正確的部分要是另一個名為 guessNumber() 的 function


其中一個可以參考的架構例如以下

def guessNumber(ans, num):
    if ans 大於或小於 num:
        print(太大/太小)
        return 一個東西
    else:
        猜對
        return 另一個東西


def main():
    向使用者取得範圍
    隨機產生那個正確數字

    while(一個條件):
        要求使用者猜數字
        guessNumber(答案, 使用者的輸入)

main()




Additional Readings

chapter 6 Functions https://runestone.academy/ns/books/published/thinkcspy/Functions/toctree.html