iT邦幫忙

6

【Python 超入門】(10) 自定義函數介紹- 主人向他的機器人管家抱怨:「你跟我這麼多年了,也該了解我們家的習慣了吧?」

大家好,我是「心原一馬」,內心原來一心喜歡打程式碼。
現在科技進步神速的時代,
說不定未來機器人管家普及,能夠幫人類打理各種家中雜務也不是夢想。

從前,諸如日常瑣事譬如「煮飯、洗衣、倒垃圾…」等等,
媽媽說「新好男人要會處理家務」推給爸爸,
爸爸說「小孩長大了要學做家事」推給姐姐,
姐姐說「她要準備大學聯考」推給弟弟妹妹做,
弟妹說「家中小狗每天只會吃跟玩耍」推給小狗做。
未來,說不定推給機器人管家做,家中成員不再爭吵。

以下給大家講講心原一馬腦洞大開的未來故事劇情來帶大家認識函數這個概念:

故事: 這隻機器人還真不知變通

在未來機器人有能力幫人類打理各種家中雜務的年代…
一天早晨,主人跟他的機器人管家小白說,
「嘿,小白,我出門去上班,你等等去幫忙洗衣服,下午五點鐘時去煮飯。」
小白說道:「主人,機器人絕對服從人類的指令,但是您下的指令不夠明確,無法執行。」
主人說:「不就是叫你洗衣服煮飯嗎?」
小白:「洗衣服這句話有歧義。你沒有講清楚你要我用手洗還是放到洗衣機洗?」
小白:「另外你沒有講清楚洗衣服要洗幾件; 要洗衣服那就不洗褲子的意思嗎?」
主人:「喂,這年代還有人在用手洗衣服的嗎?」
主人:「另外,人類說洗衣服的時候,表示衣服褲子都要洗的意思。」

小白:「哦~ 原來是這樣喔。我覺得你們人類的語言有時候很奇怪耶~」
小白:「像是你們晚餐前會說「開飯囉」,所以那一餐如果吃麵的話,不是應該說「開麵囉」才合邏輯嗎?」
主人:「呃…這個…」
主人哭笑不得… 這年代,語音辯識都這麼發達了,
機器人都可以跟人類聊天了,
居然自己家的小白連人類的常識都不太懂,
早知當初不要為了省錢買二手的機器人管家了。

來給小白定義個函數吧

小白:「為了使用者的安全著想,您一定要下非常明確的指令我才能夠執行的。」
主人:「喂喂,你前天才幫忙洗過衣服的。」
小白:「也許您今天想洗衣服的方式有變,我必須做嚴謹的確認才行。」
大概主人也有點煩了,說:「好,小白我跟你說,你看到浴室的洗衣籃了嗎?」
小白: 「看到了。」
主人: 「以後我下洗衣服這個指令時,你就把洗衣籃裡的衣物全部放進洗衣機裡,然後按下洗衣機開關。不必再問說用手洗還是放到洗衣機洗?要洗幾件?知道嗎?」
小白: 「哦哦~ 知道了,你這樣講我清楚多了。」

自定義函數- 把常常在程式碼中使用的指令包裝起來

好的,回到我們的教學正文。
函數可以把它想成是把常做的事情用一個指令包裝起來

例如我們之前教過幾個內建函數:
print(字串): 在螢幕上顯示出字串
input() : 取得使用者輸入的字串
str(變數): 將數字型態的變數轉換成字串型態
int(變數): 將字串型態的變數轉換成數字型態
len(列表): 取得列表的元素個數

以上是程式設計師們可能會常用到的功能,
所以python設計者把它寫好讓程式設計師可以直接使用。
可是有時候我們也有需要自訂函數的狀況。

以下是基礎的函數定義方法:

def 函數():
    要執行的程式碼1
    要執行的程式碼2
    …
    要執行的程式碼N

定義函數時,函數名稱後面會加上一組小括號,後面再加冒號,
與之前幾篇介紹if-else邏輯和for/while迴圈時一樣,
要放進函數中的程式碼需要空四格做為縮排。

