世界上第一門程式語言究竟是誰?

2022-05-28 15:00:22

寫在前面

它們不厭其煩地執行人的指令;它們收集世間萬物的知識,供人頃刻之間隨心調取;它們是現代社會的中流砥柱,但其存在卻往往備受忽視。

它們就是計算機,是人類迄今為止最偉大的發明成就,是登峰造極、至高無上的終極工具。

電腦科學的問世,推動了人類歷史上最非比尋常的社會變革之一。

程式語言作為計算機的靈魂,存在感卻遠遠高於計算機,它就像一個紐帶把我們和計算機深深的聯絡在了一起。

程式語言的發展更是和計算機的演變有著密不可分的關係。

不知道大家有沒有好奇過,世界上第一門程式語言是什麼?

今天就由我帶領大家穿越到這一切的起點,去看一看計算機和程式語言的愛恨情仇。

正文

圖靈機

不同的的人面對計算機有著不同的給關注點,有人覺得計算機的設計很巧妙,有人覺得計算機的結構佈置很酷,有人覺得計算機的介面很漂亮……

但計算機的美妙之處並不在於其閃爍的燈光、旋轉的磁碟、成排的晶片和電線,而在於,這些部件的背後隱藏著一個優雅而簡單的想法。這個想法同電晶體、作業系統、網路和文書處理器毫無關聯,也不可能有任何關聯,因為它的誕生時間比這些裝置都早。

圖靈的相關生平,這裡不多做贅述,這裡我們只關注他的「可計算性理論」。

還記得當時初學馮諾依曼結構時,我們班裡的同學還就馮諾依曼機與圖靈機誰的貢獻最大,而吵的不可開交,最後也是誰也沒說服誰。

馮諾依曼機大家都不陌生,可以簡單地理解成馮諾依曼機更側重於硬體的實現

圖靈機偏重的抽象模型是「可計算」和「不可計算」這個計算機的邊界

  1. 世界上有很多問題,其中只有一小部分是數學問題;
  2. 在數學問題中,只有一小部分是有解的;
  3. 在有解的問題中,只有一部分是理想狀態的圖靈機可以解決的;
  4. 在後一類的問題中,又只有一部分是今天實際的計算機可以解決的。

這個時候可能有人就要說了,這和今天要講的世界上第一個程式語言有什麼關係?你這不是上來就先給計算機定下了一個邊界了嗎?

是的,先展示這段的目的就是為了告訴大家:不要迷信計算機,不要迷信我們的程式語言

不管是在人工智慧和深度學習大行其道的今天,有些問題也是無法用計算機去解決的,我們要始終懷著一種敬畏之心來面對這個世界。

打孔卡片

這一切都要從打孔卡片開始說起。

美國憲法中要求,每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