本週的作業是簡單的流程控制。在 Java 裡有 If...else if...else, switch...case, for, while... 和 do...while 五種,但是 Python 卻只有 if...elif...else, for...else..., while ... else 三種!差了兩種,難道 Python 比較弱嗎?其實也不是,只是 Java 已經把許多工具準備好了,在應用上會比較快一點。而 Python 的「極簡美學」是不會允許疊床架屋的取向。
廢話不多說,馬上進入本週的題目:「請寫出一個能將數字分數轉為等第分數 (e.g., A, B, C... F) 的程式!」因為每個禮拜天上午還要工作的緣故,這個禮拜的課程應該會錯過「使用者輸入」的部份,所以,我自己把題目再加上『數字分數的取得,是由使用者輸入的』的條件。經實作以後,在 Java 上,做出了以下三種版本:
Java 評等機器人一號 (完全使用 if...else if...else 的運算;耗用資源 2*5 = 10)
public class gradingRobotOne {
public static void main (String [] args) {
java.util.Scanner readingRobot = new java.util.Scanner(System.in);
System.out.print("請輸入分數:");
int input = readingRobot.nextInt();
String gradeLevel = "未判定!";
System.out.println("您輸入的分數為" + input);
System.out.println("===================");
if (input > 100 || input < 0) {
System.out.println("分數應該介於 0 到 100 之間!");
}
else {
if (input <= 100 && input > 90){
gradeLevel = "A";
}
else if (input <=90 && input > 80){
gradeLevel = "B";
}
else if (input <=80 && input > 70){
gradeLevel = "C";
}
else if (input <=70 && input >= 60){
gradeLevel = "D";
}
else {
gradeLevel = "F";
}
}
System.out.println("成績評定為:" + gradeLevel);
}
}
運作畫面:
if...else if...else 的語法像是一把雙面刃,每做「一個」判斷,就要佔「兩個」資源數,感覺實在是太不划算了。如果用 switch 的話,每做一次比對,只會佔用「一個」資源數,這麼看來,也許用 switch 會是比較好的選擇哦!依據此原則,做出機器人二號!
Java 評等機器人二號 (完全使用 switch...case 的運算;耗用資源 41+2*1=43)
public class gradingRobotTwo {
public static void main (String [] args) {
java.util.Scanner readingRobot = new java.util.Scanner(System.in);
System.out.print("請輸入分數:");
int input = readingRobot.nextInt();
System.out.println("你輸入的分數為:" + input);
System.out.println("====================");
char grade = 'N';
if (input > 100 || input < 0) {
System.out.println("分數應該介於 0 到 100 之間!");
System.out.println("成績無法評定");
}
else {
switch (input){
case 100:
grade = 'A';
break;
case 99:
grade = 'A';
break;
case 98:
grade = 'A';
break;
case 97:
grade = 'A';
break;
case 96:
grade = 'A';
break;
case 95:
grade = 'A';
break;
case 94:
grade = 'A';
break;
case 93:
grade = 'A';
break;
case 92:
grade = 'A';
break;
case 91:
grade = 'A';
break;
case 90:
grade = 'A';
break;
case 89:
grade = 'B';
break;
case 88:
grade = 'B';
break;
case 87:
grade = 'B';
break;
case 86:
grade = 'B';
break;
case 85:
grade = 'B';
break;
case 84:
grade = 'B';
break;
case 83:
grade = 'B';
break;
case 82:
grade = 'B';
break;
case 81:
grade = 'B';
break;
case 80:
grade = 'B';
break;
case 79:
grade = 'C';
break;
case 78:
grade = 'C';
break;
case 77:
grade = 'C';
break;
case 76:
grade = 'C';
break;
case 75:
grade = 'C';
break;
case 74:
grade = 'C';
break;
case 73:
grade = 'C';
break;
case 72:
grade = 'C';
break;
case 71:
grade = 'C';
break;
case 70:
grade = 'C';
break;
case 69:
grade = 'D';
break;
case 68:
grade = 'D';
break;
case 67:
grade = 'D';
break;
case 66:
grade = 'D';
break;
case 65:
grade = 'D';
break;
case 64:
grade = 'D';
break;
case 63:
grade = 'D';
break;
case 62:
grade = 'D';
break;
case 61:
grade = 'D';
break;
case 60:
grade = 'D';
break;
default:
grade = 'F';
break;
}
System.out.println("成績評定為:" + grade);
}
}
}
運作畫面:
本來以為一號的解法已經夠浪費資源了,想不到因為從使用者手上取得的輸入分數可能會落在 60~100 之間而有 41 種不同的可能性,每種可能性的比對都佔一個資源,再加上一開始排除 100 以上和 0 分以下的判斷,結果反而讓耗用資源數比純粹的 if...else if...else 多了一倍多呢! 為了解決以上的問題,套用「食神」裡的台詞『
爭什麼爭,摻在一起做撒尿牛丸不就好了』!接下來的機器人三號,就要同時使用 if 和 switch 的語法來實作。首先要開刀切除的就是那 41 種可能性的比對,如果我們把從 60 ~ 100 之間的數字都除以 10 ,然後再取它的個位數來做比對,再把 100 這個特別的數字額外處理,這樣一下子需要比對的數字就從 41 個縮減變成 4 個了!
Java 評等機器人三號 (混合 if...else if...else 和 switch...case 的運算;耗用資源 4+2*2=8)
public class gradingRobotThree {
public static void main (String [] args) {
java.util.Scanner readingRobot = new java.util.Scanner(System.in);
System.out.print("請輸入分數:");
int input = readingRobot.nextInt();
System.out.println("你輸入的分數為:" + input);
System.out.println("====================");
char grade = 'N';
int score = (int)(input*0.1);
if (score > 10 || score < 0) {
System.out.println("分數應該介於 0 到 100 之間!");
System.out.println("成績無法評定");
}
else if (score == 10){
grade = 'A';
}
else {
switch (score) {
case 9:
grade = 'A';
break;
case 8:
grade = 'B';
break;
case 7:
grade = 'C';
break;
case 6:
grade = 'D';
break;
default:
grade = 'F';
break;
}
}
System.out.println("成績評定為:" + grade);
}
}
運作畫面:
哈哈~做到這裡,真是忍不住說一聲「食神好棒啊!」果然讓耗用的運算資源大大降低了不少咧! Java 的部份做到這裡,那一樣的功能,在 Python 裡會怎麼做呢? 首先是 Python 的 if...elif...else 的語法。
Python 評等機器人一號(完全使用 if...elif...else 的運算;耗用資源 2*5 = 10)
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Filename: gradingRobotOne.py
grade = raw_input("請輸入分數: ") #利用 raw_input 的函式取得鍵盤輸入
score = int(grade) #從 raw_input 取得的字串類型 (String) 資料,轉換為 int 的類型
print "====================="
print "您輸入的分數為:", score
gradeLevel = "未判定!" #預設成績等級為「未判定!」
#利用判斷式把輸入的成績分等級
if score > 100 or score < 0:
print "分數應該介於 0 到 100 之間!"
elif score <= 100 and score >= 90:
gradeLevel = "A"
elif score <= 89 and score >= 80:
gradeLevel = "B"
elif score <= 79 and score >=70:
gradeLevel = "C"
elif score <= 69 and score >=60:
gradeLevel = "D"
else:
gradeLevel = "F"
print "成績評定為:", gradeLevel #印出評定結果
運作畫面:
整體看起來,和 Java 版本的評等機器人一號幾乎沒什麼分別。只是 Python 的程式碼結構較為簡單,掐頭去尾大概可以少個七~十行左右吧!果然是「載入 Python,少做多玩」的精神。 寫到這裡,應該就沒有其他的 Python 寫法了吧?畢竟 Python 本身是沒有帶著 switch 的語法的。而在 Java 版本的評等機器人二號和三號裡,switch 都是主要的判斷式呢。嗯…不過我不信邪,難道先天沒有,後天不能學嗎?在網路上查了查, Python 的 switch 能力,可以透過它特有的 dictionary 資料結構配合預先制定的 function 來達成。這樣說起來太玄了,還是直接看看程式碼配合在其中的註解來神遊一下吧!
Python 評等機器人二號 (使用「自製的」switch 配合 if...elif...else 的句法;耗用資源數:5+2*3 = 11)
#! /usr/bin/python
# -*- coding: utf-8 -*-
# Filename: gradingRobotTwo.py
grade = raw_input ("請輸入分數: ") #利用 raw_input 的函式取得鍵盤輸入
score = int(grade) #從 raw_input 取得的字串類型 (String) 資料,轉換為 int 的類型
numLevel = int(score * 0.1)
print "====================="
print "您輸入的分數為:", score
gradeLevel = '未判定!' #預設成績等級為「未判定!」
#建立模擬 switch 語法的執行內容
def funcA():
gradeLevel = 'A'
return gradeLevel
def funcB():
gradeLevel = 'B'
return gradeLevel
def funcC():
gradeLevel = 'C'
return gradeLevel
def funcD():
gradeLevel = 'D'
return gradeLevel
def funcF():
gradeLevel = 'F'
return gradeLevel
#利用 dictionary 的資料結構建立模擬 switch 語法的 case 條件
values = {
9:funcA,
8:funcB,
7:funcC,
6:funcD,
5:funcF,
}
#設定判斷式,先排除超過 100 和低於 0 分,再額外設定 100 分和所有低於 60 分的條件,餘項目呼叫模擬 switch 的內容
if score > 100 or score < 0:
print "分數應該介於 0 到 100 之間!"
elif score == 100:
gradeLevel = 'A'
elif score <= 59:
values.get('5')()
else:
gradeLevel = values[numLevel]()
print "成績判定為:" + gradeLevel #印出結果
運作畫面:
哈~雖然用自製的 switch 在 Python 下編寫出的程式耗用資源還是比 Java 的評等機器人三號多了些,但是 Python 的程式是不用「預先編譯」的哦!所以如果遇上一個評等標準要一直改來改去的操作環境時, Python 評等機器人二號就比 Java 的評等機器人三號更能勝任愉快了! (ps.我覺得我好像有一點對 Python 偏心…)
Note: Thanks for the feedback from an expert. Now I know that there is a handy module which can do better than the Python version of gradingRobotTwo.py (感謝匿名的高手回覆,我才知道還有一個好用的模組可以達到和 Python 版機器人二號一樣的目的,但卻更省資源的寫法) Python gradingRobotThree (By adopting bisect module, CPU resource consuming drop down to the lowest record: 2*1 = 2)(Python 評等機器人三號!藉由 bisect 模組的採用,這個版本的資源耗損達到了歷史的新低點: 2*1 = 2)
#! /usr/bin/python
# -*- coding:utf-8 -*-
# Filename: gradingRobotThree.py
from bisect import bisect #Thanks for the feedback with this handy module :)
grade = raw_input ("Please key-in the grade: ")
score = int(grade)
#numLevel = int(score * 0.1)
print "====================="
print "The grade is:", score
gradeLevels = "FDCBA"
breakpoints = [60, 70, 80, 90] # This can be rughly seen as [F < 60 => D < 70 => C < 80 => B < 90 => A]
def grading(total):
return gradeLevels[bisect(breakpoints, total)]
if score > 100 or score < 0:
print "The score number should be between 0 and 100 !"
else:
print "The grade level is:" + grading(score)
Screenshot: