Basics
2018.07.24
2018.07.24
在計算機科學的領域,programming 這個詞中譯為「程式設計」(或被譯為「編程」)。這個詞有兩個部分 ,一個是「程式」,一個是「設計」。「程式」就是給計算機的指令,讓計算機能夠依循這些指令工作;「設計」即是完成程式的過程。然而僅有這樣的理解,是不足以開始設計程式的;在進一步瞭解如何設計之前,我們應還得釐清:
第二個問題可以先粗略回答:作業系統有提供我們一些方式,使得我們可將程式傳達給計算機;我們能利用鍵盤輸入資訊,利用「純文字編輯器」編輯並記錄程式,而後經由「編譯器」或「直譯器」將程式轉換為計算機能讀懂的訊息,讓計算機執行。
要回答第一個問題,我們先淺談計算機能做的事情。計算機能做的事情相當簡單,針對每個位元 (記憶資訊的最小單位),它僅能識別其兩種狀態 (通常我們用 0 與 1 表示);此外,它能讀取或改動記憶裝置中某個位元的狀態。要設計程式,最直接的方式就是一長串由 0 和 1 構成的數列,然後將這串數列傳達給計算機,CPU 接收到特定的數列時,就會執行特定的動作。
Example: A function in hexadecimal representation of 32-bit x86 machine code to calculate the nth Fibonacci number:
8B542408 83FA0077 06B80000 0000C383
FA027706 B8010000 00C353BB 01000000
B9010000 008D0419 83FA0376 078BD989
C14AEBF1 5BC3
(source wikipedia: low-level programming language)
這樣的程式被稱為「機器語言 (machine code)」,是計算機直接能讀懂的,人要讀懂需要有一份「指令」與「數字」的對照表。為了省去人工對應的麻煩,「組合語言 (assembly language)」相應而生。這兩類語言被稱為「低階語言 (low-level language)」,要撰寫低階語言,我們必須知道該 CPU 之「指令集 (instruction set)」,如此才能夠知道這顆 CPU 能進行何種計算。
Example: The same Fibonacci number calculator as above, but in x86 assembly language using MASM syntax:
fib:
mov edx, [esp+8]
cmp edx, 0
ja @f
mov eax, 0
ret
@@:
cmp edx, 2
ja @f
mov eax, 1
ret
@@:
push ebx
mov ebx, 1
mov ecx, 1
@@:
lea eax, [ebx+ecx]
cmp edx, 3
jbe @f
mov ebx, ecx
mov ecx, eax
dec edx
jmp @b
@@:
pop ebx
ret
(source wikipedia: low-level programming language)
利用組合語言開發程式有幾項缺點:
為了降低開發程式的成本,「高階語言」便因此誕生。
unsigned fib(unsigned n) {
if (!n)
return 0;
else if (n <= 2)
return 1;
else {
unsigned a, c;
for (a = c = 1; ; --n) {
c += a;
if (n <= 3) return c;
a = c - a;
}
}
}
(source wikipedia: low-level programming language)
計算機能讀懂的僅有機器語言,要讓高階語言被執行,我們需要一個特殊的程式,幫我們將高階語言撰寫而成的程式轉換為機器語言;這個程式稱為「編譯器 (compiler)」。知道編譯器所扮演的角色後,我們可大致刻畫「設計」的流程:
編輯 -> 編譯 -> (語法錯誤?) -> 執行 -> (執行錯誤?) -> (邏輯錯誤?) -> ...
而後,有學者 (S. Russell 1958) 提出了「直譯器 (interpreter)」的概念,讓高階語言直接被執行 (讀一行,(轉譯) 執行一行,而不用將所有的程式碼透過編譯轉為機器碼)。Python 即為一種直譯式的程式語言。相較於編譯式的語言,直譯式語言的好處在於一行 (段) 程式輸入後能直接看到執行的結果,一般來說,程式開發的效率較編譯式的為佳,但執行的效率低於編譯式的語言。使用直譯式的程式就好像在使用電子計算機,我們敲了數字和運算子進去,它馬上幫我們計算運算後的結果並呈現。Python 也提供了類似的執行介面 (python shell),讓我們能用操作電子計算機的模式來使用;但當需完成的任務要用到較複雜的指令時,shell 的即時運算模式反而會造成編輯上的困難,因此 Python 也允許我們將程式鍵入檔案後,再做執行。以下,我們會先利用 shell 做基本的說明,最後再討論編輯程式檔案時要注意的事項。
在開始設計程式之前,我們要回憶一下計算機的五大基本單元。
計算機的工作模式,可分為下列幾大步驟:
我們需要會的第一件事,就是將外部的資訊讀入計算機 (記憶體) 內,並瞭解計算機如何從記憶體存取資訊。
讀取外部資訊的方式有很多,由鍵盤輸入的資訊我們稱之為「標準輸入 (standard input)」。在 Python 中,我們可利用 input() 這道指令來讀取鍵盤輸入。
input()
要執行一指令,我們須鍵入 enter/return。input() 被執行後,我們會看到游標停留在下一行的開頭,此時 Python 正等待我們輸入資訊;當我們欲輸入的資訊完成後,須鍵入 enter/return,input() 才會將我們鍵入的訊息帶給計算機。
通常由鍵盤讀入資訊後,我們期望對該資訊做些處理,因此我們必須告訴計算機將這些資訊「記」下來 (存至記憶體中),使得我們之後能夠取用。
x = input()
上面這行指令 (程式碼) 所做的事情為「讀取鍵盤輸入,並存入記憶體的某位址,將該位址貼上名為 x 的標籤」。
記憶體如同一格格的儲存格,每一格都能存資訊;若我們希望計算機將 input() 所讀取之資訊記錄在記憶體中,Python 會主動幫我們由記憶體中找一個空格,並幫我們將資訊放入;記憶體位址的標籤,稱為「變數」;未來當我們想要取得該格之資訊時,只需將標籤的「名稱」告訴計算機即可。「=」稱為「指定 (assignment)」,其作用就是將其左方的標籤貼到右方資訊所存放的記憶體位址。
注意:當看到「xxx = 」這樣的程式碼時,我們的解讀是「有某筆資訊要被放至記憶體中,且我們要將那個位址標記成 xxx」;指定的右邊,為讀取資訊的方式,目前我們已經知道標準輸入這一方式,之後我們會陸續看到其他的方式。
不同的程式語言對記憶體的處理方式不同。在使用 Python 時,使用者並無法直接對記憶體做控制。針對 Python 3 的討論,我們僅會在釐清某些觀念時,才會談到記憶體。
現在我們已經知道如何將鍵盤輸入的資訊傳達給計算機,若我們想要讓計算機處理某個存放於記憶體中的資訊,我們直接以該變數名代替即可。舉例來說,若我們想將前述存於變數 x 所對應位址之資訊輸出於螢幕上,我們可以使用下列指令
print(x)
在螢幕上呈現的結果,我們稱為「標準輸出 (standard output)」,在 Python 3 中以 print() 這個指令完成。我們會將欲輸出的資訊放置於小括號中。
我們可再嘗試幾個例子:
>>> print(123)
123
若想在同一行輸出多筆資訊,我們可在 print() 的小括號中將欲輸出的資訊以「,」分隔,輸出的多筆訊息兩兩間會以一空白區隔。
>>> print(123, 456, 789)
123 456 789
上述的例子,都是印出數字,接著我們來試試印出 abc 這三個英文字母。
>>> print(abc)
Traceback (most recent call last):
File “<stdin>?, line 1, in <module>
NameError: name ‘abc’ is not defined
為什麼 print(abc) 無法如預期的印出 abc 呢?看起來 123 和 abc 在 Python 中是被用不同的方式對待的。要解釋這件事,我們要再對變數有多一些的認識。
變數具有幾項屬性:
變數的命名規則
and as assert break class continue
def del elif else except exec
finally for from global if import
in is lambda nonlocal not or
pass raise return try while with
yield True False None
注意:在 Python 中,符合「變數命名規則」的字串皆被視為變數。
在多數程式語言中,不同類別的值有不同的處理方式,通常在使用一個變數之前,我們必需做「宣告」的動作,告訴計算機變數的「名字」以及其中所要存放的資料「類別」。
要判斷一個變數的類別,我們可利用 type() 指令。
>>> x = input()
123
>>> type(x)
<class ‘str’>
在 Python 中,變數使用前不需宣告,當我們「指定」值給變數時,Python 會自動幫我們判定該變數存放的是何種類別的值。這樣的判斷不見得時時如我們的期望,因此,當 Python 設定的類別與我們的期望相異時,我們須透過「類別轉換 (type conversion)」改變該變數的類別。
考慮下列程式,我們想要讀入 2 個數字,並輸出 2 數相加的結果。
>>> x = input()
>>> print(x)
'1'
>>> y = input()
>>> print(y)
'2'
>>> print(x + y)
'12'
由執行的結果可知,我們雖然成功將 1 與 2 兩數分別讀入變數 x 與 y,但 x + y 產生的結果確不如我們預期;原因在於利用 input() 讀入的資訊,類別皆為「字串 (str)」,Python 對字串進行「+」的運算和整數的加法是不同的 (字串會在後面的章節介紹)。以此處的數值處理為例,我們可用 int() 或 float() 將讀入的資訊轉換為數值,即可做數值運算。
>>> x = int(input())
>>> print(x)
1
>>> y = int(input())
>>> print(y)
2
>>> print(x + y)
3
最後,我們試著利用第二種執行 Python 程式的方式,即利用純文字編輯器將程式碼存入一檔案,而後再利用 Python 直譯器執行程式。此處我將新增的文字檔命名為 myProgram.py,並將內容編輯如下:
x = int(input())
y = int(input())
print(x + y)
之後,在「終端機」或是「命令提示字元」切換至 myProgram.py 同一目錄下,鍵入
$ python3 myProgram.py
即可執行這三行程式碼。
Python 有嚴格的格式規定,有幾點要注意: