commonlisp編譯器
A. lisp語言的快速入門
Common Lisp語言快速入門: Lisp是軟體領域的分裂力量。一方面,Lisp愛好者誓言Lisp比軟體領域內的其它語言都更加快捷、整潔和強大;而反對者則辯稱,不可靠的執行和庫支持不足使得開發者難以在其中編寫任何真正的軟體。事實上,他們都有各自的道理。
第一版Lisp於大約50年前推出,這使得它和FORTRAN一樣,成為現在仍在使用的最古老的編程語言之一。可以證明,它擁有(它們將會擁有)最龐大的特性列表,它也是第一種包括一整套今天我們全都認為是標准語言特性的語言,如垃圾收集、遞歸、函數作為對象、甚至是普通的if-then-else子句。同時,人們也認為它是一款優秀的教學語言:MIT使用方案、Lisp衍生物,傳授它們的介紹性編程類。
我們將一起學習最強大和項目就緒的Lisp版本:Common Lisp,使其正常運行,並了解一些Lisp應用。 代碼和數據並無差異——在Lisp中,代碼只是一個函數對象列表。源代碼和數據源之間不存在區別,允許Lisp把它的內在呈現給編譯器、注釋器和程序員。這允許你方便地本地讀入和評估代碼,甚至可以允許你使用宏。
宏——定義和重新定義任何語言元素。不喜歡條件、或循環或函數的運行方式嗎?好,你可以定義自己的注釋。如果你在代碼中多次執行某個特定的任務,把那個特性添加到語言中不是更加方便嗎?應用Lisp,你可以實現上述功能。
速度——雖然在許多情況下Common Lisp不如C或OCaml這類速度巨人快捷,但它在一系列測試中表現良好,特別是在執行一小段程序的情況下。了解一些基本的編譯器知識,你就能夠編寫出處理列表和大型數字的代碼,在執行速度和內存使用方面都要優於其它語言。
簡化——Lisp的一切功能都基於一些基本的理念——一旦你了解那些理念,你就幾乎能夠處理任何問題。Lisp程序員常常自誇說,僅僅用幾百行代碼,你就能在幾乎任何語言(如C++或Haskell)中執行一個Lisp注釋器。
靈活——以你喜歡的任何方式編寫代碼。更喜歡功能性的編程方法嗎?沒問題!想要完全反復編程嗎?草草寫下幾個快速的宏就可以完成任務。你可以用最方便最高效的方式編寫程序,而且這些程序都能良好地運行。 好,行!我選擇了Lisp,我如何進行安裝呢?
這是棘手的問題。不像是Python或C#一樣,Common Lisp沒有標准執行——該語言由一個規范,而不是執行來定義。Common Lisp也沒有C語言的特點,及在每一個平台上都是一種支配性的執行或流行的執行。每個版本都應執行上述標准,但有一些細節要由編譯器或注釋器來處理,這使得每個執行都稍有不同。
你可以使用幾種選項——在本文中我使用CLISP,它在Windows、Linux和Mac(僅PPC)上運行良好。如果你使用英特爾Mac,則必須使用其它執行,如Allegro Common Lisp或SBCL。對於這篇快速入門中的簡單例子,你使用哪個執行都不要緊。
使用Lispbox可以快速安裝Common Lisp系統,但遺憾的是,Lispbox在2012年停止了更新。Lispbox為你提供一個Common Lisp執行、Emacs、SBCL和Slime——Emacs高級Lisp整合模式,許多Lisp程序員會告訴你說,這個組合是使用Lisp的唯一方法。如果你並非Emacs用戶(我本人也和你一樣),不要擔心,它並非必要條件,只是使得編寫Common Lisp程序更加簡便。而如果想要使用最新的Emacs、SBCL和Slime,網路上也有許多教程關於如何配置它們。
安裝過程因平台而異,在Windows中你只能運行安裝程序,多數Linux軟體包管理器提供安裝包等。選擇一個執行並遵循安裝指令即可。打開Common Lisp (CLisp)交互環境的方式是M-x slime,如果你不理解這是什麼意思的話可以通過Ctrl+h松開後按t來學習Emacs的基本操作。但由於Emacs的默認字體不支持中文,所以為了避免教程亂碼,點擊程序窗口左上角的Options,選擇Set Default Font,選擇支持中文的字體後保存。打開REPL(互動式提示符)之後再返回這里,我們繼續往後講。 REPL代表「閱讀-評估-列印-循環」(Read-Evaluate-Print-Loop),它簡單表示注釋器的一個互動式提示符。你可以從這里輸出一些簡單的Lisp代碼。如果你使用另一種注釋器提示符,你可以在這里使用提示符作為計算器輸出一些基本的數學表達式——但它不能正常運行。在CLISP中輸入5*2不會返回任何有意義的結果:
> 5 * 2
5
> 5
5
> 2
2
Lisp並非以那種方式運行,運算符,如「+」不是在數字之間,而是在數字前面,就好像它們是函數名稱一樣。因此,如果你想將REPL當作計算器使用,你必須輸入:
> (* 5 2)
10
> (+ 1 2 3 4)
10
> (+ (* 5 2) (* 10 3) (/ 100 4))
65
對你來說,理解這種用法可能更難一些,但它擁有一些優勢:它便於編譯器解析、它對所有函數和運算符都一樣、它讓你給函數添加盡可能多的自變數——例如,在上面的第二個例子中,你可以任意擴充加數,使得加法函數和總計函數完全一樣。
另外你會注意到,函數名在括弧內,而不像許多其它語言一樣函數名在括弧以外。這表示你要寫(函數自變數)而不是函數(自變數)。
每個Lisp表達式會返回一個值,一個函數總是返回最後一個表達式的結果——即使是NIL,NULL在Java或C++中的對等值也是這樣。因此在Lisp中顯示「Hello World」相當簡單:
> Hello World
Hello World
如果你希望在屏幕上列印一些內容,並返回其它內容,你應該使用列印函數:
> (print Hello World)
Hello World
Hello World
這個字元串顯示兩次,一個是列印結果,一個是函數返回的結果。
Lisp表示LIST Processor(列表處理器),Lisp中的幾乎所有內容都以列表的形式存在,因此有時你必須處理列表。定義列表非常容易:
> (list 1 2 3 4 5)
(1 2 3 4 5)
> '(1 2 3 4 5)
(1 2 3 4 5)
第二種定義方法叫做引用,除定義簡單的列表外,它還有更多用途,不過我們必須在後面的另一篇文章中討論那個主題。 Lisp擁有全部標准控制流程方法。定義一個重復一個值的簡單循環相當容易:
> (dotimes (i 10) (print i))0
1
2
3
4
5
6
7
8
9
NIL
同樣,重復一個列表也很簡單:
> (dolist (i '(0 1 2 3 4 5 6 7 8 9)) (print i))
0
1
2
3
4
5
6
7
8
9
NIL
以上兩個函數都是DO函數的特殊版本,它就像在其它語言中組合使用while和for函數一樣。它由三個部分組成:循環變更定義、終止條件和語句主體:
> (do ((i 0 (+ 1 i))) ((> i 10)) (print i))
0
1
2
3
4
5
6
7
8
9
10
NIL
在這個例子中,變更定義部分為((i 0 (+ 1 i))),它定義變數i為0,並在每次循環時調用函數(+ 1 0)。終止條件為((> i 10)),表示在i大於10時函數終止運行。最後主體部分列印i的值。
Lisp中也有條件函數,最基本的條件函數為if函數:
> (if (> 10 20) (print Hello) (print World))
World
World
if函數由三部分組成:條件、then語句和else語句。如果條件為真,則執行then語句,否則就執行else語句。
你可能已經注意到,到現在為止我們僅使用了單個的語句——但如果你需要把幾個語句連接在一起,那該怎麼辦呢?在Lisp中,要將幾個語句連接起來,你需要使用progn這個特殊的控制流程函數:
> (progn (print Hello) (print World))
Hello
World
World
例如,上例允許你在條件函數和循環中使用幾個語句。
如果你想要更進一步的了解和學習Common Lisp,可以搭配學習ANSI Common Lisp 和Practical Common Lisp (實用Common Lisp編程),如果你想再更進一步,可以看一看On Lisp 等參考書。
B. 如何評價 Racket 這門編程語言
Racket的誕生與發展
簡單介紹一下Racket的發展,詳見知乎的一個關於Racket的問題回答:
1958年,人工智慧之父John McCarthy 發明了一種以 Lambda 演算為基礎的符號處理語言,1960年 McCarthy 發表著名論文Recursive Functions of Symbolic Expressions and Their Computation by Machine, 從此這種語言被命名為 LSIP (List Processor),其語法被命名為:符號表達式(S-Expression)。LISP構建在7個函數[atom car cdr cond cons eq quote]和2個特型[lambda label]之上。
Lisp誕生之初是為了純粹的科學研究,代碼執行像數學公式一樣,以人的大腦來演算。直到麥卡錫的學生斯蒂芬·羅素將eval函數在IBM 704機器上實現後,才開啟了Lisp作為一種計算機語言的歷史。1962年,第一個完整的Lisp編譯器在MIT誕生,從此之後Lisp以MIT為中心向全世界傳播。之後十多年,出現了各種Lisp方言。
1975年,Scheme誕生。Scheme同樣誕生與MIT,它的設計哲學是最小極簡主義,它只提供必須的少數幾個原語,所有其他的實用功能都由庫來實現。在極簡主義的設計思想下,Scheme趨於極致的優雅,並作為計算機教學語言在教育界廣泛使用。
1984年,Common Lisp誕生。在二十世紀七八十年代,由於Lisp方言過多,社區分裂,不利於lisp整體的發展。從1981年開始,在一個Lisp黑客組織的運作下,經過三年的努力整合後,於1984年推出了Common Lisp。由於Scheme的設計理念和其他Lisp版本不同,所以盡管Common Lisp借鑒了Scheme的一些特點,但沒有把Scheme整合進來。此後Lisp僅剩下兩支方言: Common Lisp 和 Scheme。
從二十世紀九十年代開始,由於C++、Java、C#的興起,Lisp逐漸沒落。直到2005年後,隨著科學計算的升溫,動態語言JavaScript、Python、Ruby的流行,Lisp又漸漸的回到人們的視線。不過在Lisp的傳統陣地教育界,Python作為強有力的挑戰者對Scheme發起沖鋒;在2008年,MIT放棄了使用Scheme作為教學語言的SICP(計算機程序的構造和解釋)課程,而啟用Python進行基礎教學。同時美國東北大學另立爐灶,其主導的科學計算系統PLT Scheme開始迅猛發展;2010年,PLT Scheme改名為Racket。近幾年,The Racket Language連續成為年度最活躍語言網站,並駕齊驅的還有haskell網站。
符號表達式 S-Expression
首先說一下S表達式:S-表達式的基本元素是list與atom。list由括弧包圍,可包涵任何數量的由空格所分隔的元素,原子是其它內容。其使用前綴表示法,在Lisp中既用作代碼,也用作數據。如:1+2*3 寫成前綴表達式就是(+ 1 (* 2 3)) 。
優點:容易parse,簡單純粹,不用考慮什麼優先順序等,也是實現代碼即數據的前提;
缺點:可讀性不是很強;
高階函數
高階函數至少滿足下列一個條件:
接受一個或多個函數作為輸入;
輸出一個函數;
微積分中的導數就是一個例子,映射一個函數到另一個函數。在無類型 lambda 演算中,所有函數都是高階的。在函數式編程中,返回另一個函數的高階函數被稱為Curry化的函數。Curry化即把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,並且返回接受餘下的參數而且返回結果的新函數的技術。如 f(x,y)=x+y, 如果給定了 y=1,則就得到了 g(x)=x+1 這個函數。
Lambda 表達式
Racket中實用Lambda表達式來定義匿名函數,《如何設計程序》書中給出的使用原則是:如果某個非遞歸函數只需要當參數使用一次,實用Lambda表達式。如果想用Lambda表達式來表達遞歸,就需要引入Y組合子,Y 就是這樣一個操作符,它作用於任何一個 (接受一個函數作為參數的) 函數 F,就會返回一個函數 X。再把 F 作用於這個函數 X,還是得到 X。所以 X 被叫做 F 的不動點(fixed point),即 (Y F) = (F (Y F)) 。
惰性求值
惰性求值(Lazy Evaluation),說白了就是某些中間結果不需要被求出來,求出來反而不利於後面的計算也浪費了時間。參見:惰性求值與惰性編程。
惰性求值是一個計算機編程中的一個概念,它的目的是要最小化計算機要做的工作。惰性計算的最重要的好處是它可以構造一個無限的數據類型。使用惰性求值的時候,表達式不在它被綁定到變數之後就立即求值,而是在該值被取用的時候求值。語句如 x:=expression; (把一個表達式的結果賦值給一個變數)明顯的調用這個表達式並把計算並把結果放置到 x 中,但是先不管實際在 x 中的是什麼,直到通過後面的表達式中到 x 的引用而有了對它的值的需求的時候,而後面表達式自身的求值也可以被延遲,最終為了生成讓外界看到的某個符號而計算這個快速增長的依賴樹。
閉包
閉包在計算機科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變數的函數。自由變數是在表達式中用於表示一個位置或一些位置的符號,比如 f(x,y) 對 x 求偏導時,y就是自由變數。這個被引用的自由變數將和這個函數一同存在,即使已經離開了創造它的環境也不例外。在函數中(嵌套)定義另一個函數時,如果內部的函數引用了外部的函數的變數,則可能產生閉包。運行時,一旦外部的 函數被執行,一個閉包就形成了,閉包中包含了內部函數的代碼,以及所需外部函數中的變數的引用。其中所引用的變數稱作上值(upvalue)。網上有很多將JavaScript閉包的文章,如果你對LISP有系統的了解,那麼這個概念自然會很清楚了。
快排的Racket實現
#langracket
(define(quick-sortarray)
(cond
[(empty?array)empty];快排的思想是分治+遞歸
[else(append
(quick-sort(filter(lambda(x)(<x(firstarray)))array));這里的array就是閉包
(filter(lambda(x)(=x(firstarray)))array)
(quick-sort(filter(lambda(x)(>x(firstarray)))array)))]))
(quick-sort'(132534509824))
;;運行結果'(012334455982)
通過這個例子,就可以感受到基於lambda運算元的 Racket 語言強大的表達能力了,高階函數、lambda表達式和閉包的使用是Racket所描述的快排十分的精煉,這和 基於馮諾依曼模型C語言是迥然不容的思維模式。後面,隨著Racket 學習的進一步深入,嘗試寫一下解釋器
C. 請問LISP編程語言的編譯器是用什麼語言編寫的
LISP(全名LISt Processor,即鏈表處理語言),由約翰·麥卡錫在1960年左右創造的一種基於λ演算的函數式編程語言。 ——網路
使一種函數編程語言,曾被用於人工智慧,語言都是一些符號,函數,很簡單的語言(因此也做不出什麼程序來或是很難做出,更別談人工智慧),不需要英語基礎。
D. LISP代碼是什麼
LISP(全名LISt Processor,即鏈表處理語言),由約翰·麥卡錫在1960年左右創造的一種基於λ演算的函數式編程語言。
LISP有很多種方言,各個實現中的語言不完全一樣。各種LISP方言的長處在於操作符號性的數據和復雜的數據結構。1980年代Guy L. Steele編寫了Common Lisp試圖進行標准化,這個標准被大多數解釋器和編譯器所接受。在Unix/Linux系統中,還有一種和Emacs一起的Emacs Lisp(而Emacs正是用Lisp編寫的)非常流行,並建立了自己的標准。
LISP的祖先是1950年代Carnegie-Mellon大學的Newell、Shaw、Simon開發的IPL語言。
LISP語言的主要現代版本包括Common Lisp和Scheme。
lisp擁有理論上最高的運算能力
1 基本介紹
Lisp的表達式是一個原子(atom)或表(list),原子(atom)是一個字母序列,如abc;表是由零個或多個表達式組成的序列,表達式之間用空格分隔開,放入一對括弧中,如:
abc
()
(abc xyz)
(a b (c) d)
最後一個表是由四個元素構成的,其中第三個元素本身也是一個表。
正如算數表達式1+1有值2一樣,Lisp中的表達式也有值,如果表達式e得出值v,我們說e返回v。如果一個表達式是一個表,那麼我們把表中的第一個元素叫做操作符,其餘的元素叫做自變數。
Lisp的7個公理(基本操作符):
(quote x)返回x,我們簡記為'x
(atom x)當x是一個原子或者空表時返回原子t,否則返回空表()。在Lisp中我們習慣用原子t表示真,而用空表()表示假。
> (atom 'a)
t
> (atom '(a b c))
()
> (atom '())
t
現在我們有了第一個需要求出自變數值的操作符,讓我們來看看quote操作符的作用——通過引用(quote)一個表,我們避免它被求值。一個未被引用的表達式作為自變數,atom將其視為代碼,例如:
> (atom (atom 'a))
t
反之一個被引用的表僅僅被視為表
> (atom '(atom 'a))
()
引用看上去有些奇怪,因為你很難在其它語言中找到類似的概念,但正是這一特徵構成了Lisp最為與眾不同的特點——代碼和數據使用相同的結構來表示,而我們用quote來區分它們。 (eq x y)當x和y的值相同或者同為空表時返回t,否則返回空表()
> (eq 'a 'a)
t
> (eq 'a 'b)
()
> (eq '() '())
t
(car x)要求x是一個表,它返回x中的第一個元素,例如:
> (car '(a b))
a
(cdr x)同樣要求x是一個表,它返回x中除第一個元素之外的所有元素組成的表,例如:
> (cdr '(a b c))
(b c)
(cons x y)要求y是一個表,它返回一個表,這個表的第一個元素是x,其後是y中的所有元素,例如:
> (cons 'a '(b c))
(a b c)
> (cons 'a (cons 'b (cons 'c ())))
(a b c)
2 Common LISP的Hello World程序
下面是一個在標准輸出設備上輸出Hello World的簡單程序,這種程序通常作為開始學習編程語言時的第一個程序:
(format t "Hello, world!~%")
E. lisp有什麼比較好用的編譯器么
如果你是學的是common lisp的話:可以使用 lisp in a box。
lisp in a box 是一個在emacs的基礎上擴展的ide
如果你是學的是autolisp的話:可以使用 cad裡面自帶的vlisp開發環境。
在cad的命令行輸入vlisp或者vide後回車即可進入。
如果你是學的是elisp的話:可以 直接使用emacs。
如果你是學的是scheme的話:可以 直接使用ChezScheme
如果你想自己動手做一個的話可以參考sicp後幾章或者看看王垠的如何寫解釋器那篇文章