寫在前面 它們不厭其煩地執行人的指令;它們收集世間萬物的知識,供人頃刻之間隨心調取;它們是現代社會的中流砥柱,但其存在卻往往備受忽視。 它們就是電腦,是人類迄今為止最偉大的發明成就,是登峰造極、至高無上的終極工具。 電腦科學的問世,推動了人類歷史上最非比尋常的社會變革之一。 而編程語言作為電腦 ...
寫在前面
它們不厭其煩地執行人的指令;它們收集世間萬物的知識,供人頃刻之間隨心調取;它們是現代社會的中流砥柱,但其存在卻往往備受忽視。
它們就是電腦,是人類迄今為止最偉大的發明成就,是登峰造極、至高無上的終極工具。
電腦科學的問世,推動了人類歷史上最非比尋常的社會變革之一。
而編程語言作為電腦的靈魂,存在感卻遠遠高於電腦,它就像一個紐帶把我們和電腦深深的聯繫在了一起。
編程語言的發展更是和電腦的演變有著密不可分的關係。
不知道大家有沒有好奇過,世界上第一門編程語言是什麼?
今天就由我帶領大家穿越到這一切的起點,去看一看電腦和編程語言的愛恨情仇。
正文
圖靈機
不同的的人面對電腦有著不同的給關註點,有人覺得電腦的設計很巧妙,有人覺得電腦的結構佈置很酷,有人覺得電腦的界面很漂亮……
但電腦的美妙之處並不在於其閃爍的燈光、旋轉的磁碟、成排的晶元和電線,而在於,這些部件的背後隱藏著一個優雅而簡單的想法。這個想法同晶體管、操作系統、網路和文字處理器毫無關聯,也不可能有任何關聯,因為它的誕生時間比這些設備都早。
圖靈的相關生平,這裡不多做贅述,這裡我們只關註他的“可計算性理論”。
還記得當時初學馮諾依曼結構時,我們班裡的同學還就馮諾依曼機與圖靈機誰的貢獻最大,而吵的不可開交,最後也是誰也沒說服誰。
馮諾依曼機大家都不陌生,可以簡單地理解成馮諾依曼機更側重於硬體的實現。
圖靈機偏重的抽象模型是“可計算”和“不可計算”這個電腦的邊界
- 世界上有很多問題,其中只有一小部分是數學問題;
- 在數學問題中,只有一小部分是有解的;
- 在有解的問題中,只有一部分是理想狀態的圖靈機可以解決的;
- 在後一類的問題中,又只有一部分是今天實際的電腦可以解決的。
這個時候可能有人就要說了,這和今天要講的世界上第一個編程語言有什麼關係?你這不是上來就先給電腦定下了一個邊界了嗎?
是的,先展示這段的目的就是為了告訴大家:不要迷信電腦,不要迷信我們的編程語言。
不管是在人工智慧和深度學習大行其道的今天,有些問題也是無法用電腦去解決的,我們要始終懷著一種敬畏之心來面對這個世界。
打孔卡片
這一切都要從打孔卡片開始說起。
美國憲法中要求,每10年就得進行一次人口普查。到了19世紀末期,人口增長的實在是太頻繁了,以至於1880的人口普查歷時8年才最終完成,當時還都是通過紙和筆來完成的。
1890年,Herman Hollerith被授命去解決這一問題,他最終使用了穿孔卡來存儲數據,並用一臺製表機(tabulating machine)來進行統計和排序。
在 20 世紀的大部分時間里,穿孔卡在數據處理行業得到了廣泛的應用,其中專業且日益複雜的單元記錄機器被組織成半自動數據處理系統,使用穿孔卡進行數據輸入、輸出和存儲。
1896年,Hollerith成立了製表機器公司,開始了自己的事業。他把自己的設備和卡片出售給大的保險公司,以及包括英國,義大利,德國,俄羅斯,澳大利亞,加拿大,法國,挪威,波多黎各,菲律賓等國在內的多國政府。
他的公司後來跟別的公司進行了合併,併在1924年最終成為了國際商業機器公司。沒錯,它就是IBM。
這就是我們的第一站:打孔機和打孔卡片。
有人可能會有疑問,這打孔卡片也算是一門編程語言嗎?這就要看大家怎麼去定義編程語言了。
在我看來,只要其涉及數據處理與一定的計算規則都可以稱為一種語言。就像我們常說的:
程式=數據+演算法
哪怕這些數據只是最常見的自然數,哪怕這些演算法只是加減乘除。
機器代碼
打孔卡片靠其出色的能力盛行了一段時間,但你能想想那碩大的機器和成噸種的卡片嗎?
幸運的是這些都只是暫時的,而拯救我們的就是電子學與電腦科學的結合,讓我們脫離了笨重的機械式電腦。
晶體管的出現更是讓“開/關”動作變得簡潔又優雅。
而我們這次的主角——機器代碼,就是這一開一關動作的化身:0和1。
機器代碼之所以被稱為機器代碼,就是因為這種代碼是可以直接被機器(電腦)所讀取。
用二進位代碼表示的電腦能直接識別和執行的一種機器指指令系統令的集合。
大家可以隨便打開一個自己用任何一種高級語言編譯好的二進位文件(.bin)來查看這些機器碼到底長什麼樣子。
最右邊就是我們的機器碼,但為什麼不是01組合,怎麼還有其他數字和字母呢?
這是因為二進位表示的數字實在是太小了,所以大多採用的是十六進位來表示。
彙編語言
用機器語言編寫程式,編程人員要首先熟記所用電腦的全部指令代碼和代碼的涵義。
手編程式時,程式員得自己處理每條指令和每一數據的存儲分配和輸入輸出,還得記住編程過程中每步所使用的工作單元處在何種狀態。這是一件十分繁瑣的工作。
編寫程式花費的時間往往是實際運行時間的幾十倍或幾百倍,而且,編出的程式全是些0和1的指令代碼,直觀性差,還容易出錯。
那麼有沒有一種方式,讓我們能夠更容易的記住這些機器指令?
彙編語言閃亮登場。
彙編語言的主體是彙編指令。彙編指令和機器指令的差別在於指令的表示方法上,彙編指令是機器指令便於記憶的書寫格式。說白了,彙編語言就是助記符(Mnemonics)。
可能有人會問,我們用彙編語言編寫程式,可是電腦只認識機器指令,那該怎麼辦?這時候就需要一個能將彙編語言轉換成機器指令的工具,我們稱其為編譯器。程式員用彙編語言寫出源代碼,再用彙編編譯器將其編譯為機器碼,最後由電腦執行。
再者,彙編語言指令是機器指令的一種符號表示,而不同類型的CPU 有不同的機器指令系統,也就有不同的彙編語言,所以,彙編語言程式與機器有著密切的關係。
所以,除了同系列、不同型號CPU 之間的彙編語言程式有一定程度的可移植性之外,其它不同類型(如:小型機和微機等)CPU 之間的彙編語言程式是無法移植的,也就是說,彙編語言程式的通用性和可移植性要比高級語言程式低。(因為高級語言可以再不同類型的電腦上用不同的編譯器翻譯成機器語言)
Fortran
Fortran語言最初是由 IBM 公司在 50 年代開發的。
當時,創建語言的目的是專門解決一組特定的問題:Fortran 語言的目的是科學處理。
它仍然是在高性能計算領域最流行的語言之一,而且作為基準,用於世界最快的超級電腦程式的語言的排名。
Fortran 語言建立使用星號乘法,這是今天仍在使用的所有語言中的約定。
這是它的外觀:
Program Hello
Print *, "Hello World!"
End Program Hello
COBOL
COBOL (Common Business-Oriented Language)
被設計用於商業用途。這是企圖使編程語言更類似於英語,讓程式員和管理人員可以讀取它。
它的設計者有 Grace Hopper(發現 “Bug” 的人),以及發明瞭類似英語的數據處理語言 FLOW-MATIC的人,也是最合適,幫助創建一個看起來類似英文通用商業語言的人選。
這是一個 COBOL 的 Hello World 程式:
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
ENVIRONMENT DIVISION.
DATA DIVISION.
PROCEDURE DIVISION.
MAIN.
DISPLAY 'Hello, world.'.
STOP RUN.
ALGOL 60
ALGOL 60 (ALGOrithmic Language 1960)
是一個委員會推動的,非常好,有影響力的語言, 發佈於 1960 年。
它從來沒有得到普及,但它推出了許多重要概念,包括擺脫 GOTO。在行與行之間跳來跳去行的語言,如 BASIC,難以遵循程式的流程,導致編寫程式容易出錯。
ALGOL 60 引入結構化程式設計和模塊:它使用 BEGIN 和 END(因為大括弧是不可用),感謝 ALGOL 60 現在我們有了代碼塊,而不是 GOTO。
ALGOL 也不希望太過專業,從而適合良好的科學和業務處理
下麵是它的樣子:
procedure Absmax(a) Size:(n, m) Result:(y) Subscripts:(i, k);
value n, m; array a; integer n, m, i, k; real y;
comment The absolute greatest element of the matrix a, of size n by m
is transferred to y, and the subscripts of this element to i and k;
begin integer p, q;
y := 0; i := k := 1;
for p:=1 step 1 until n do
for q:=1 step 1 until m do
if abs(a[p, q]) > y then
begin y := abs(a[p, q]);
i := p; k := q
end
end Absmax
Pascal
Pascal 是在 1968 - 1969 年設計, 由 Niklaus Wirth出版於1970年,其靈感來自 ALGOL
它最初是非常流行的,雖然最初設計為教學工具,很長一段時間很多人用它做通用編程。
但是,它不是模塊化不夠,有一些使得編程難的設計挑戰。
比如有一個片段:
while a <> b do WriteLn('Waiting');
if a > b then WriteLn('Condition met') {no semicolon allowed!}
else WriteLn('Condition not met');
for i := 1 to 10 do {no semicolon for single statements allowed!}
WriteLn('Iteration: ', i);
repeat
a := a + 1
until a = 10;
case i of
0 : Write('zero');
1 : Write('one');
2 : Write('two');
3,4,5,6,7,8,9,10: Write('?')
end;
B語言
B 在貝爾實驗室開發於 1969 年, 它的靈感來自於 Fortran 和 BCPL。
還記得有個同學開玩笑說,既然有C語言,那肯定會有B語言和A語言,沒想到還真有B語言(其實還有個E語言就不說了)。
B 引入了 +=
操作符 (儘管寫成 =+), 以及自增/自減操作符 (++
和 –
)
printn(n,b) {
extrn putchar;
auto a;
if (a=n/b) /* assignment, not test for equality */
printn(a, b); /* recursive */
putchar(n%b + '0');
}
C語言
C 誕生於 B 加上從 Pascal 加入一些好的想法。它是在貝爾實驗室(再次)的 Dennis Ritchie 在 1969 年和 1973 年之間開發。
相比較於機器語言與彙編語言來說,C已經擁有更強的表達能力,可方便地表示數據的運算和程式的控制結構,能更好的描述各種演算法,而且容易學習掌握。
但高級語言編譯生成的程式代碼一般比用彙編程式語言設計的程式代碼要長,執行的速度也慢。
關於編程語言就說到這裡,如果把每一門語言都說一遍的話,可能幾天幾夜都說不完。
感興趣的同學可以去維基百科上看一看
第一個編譯器是什麼語言編譯的?自舉
除了第一個編程語言外,相信大家肯定還有一個疑問:世界上第一個編譯器使用什麼語言來編譯的?
這就類似於那個世界上是先有雞還是先有蛋的問題。
其實在編譯原理中,有一個概念:自舉。
編程語言是自舉的,指的是說,我們能用自己寫出來的程式編譯自己。但是自舉,並不要求這門語言的第一個編譯器就是用自己寫的。
比如,這裡說到的 Go,先是有了 Go 語言,我們通過 C++ 寫了編譯器 A。然後呢,我們就可以用這個編譯器 A,來編譯 Go 語言的程式。接著,我們再用 Go 語言寫一個編譯器程式 B,然後用 A 去編譯 B,就得到了 Go 語言寫好的編譯器的可執行文件了。這個之後,我們就可以一直用 B 來編譯未來的 Go 語言程式,這也就實現了所謂的自舉了。
更詳細的關於雞蛋問題,可以直接看 Wikipedia 上這個鏈接,裡面講了多種這個問題的解決方案。
寫在最後
在上文中,我們大致瞭解了電腦和編程語言的發展史。
如果從嚴謹一點的角度去考慮的話,Fortran語言應該是世界上第一門高級編程語言。
編程語言千千萬,這些語言之間沒有高低貴賤,更不存在什麼歧視鏈, 有的只有不同的應用環境適合哪一種編程語言。
最後祝願大家不管使用哪一種語言都能bug少少!!!
(室友和同門都申請離校回家了,我還悲慘的在學校呆著,可快點解封吧~我要出去吃燒烤,吃火鍋!)
參考文獻:
《電腦:一部歷史》彼得·本特利
http://foorious.com/articles/brief-history-of-programming-languages/
https://segmentfault.com/a/1190000004303544
https://time.geekbang.org/column/article/91793
https://www.cnblogs.com/ysocean/p/7580162.html