跳轉到內容

newLISP 簡介/基礎

來自華夏公益教科書,開放世界開放書籍

下載和安裝

[編輯 | 編輯原始碼]

安裝說明可在 newlisp.org 找到。

安裝 newLISP 後,有各種方法可以執行它。有關詳細資訊,請參閱 newLISP 文件。最直接的方法是透過在控制檯或終端視窗中鍵入 newlisp 命令來從命令列執行 newLISP 直譯器。

$ newlisp
newLISP v.x on OSX IPv4 UTF-8, execute 'newlisp -h' for more info.

>

這對於嘗試簡短的表示式、測試想法和除錯非常有用。您可以在此環境中編寫多行程式碼,方法是在 [cmd][/cmd] 之間包含這些行。在新版本中,您只需在空白行上使用回車鍵即可開始和結束多行塊。

>[cmd]
(define (fibonacci n)
 (if (< n 2) 
  1
  (+ (fibonacci (- n 1))
     (fibonacci (- n 2)))))
[/cmd]
>(fibonacci 10)
89
>

newLISP 的視覺化介面 newLISP-GS 為 newLISP 應用程式提供了圖形工具包,它還為您提供了用於編寫和測試程式碼的開發環境:newLISP 編輯器。在 Windows 上,它以桌面圖示和程式開始選單中的資料夾的形式安裝。在 MacOS X 上,應用程式包和圖示安裝在應用程式資料夾中。newLISP-GS 編輯器為您提供多個帶標籤的視窗、語法著色以及一個用於檢視執行程式碼結果的監視區域。

the newLISP-GS Editor

您還可以從命令列執行 newLISP-GS 編輯器:您可以在 C:/Program Files/newlisp/newlisp-edit(Windows)或 /usr/bin/newlisp-edit(在 Unix 上)找到該檔案。(這是一個 newLISP 原始檔,因此您也可以檢視程式碼。)

您可以在自己喜歡的文字編輯器中編輯 newLISP 指令碼。在 MacOS X 上,您可以使用 BBEdit、TextWrangler 或 TextMate 執行 newLISP 指令碼,或者您可以使用預安裝的 Unix 文字編輯器,例如 vim 或 emacs。在 Windows 上,您可以使用 UltraEdit、EditPlus 或 NotePad++,僅舉幾例。如果您使用 Linux,您比我更瞭解文字編輯器,您可能已經有自己的偏好。

newLISP 網站在 http://newlisp.org/index.cgi?Code_Contributions 主持多個流行編輯器的配置檔案。

在 Unix 上,newLISP 指令碼的第一行應該是

#!/usr/local/bin/newlisp

或者

#!/usr/bin/env newlisp

如果您更喜歡在外部文字編輯器中執行 newLISP,則必須使用更多 println 函式來檢視每個函式或表示式返回的值。

通常,您使用 exit 函式結束 newLISP 指令碼或控制檯會話

(exit)

newLISP 的三大基本規則

[編輯 | 編輯原始碼]

您只需要學習三個基本規則來使用 newLISP 進行程式設計。以下是第一個規則

規則 1:列表是元素的序列

[編輯 | 編輯原始碼]

列表是被括號括起來的元素序列

(1 2 3 4 5)              ; a list of integers
("the" "cat" "sat")      ; a list of strings
(x y z foo bar)          ; a list of symbol names 
(sin cos tan atan)       ; a list of newLISP functions
(1 2 "stitch" x sin)     ; a mixed list
(1 2 (1 2 3) 3 4 )       ; a list with a list inside it
((1 2) (3 4) (5 6))      ; a list of lists

列表是 newLISP 中的基本資料結構,也是您編寫程式程式碼的方式。但現在還不要輸入這些示例——還有兩個規則要學習!

規則 2:列表中的第一個元素是特殊的

[編輯 | 編輯原始碼]

當 newLISP 看到一個列表時,它會將第一個元素視為一個函式,然後嘗試使用其餘元素作為該函式所需的資訊。

(+ 2 2)

這是一個包含三個元素的列表:名為 + 的函式,後面跟著兩個數字。當 newLISP 看到這個列表時,它會對其進行評估並返回 4(當然)的值。請注意,第一個元素被 newLISP 視為一個函式,而其餘元素被解釋為該函式的引數——函式期望的數字。

以下是一些更進一步的示例,說明了這兩個基本規則

(+ 1 2 3 4 5 6 7 8 9)

返回 45。+ 函式將列表中的所有數字加起來。

(max 1 1.2 12.1 12.2 1.3 1.2 12.3)

返回 12.3,即列表中最大的數字。同樣,列表的長度沒有(合理的)限制:如果一個函式樂於接受 137 個專案(max+ 都可以),那麼您可以傳遞 137 個專案。

(print "the sun has put his hat on")
"the sun has put his hat on"

列印字元字串 the sun has put his hat on。(它還會返回字串,這就是為什麼當您在控制檯中工作時,有時會看到事物重複出現兩次。)print 函式可以列印單個字元字串,或者您可以提供要列印的元素序列

(print 1 2 "buckle" "my" "shoe")
12bucklemyshoe

