Scheme语言

**Scheme** 是一種函式語言程式設計語言，是 Lisp 的兩種主要方言之一，不同於與之並列的 Common Lisp，Scheme 遵循極簡主義哲學，以一個小型語言核心作為標準，加上各種強力語言工具（語法糖）來擴充語言本身。Scheme 是第一個使用靜態作用域的 Lisp 方言，也是第一個引入頭等續體和「乾淨宏」的程式語言。

## 簡介

在 1975 年，麻省理工學院的傑拉德・傑伊・薩斯曼與蓋伊・史提爾二世，開發出了 Scheme 語言最初版本，隨後兩人通過發表「λ 論文集」而不斷對它進行完善和推廣。Scheme 與 λ 演算關係十分密切，故將小寫字母「λ」用作標誌。

麻省理工學院與其他一些院校，曾採用 Scheme 教授電腦科學入門課程。著名的入門教材《電腦程式的構造和解釋》（SICP），利用 Scheme 來詮釋程式設計。Scheme 有眾多實現可視為一個主要優勢，然而不同實現之間的差異成為了它的一個劣勢，Scheme 掌控委員會聲稱，它是「世上最不可移植的程式語言」，並且是一個「程式語言家族」而非一個單一的語言。

## 歷史

### 起源

Scheme 起源於 1958 年由約翰・麥卡錫提出的 Lisp 語言。麥卡錫通過 Lisp 證明了，經由幾個簡單的算子，與用作匿名函式的借鑒自阿隆佐・邱奇的 λ 表示法，就可以構建出圖靈完備的系統。麥卡錫提出的 S - 表達式，可以將程式與資料用相同的結構儲存，這被稱為同像性。Scheme 的語法即來自 S - 表達式，這一特性使得在 Scheme 中實現自循環直譯器變得非常簡單。

在 1973 年，麻省理工學院的 Carl Hewitt 提出的一種叫做演員模型的計算模型，並用 Lisp 開發當時叫做 Planner-73 的新語言來實現它。傑拉德・薩斯曼與蓋伊・史提爾為了理解演員模型，決定在 Maclisp 工作環境中實現一個微型 Lisp 直譯器，並接著增加建立演員和傳送訊息的機制。兩人很快發現了演員模型與 λ 演算之間的相似性，所謂「演員」就是彼得・蘭丁提出的閉包，而 Joel Moses 在 1970 年已將它介入 Lisp 用來解決函式參數問題；故而實現演員的關鍵，是將詞法作用域介入到 Lisp 中。

在 1975 年，基於對 λ 演算表達能力的理解，傑拉德・薩斯曼與蓋伊・史提爾開發出了新型 Lisp 直譯器，並將在其上完成的微縮版的演員實現命名為「Schemer」，後因 ITS 作業系統檔名的字元數限制而改為 Scheme。儘管 Hewitt 指出了 Scheme 不能表達特定類型的原語演員，Scheme 直譯器本身採用的簡約的語法和語意，很快贏得廣泛接受。

在 1978 年，蓋伊・史提爾二世和傑拉德・傑伊・薩斯曼發表了《修訂的 Scheme 報告：一種 LISP 方言》，從而完善了 Scheme 的功能，並正式將其確立為 Lisp 的一種主要方言。在 1998 年，二人在總結 Scheme 歷史時指出，簡單而強大的 λ 演算，使得 Scheme 最終得以實現極度的精簡化。

### λ 論文集

「λ 論文集」是傑拉德・薩斯曼與蓋伊・史提爾撰寫的關於 Scheme 的一系列論文，最早作為麻省理工學院的內部備忘錄發表。通常認定 λ 論文集包括：

### 標準化

Scheme 的業界標準，是由專門的掌控委員會發表的《第 n 次修訂的演算法語言 Scheme 報告》（Revisedn Report on the Algorithmic Language Scheme），這種標題形式參照了 ALGOL 60 標準文件的標題。Scheme 曾經由 IEEE 標準化為 IEEE 1178–1990，它於 2019 年 11 月 07 日停用。