例如故事中主人希望小白去洗衣服的指令,它的邏輯大概是這樣的:
(請注意,以下只是表達一段程式邏輯,並不是真正可執行的程式碼哦~)

for 衣物 in 洗衣籃:
    把衣物放進洗衣機裡面
按下洗衣機的開關

可是如果每次主人叫小白去洗衣服的時候,
都要把這些指令跟他清楚的交代,
這樣實在太麻煩了,
因此,我們可以定義一個洗衣服函數,
然後把洗衣服的明確指令放進去,

def 洗衣服():
    for 衣物 in 洗衣籃:
        把衣物放進洗衣機裡面
    按下洗衣機的開關

這樣以後主人要叫小白去洗衣服時,
只要叫告訴它去洗衣服
就不必再把這些細節都告訴它了。

定義有參數的函數

更進階的,你也可以讓函數有「輸入」和「輸出」:

def 函數(參數):
    要執行的程式碼1
    要執行的程式碼2
    …
    要執行的程式碼N
    return 結果

學習兩個新的詞: 參數回傳值
參數是從外部輸入給函數使用的資料
回傳值是從函數傳回給主程式的資料,在python內會用return這個關鍵字取得回傳值。

簡易範例: 請問芳齡?

我們知道人類在計算年紀有「虛歲」和「實歲」兩種算法,
虛歲比實歲多一歲。
我們結合上一篇教的【Python 超入門】(9) input()函數- 使用者跟電腦對話的方法
讓使用者輸入實歲年齡,
程式會幫忙計算出他的虛歲年齡。如下:

#定義一個函數,回傳輸入的數字加一
def addOne(x):
    return x+1

n= int(input("你今年實歲幾歲?")) # 取得使用者輸入的實歲年齡,把字串轉換成數字
year = addOne(n) #計算他的虛歲年齡
print("你今年虛歲"+str(year)+"歲")

範例: 計算全班分數平均

現在要把前幾堂課學過的內容靈活運用囉~
假設我們現在用一個列表來存班上每個人的數學考試分數,
例如:

scores = [50,60,35,20,40,0]

這時如果我們想要計算班平均分數的話,
我們可能會這樣寫:

scores = [50,60,35,20,40,0]
totolScore = 0 #用totolScore變數儲存全班的分數總和
for s in scores: #在迴圈中把每個人的分數加總
    totolScore +=s
average = totolScore/len(scores) #平均分數= 全班分數/全班人數,len()是計算列表元素個數的內建函數
print(average) #把結果印出來

結果為34.166666666666664
(大家可以拿計算機驗證一下,答案是205/6的結果,
至於為什麼答案不是大家想像中的34.166666666666666
並非今天要討論的重點,
大家心中先有個概念就是電腦計算無窮小數可能會有誤差就行了。)

這樣寫程式會正確,可是如果要一次處理多個班級的資料呢?
例如現在有三個班:

scores1 = [70, 60, 75, 65, 15, 15]
scores2 = [45, 10, 50, 5, 30, 50, 75]
scores3 = [70, 5, 10, 50, 60]

你在把剛剛的程式碼複製三次來改寫試試:

scores1 = [70, 60, 75, 65, 15, 15]
scores2 = [45, 10, 50, 5, 30, 50, 75]
scores3 = [70, 5, 10, 50, 60]

totolScore1 = 0 #用totolScore變數儲存全班的分數總和
for s in scores1: #在迴圈中把每個人的分數加總
    totolScore1 +=s
average1 = totolScore1/len(scores1) #平均分數= 全班分數/全班人數,len()是計算列表元素個數的內建函數
print(average1) #把結果印出來

totolScore2 = 0 #用totolScore變數儲存全班的分數總和
for s in scores2: #在迴圈中把每個人的分數加總
    totolScore2 +=s
average2 = totolScore2/len(scores2) #平均分數= 全班分數/全班人數,len()是計算列表元素個數的內建函數
print(average2) #把結果印出來

