附錄 A Scheme方言
====================
所有主要的Scheme方言都實現了R5RS規范。如果只使用R5RS中規定的功能,我們就能寫出在這些方言中都能正常運行的代碼。然而R5RS可能是為了更好的統一性,或是由于不可避免的系統依賴,在一些通用編程中無法忽略的重要問題上沒有給出標準。因此這些Scheme方言不得不用一種特殊的非標準手段來解決這些問題。
本書使用了Scheme的MzScheme方言,因此也使用了一些非標準的特性。以下是本書中所有非標準的、依賴于MzScheme提供的特性:
- 命令行(包括打開一個偵聽會話以及Shell腳本)
- `define-macro`
- `delete-file`
- `file-exists?`
- `file?or?directory?modify?seconds`
- `fluid?let`
- `gensym`
- `getenv`
- `get?output?string`
- `load?relative`
- `open?input?string`
- `open?output?string`
- `read?line`
- `reverse!`
- `system`
- `unless`
- `when`
以上這些命令中除了`define-macro`和`system`外都是在MzScheme的默認環境中就有的。而這兩個缺少的則可以在MzScheme的標準庫中找到,通過以下方式顯式地加載。
```scheme
(require (lib "defmacro.ss")) ;provides define-macro
(require (lib "process.ss")) ;provides system
```
另外還可以把這兩段代碼放在MzScheme的初始化文件中(在Unix系統下是用戶家目錄下的`.mzschemerc`文件)。
一些非標準的特性(如`file-exists?`和`delete-file`)事實上在很多Scheme實現中已經是“標準”的特性了。另一些特性(如`when`和`unless`)或多或少有種“插件”式的定義(在本書中給出),因此可以在任何不具備這些過程的Scheme中加載。其他的需要針對每種方言來定義(如`load-relative`)。
本章描述了如何給你使用的Scheme方言加上本書中用到的這些非標準特性。想要了解更多關于你使用的Scheme方言,請參考其實現者提供的文檔(附錄E)。
## A.1 調用和初始化文件
很多Scheme方言就像MzScheme一樣都會從用戶的家目錄中載入初始化文件。我們可以把非標準功能的定義都放到這個初始化文件中,這樣非常方便。比如,非標準過程`file-or-directory-modify-seconds`可以添加到Guile語言中,只要把下面的代碼放到Guile的初始化文件(`~/.guile`)中即可:
```scheme
(define file-or-directory-modify-seconds
(lambda (f)
(vector-ref (stat f) 9)))
```
另外,不同的Scheme方言有他們自己的不同的命令來啟動對應的偵聽器。下面的表格列出了不同Scheme方言對應的啟動命令和初始化文件位置:
<table><tr><td><span style="margin-left: 2em"> </span><em>Dialect name</em> </td><td> <em>Command</em> </td><td> <em>Init file</em> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>Bigloo </td><td> <code class=verbatim>bigloo</code> </td><td> <code class=verbatim>~/.bigloorc</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>Chicken </td><td> <code class=verbatim>csi</code> </td><td> <code class=verbatim>~/.csirc</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>Gambit </td><td> <code class=verbatim>gsi</code> </td><td> <code class=verbatim>~/gambc.scm</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>Gauche </td><td> <code class=verbatim>gosh</code> </td><td> <code class=verbatim>~/.gaucherc</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>Guile </td><td> <code class=verbatim>guile</code> </td><td> <code class=verbatim>~/.guile</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>Kawa </td><td> <code class=verbatim>kawa</code> </td><td> <code class=verbatim>~/.kawarc.scm</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>MIT Scheme (Unix) </td><td> <code class=verbatim>scheme</code> </td><td> <code class=verbatim>~/.scheme.init</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>MIT Scheme (Win) </td><td> <code class=verbatim>scheme</code> </td><td> <code class=verbatim>~/scheme.ini</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>MzScheme (Unix, Mac OS X) </td><td> <code class=verbatim>mzscheme</code> </td><td> <code class=verbatim>~/.mzschemerc</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>MzScheme (Win, Mac OS Classic) </td><td> <code class=verbatim>mzscheme</code> </td><td> <code class=verbatim>~/mzschemerc.ss</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>SCM </td><td> <code class=verbatim>scm</code> </td><td> <code class=verbatim>~/ScmInit.scm</code> </td></tr>
<tr><td><span style="margin-left: 2em"> </span>STk </td><td> <code class=verbatim>snow</code> </td><td> <code class=verbatim>~/.stkrc</code> </td></tr>
</table>
## A.2 Shell腳本
使用Guile編寫的Shell腳本的初始行差不多應該是:
```shell
":";exec guile -s $0 "$@"
```
在Guile腳本中,調用過程`(command-line)`會以列表的形式返回腳本的名稱和參數。如果只需要參數,只需要獲得列表的`cdr`部分即可。
用Gauche編寫的Shell腳本以:
```shell
":"; exec gosh -- $0 "$@"
```
開頭。在腳本中變量`*argv*`中保存著腳本的參數列表。
用SCM編寫的Shell腳本以:
```shell
":";exec scm -l $0 "$@"
```
開頭。腳本中變量`*argv*`保存著一個列表,列表中包括Scheme可執行文件的名稱,腳本的名稱,`-l`這個選項,還有腳本的參數。如果只需要參數,對列表執行`cdddr`即可。
STk的Shell腳本以:
```shell
":";exec snow -f $0 "$@"
```
開頭。在腳本中變量`*argv*`中保存著腳本的參數列表。
## A.3 `define-macro`
本文中使用的`define-macro`宏在Scheme的很多方言如Bigloo,Chicken,Gambit,Gauche,Guile,MzScheme和Pocket中都有定義。在其他Scheme方言中定義宏的方式基本上是相同的。本節將指出其他Scheme方言是如何表示如下一段代碼片段的:
```scheme
(define-macro MACRO-NAME
(lambda MACRO-ARGS
MACRO-BODY ...))
```
在MIT Scheme第7.7.1或更高版本中,上述代碼被寫為:
```scheme
(define-syntax MACRO-NAME
(rsc-macro-transformer
(let ((xfmr (lambda MACRO-ARGS MACRO-BODY ...)))
(lambda (e r)
(apply xfmr (cdr e))))))
```
在老版本的MIT Scheme中:
```scheme
(syntax-table-define system-global-syntax-table 'MACRO-NAME
(macro MACRO-ARGS
MACRO-BODY ...))
```
在SCM和Kawa中:
```scheme
(defmacro MACRO-NAME MACRO-ARGS
MACRO-BODY ...)
```
在STk中:
```scheme
(define-macro (MACRO-NAME . MACRO-ARGS)
MACRO-BODY ...)
```
## A.4 `load-relative`
過程`load-relative`可以在Guile中如下定義:
```scheme
(define load-relative
(lambda (f)
(let* ((n (string-length f))
(full-pathname?
(and (> n 0)
(let ((c0 (string-ref f 0)))
(or (char=? c0 #\/)
(char=? c0 #\~))))))
(basic-load
(if full-pathname? f
(let ((clp (current-load-port)))
(if clp
(string-append
(dirname (port-filename clp)) "/" f)
f)))))))
```
在SCM中可以這樣寫:
```scheme
(define load-relative
(lambda (f)
(let* ((n (string-length f))
(full-pathname?
(and (> n 0)
(let ((c0 (string-ref f 0)))
(or (char=? c0 #\/)
(char=? c0 #\~))))))
(load (if (and *load-pathname* full-pathname?)
(in-vicinity (program-vicinity) f)
f)))))
```
對于STk,下面的`load-relative`過程僅在沒有使用`load`過程時生效:
```scheme
(define *load-pathname* #f)
(define stk%load load)
(define load-relative
(lambda (f)
(fluid-let ((*load-pathname*
(if (not *load-pathname*) f
(let* ((n (string-length f))
(full-pathname?
(and (> n 0)
(let ((c0 (string-ref f 0)))
(or (char=? c0 #\/)
(char=? c0 #\~))))))
(if full-pathname? f
(string-append
(dirname *load-pathname*)
"/" f))))))
(stk%load *load-pathname*))))
(define load
(lambda (f)
(error "Don't use load. Use load-relative instead.")))
```
----------------------------
我們使用`~/filename`表示用戶家目錄中被調用的文件。