1998 年通過的 R5RS 是現在被普遍接受的標準。2007 年通過的 R6RS，做出了很大的變動，Scheme 社群中有使用者指責它在堆積華而不實的功能。Scheme 掌控委員會決定在 R7RS 中，將 Scheme 分化為兩個獨立而相容的語言：一個是 2013 年通過的 R7RS-small，它為教學場合提供對 IEEE/R5RS 標準的及時更新，R7RS-small 目前已經有了一些實現支援；而另一個是作為標準庫合集的 R7RS-Large，它包含了各種提議被接納並進入凍結狀態的 Scheme 實現要求（SRFI），以此為實際編程場合提供對 R6RS 標準的繼續完善，Larceny 和 Chibi 提供了 R7RS-Large 過渡草案紅色版（即資料結構部份）的全部 SRFI 的實現。

## 命名法

在正式場合比如 Scheme 標準的命名法中，提及一個 λ 表達式或原始過程時，偏好使用詞語「過程」而非「函式」。在一般使用中，詞語「過程」和「函式」是可互換使用的。過程應用有時被正式稱呼為「組合」（combination）。

同其他 Lisp 一樣，在 Scheme 中使用術語「thunk」，來提及沒有實際參數的過程。術語「適當」（proper）尾遞迴，稱謂所有 Scheme 實現的一個性質，它們都進行尾遞迴最佳化，從而支援無限數目的活躍尾遞迴。

## 基礎特徵

Scheme 大體上是一個函數式程式語言，並支援其他程式設計範式。它同其他 Lisp 程式語言家族語言共享了很多特徵。它的非常簡單的語法基於 Lisp 的 S - 表達式：即圓括號包圍的列表，其中的字首是算符，而隨後那些的是實際參數。故而 Scheme 程式由巢狀的列表的序列構成。列表也是 Scheme 中的主要資料結構，這導致了在原始碼和資料格式之間的緊密等價性，即同像性。Scheme 程式可以輕易的動態建立和求值 Scheme 代碼片段。

Scheme 與其他 Lisp 方言的列表，都是基於最基礎的數據結構有序對（pair）。Scheme 從它的 Lisp 先輩繼承了一組豐富的列表處理原始運算，比如 `cons`、`car` 和 `cdr`。Scheme 的變數都使用動態強型別系統，Scheme 中的過程都是頭等物件，即頭等函式，因此過程可以作為值賦值給變數，或作為實際參數傳遞給過程。

本章主要集中於語言的創新特徵，它們使得 Scheme 區別於其他 Lisp 方言。除非專門指出，這裡描述的特徵都有關於 R5RS 標準。在本章例子中使用「`===> 结果`」表示法，來指示求值在緊前行上的表達式的結果。這同於 R5RS 中使用的約定。本章節描述的特徵使得 Scheme 不同於在它之前的其他程式語言。Scheme 的這些方面強烈的影響了 Scheme 語言的所有產品，並共通於始自 1975 年最初的 λ 論文，特別是自從 R2RS 以來，所有版本的 Scheme 程式語言。

### 極簡主義

Scheme 的簡約性，使它成為具備同級別功能的程式語言中最易於實現的語言，這得益於使用 λ 演算來從更原始的形式匯出多數的語言語法。例如在 R5RS 標準中，定義了 23 個基於 S - 表達式的語法構造，其中 14 個被歸類為衍生形式或庫形式，它們可以被寫為涉及原則上為 lambdad 的更基礎形式的宏。正如 R5RS（§3.1）所說：「最基礎的變數繫結構造是 lambda 表達式，因為所有其他的變數繫結構造，都可以依據 lambda 表達式來做出解釋」。

　
: **基礎形式**：`define`、`lambda`、`quote`、`if`、`define-syntax`、`let-syntax`、`letrec-syntax`、`syntax-rules`、`set!`

　
: **衍生形式**：`do`、`let`、`let*`、`letrec`、`cond`、`case`、`and`、`or`、`begin`、命名 `let`、`delay`、`unquote`、`unquote-splicing`、`quasiquote`

下面是一個宏的例子，它將 `let` 實現為使用 `lambda` 來進行變數繫結的一個表達式：

使用上述定義的 `let`，一個 Scheme 實現可以將 `(let ((a 1)(b 2)) (+ b a))`，重寫為 `((lambda (a b) (+ b a)) 1 2)`，這將實現任務縮減為編碼過程實例化的工作。

### 詞法作用域

不像更早期的 Lisp 比如 Maclisp，Scheme 像多數現代語言一樣，採用了詞法作用域：在一個程式單元中所有可能的變數繫結，都可以通過閱讀這個程式單元來分析出來，而不需要考慮到它可能在其中被呼叫的那些上下文。這對比於動態作用域，它是早期 Lisp 方言的特徵，因為在當時的編譯器和直譯器中，用來實現詞法作用域演算法的原始的文字替換方法，關聯著處理代價。在動態作用域的 Lisp 中，對一個過程內的自由變數的參照，依賴於這個呼叫的上下文，完全有可能參照到這個過程外部的相當不同的繫結。

### λ 演算

邱奇的 λ 演算的數學表示法，啟發了 Lisp 使用 `lambda` 作為介入一個過程的關鍵字，並影響了 Lisp 中涉及到使用高階函式的函式語言程式設計技術的發展。但是早期的 Lisp 由於對自由變數的處理方式，而不適合表達 λ 演算。

一個正式的 λ 系統，擁有一些公理和一個完備的推理規則。這有助於使用數理邏輯和工具來進行分析。在這個系統中，演算可以被看作直接的演繹。λ 演算的語法，來自用 `x, y, z, ...`、圓括號、空格、點號和符號 λ 形成的遞迴表達式。λ 演算的功能包括：首先，充當強力的數理邏輯的起點。其次，它可以縮減編程者在考慮實現細節上的要求，因為它可以用於類比機器求值。最後，λ 演算建立了一個堅實的元理論。

在 Scheme 中，`lambda` 關鍵字被用於定義匿名過程，並且使用 `define` 基礎形式來定義命名過程，即將一個 `lambda` 過程繫結到指名的全域變數。在 Scheme 中，`(define (foo x) (+ x 1))` 與 `(define foo (lambda (x) (+ x 1)))`，在語法上是等同的。例如有序對可以表示為：

這樣定義出來的 `cons`、`car` 和 `cdr`，滿足恆等式 `(car (cons x y))` 等於 `x`，和 `(cdr (cons x y))` 等於 `y`。甚至遞迴也可以表示為利用 λ 演算的 Y 組合子。

詞法作用域的介入，通過在某些形式的 λ 表示法，和在工作程式語言中它們的實際表達之間做出等價，解決了早期 Lisp 的有關問題。Sussman 和 Steele 展示了，通過將 λ 表達式用作「控制結構和環境修改器」，而非簡單的過程實例化，可以用這個新語言優雅的匯出其他程式語言包括 ALGOL 和 Fortran 的，所有指令式和聲明式語意，和其他 Lisp 的動態作用域。他們在第一篇 λ 論文中，與對 Scheme 的首次描述一起，介入了續體傳遞風格，並在後續的論文中，他們推進演示了在這種實際使用中體現出的 λ 演算的原生能力。

### 過程應用中的求值次序

Scheme 採用了傳值呼叫，但不同於多數 Lisp 規定了過程實際參數的求值次序，Scheme 不規定求值次序。對比於其他 Lisp，Scheme 表達式在算符位置（第一個專案）上可以是一個表達式，只要在算符位置上的表達式的結果是一個過程，這種表示就是完全合法的。

在 Scheme 中，在算符和實際參數位置上的這些表達式的求值次序，可以在逐個呼叫的基礎上由實現來選擇，而唯一的約束是：「運算子和運算數表達式的任何並行求值的效果，被約束為一致於某種順序次序的求值。」（R5RS sec. 4.1.3）

`ev` 是一個過程，它描述傳遞給它的實際參數，接著返回這個實際參數的值。在呼叫過程 `+` 來加 `1` 和 `2` 之中，表達式 `(ev +)`、`(ev 1)` 和 `(ev 2)` 可以按任何次序求值，只要效果上它們不像是並列求值的。因此在上述例子被執行的時候，下列三行可以被標準 Scheme 按任何次序來顯示，然而一行的文字不可以同另一行的文字產生交錯，因為這會違反順序求值約束。

