前言
本文以 Common Lisp 為例,說明剛學程式設計時會碰到的一些基本概念。
檔案名稱 (File Name) 和副檔名 (File Extension)
程式碼本身是文字檔案 (text file)。本節說明和檔案相關的問題。
檔案名稱
大部分程式語言不會限制檔案的名稱,包括 Common Lisp。一般來說,檔案名稱只要簡明、清楚即可。像是 helloworld.lisp 就是用來寫 Hello World 程式的 Lisp 命令稿。
若要用複合字來命名檔案,建議使用 kebab-case
(烤肉串命名法)。像是 number-guessing.lisp 是一個猜數字小遊戲。不要在檔名中使用空白 (space),這是自找麻煩的舉動。
Java 是少數會限制檔案名稱的程式語言。像是 MainProgram.java 的內容如下:
public class MainProgram
{
public static void main (String[] args)
{
/* Implement your code here. */
}
}
在此處,類別名稱 MainProgram
和檔案名稱 MainProgram 必須一致,否則會無法編譯。
副檔名
Unix 的可執行命令稿不需要副檔名。但 Windows 的命令稿仍然要用副檔名來區分檔案格式。故仍建議在程式碼加上副檔名。Common Lisp 程式碼的副檔名是 .lisp 。
Unix 的可執行檔同樣不需要副檔名。Windows 可執行檔的副檔名是 .exe 。根據編譯 Common Lisp 程式碼時,所在的宿主系統 (host system) 來決定執行檔的副檔名。
編譯 (Compilation) 和直譯 (Interpretation)
執行編譯語言的程式碼,要先將程式碼轉成相對應的執行檔後執行該程式。由於程式碼已經轉換成相對應的機械碼了,不需要每次都從頭轉換,所以執行起來會比較快。
執行直譯語言的程式碼,不需要先編譯,而是即時將命令稿轉為相對應的程式。雖然省下編譯的動作,但每次都要從頭解析程式碼、經轉換後才執行程式,所以執行起來比較慢。
大部分的程式語言只有編譯或直譯其中一種模式。C、C++、Pascal 是編譯語言 (compiled language),Python、Ruby、JavaScript 則是直譯語言 (interpreted language)。至於 Common Lisp 比較特別,有許多實作品兼具編譯和直譯兩種模式。
直譯模式
建立 Common Lisp 命令稿 hello.lisp ,填入以下內容:
(write-line "Hello World")
執行該命令稿:
$ sbcl --script hello.lisp
Hello World
執行該命令稿時不會產生執行檔,所以是直譯模式。
編譯模式
Common Lisp 的編譯模式相對不易使用,需要額外寫指令。本小節展示這個過程。
主程式 main.lisp 的內容如下:
;; The main function.
(defun main ()
(write-line "Hello World")
(quit))
這裡的函式 main
是必須的,因為編譯的指令會用到。至於函式名稱 main
只是約定俗成,實際上可以取別的名字。
工具命令稿 make.lisp 的內容如下:
;; Load the main program.
(load "main.lisp")
;; Compile the main program.
(let ((program "program"))
(when (equal (software-type) "Win32")
(setq program "program.exe"))
(sb-ext:save-lisp-and-die program
:toplevel #'main
:executable t))
;; Quit the utility script.
(quit)
這裡不逐行講解。簡單地說,載入 main
函式後,將該函式當成頂層函式來編譯程式碼。最後離開此命令稿。
編譯後執行主程式:
$ sbcl --script make.lisp
$ ./program
Hello World
由於編譯模式對理解程式設計的概念沒啥幫助,本系列文章日後不會使用此模式。此處僅是展示編譯 Common Lisp 程式碼的過程。
交互性程式設計 (Interactive Programming)
許多程式語言都有交互式環境。這種環境又稱為 REPL (註) 環境。一般來說,交互式環境是用來測試簡短的程式碼,以學習程式設計的概念。但 Lisp 的交互式環境不僅於此,可用來邊撰寫邊執行程式碼,比大部分的程式語言有更快的迭代。
(註) REPL 為 Read-Eval-Print-Loop 的縮寫。
使用先前提到的 SLIME 或 Alive 等編輯器外掛,就可以用 REPL 模式來撰寫 Lisp 程式碼。
執行 SBCL 的主程式,但不加參數,就會進入 Common Lisp 的交互模式:
$ sbcl
This is SBCL 2.3.2, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
*
輸入 Lisp 指令後,會立即執行及回饋結果給使用者,然後返回提示符:
* (+ 1 2 3 4 5)
15
*
最後要離開此交互式環境了,就輸入 (quit)
指令:
* (quit)
大小寫敏感性 (Case Sensitivity)
很多程式語言的程式碼將英文大小寫視為相異指令,這種程式語言具有大小寫敏感性。
Common Lisp 程式碼則不具大小寫敏感性。按照社群慣例,會以英文小寫字母來寫程式碼。
空白 (Space)、縮進 (Indentation)、換行 (End of Line)
大部分程式語言不會限制空白、縮進、換行等排版元素。只要程式碼看起來整齊美觀即可。Common Lisp 對這些排版元素也是採取相對寬鬆的態度。
Python 的縮進是有意義的,同一層的程式碼要使用相同深度的縮進。像是以下簡短範例:
def power(base, exp):
if 0 == exp:
return 1.0
elif 0 < exp:
return power(base, exp - 1) * base
else:
return power(base, exp + 1) / base
assert 1 == power(3, 0)
assert 81 == power(3, 4)
assert (1.0/81) == power(3, -4)
在這個例子中,函式使用一層縮進,if
敘述再加上一層縮進。
註解 (Comment)
程式碼是帶有特定語法的文字檔案,在程式碼中加入不符合語法的文字會導致編譯錯誤。
註解的目的是為了在程式碼中加入自由文字 (free text)。加在註解的文字是給程式設計者閱讀的,編譯器或直譯器會忽略註解的內容。
在 Common Lisp 中,位於 ;
後同一行的文字視為註解。但 ;
相對不易閱讀。按照社群慣例,;;;;
用於整份檔案開頭的註解,;;
用於一段程式碼區塊前,;
僅用於程式碼後,簡短的註解。
主函式 (Main Function)
主函式是程式的進入點、起始點。編譯語言通常有主函式,而直譯語言則無。
Common Lisp 在直譯模式時不需要主函式,直接將指令寫在命令稿頂層即可。在編譯模式時,則需要指定一個任意名稱的頂層函式權充該程式的主函式。
離開狀態 (Exit Status)
程式執行完畢時,會回傳一個 0 至 255 間的整數,用來代表程式的狀態。一般來說,0 代表程式執行成功,1 或其他非零的數字則代表執行失敗。
程式設計者可自行設計每個回傳值的意義,但不應偏離社群慣例。最常用的離開狀態是 0 和 1,其他相對少見回傳值則應載明在程式的說明手冊中。
以下 Common Lisp 範例程式回傳執行成功的狀態:
(write-line "Hello World")
;; Quit with status code.
(sb-ext::quit :unix-status 0)