03 4月 2009

Java & Python 程式碼疊疊樂之二

週的作業是簡單的流程控制。在 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:

6 則留言:

  1. 1. it is even better to biset

    from bisect import bisect

    grades = "FDCBA"
    breakpoints = [60, 70, 80, 90]

    def grade(total):
    return grades[bisect(breakpoints, total)]

    print grade(50)

    2. or using range
    e.g.
    if x in range(0, 59): return 'F' ...

    3. if score <= 69 and score >=60:
    can be rewritten as
    if 69 <= score <= 69 :

    it is much better in python

    回覆刪除
  2. oops, typo error,
    it should be
    in range(0, 60) or in range(59)

    回覆刪除
  3. Hello, anonymous,

    Thanks for your feedback.
    I'll check on the bisect and try to fix this.

    Many many thanks. :)

    Best,
    Peter. w

    回覆刪除
  4. oops,
    if 69 <= score <= 69 :

    should be

    if 60 <= score <= 69 :



    by the way, "if x in range ()" is good way to use, it is simple as natural English sentence.

    回覆刪除
  5. Hi, Peter:

    建議等級判別的地方以陣列及數學計算即可,例如:

    Grade = ["E","D","C","B","A"];
    Score = int(raw_input());
    print Grade[int((Score-51)/10)];

    回覆刪除
  6. I like that, but it has boundary problem

    In [1]: int((60-51)/10)
    Out[1]: 0

    In [2]: int((61-51)/10)
    Out[2]: 1

    In [3]: int((69-51)/10)
    Out[3]: 1

    In [4]: int((70-51)/10)
    Out[4]: 1

    In [5]: int((71-51)/10)
    Out[5]: 2


    if you use 100 -50, then another boundary problem, it supposes to get 4 not 5

    In [10]: int((100-50)/10)
    Out[10]: 5

    回覆刪除