### 塊結構

Scheme 的代碼塊結構，不同於早先 Lisp 的語言構造。自從 R2RS 開始，Scheme 中的塊，是通過三個「繫結構造」來實現的：`let`、`let*` 和 `letrec`。例如，下列構造建立一個塊，其中叫做 `var` 的符號被繫結到數 `10`：

依據編程者的需要，塊可以巢狀來建立任意複雜的塊結構。使用塊結構來建立局部繫結，減輕了不然可能發生的命名空間衝突。

`let` 的變體之一 `let*`，允許繫結參照在前面相同構造中定義的變數，例如：

`let` 的另一個變體 `letrec`，被設計用來確使互遞迴過程可繫結彼此。

下面的例子是侯世達雌雄序列：

在一個單一的 `letrec` 中的所有過程，可以通過名字參照其他的過程，還有在相同的 `letrec` 中此前定義的變數的值，但是它們不可以參照在相同的 `letrec` 中此後定義的變數的值。

`let` 的變體「命名 `let`」形式，在 `let` 關鍵字之後有一個識別碼。它將這些 `let` 變數繫結到一個過程的實際參數，這個過程的名字由這個識別碼給出，它的過程體是 `let` 形式的主體。這個過程體可以通過呼叫這個過程達成想要的重複。命名 `let` 被廣泛用於實現迭代。

一個簡單的計數器例子:

就像 Scheme 中的任何過程一樣，在命名 `let` 中建立的這個過程是頭等物件。

### 適當尾遞迴

Scheme 擁有一個迭代構造 `do`，但是在 Scheme 中更推崇的慣用法，是使用尾遞迴來表達迭代。遵循標準的 Scheme 實現被要求最佳化尾遞迴，從而支援無限數量的活躍尾遞迴（R5RS sec. 3.5），這個性質被 Scheme 報告描述為「適當」（proper）尾遞迴，它使得 Scheme 編程者可以安全的使用遞迴結構來書寫迭代演算法，這在很多時候是更符合直覺的。尾遞迴過程和命名 `let` 形式，提供對使用尾遞迴的迭代的支援。

### 頭等續體

在 Scheme 中續體是頭等物件。自從 R2RS 開始，Scheme 提供了過程 `call-with-current-continuation`（簡寫為 `call/cc`），通過把當前續體包裝成脫離（escape）過程來擷取它，並把它繫結到編程者提供的一個過程的一個形式參數（R5RS sec. 6.4）。頭等續體使得編程者能夠建立非局部控制結構比如迭代、協程和回溯。

續體可以被用來類比指令式程式語言中 return 語句的行為。下列函式 `find-first` 接受函式 `func` 和列表 `lst`，返回 `lst` 中的第一個元素 `x`，它使得 `(func x)` 返回真。

David Madore 在 1999 年提出的陰陽謎題，展示了 Scheme 可以將續體當作頭等物件處理，繫結它們到變數，和把它們作為給過程的實際參數來傳遞。

### 統一的命名空間

對比於 Common Lisp，在 Scheme 中所有的資料和過程共享一個共同的命名空間，而 Common Lisp 中有函式和資料分離的命名空間，使得一個函式和一個變數可以有相同的名字，並且將一個函式作為值參照時要求特殊的表示法。這有時叫做「Lisp1 與 Lisp2」差異，二者分別稱謂 Scheme 的統一的命名空間，和 Common Lisp 的分立的命名空間。

在 Scheme 中， 可以使用操縱和繫結資料的原始運算來繫結過程。沒有等價於 Common Lisp 的 `defun` 和 `#'` 的原始運算。

## 實現標準

本章歸檔多年來做出的給與 Scheme 特定特徵的設計決定，它們不是最初設計的直接產物。

### 注釋

直到 R5RS 標準，在 Scheme 中的標準注釋是分號，它使得這行餘下部份對 Scheme 不可見。許多實現支援可替代的約定，允許注釋擴充為多於一行，而 R6RS 標準允許其中的兩種：一個完整的 S - 表達式，可以通過前導 `#;` 而變成一個注釋（介入於 SRFI 62），和通過用 `#|` 和 `|#` 圍繞文字，產生的「多行注釋」或「塊注釋」。

### 在布林表達式中非布林值的處理

在多數 Lisp 方言包括 Common Lisp 中，布林表達式中的值 `NIL` 按照慣例被求值為值假。在 Scheme 中，自從 1991 年的 IEEE 標準，除了 `#f` 的所有的值，包括在 Scheme 中寫為 `'()` 的 `NIL` 的等價者，在布林表達式中求值為值真（R5RS sec. 6.3.1）。

在多數 Lisp 中表示布林值真的常數是 `T`，而在 Scheme 中它是 `#t`。

### 乾淨宏

Scheme 的語法可以輕易的通過宏系統來擴充。R5RS 標準介入了強力的乾淨宏系統，它允許編程者使用一種簡單的模式匹配子語言，向語言增加的新的語法構造（R5RS sec 4.3）。在此前的 R4RS 標準中，乾淨宏系統已經作為「進階」系統，和「低階」宏系統一起被編排入標準的附錄中，二者都被當作對 Scheme 的擴充而非語言的本質部份。

乾淨宏的實現，也叫做 `syntax-rules`, 被要求遵守語言的其餘部份的詞法作用域。這是通過針對宏展開的特殊命名和作用域規則來確保的，從而避免在其他程式語言的宏系統中可能出現的常見編程錯誤。R6RS 規定了更加複雜的變換系統 `syntax-case`，它已經作為對 R5RS Scheme 的一個語言擴充而能夠獲得到有一段時間了。例如：

宏和過程的呼叫看起來非常相似，二者都是 S - 表達式，但是它們被不同的對待。在編譯器遇到程式中的一個 S - 表達式的時候，它首先檢視這個符號是否被定義為在當前詞法範圍內的語法關鍵字。如果是這樣，它接著嘗試展開這個宏，將在這個 S - 表達式尾部的專案當作實際參數，而不用編譯代碼來求值它們，遞迴的重複這個處理過程直到沒有餘留的宏呼叫。如果它不是一個語法關鍵字，編譯器編譯代碼來求值在這個 S - 表達式尾部的實際參數，並接著求值在這個 S - 表達式頭部的符號所表示的變數，把它作為過程來呼叫，並把最終的尾部表達式作為實際參數傳遞給它。

多數 Scheme 實現還提供額外的宏系統。其中最流行是語法閉包、顯式重新命名宏和 `define-macro`，它是類似於 Common Lisp 中提供的 `defmacro` 系統的非乾淨宏。

不能指定一個宏是否為乾淨的，是宏系統的一個缺點。可作為替代的展開模型比如作用域集合，提供一種潛在的解決方案。

### 延遲求值

自從 R2RS 開始，Scheme 通過 `delay` 形式和過程 `force` 支援延遲求值。

這些原始運算，產生或處理叫做 promise 的值，promise 的原本定義的文字內容被留存了，並且在第一次使用 `force` 之後它的值也被留存了。promise 永遠只求值一次。它們可以被用來實現進階的惰性求值構造比如串流。

在 R5RS 中，給出了 `delay` 和 `force` 的推薦實現，將 promise 實現為沒有實際參數的一個過程（thunk），並使用記憶化來確保它永遠只求值一次，與呼叫 `force` 的次數無關（R5RS sec. 6.4）。在 R6RS 標準中，它們不再是原始運算，轉而作為 R5RS 相容庫的一部份提供（rnrs r5rs (6)）。

SRFI 41 確使有限和無限序列能夠具有非凡經濟性的表達。例如，這是使用在 SRFI 41 中的函式定義的一個斐波那契數列：

### eval 及其執行環境

R5RS 標準之前，在其他 Lisp 中無處不在的 `eval` 過程，在 Scheme 中沒有等價者。在第一篇 λ 論文中，曾經將 `evaluate` 描述為「類似於 LISP 函式 EVAL」，但在具有詞法作用域的 Scheme 中，對這個表達式在哪個環境中求值存在困惑。例如，不明確求值下列表達式的結果應當是 `5` 還是 `6`：

在求值實際參數 `name` 的時候，在外層環境中找到了它的定義；在求值結果表達式 `(+ 2 3)` 的時候，如果在外層環境中求值，結果是兩個運算數的總和；如果在內層環境中求值，這裡符號 `+` 已經被繫結到過程 `*` 的值，結果是兩個運算數的乘積。為此在 1978 年的最初修訂報告中，將 `evaluate` 替代為 `enclose`，它接受分別為代碼和執行環境的兩個實際參數。由於各種技術和實際原因，第二次、第三次和第四次修訂報告省略了任何 `eval` 的等價者。

R5RS 通過規定三個返迴環境的過程，並提供接受一個 S - 表達式和一個環境的過程 `eval`，它在提供的這個環境內求值這個表達式（R5RS sec. 6.5），解決了這個困惑。R6RS 通過提供叫做 `environment` 的一個過程進行了擴充，編程者通過它可以精確的指定將哪個物件匯入到求值環境之內。

通過現代 Scheme（通常相容於 R5RS）來求值表達式 `expr`，你需要定義如下這樣的一個函式 `evaluate`：

`interaction-environment` 是直譯器的全域環境。

### 數值塔

Scheme 規定了數值資料類型的相較完全的集合，包括複數和有理數類型，這在 Scheme 中叫做「數值塔」（R5RS sec. 6.2）。標準對它們作抽象處理，而不委以任何特定的內部表示。

數可以有精確性品質。一個精確數只能從涉及其他精確數的精確運算產生，不精確是傳染的。標準規定任何兩個實現對產生精確數的所有運算都必須產生等價的結果。

R5RS 標準規定了過程 `exact->inexact` 和 `inexact->exact`，它們可以用於改變一個數的精確性。`inexact->exact` 產生「在數值上最接近實際參數的精確數」。`exact->inexact` 產生「在數值上最接近實際參數的不精確數」。R6RS 標準在主報告中省略了這些過程，而是在標準庫中將它們規定為 R5RS 相容過程（rnrs r5rs (6)）。

在 R5RS 標準中，不要求 Scheme 實現去實現整個數值塔，而是它們必須實現「符合實現用途和 Scheme 語言精神二者的連貫子集」（R5RS sec. 6.2.3）。新的 R6RS 標準要求實現整個數值塔，並且「精確整數物件和精確有理數物件實際上有無限的大小和精度，並且對於實現特定過程... 所以它們在得到精確實際參數時總是返回精確結果」（R6RS sec. 3.4, sec. 11.7.1）。

在支援精確有理複數的實現中的精確算術例子：

在既不支援精確有理數也不支援複數卻接受有理數表示法的實數的實現中相同算術的例子：

二者實現都符合 R5RS 標準，但是第二個實現不符合 R6RS，因為它沒有實現完整的數值塔。

### 原始資料類型不相交

在 Scheme 中原始資料類型是不相交的。對於任何 Scheme 物件，下列謂詞中只有一個可以為真：`boolean?`、`pair?`、`symbol?`、`number?`、`char?`、>`string?`、`vector?`、`port?` 和 `procedure?`（R5RS sec 3.2）。

作為對比，在數值資料類型之內，對數值值是可以有交疊的。例如，一個整數值可以同時滿足如下謂詞：`integer?`、`rational?`、`real?`、`complex?` 和 `number?`（R5RS sec 6.2）。

### 等價謂詞

Scheme 在任意物件之間有三種不同類型的等價，它們通過三種不同的「等價謂詞」來指示，即測試等式的關係算符 `eq?`、`eqv?` 和 `equal?`：

* `eq?` 求值為 `#f`，除非它的實際參數列示在記憶體中相同的物件；
* `eqv?` 大體上同於 `eq?`，但是特殊處理原始物件（比如字元和數），使得表示相同值的數 `eqv?` 為真，即使它們不參照相同的物件；
* `equal?` 比較資料結構比如列表、向量和字串，來確定它們是否有全等的結構並且其內容 `eqv?` 為真（R5RS sec. 6.1）。

在 Scheme 中還存在依賴於類型的等價運算：`string=?` 和 `string-ci=?` 比較兩個字串（後者進行不依賴大小寫比較）；`char=?` 和 `char-ci=?` 比較字元；`=` 比較數。