totolScore3 = 0 #用totolScore變數儲存全班的分數總和
for s in scores3: #在迴圈中把每個人的分數加總
    totolScore3 +=s
average3 = totolScore3/len(scores3) #平均分數= 全班分數/全班人數,len()是計算列表元素個數的內建函數
print(average3) #把結果印出來

這樣程式碼是不是顯的冗長難讀?
我們可以在這段程式碼之前先定義好計算平均的函數:

def average(scores):
    totolScore = 0
    for s in scores:
        totolScore +=s
    average = totolScore/len(scores)
    return average

再於主程式中呼叫該函數:

def average(scores):
    totolScore = 0
    for s in scores:
        totolScore +=s
    average = totolScore/len(scores)
    return average
    
scores1 = [70, 60, 75, 65, 15, 15]
scores2 = [45, 10, 50, 5, 30, 50, 75]
scores3 = [70, 5, 10, 50, 60]

print(average(scores1))
print(average(scores2))
print(average(scores3))

結果:
50.0
37.857142857142854
39.0

這樣寫程式是不是變得乾淨好讀了呢?
最近教的觀念逐漸變深,
歡迎各位在留言區留下任何疑問或回饋哦~
預計下一篇文章就能進入【Python 超入門】系列的完結篇了~
但心原一馬並不會止步於此,
敬祝各位邦友們學習順利~

結尾彩蛋

什麼?你以為本篇結束了嗎?
故事還有後續呢~

主人交代完機器人管家小白記得洗衣服、煮飯後,就上班去了…
在路上,主人突然想到: 「啊,忘記交代小白說等衣服洗完要晒衣服了,趕快打電話跟它說一下~」
下班回家後,主人發現陽台在晒的衣物好像少了一半,去問小白,
小白說:「哦哦~ 你們人類講話很不嚴謹耶~ 你只有說洗衣服表示衣服褲子都要洗的意思,倒沒說晒衣服也是衣服褲子都要晒的意思啊?」


1
瓦力
iT邦新手 5 級 ‧ 2019-07-11 12:14:29

什麼?【Python 超入門】系列要完結篇了嗎?/images/emoticon/emoticon70.gif

小馬大大的結尾彩蛋
寫的好貼切~
真的就是一個口令一個動作~
(如果這是小孩真的會想掐掐死~XD)
所以 人的腦袋 還是很神奇的啊~~

期待 期待後續的教學~~~

謝謝你欣賞我的文章哦~
如果在「人工智能」的時代真有這種機器人的話,
大概會被當成「人工智障」吧,哈哈哈~
這邊主要是想比喻寫程式語法的嚴謹性。

敬請期待後續的教學~
小馬會繼續努力

1
medivh0102
iT邦新手 5 級 ‧ 2020-03-09 23:35:32

一馬哥你好:這次不懂的地方有點多 再麻煩您教導了,謝謝。
1.

def average (score): #這邊的average僅代表名稱 不具任何計算意義
    totalScore = 0

電腦會跳出 local variable is assigned but never used 這行字,請問是甚麼意思呢?
一開始是顯示別的錯誤,後來發現是S沒有大寫,請問是甚麼原因呢?是因為totalScore是內建參數名稱嗎所以連大小寫差一個字都不能有錯?

看更多先前的回應...收起先前的回應...
def addOne(age):
    return age+1
age= int(input("請問年齡? ")) #int:字串轉數字:透過系統詢問使用者年齡
year = addOne(age)
print ("你今年虛歲"+str(year)+"歲")
#使用者再LINE 3輸入的數字為字串,被int轉換為數字。接著被帶入到LINE 4-addOne 然後addOne觸發函數(+1) 所以print之前會再將輸入的數字+1接著透過str轉為數字後輸出。

虛歲的函數邏輯我自己是這樣備註解釋的
可是再班級的平均分數那裏就真的看不懂了,
除了列表totalScore+=S 這個以外
明明srore後面有1-3的數字
可是上面函數的score後面沒有數字也可以讀取呢?

def average (score): #這邊的average僅代表名稱 不具任何計算意義
    totalScore = 0
    for A in score:
        totalScore+=A

想了很久還是無法理解中間的邏輯,還請一馬哥解釋 totalScore+=A 的邏輯是甚麼
寫開來的話是totalScore = A+1 嗎?

您好,先解答第一個問題:

def average (score):
    totalScore = 0

關於這段程式,totalScore並非內建的參數名稱,
之所以會出錯,是因為這個變數在之後第四行會用到

def average (score):
    totalScore = 0
    for A in score:
        totalScore+=A

因為python大小寫是有區別的,
totalScoretotalscore是不同的,
所以如果你第二行的s是小寫,
第四行的S是大寫就會錯

所以重點是變數名稱一致即可,
你要取名叫totalScore還是totalscore都可以

以日常生活為比喻,
假設你養了一隻寵物,
你一開始幫它取什麼名字都可以,
但不能一開始取名「小貓」,之後又叫它「小狗」

關於第二個問題,
函數內的參數名字和傳進函數內的變數名稱不一定要相同,
打個數學上的比方(先假設你基礎數學概念還行),
假設你有個數學上的函數叫f(x)=x+1
現在告訴你a=10,問你f(a)的值是多少,
你一樣是可以計算的對吧?
算法就是將a的值代入x,得到f(10)=10+1

類似的,我們的函數長相雖然是average(score)
但傳進函數的變數名稱不一定非得叫做score不可,
名稱叫做score1當然也是可以的

def average (score):
    #你的程式…
score1 = [70, 60, 75, 65, 15, 15]
average(score1) #把score1的內容代入函數的score去計算

關於第三個問題,
小馬在【Python 超入門】(4) 變數型態-list: 能納百物的百寶袋這篇有解釋過「+=」的涵義,
你先試著自己找答案,
真的看不懂可以再發問哦~
加油~ 希望有幫到你

對了,給你個自我檢測,
請看以下程式碼:

x = 20
a = 5
x += a
print(x)
print(a)

試著想想看,這段程式碼會印出什麼數字,
告訴小馬原因

一馬哥你好,我來交卷了
答案為
25&5

因為:

x+=a #可以展開為x=x+a == x=20+5 ==25

所以輸出的x將會是計算過後的20+5=25
a則是因為沒有經過任何計算,所以還是保留原先的5

答對了,希望這樣有解開你對totalScore+=A是什麼的疑惑哦 ^^

def average (score): #這邊的average僅代表名稱 不具任何計算意義 代表一個進行「平均值計算」的函數
    totalScore = 0 #代表數字為0 
    for A in score: 
#把變數score這些內容代入到列表編號A裡面 數值來源為score1~score3
        totalScore+=A 
#展開為totalScore = totalScore +A,因為tatalScore的變數為0 
#所以totalsocre+A == 0+[列表內的數字總和]
        average = totalScore/len(score) 
 #將上述的總和數字做平均計算後得到的數值命名為 「average」。
    return average #這邊的average表示進行"平均"計算
#以下這三行數字皆為列表A的內容
scores1 = [70, 60, 75, 65, 15, 15]
scores2 = [45, 10, 50, 5, 30, 50, 75]
scores3 = [70, 5, 10, 50, 60]

謝謝一馬哥,重新看了一次後清楚多了
自己做了這樣的解釋
請一馬哥檢查,謝謝。

讚哦,溫故而知新,能以自己方式吸收理解,
是個用功向學的好學生呢~
/images/emoticon/emoticon42.gif

0
h104651
iT邦新手 5 級 ‧ 2020-05-21 10:20:12

自己補充一下,因為len()這內建函數一直看不太懂

後來查了一下就通了
score = [1,2,3,4,5,6]
len(score) == 6 #函數裡有六個數字

L = "心原一馬怎麼這麼帥"
len(L) == 9 #字串裡有9個字

希望可以幫到一些人
非常感謝馬哥文章!!!!!!!!

我要留言

立即登入留言