機器指令是CPU唯一聽得懂的語言,因此無論工程師使用什麼樣的程式語言來程式設計,最終執行在計算機硬體上的程式都是機器指令。
早期的程式設計師們直接使用機器語言進行程式設計。程式設計師將用0, 1數字編成的程式程式碼打在紙帶或卡片上,1打孔,0不打孔,再將程式透過紙帶機或卡片機輸入計算機的記憶體,執行並運算。機器語言是二進位制的編碼,只由0和1組成,指令本身倒是簡單,但是直接使用機器語言編碼,意味著程式設計師需要記住0和1組成的編碼到底是什麼意思,這對人類來說就太不直觀了,即不方便閱讀和修改,也容易產生錯誤。
為了解決這個問題,人們使用符號來輔助記憶機器指令,這種與機器指令直接對應的符號被稱為彙編指令,彙編指令組成了組合語言(assembly language)。使用匯編語言程式設計要比直接使用計算機指令要方便一些,至少不需要記住那些0和1的組合了。
計算機能讀懂的只有機器指令,那麼如何讓計算機執行程式設計師用匯編語言編寫的程式呢?這時,就需要有一個能將彙編指令轉換成機器指令的翻譯程式,這種程式我們稱其為彙編器(assembler)。程式設計師用匯編語言寫出源程式,再用匯編器將其翻譯為機器碼,由計算機執行。
組合語言具有機器相關性,彙編指令是機器指令的一種符號表示,而不同型別的CPU有不同的指令集,因此它們使用的組合語言也不同。組合語言程式與機器有著密切的關係,不同型別CPU之間的組合語言程式是沒有辦法互相使用的,這大大降低了組合語言的通用性和適應性。不同的組合語言就像是各自一方的地方方言,使用它們無法進行直接的交流,如果程式設計師想寫一個彙編程式在不同的計算機上執行,那麼就需要編寫不同版本的彙編程式,這也意味著工作量的加倍。
此外,組合語言所操作的物件是暫存器和儲存器,它們是位元組物件,而不是變數、結構等抽象的資料。這意味著程式設計師要在物理儲存空間裡自己組織資料的結構,賦予位元組抽象的含義,並且在程式裡精確的根據物件的物理地址來操作它們,這無疑大大增加了程式的複雜度並降低了程式碼的可讀性。
所以,即使組合語言簡單高效,但是它的適用範圍十分有限,它通常被應用在底層、硬體操作或者高要求效能最佳化的場合。
由於機器語言和組合語言的侷限性,“高階”程式設計語言應運而生。所謂的“高階”自然是相對於組合語言而言,高階語言使得程式設計師能夠更多的站在解決問題的角度上思考問題,而不是與計算機的物理細節打交道,所以高階語言對與計算機硬體來說具有更“高”的抽象層次。
高階語言有兩個設計目標:程式碼的機器無關性和更容易地進行程式設計。
高階語言的機器無關性是透過特殊的“翻譯”工具來實現的。程式設計師使用高階語言寫的程式(稱為“源程式”或者“原始碼”)無法直接在計算機上執行,所以需要“翻譯”工具將源程式轉換為能夠在目標計算機上執行的由機器語言組成的程式,又被稱為“目標程式”。如果高階語言程式需要執行在某種CPU上,那麼就需要相對應的“翻譯”工具來完成從源程式到目標程式的轉換,因此“翻譯”工具是高階語言實現機器無關性的關鍵,它向程式設計師隱藏了不同CPU之間的差別,使得程式設計師程式設計一次就可以執行在不同的計算機上——這當然是一個美好的願景,實際情況則要複雜得多。
根據“翻譯”工具不同的工作方式,高階語言可以分為編譯型語言和解釋型語言。還有一些語言的實現採用了編譯型和解釋型混合的形式,比如Java,它的語言規範定義了一種與機器無關的中間形式,稱為位元組碼(byte code)。
高階語言的另一個目標是更容易的進行程式設計。現存的許多高階語言都可以基於“如何進行程式設計”加以分類,從而歸於某種語言族,這也被稱為程式設計正規化。大的語言分類為:說明式和命令式。其中說明式語言從問題的本身出發,關注的是“做什麼”而不是“怎麼做”,因此它們更強調如何對問題進行描述,從而隱藏了語言解決問題的具體實現的細節;而命令式語言從計算機硬體的抽象出發,關注的是如何去解決問題,它們強調的是把問題分解為計算機可以理解的步驟,因此需要程式設計師不僅要理解問題本身還要理解實現的原理和過程。簡單來說,說明式語言是用來說明問題的,問題的解決由語言來處理;命令式語言是命令計算機做事情的,解決問題的邏輯由程式設計師確定。
下圖給出了更加細分的語言分類/程式設計正規化:
· 函式式語言將函式作為語言的核心,在函式式語言裡函式的引數和返回值也是函式。純正的函式式語言沒有可變數和迴圈,因此函式的定義一般基於遞迴來表示。函式式語言從數學上來源於丘奇在1930年代提出的λ演算,可以證明的是,λ演算與圖靈機在解決問題的能力上是等價的。在本質上,函式式語言的程式被看作是一個從輸入到輸出的函式,而這個函式的定義是基於一些更簡單的函式,經過逐步細化完成的。函式的遞迴表示使得函式式語言更傾向於用來描述問題,因此它被歸於說明式語言的範疇。
· 資料流語言關注資料的流動,而程式由一系列的功能節點組成。程式接收輸入資料,並顯示地展示資料是如何一步步地被處理和修改,直到輸出。功能節點由輸入觸發,一旦上游的輸入準備好了,功能節點就開始工作,處理輸入資料併產生輸出。
· 邏輯式語言把邏輯推理直接變成程式的執行,它們把計算看作是一種有目標的推理過程,設法根據一組邏輯規則來找出滿足特定關係的值。邏輯式語言程式描述的是資料物件之間的關係,這些關係以事實和規則表述,根據規則找出合乎邏輯的結果就是推理。因此邏輯語言程式的設計就是陳述事實,制定規則,程式設計的過程就是構造證明,而程式的執行就是推理,推理的過程由直譯器來完成,程式設計師不需要關心實現細節。
· 過程式語言把計算描繪為處理器操作記憶體上的資料,這是最接近馮·諾依曼架構思想的語言,因此也被稱為馮·諾依曼語言。這類語言包括Fortran,Pascal,Basic,C等等,都是程式設計師比較熟悉的,也是最成功的幾種語言之一。過程式語言透過過程呼叫來組織程式,它把儲存在記憶體中的資料抽象為帶有結構的變數,把一組操作資料的指令抽象為過程,透過過程修改變數的值,進而影響後續的計算,因此過程式語言的本質是透過過程來處理資料。
· 面嚮物件語言不再直接處理資料,而是把程式看作是一些獨立的物件之間的相互作用。大部分面嚮物件語言都與過程式語言有著比較深的淵源,只是在程式設計上採取了更加結構化的方式,抽象層次更高一些。在大的方面,面嚮物件語言的程式由一組相互作用的物件組成,物件封裝了資料(或者說狀態)和處理這些資料的方法(或者說行為),整個程式就是描述物件之間如何發生作用。當然,在具體到某個物件的實現上,面嚮物件語言依然遵循著過程式語言資料加過程的模式。
機器指令是CPU唯一聽得懂的語言,因此無論工程師使用什麼樣的程式語言來程式設計,最終執行在計算機硬體上的程式都是機器指令。
早期的程式設計師們直接使用機器語言進行程式設計。程式設計師將用0, 1數字編成的程式程式碼打在紙帶或卡片上,1打孔,0不打孔,再將程式透過紙帶機或卡片機輸入計算機的記憶體,執行並運算。機器語言是二進位制的編碼,只由0和1組成,指令本身倒是簡單,但是直接使用機器語言編碼,意味著程式設計師需要記住0和1組成的編碼到底是什麼意思,這對人類來說就太不直觀了,即不方便閱讀和修改,也容易產生錯誤。
為了解決這個問題,人們使用符號來輔助記憶機器指令,這種與機器指令直接對應的符號被稱為彙編指令,彙編指令組成了組合語言(assembly language)。使用匯編語言程式設計要比直接使用計算機指令要方便一些,至少不需要記住那些0和1的組合了。
計算機能讀懂的只有機器指令,那麼如何讓計算機執行程式設計師用匯編語言編寫的程式呢?這時,就需要有一個能將彙編指令轉換成機器指令的翻譯程式,這種程式我們稱其為彙編器(assembler)。程式設計師用匯編語言寫出源程式,再用匯編器將其翻譯為機器碼,由計算機執行。
組合語言具有機器相關性,彙編指令是機器指令的一種符號表示,而不同型別的CPU有不同的指令集,因此它們使用的組合語言也不同。組合語言程式與機器有著密切的關係,不同型別CPU之間的組合語言程式是沒有辦法互相使用的,這大大降低了組合語言的通用性和適應性。不同的組合語言就像是各自一方的地方方言,使用它們無法進行直接的交流,如果程式設計師想寫一個彙編程式在不同的計算機上執行,那麼就需要編寫不同版本的彙編程式,這也意味著工作量的加倍。
此外,組合語言所操作的物件是暫存器和儲存器,它們是位元組物件,而不是變數、結構等抽象的資料。這意味著程式設計師要在物理儲存空間裡自己組織資料的結構,賦予位元組抽象的含義,並且在程式裡精確的根據物件的物理地址來操作它們,這無疑大大增加了程式的複雜度並降低了程式碼的可讀性。
所以,即使組合語言簡單高效,但是它的適用範圍十分有限,它通常被應用在底層、硬體操作或者高要求效能最佳化的場合。
由於機器語言和組合語言的侷限性,“高階”程式設計語言應運而生。所謂的“高階”自然是相對於組合語言而言,高階語言使得程式設計師能夠更多的站在解決問題的角度上思考問題,而不是與計算機的物理細節打交道,所以高階語言對與計算機硬體來說具有更“高”的抽象層次。
高階語言有兩個設計目標:程式碼的機器無關性和更容易地進行程式設計。
高階語言的機器無關性是透過特殊的“翻譯”工具來實現的。程式設計師使用高階語言寫的程式(稱為“源程式”或者“原始碼”)無法直接在計算機上執行,所以需要“翻譯”工具將源程式轉換為能夠在目標計算機上執行的由機器語言組成的程式,又被稱為“目標程式”。如果高階語言程式需要執行在某種CPU上,那麼就需要相對應的“翻譯”工具來完成從源程式到目標程式的轉換,因此“翻譯”工具是高階語言實現機器無關性的關鍵,它向程式設計師隱藏了不同CPU之間的差別,使得程式設計師程式設計一次就可以執行在不同的計算機上——這當然是一個美好的願景,實際情況則要複雜得多。
根據“翻譯”工具不同的工作方式,高階語言可以分為編譯型語言和解釋型語言。還有一些語言的實現採用了編譯型和解釋型混合的形式,比如Java,它的語言規範定義了一種與機器無關的中間形式,稱為位元組碼(byte code)。
高階語言的另一個目標是更容易的進行程式設計。現存的許多高階語言都可以基於“如何進行程式設計”加以分類,從而歸於某種語言族,這也被稱為程式設計正規化。大的語言分類為:說明式和命令式。其中說明式語言從問題的本身出發,關注的是“做什麼”而不是“怎麼做”,因此它們更強調如何對問題進行描述,從而隱藏了語言解決問題的具體實現的細節;而命令式語言從計算機硬體的抽象出發,關注的是如何去解決問題,它們強調的是把問題分解為計算機可以理解的步驟,因此需要程式設計師不僅要理解問題本身還要理解實現的原理和過程。簡單來說,說明式語言是用來說明問題的,問題的解決由語言來處理;命令式語言是命令計算機做事情的,解決問題的邏輯由程式設計師確定。
下圖給出了更加細分的語言分類/程式設計正規化:
· 函式式語言將函式作為語言的核心,在函式式語言裡函式的引數和返回值也是函式。純正的函式式語言沒有可變數和迴圈,因此函式的定義一般基於遞迴來表示。函式式語言從數學上來源於丘奇在1930年代提出的λ演算,可以證明的是,λ演算與圖靈機在解決問題的能力上是等價的。在本質上,函式式語言的程式被看作是一個從輸入到輸出的函式,而這個函式的定義是基於一些更簡單的函式,經過逐步細化完成的。函式的遞迴表示使得函式式語言更傾向於用來描述問題,因此它被歸於說明式語言的範疇。
· 資料流語言關注資料的流動,而程式由一系列的功能節點組成。程式接收輸入資料,並顯示地展示資料是如何一步步地被處理和修改,直到輸出。功能節點由輸入觸發,一旦上游的輸入準備好了,功能節點就開始工作,處理輸入資料併產生輸出。
· 邏輯式語言把邏輯推理直接變成程式的執行,它們把計算看作是一種有目標的推理過程,設法根據一組邏輯規則來找出滿足特定關係的值。邏輯式語言程式描述的是資料物件之間的關係,這些關係以事實和規則表述,根據規則找出合乎邏輯的結果就是推理。因此邏輯語言程式的設計就是陳述事實,制定規則,程式設計的過程就是構造證明,而程式的執行就是推理,推理的過程由直譯器來完成,程式設計師不需要關心實現細節。
· 過程式語言把計算描繪為處理器操作記憶體上的資料,這是最接近馮·諾依曼架構思想的語言,因此也被稱為馮·諾依曼語言。這類語言包括Fortran,Pascal,Basic,C等等,都是程式設計師比較熟悉的,也是最成功的幾種語言之一。過程式語言透過過程呼叫來組織程式,它把儲存在記憶體中的資料抽象為帶有結構的變數,把一組操作資料的指令抽象為過程,透過過程修改變數的值,進而影響後續的計算,因此過程式語言的本質是透過過程來處理資料。
· 面嚮物件語言不再直接處理資料,而是把程式看作是一些獨立的物件之間的相互作用。大部分面嚮物件語言都與過程式語言有著比較深的淵源,只是在程式設計上採取了更加結構化的方式,抽象層次更高一些。在大的方面,面嚮物件語言的程式由一組相互作用的物件組成,物件封裝了資料(或者說狀態)和處理這些資料的方法(或者說行為),整個程式就是描述物件之間如何發生作用。當然,在具體到某個物件的實現上,面嚮物件語言依然遵循著過程式語言資料加過程的模式。