### 輸入 / 輸出

Scheme 的輸入和輸出基於了「埠」資料類型（R5RS sec 6.6）。R5RS 定義了兩個預設埠，可通過過程 `current-input-port` 和 `current-output-port` 來存取，它們對應於 Unix 概念的標準輸入和標準輸出。多數實現還提供 `current-error-port`。標準通過標準過程比如 `with-input-from-file` 和 `with-output-to-file`，支援輸入和標準輸出的重新導向。多數實現提供有重新導向能力的字串埠，使用在 SRFI 6 中描述的過程，確使很多常規的輸入 - 輸出運算能在字串緩衝區上進行而非在檔案上。R6RS 標準規定了更多複雜和有能力的埠過程和很多新的埠類型。

下面的例子是使用嚴格的 R5RS Scheme 書寫的。

例子 1，預設輸出到 `current-output-port`：

例子 2，類似例子 1 但對輸出過程使用可選的埠參數的例子：

類似例子 1，但是輸出被重新導向到一個新建檔案：

類似例子 2，但是具有顯式的檔案打開和埠關閉來傳送輸出到檔案：

類似例子 2，但是使用 `call-with-output-file` 來傳送輸出到一個檔案：

對輸入提供了類似的過程。R5RS Scheme 提供了謂詞 `input-port?` 和 `output-port?`。對於字元輸入和輸出提供了 `write-char`、`read-char`、`peek-char` 和 `char-ready?`。為了書寫和閱讀 Scheme 表達式，Scheme 提供了 `read` 和 `write`。在讀運算時，如果輸入埠到達了檔案的結束處，則返回的結果是 `end-of-file` 物件，並且這可以使用謂詞 `eof-object?` 來測試。

除了標準之外，SRFI 28 定義了一個基本的格式化過程，類似 Common Lisp 的 `format` 並以此命名。

### 標準過程的重定義

在 Scheme 中，過程被繫結到變數。在 R5RS 中語言標準正式的授權編程者可以變更內建過程的變數繫結，在效果上重定義它們（R5RS "Language changes"）。例如，通過重定義 `+` 可以將它擴充為同接受數一樣接受字串：

在 R6RS 中所有繫結，包括標準過程，都屬於某個庫，並且所有匯出的繫結都是不可變的（R6RS sec 7.1）。因此，禁止通過變更進行標準過程的重定義。轉而，可以在標準過程的名字下匯入不同的過程，這在效果上類似於重定義。

## 標準形式和過程概述

Scheme 語言正式定義於 1998 年的 R5RS 和 2007 年 R6RS 標準之中。它們描述了標準「形式」，即關鍵字和隨同的語法，它們提供語言的控制結構，和執行通常任務的標準過程。

在標準 Scheme 中，從一個資料類型轉換到另一個資料類型的過程，在它們的名字中包含字串 `->`，謂詞結束於一個 `?`，而改變已經分配了資料的值的過程結束於一個 `!`。Scheme 編程者通常跟從這些命名約定。

### 標準形式

本表格描述 Scheme 中的標準形式。某些形式出現在不止一行之中，因為它們不能被輕易的歸類入語言中的一個單一功能。

在表格中標記了「L」的形式被歸類為標準中的衍生的「庫」形式，並且在實踐中通常被實現為使用了更基礎形式的宏，這使得實現任務比在其他語言中要容易許多。

注意 `begin` 在 R5RS 中被定義為庫語法，但是展開者需要知道它來完成分片功能。在 R6RS 中它不再是庫語法。

### 標準過程

下面兩個表格描述在 R5RS Scheme 中的標準過程。R6RS 更加具有可延伸性而如此總結是不實際的。

某些過程出現在不止一行之中，因為它們不能輕易的分類入語言的一個單一功能。

在其名字中包含 `-ci` 的字串和字元過程，在它們的實際參數之間進行不依賴大小寫的比較：相同字元的大寫和小寫版本被認為是相等的。

接受多於一個實際參數的 `-` 和 `/` 在 R5RS 中被定義了但留作為可選的。

## Scheme 實現要求

