計算機語言是人與計算機進行交流的工具掰吕,是用來書寫計算機程序的工具果覆。
可以通俗地理解為,你用用特定的語言與特定的對象(特定操作系統(tǒng)與CPU的計算機)溝通殖熟,關(guān)鍵是需要有個翻譯局待,這個翻譯就是編譯器或解釋器,同樣的語言菱属,針對不同的對象(特定的CPU和操作系統(tǒng))需要有不同的編譯器或解釋器钳榨。所以說編程語言是“設(shè)計”出來的,設(shè)計只需要思考和寫文檔纽门,而該語言的編譯器或解釋器才是“開發(fā)”出來的薛耻。
(編譯原理講到了“自舉編譯器”。大意就是先用底層語言(應(yīng)該是匯編)寫一個能運行赏陵,但效率極低的C語言編譯器(底層語言不好優(yōu)化)饼齿,有了C語言的編譯器以后,就可以用C語言好好寫一個編譯器了蝙搔,用之前那個運行沒問題候醒,但效率低得編譯器編譯一下,就得到了可以使用的編譯器了杂瘸。)
編譯器也是程序倒淫,所以也需要用編程語言來編寫,很多編程語言是用別的更基礎(chǔ)的語言開發(fā)的败玉,其中用最多的就是C語言敌土。C語言編譯器很多,大部分都是用別的C語言編譯器編譯出來的运翼,而最早的C語言編譯器是用匯編語言寫出來的返干,最早的匯編語言編譯器是通過“編譯器自舉”開發(fā)出來的。
從最基本的角度看血淌,一種編程語言就是把一組特定的詞匯矩欠,按照一組特定的語法規(guī)則組合到一起财剖,形成計算機可以通過某種方式“理解”的東西,可以讓計算機據(jù)此執(zhí)行特定的動作癌淮。
首先要決定你想設(shè)計的語言應(yīng)該解決什么問題躺坟。面對不同的領(lǐng)域、不同的需求乳蓄、不同的抽象層級咪橙、不同的思考范式,也就產(chǎn)生了各有特長的編程語言虚倒。專注于高效美侦、便捷地解決某特定范疇之內(nèi)問題的語言,叫做領(lǐng)域?qū)S谜Z言(Domain Specific Language魂奥,DSL)菠剩,而可以跨越若干領(lǐng)域解決問題的語言,叫做泛用語言(General-Purpose Language耻煤,GPL)赠叼。常見的 DSL 比如 MATLAB、SQL 等等违霞;常見的 GPL 如匯編嘴办、C、Python买鸽。當然涧郊,兩類語言之間的分界并不是很明顯,有些語言一開始是作為 DSL 設(shè)計的眼五,后來漸漸朝著 GPL 的方向發(fā)展妆艘,比如 PHP 和 JavaScript;反過來也有大量基于 GPL 開發(fā)而來的 DSL看幼。
先看看這件事情的最底層批旺。所謂“計算機執(zhí)行動作”,其實只是“把一個二進制數(shù)字傳入 CPU诵姜,然后等待什么事情發(fā)生”的形而上描述汽煮。二進制計算機所能理解的唯一東西就是二進制數(shù)字,稱為“機器碼”棚唆。比如:
10110 000 01100001
這串數(shù)字暇赤,對于某顆 CPU 來說,就是“把 01100001 放到 000 號寄存器里”的指令宵凌,其中“10110”的部分鞋囊,就是 CPU 能懂得的“放入”指令。這樣的指還有許許多多瞎惫,比如做加法溜腐、求邏輯“與”译株,跳轉(zhuǎn),加密等等挺益,全都只是一些二進制數(shù)字而已歉糜。
對人類來說,這種純數(shù)字的寫法太難記憶矩肩,就把它轉(zhuǎn)寫成:
MOV AL, 97
其中 MOV 代表“10110”现恼,AL 代表 000 號寄存器肃续,97 則是二進制數(shù) 01100001 的十進制表示黍檩。其他的數(shù)字指令也一并用這種簡記法來轉(zhuǎn)寫。使用這樣的一種轉(zhuǎn)寫方法來寫程序始锚,就是匯編語言(當然刽酱,這是一種極度簡化的說法)。匯編語言談不上太多設(shè)計瞧捌,其實幾乎就是在直接告訴 CPU 應(yīng)該做什么棵里。把匯編語言轉(zhuǎn)化為機器碼的程序,稱為“匯編器(Assembler)“姐呐。
匯編語言的優(yōu)勢是很低級殿怜,你能直接控制 CPU 的行為;匯編語言的缺點也是它太低級曙砂,你必須直接控制 CPU 的行為头谜。看看“把 A 的值放進甲寄存器鸠澈;B 的值放進乙寄存器柱告;把乙寄存器的值放進 A;把甲寄存器的值放進 B笑陈〖识龋”這段匯編指令執(zhí)行后是什么結(jié)果?運行一下之后會看到涵妥,A 和 B 的值互換了乖菱。那么,能不能直接寫“交換變量 A 和 B 的值”蓬网,然后由計算機來分解為一串機器碼的組合呢块请?
所謂的“高級”編程語言就是這樣的原理。將高級編程語言翻譯成機器碼(或者其他更接近機器碼的形式)的過程拳缠,也就是計算機“理解”語言的過程墩新,叫做“編譯”,而完成這一工作的程序窟坐,叫做“編譯器(compiler)”或者“解釋器(interpreter)”海渊,兩者的區(qū)別是绵疲,編譯器一次性解析所有代碼并轉(zhuǎn)換成機器碼(但通常不會運行),而解釋器則每解析一小部分就運行一小部分臣疑。
接下來就要考慮兩個問題:高級語言要讓人寫起來方便盔憨;也要讓計算機易懂。因為人類是難搞的物種讯沈,所以前者通常是語言設(shè)計的重點郁岩。畢竟,只要懂些編程的基本知識缺狠,任何人都可以在三天時間里設(shè)計出一門計算機語言问慎,并且讓計算機讀懂它(也就是寫出編譯器),但要讓一種計算機語言寫起來舒服挤茄、讀起來易懂如叼、管理起來方便,所需耗費的心力和時間則相去不可以道里計穷劈。探尋這一問題的種種思潮所引發(fā)的范式轉(zhuǎn)換和生產(chǎn)力革命笼恰,是計算機歷史的永恒主題之一。計算機語言越來越高級歇终,使用起來越來越簡單社证,實現(xiàn)卻越來越復(fù)雜;許多編程觀念比如面向?qū)ο螅╫bject orientation)评凝、函數(shù)編程(functional programming)追葡、事件驅(qū)動(event driven)之誕生、沉寂肥哎、重現(xiàn)辽俗、興盛和定型,都經(jīng)由編程語言有所體現(xiàn)篡诽。
當然這并不是說編譯部分就不重要崖飘。可靠杈女、高效朱浴、靈活的編譯器是一切編程工作的基石。我們?nèi)粘K玫木幾g器都是如此千錘百煉的東西达椰,以至于你很少會意識到它們本身也是復(fù)雜的軟件工程項目翰蠢,也有可能出問題,也在不斷地發(fā)展著啰劲。十年前和現(xiàn)在的編譯器梁沧,從架構(gòu)理念到實現(xiàn)都有不小的差別。好在這種差別算不上天翻地覆蝇裤,計算機語言編譯的大致過程一直都是如下幾個步驟:
高級語言的源代碼經(jīng)過詞法分析(lexical analysis)成為一堆符記(token)廷支;
符記經(jīng)過句法分析(syntactic analysis)成為語法樹(abstract syntax tree, AST)频鉴;
語法樹經(jīng)過優(yōu)化,比如去除冗余的部分恋拍,最后映射成為機器碼(machine code)垛孔;
第一步,詞法分析施敢,根據(jù)的是語言設(shè)計者所規(guī)定的詞匯規(guī)則周荐。比如 PHP 規(guī)定變量前頭必須加個 $ 符號,就是這樣的規(guī)則僵娃。通常通過正則表達式(regular expression)給出這些規(guī)則概作。根據(jù)規(guī)則來分析源代碼的編譯器組件叫做詞法分析器(lexer)或者掃描器(scanner)。掃描器可以自己手寫悯许,也可以讓叫做 scanner generator 的程序讀取一個正則表達式仆嗦,然后幫你生成一個 scanner辉阶。詞法分析的目的是判斷人類寫下的每個詞是不是合乎拼寫規(guī)則先壕,如果不符的話,顯然也就無法編譯了谆甜。
第二步垃僚,句法分析,根據(jù)的是語言設(shè)計者所規(guī)定的語法規(guī)則规辱。關(guān)于形式語言的語法理論谆棺,涉及到語言學(xué)和數(shù)理邏輯,是一個復(fù)雜而艱深的領(lǐng)域罕袋,好在對于設(shè)計一門計算機語言來說改淑,只需要知道,計算機語言的語法通常是上下文無關(guān)文法(context-free grammar)即可:生成一條計算機語句的規(guī)則與這一規(guī)則所處的環(huán)境無關(guān)浴讯。這樣一來朵夏,解析一條編程語句的過程就是確定的無二的。根據(jù)規(guī)則來將上一步驟獲得的詞匯解析為特定的數(shù)據(jù)結(jié)構(gòu)——比如語法樹——的工具榆纽,叫做句法分析器(parser)仰猖。同樣,句法分析器可以自己寫奈籽,也可以用特定的方法(最常見的是巴克斯范式(BNF))給出下上下文無關(guān)文法的形式語言描述饥侵,然后用所謂 parser generator 來生成∫缕粒可以說躏升,語法樹(或者類似的中間產(chǎn)物)代表了從編程語句中提煉出來的意義,這是整個編譯過程的核心所在狼忱。
第三步膨疏,語法樹或者其他的語義表示方法經(jīng)由優(yōu)化器(optimizer)的修剪盗温,送入負責(zé)將特定結(jié)構(gòu)轉(zhuǎn)化為目標機器代碼的程序生成可以運行的二進制程序。這一步必須考慮執(zhí)行效率優(yōu)化和目標架構(gòu)的特點成肘,與高級編程語言本身已經(jīng)并無太大關(guān)聯(lián)了卖局。
至此便可以開始設(shè)計編程語言了。
簡單來說双霍,定義一門語言砚偶,有點像定義一個宇宙。一開始洒闸,宇宙空空如也染坯。為了讓這個宇宙能夠開始運轉(zhuǎn),并衍生出超越我們想象的復(fù)雜世界丘逸,我們作為“造物主”单鹿,需要準備好兩種東西:一是元素——就是提供一些基本的數(shù)據(jù)類型;二是規(guī)則——基本元素之間的運算法則深纲。
以上兩種東西合起來仲锄,就是這門語言的“類型系統(tǒng)”。數(shù)學(xué)一點而言湃鹊,就是定義一個基本集合儒喊,并定義在這個集合之上的運算。
這看起來沒什么了不起币呵。常常被人們忽略怀愧,但事實上,我們復(fù)雜的世界本質(zhì)上也不過是由一些簡單的基本單元基于一些基本規(guī)則運動的結(jié)果余赢。語言設(shè)計也遵循這一原則芯义。
可以說,類型系統(tǒng)是一門語言的核心妻柒。因為一門編程語言扛拨,本質(zhì)而言,主要做兩件事情:一是描述信息蛤奢;二是處理信息鬼癣。
描述信息需要使用存儲空間,而處理信息需要使用運算啤贩。問題是運算本身對存儲空間會進行特別的解釋和假設(shè)待秃,這就導(dǎo)致了我們的存儲空間盡管都是字節(jié)或之類的看起來別無二致的通用的空間,但從語言角度來看必須對這些空間進行特別的限定和理解痹屹,于是便產(chǎn)生了”類型“的概念章郁。
例如C語言有double和long,本質(zhì)而言兩種類型都是使用字節(jié)進行存儲。但由于運算時采取完全不同的解釋(甚至?xí)捎胏pu內(nèi)部不同的運算器件),因此有必要對他們進行區(qū)分。
除了基本的類型系統(tǒng)之外叶圃,語言還需要有一些流程控制機制商源。和代碼組織機制冕杠。這些都需要設(shè)計。可以借鑒現(xiàn)有的編程語言,也可以創(chuàng)造獨一無二的新方式泣港。隨你自由。
至于對象价匠、變量什么的当纱,其實之前設(shè)計類型系統(tǒng)的時候就已經(jīng)涵蓋了。函數(shù)之類的在類型系統(tǒng)中也會涉及踩窖,同時也會影響代碼組織機制等等坡氯。
大家知道世界上最早的編程語言是什么嗎?一般認為是1954年開始開發(fā)的FORTRAN語言洋腮。
然后箫柳,仔細想想看,到底什么才是編程語言徐矩?如果將對機器的控制也看成是編寫“程序”的話滞时,那么編程的起源便可以追溯到杰卡德織機上面所使用的打孔紙帶叁幢。
1801年滤灯,正值工業(yè)期間,杰卡德織機的發(fā)明使得提花編織的圖案可以通過“程序”來自動完成曼玩。從前在各個家庭中出現(xiàn)了自動紡織機鳞骤,用于家庭作坊式的自動紡織生產(chǎn),而杰卡德織機則相當于是這些家庭紡織機的放大版黍判。我想那些自動紡織機應(yīng)該也可以通過類似打孔紙帶的東西來輸入圖案豫尽,當然,最近的年輕人恐怕沒有親眼見過紡織機吧顷帖。
這種用打孔機來控制機器的想法美旧,對各個領(lǐng)域都產(chǎn)生了影響。例如在英國從事通用計算機研發(fā)的查爾斯·巴貝奇贬墩,就在自制的“分析機”上用打孔紙帶來控制程序榴嗅,遺憾的是,由于資金和其他一些問題陶舞,巴貝奇在生前未能將他的分析機制造出來嗽测。
不過,分析機的設(shè)計已經(jīng)完成肿孵,用于分析機的程序也作為文檔保留了下來唠粥。協(xié)助開發(fā)這些程序的疏魏,是英國詩人拜倫之女愛達·洛夫萊斯,據(jù)說她和巴貝奇是師兄妹關(guān)系晤愧。如果不算分析機的設(shè)計者巴貝——那么世界上第一位程序員實際上是一位女性大莫。為了紀念她,還有一種編程語言是以她的名字Ada命名官份。
在被稱為世界上第一臺計算機的ENIAC(1946年)中葵硕,程序不是用打孔紙帶,而是通過接電線的方式來輸入贯吓,讓人總覺得這是一種退步懈凹。
不過,無論是打孔紙帶悄谐,還是接電線介评,都不太可能實現(xiàn)太復(fù)雜的程序,真正的程序恐怕還要等到存儲程序式計算機出現(xiàn)以后爬舰。一般認為们陆,世界上第一臺存儲程序式電子計算機,是1949年出現(xiàn)的EDSAC情屹。
到了這個時候坪仇,所謂的“機器語言”就算正式問世了。當時的計算機程序都是用機器語言來縮寫的垃你。那個時候不要說是編譯器椅文,連匯編器都沒有發(fā)明出來呢,因此使用機器語言也就是理所當然的事情了惜颇。
說到底皆刺,機器語言就是一連串數(shù)字,將計算的步驟從指令表中查出對應(yīng)的機器語言編碼凌摄,再人工寫成數(shù)列羡蛾,這個工作可不容易∠强鳎或者說痴怨,以前的人雖然沒有意識到,但從我們現(xiàn)代人的角度來看器予,這種辛苦簡直是難以置信浪藻。比如說,把引導(dǎo)程序的機器語言數(shù)列整個背下來劣摇,每次啟動的時候手動輸入進去珠移;將機器語言指令表全部背下來,不用在紙上打草稿就能直接輸入機器語言指令并正確運行--“古代”的程序員們留下了無數(shù)的光輝事跡(或者是傳說),那時候的人們真是太偉大了钧惧。
然而有一天暇韧,有一個人忽然想到,查表這種工作本來應(yīng)該是計算機最擅長的浓瞪,那到讓計算機自己做不做好了嗎懈玻?于是,人們用更加容易記憶的指令(助記符)來代替數(shù)值乾颁,并開發(fā)了一種能夠自動生成機器語言的程序涂乌,這就是匯編器。
匯編器是用來解釋“匯編語言”的程序英岭,匯編語言中所使用的助記符湾盒,和計算機指令是一一對應(yīng)的關(guān)系。早期的計算機主要還是用于數(shù)值計算诅妹,因此數(shù)學(xué)才是主宰罚勾。在數(shù)學(xué)的世界里,數(shù)百年傳承下來的“語言”就是算式吭狡,因此用接近算式的形式來編寫計算機指令就顯得相當方便尖殃。隨后,F(xiàn)ORTRAN于1954年問世了划煮。FORTRAN這個名字的意思是:算式翻譯器(FORmula TRANslator).
也就是說送丰,編程語言是由編程者根據(jù)自己的需要發(fā)明出來的。早期的計算機弛秋,由于性能不足器躏、運算成本高,因此編寫和維護都被看成是非人的工作铐懊,而編程語言正是其開發(fā)擺脫非人性的象征邀桑。
其實,由助記符自動生成機器語言的匯編器科乎,以及由人類較易懂的算式語句生成機器語言的編譯器,當時都被認為是革新性的技術(shù)贼急,被稱為“自動編程”茅茂。此外,編譯器開發(fā)技術(shù)的研究甚至被視為人工智能研究的一部分太抓。
未來的編程語言可能不會像過去的語言那樣空闲,讓語言本身單獨存在,而是和編輯器走敌、調(diào)試器碴倾、性能分析器等一切工具相互配合,以達到提高整體生產(chǎn)效率的目的。
計算機語言的發(fā)展過程
按照計算機語言的發(fā)展階段跌榔,可以分為機器語言异雁、匯編語言和高級語言三類。
7解釋型僧须、編譯型纲刀、混合型語言比較
8常用語言的應(yīng)用領(lǐng)域
9 為什么這么多的語言?
語言設(shè)計人員設(shè)計的語言是為了解決特定的問題的目的而設(shè)計的(以用其編寫的程序應(yīng)用于特定領(lǐng)域)
語言設(shè)計人員設(shè)計的語言在以下方面有側(cè)重點的取舍:編程簡單担平、程序易讀示绊、執(zhí)行效率高;
10 結(jié)構(gòu)程序設(shè)計與面向?qū)ο蟪绦蛟O(shè)計
傳統(tǒng)的結(jié)構(gòu)程序設(shè)計采取的方式是先考慮求解問題的算法暂论,然后再尋找合適的數(shù)據(jù)結(jié)構(gòu)面褐。即傳統(tǒng)的結(jié)構(gòu)程序是:程序=算法+數(shù)據(jù)結(jié)構(gòu)。
面向?qū)ο蟮能浖_發(fā)思想認為程序是由對象組成的取胎,而所有的這些程序代碼又都是是放在類中的盆耽。
傳統(tǒng)的過程化程序設(shè)計,必須從頂部的main函數(shù)開始編寫程序扼菠。在設(shè)計面向?qū)ο蟮南到y(tǒng)時沒有所謂的頂部摄杂。而是從設(shè)計類開始,然后再往每個類中添加方法循榆。
C語言是支持結(jié)構(gòu)程序設(shè)計的語言析恢,而C++既支持結(jié)構(gòu)程序設(shè)計,同時也支持面向?qū)ο蟪绦蛟O(shè)計秧饮。
11 一個成功的編程語言必須滿足4個準則
需要建立一個明顯的社區(qū)映挂。只有讓采用者安心,他才會去使用此技術(shù)盗尸;
需要具備可移植性柑船,如Java虛擬機已經(jīng)提高了后繼語言的門檻;
需要提供經(jīng)濟上的動機泼各,生產(chǎn)力鞍时、無線運算、數(shù)據(jù)搜索扣蜻;
它需要展示技術(shù)優(yōu)點逆巍;
如Java是一個很棒的靜態(tài)面向?qū)ο笳Z言,具有可移植性及大量的API莽使、產(chǎn)品锐极、開放源碼項目,也是一個設(shè)計良好的語言和虛擬機芳肌。