它會列印兩個數字和三個字串(雖然格式不太好,因為您還沒有接觸到 format 函式)。


directory 函式

(directory "/")

生成指定目錄(在本例中為根目錄 "/")的列表

("." ".." ".DS_Store" ".hotfiles.btree" ".Spotlight-V100" 
".Trashes"".vol" ".VolumeIcon.icns" "Applications" 
"automount" "bin" "cores" "Desktop DB" "Desktop DF" 
"Desktop Folder" "dev""Developer" "etc" "Library" 
"mach" "mach.sym" "mach_kernel" "Network" "private" 
"sbin" "System" "System Folder" "TheVolumeSettingsFolder" 
"tmp" "User Guides And Information" "Users" "usr" 
"var" "Volumes")

如果您沒有指定目錄,它會列出當前目錄

(directory)
("." ".." "2008-calendar.lsp"  "allelements.lsp" "ansi.lsp" 
"binary-clock.lsp" ... )

有一個 read-file 函式可以讀取文字檔案的內容

(read-file "/usr/share/newlisp/modules/stat.lsp")

這裡,該函式需要一個引數——檔名——並將檔案的內容以字串形式返回給您。

這些是 newLISP 程式碼構建塊的典型示例——包含函式呼叫的列表,後面可能還有該函式需要的任何額外資訊。newLISP 有 380 多個函式,您可以參考出色的 newLISP 參考手冊以瞭解所有函式的詳細資訊以及如何使用它們。

您可以嘗試這些示例。如果您在終端中使用 newLISP,只需輸入它們即可。如果您將這些行輸入文字編輯器並將其作為指令碼執行,除非您將表示式包含在 println 函式中,否則您可能不會看到函式呼叫的結果。例如,鍵入

(println (read-file "/usr/share/newlisp/modules/stat.lsp"))

以列印 read-file 函式的結果。

每個 newLISP 表示式都會返回一個值。即使是 println 函式也會返回一個值。您可以說,列印操作實際上只是一個副作用,其主要任務是返回一個值。您可能會注意到,當您在控制檯視窗中以互動方式使用 println 時,您會看到返回值出現兩次:一次是列印時,一次是返回值返回到呼叫函式(在本例中為頂層)時。

在您學習第三條規則之前,還有一件有用的事情需要檢視。

巢狀列表

[編輯 | 編輯原始碼]

您已經發現一個列表巢狀在另一個列表中。以下是一個示例

(* (+ 1 2) (+ 3 4))

當 newLISP 看到這個時,它會思考如下

嗯。讓我們從那些內部列表中的第一個開始。我可以輕鬆地做到

(+ 1 2)

它的值是 3。我也可以輕鬆地做到第二個列表

(+ 3 4)

足夠簡單。它評估為 7。

因此,如果我將這兩個內部列表替換為這些值,我將得到

(* 3 7)

這真的很容易。我將為這個表示式返回 21 的值。

(* (+ 1 2) (+ 3 4))
(* 3 (+ 3 4))
(* 3 7)
21