由於 Scheme 的極簡主義，很多常見過程和語法形式不是由標準定義的。 為了保持核心語言很小並促進擴充的標準化，Scheme 社群擁有「Scheme 實現要求」（SRFI）流程，通過對擴充提案的仔細研討來定義擴充庫。這提升了代碼可移植性。很多 SRFI 被幾乎所有 Scheme 實現支援。

在不同的實現中具有相當廣泛支援的 SRFI 包括：

## 實作

Scheme 的精簡設計，使得程式語言設計人士與愛好者，特別鍾愛研究它，故而它有不斷湧現的新實作，而活躍開發的實作也在持續跟進語言標準更新。儘管 Scheme 有眾多實現是它的一個主要長處，但是由於 Scheme 沒有庫函式標準，造成了很多不可互通的實作互相競爭，試圖使用 Scheme 編寫既複雜又便於移植的程式，往往比較困難。R6RS 和 R7RS-Large，在核心語言之外製定了庫函式標準，使得編譯器開發者和貢獻者可以實現 Scheme 的可移植庫。

幾乎所有 Scheme 實作都有傳承自 Lisp 的「讀取﹣求值﹣輸出循環」互動模式，一些 Scheme 實作亦可作為編譯器。很多用 C 語言及衍生語言寫成的軟體，都利用 Scheme 作為手稿語言，很多嵌入式系統語言即是基於 Scheme。Chez Scheme 和 Larceny 等實現，可以將 Scheme 程式編譯為本機二進制碼。Gambit 和 Chicken 等實現，可將 Scheme 程式編譯為 C 代碼，Bigloo 還能編譯成 JVM 位元組碼或者.Net 位元組碼。

一些實現支援額外的特徵。比如 Kawa 提供與 Java 類別的整合，而 Scheme 到 C 編譯器通常易於使用 C 寫成的外部庫，甚至允許將實際 C 代碼嵌入到 Scheme 原始碼中。另一個例子是用 java 書寫的 Pvts，它提供了支援 Scheme 學習的一組可視工具。

## 教科書

* 《電腦程式的構造和解釋》（SICP），由 Scheme 創始人之一傑拉德・薩斯曼與哈爾・阿伯爾森編寫，它是最著名的使用 Scheme 語言的電腦科學教科書。書中用 Scheme 自身實現了 Scheme 的自循環直譯器，其簡單的語法可以幫助使用者理解 Scheme 的執行過程。
* 《程式設計方法》，由馬蒂亞斯・費萊森等人編寫，它對 SICP 中的一些被認為過於艱澀的概念進行了改進。
* 《程式語言的本質》，由丹尼爾・福瑞得曼等人編寫，它是以 Scheme 為基礎的教科書。
* 《簡單的 Scheme：介紹電腦科學》，由柏克萊加州大學資深講師布萊恩・哈維編寫，它是一本專為中學級別，無電腦科學基礎的學生編寫的入門書。

## 實際用處

很多著名的電腦科學院校都利用 Scheme 來教授入門級課程。以下為一些最為著名的教授 Scheme 的學校：

自由軟體影像處理程式 GIMP 利用 Scheme 為嵌入式指令碼語言。GNOME 中有到核心庫的一個 GNU 的擴充語言 Guile 包裝器。在 2012 年出現的 Julia 所採用的語法解析器，是用 Scheme 方言 Femtolisp 實現的。

## 參見

* LISP
* Racket

## 註釋

## 延伸閱讀

* Gerald J. Sussman, Guy L. Steele Jr. The First Report on Scheme Revisited. 1998.

## 外部連結

* 維基教科書中有關 Scheme Programming 的文字
* 維基教科書中有關 Write Yourself a Scheme in 48 Hours 的文字
* 維基共享資源上的相關多媒體資源：Scheme
* The Scheme community website （頁面存檔備份，存於網際網路檔案館）
* A bibliography of Scheme-related research （頁面存檔備份，存於網際網路檔案館）
* Awesome Scheme （頁面存檔備份，存於網際網路檔案館）
* Bookmarklet that add Interactive Scheme REPL to any website （頁面存檔備份，存於網際網路檔案館）
* The Scheme programming language 4rd edition （頁面存檔備份，存於網際網路檔案館）