看到第一行末尾的兩個右括號嗎?它們都是必要的:第一個括號結束 (+ 3 4 列表,第二個括號結束以 (* 開頭的乘法運算。當您開始編寫更復雜的程式碼時,您會發現自己將列表放入列表中,再放入列表中,再放入列表中,並且您可能會以六個右括號來結束一些更復雜的定義。一個好的編輯器會幫助您跟蹤它們。

但是您不必擔心空格、行終止符、各種標點符號或強制縮排。並且由於所有資料和程式碼都以相同的方式(在列表中)儲存,因此您可以自由地混合它們。稍後會詳細介紹。

有些人第一次看到 LISP 程式碼時會擔心括號氾濫。其他人稱它們為指甲碎片,或者說 LISP 代表大量的令人討厭的愚蠢括號。但我更喜歡將括號視為包圍 newLISP 思想的小把手

grabbing the handles of a newLISP thought

當您在好的編輯器中編輯 newLISP 程式碼時,您可以透過抓住其控制代碼輕鬆地移動或編輯一個想法,並使用“平衡括號”命令輕鬆地選擇一個想法。您很快就會發現括號比您最初認為的更有用!

引用防止評估

[編輯 | 編輯原始碼]

現在您可以使用 newLISP 瞭解程式設計的第三條規則

規則 3:引用防止評估

[編輯 | 編輯原始碼]

要停止 newLISP 評估某個內容,請引用它。

比較這兩行

(+ 2 2)
'(+ 2 2)

第一行是一個列表,包含一個函式和兩個數字。在第二行中,該列表被引用 - 在前面有一個單引號或撇號 (')。您不需要在結束括號後新增另一個引號,因為一個就足夠了。

> (+ 2 2)
4
> '(+ 2 2)
(+ 2 2)
>

對於第一個表示式,newLISP 照常執行其工作,並熱心地評估該列表,返回數字 4。但對於第二個表示式,newLISP 一看到引號,就不會考慮透過新增數字來評估該列表;它只返回該列表,不進行評估。

此引號在 newLISP 中的作用與書面英語中的開閉引號相同 - 它們告訴讀者該單詞或短語不要按通常方式解釋,而要以某種特殊方式對待:非標準或諷刺的含義,也許是另一個人說的話,或者是不應該照字面理解的東西。

那麼,為什麼您要阻止 newLISP 評估事物呢?您很快就會遇到一些示例,其中您引用事物以防止 newLISP 認為列表中的第一個專案是一個函式。例如,當您將資訊儲存在列表中時,您不希望 newLISP 以通常的方式評估它們

(2006 1 12)                 ; today's year/month/date
("Arthur" "J" "Chopin")     ; someone's full name

您不希望 newLISP 尋找名為2006"Arthur"的函式。此外,2006不是有效的函式名,因為它以數字開頭,函式名不能以雙引號開頭,因此在任何情況下您的程式都會停止並出現錯誤。因此,您需要引用列表以阻止其第一個元素用作函式而不是資料

'(2006 1 12)                 ; evaluates to (2006 1 12)
'("Arthur" "J" "Chopin")     ; evaluates to ("Arthur" "J" "Chopin")

newLISP 將表示式視為資料 - 以及將資料視為表示式 - 的能力將在後面詳細討論。

使用垂直撇號(ASCII 碼 39)來引用列表和符號。有時,文字編輯器或其他程式會將這些簡單的垂直撇號更改為花括號。它們的作用不同,因此您必須將所有智慧引號更改為垂直撇號。

符號和引號

[編輯 | 編輯原始碼]

符號是帶有名稱的 newLISP事物。您在程式碼中定義了一些東西併為其分配一個名稱。然後,您可以在以後使用該名稱而不是內容來引用該東西。例如,在鍵入以下內容後

(set 'alphabet "abcdefghijklmnopqrstuvwxyz")

現在有一個名為alphabet的新符號,其值為一個字串,該字串包含字母表的 26 個字母。set函式將從 a 到 z 的字元字串儲存在alphabet符號中。現在,此符號可以在其他地方使用,並在使用時始終評估為字母表。每當您想要使用字母表的 26 個字母時,您都會使用此符號,而不會引用它。例如,以下是upper-case函式

(upper-case alphabet)
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

我使用該符號而不引用它,因為我想要 newLISP 使用該符號的值,而不是其名稱。我實際上對將單詞alphabet轉換為大寫不感興趣,而是對字母表本身感興趣。在本例中,newLISP 沒有永久更改符號的值,因為upper-case始終建立並返回一個新字串,使儲存在符號中的字串保持不變。

符號對應於其他程式語言中的變數。實際上,newLISP 使用符號的頻率並不像其他語言使用變數那樣高。這部分是因為值會不斷地由表示式返回並直接饋送到其他表示式,而無需儲存。例如,在以下程式碼中,每個函式都會將其結果直接傳遞給下一個包含函式

(println (first (upper-case alphabet)))
"A"

upper-case將其返回值直接傳遞給firstfirst將其返回值直接傳遞給printlnprintln既列印它,也為您提供它列印的字串作為返回值。因此,沒有必要臨時儲存值。但還有很多其他地方您確實需要符號。

以下是符號引用的另外兩個示例

(define x (+ 2 2 ))
(define y '(+ 2 2))

在第一個示例中,我沒有引用(+ 2 2)列表 - newLISP 將其評估為 4,然後將 4 分配給符號x,它評估為 4

x
;-> 4

在第二個示例中,我引用了該列表。這意味著符號y現在儲存的是一個列表,而不是一個數字。每當 newLISP 看到符號y時,它都會返回該列表,而不是 4。(當然,除非您也先引用y!)

y
;-> (+ 2 2)
'y
;-> y

順便說一下,在本文件中

; the semicolon is the comment character
;-> that ";->" is my way of saying "the value is"

以及 newLISP 直譯器列印的輸出通常顯示為

like this

設定和定義符號

[編輯 | 編輯原始碼]

建立和設定符號值的方法有很多。您可以使用defineset,如下所示

(set 'x (+ 2 2))
;-> 4
(define y (+ 2 2))
;-> 4

set期望後面跟著一個符號,但首先會評估其第一個引數。因此,您應該引用一個符號以防止它被評估(因為它可能評估為除符號以外的其他東西),或者提供一個評估為符號的表示式。define不期望引數被引用。

您還可以使用setfsetq來設定符號的值。這些期望第一個引數為符號或符號引用,因此您不必引用它。

(setf y (+ 2 2))
;-> 4
(setq y (+ 2 2))
;-> 4

這兩個函式(它們具有相同的操作)可以設定符號(變數)、列表、陣列或字串的內容。一個約定是在設定符號時使用setq,在設定列表或陣列的元素時使用setf

define還用於定義函式。參見建立您自己的函式.

破壞性函式

[編輯 | 編輯原始碼]

一些 newLISP 函式會修改它們操作的符號的值,而另一些則會建立值的副本並返回該副本。從技術上講,修改符號內容的函式被稱為破壞性函式 - 儘管您通常會使用它們來建立新資料。在本檔案中,我將描述諸如pushreplace之類的函式為破壞性函式。這僅僅意味著它們會改變某個東西的值,而不是返回一個修改後的副本。

華夏公益教科書