Scheme、Scheme処理系、SCM

-> 英語

-> フランス語

2007年 8月19日 日曜日 01:01:42 JST

目次

Scheme

Schemeは、プログラミング言語Lisp(List Processing programming language)の方言の1つです。Lispプログラムは主として値を返す手続き(関 数)で構成されますので、関数型言語と呼ばれています。最終的な成果はプ ログラムですので、プログラミング言語を関数型と手続き型に分類すること 自体にはあまり意味はありません。Lispの考え方はC言語などにも存在して おり、発想に特別奇異なものもないのですが、現実にはLispで発想するプロ グラマは少いようです。

"Lisp as an Alternative to Java -- Erann Gatt, Jet Propulsion Laboratory, California Institute of Technology"という文献があります。 日付は1999年11月9日です。Lispプログラムの実行速度はCで注意深く書かれ た最高速のものには劣るけれどもC++と同等もしくはそれ以上、Javaスクリ プトよりも高速で、プログラムごとの性能の変動はC++よりも著しく少かっ たというデータが示されています。Lispプログラムのメモリ消費量はJavaと 同程度でしたが、実験結果がプログラマの熟練度に依存しておらず、プログ ラムの開発期間がC/C++やJava に比べて著しく短かったことも同時に報告さ れています。マシン語にコンパイルしたLispプログラムが使用されましたの で実行速度については割り引いて考えなければいけませんが、それ以外の特 徴はLispインタープリタにも当てはまると思われます。

Lispに愛着を持つ人々にとってこのような結果は当然で、Lispが優れたプロ グラミング言語であることは確信となっていますが、ならばLispがなぜ普及 しないのかが謎です。この謎を解き明かすのには時間がかかりそうですし、 あるい不可能かも知れません。Shiro Kawaiさんの Practical Schemeのように、実践して示すのが最善と思われます。

Schemeは、プログラミングに必要充分な機能を備えたLisp方言の中 でもっともコンパクトに設計された言語です。Schemeの仕様書は索 引を含めてa4版で50ページです。SchemeがLispであるためでもあり ますが、これほど小さな仕様で定義されたプログラミング言語は類を見ませ ん。Schemeに比べると他の言語は、円を手に入れようとして多角形 の辺の数を限りなく増やそうとしているように見えます。

Schemeの仕様が極めて明解ですので、Schemeプログラムはプ ログラミングを始めた初期の段階から、簡潔で正しく、美しいものになるこ とが保証されています。

Schemeは、「プログラミング言語は機能の上に機能を積み重ねるの ではなく、機能の追加を必要と思わせるような弱点と制限を取り除いて設計 すべきである」として、Lispの機構と構文を使用して作成されました。言語 仕様は、「実用的で効率のよいプログラミング言語を作成するためには、式 を組み立てる方法に制約がなければ極めて少数の式構成規則で足りる」と考 え抜いて設計されています(Revised5 Report on the Algorithmic Language Scheme--r5rs--Introduction)。

規則が正しければ、その規則にしたがうプログラムは正しいものになります。 必要充分な規則の数が少ければ、規則の適用範囲は広いものになります。プ ログラミング言語の規則はプログラムを表現する方法を規定するものですか ら、これはScheme の表現力が多様になることを意味します。しかも Schemeの規則に則る以上、Schemeにしたがって表現された多 様なプログラム1つ1つは、正しい明瞭なものにしかなり得ないことになりま す。

Schemeがはじめて文献に現れたのは、1975年に遡ります。Guy Lewis Steele Jr. と Gerald Jay Sussmanによって発明されました。Guy Lewis Steele Jr. は、「Common Lisp Reference Manual(First edition-1984, Second edition-1989)--Digital Press」の著者でもあり、Common Lispはあ る程度Schemeの影響も受けているとその序文の中で書いています。

Schemeの仕様は、Common Lispの研究成果を取り入れるなどの数度の 改訂を経て、「Revised4 Report on the Algorithmic Language Scheme--r4rs-1991」でほぼ固まりました。現在のScheme処理系のほ とんどは、すくなくともr4rsに準拠しています。

Schemeの最新の仕様は、「 Revised5 Report on the Algorithmic Language Scheme--r5rs-1998」で定められています。r5rsはr4rsの上位セットで す。

r5rsの拙訳を プ ログラミング言語Schemeに置いていただいています。
拙訳はわたし自身がScheme を使う必要に迫られたときに、Schemeに関す る日本語の文献を見つけることができないままに、もともとはわたし自身 のために訳出したものでした。書いたプログラムがわたしが理解したr5rs の通りに動作することから、拙訳に大きな誤りはなかろうと考えて公表し たものです。今読み返すと不備な点がたくさんありますが、1度公表して しまったので、翻訳をやり直す気になかなかなれません。便覧として使っ ていただければ幸いです。
その後r4rsとr5rsには他にも多くの日本語訳があることがわかりました。原 典はわたしにはかなりわかりにくいものでしたが、いろいろな翻訳を原典 とつき合わせることで、r5rsの理解も進むと思います。

Schemeは上に書いたような特徴からまず教育用の言語として普及 しましたが、それに止まるものではありません。このページでは、 Schemeの実用面をわたしの手が届く範囲で充実させていくつもり です。

Scheme処理 系

Schemeを実際に使うためにはScheme処理系をインストールす る必要があります。
Schemeには処理系がたくさんありますが、わたしたちがScheme を使 うにあたってもっとも気になるのが、日本語の処理です。Scheme 処理系がC で書かれている場合、char?が#tを返すSchemeの文字型には、C のchar型が 使われています。処理系が日本語localeをサポートする必要は必ずしもあり ませんが、日本語文字のような連続する2バイトが常に独立したバイトとし て処理された場合、2バイト文字は、加工はできますが入力と表示ができま せん。
わたしが使用した処理系で日本語の入力と表示ができるものは、使用頻度順に (1)SCM、(2)Gauche、(3)STklos (4)STk、(5)guile、(6)mit-schemeでした。特に GaucheではSchemeの文字列処理手続きが自然な形で拡張されて おり、使用する文字コード系をコンパイル時に指定すれば、標準の文字列処理 手続きでマルチバイト文字を処理できるようになります。

わたしが使用しているOSは、これも使用頻度順に、FreeBSD 7.0-CURRENT、 FreeBSD 6.2-STABLE、Debian Linux sarge (kernel 2.6.10)、Windows 2000(Cygwin、 MinGW)です。ユーザの数から見ると、 これは逆順になるようです。

それぞれにどのようにインストールしたかを次の表にまとめてみました。

わたしがインストールできたScheme処理系
OS SCM Gauche stklos STk guile
FreeBSD* 7.0-CURRENT**/6.1-STABLE*** rpmまたはgmake && ./build ./configure && make ./configure && gmake ./configure && make ports collection
Debian Linux Unstable****/Sarge***** rpmまたはmake ./configure && make ./configure && make ./configure && make apt-get install
Windows 2000****** make ./configure && make or binary / / /

x ...インストールできませんでした。
/ ...インストールしていません。

* FreeBSDでコンパイルする場合の注意 これまでのシステムCコンパイラーには過渡的な問題がありました。 portsのように"eval.c"を書き換えて変数にvolatile修飾子をつけてコンパイルするか(volatile struct {SCM arg_1;} t;)、buildスクリプトに引数を渡してコン パイルして、動作するSCMを作成していました。
7.0-CURRENTのシステムコンパイラがversion 4.2.1になってからは上記の書き換えは不要になったようです。
** 7.0-CURRENT
% uname -a
FreeBSD hiro 7.0-CURRENT FreeBSD 7.0-CURRENT #0: Sat Aug 18 15:40:31 JST 2007 daip@hiro:/usr/obj/usr/src/sys/DAIKERNEL i386
% cc -v
Using built-in specs.
Target: i386-undermydesk-freebsd
Thread model: posix
gcc version 4.2.1 20070719 [FreeBSD]

*** 6.2-STABLE
% uname -a
FreeBSD hiro 6.2-STABLE FreeBSD 6.2-STABLE #0: Sat Aug 11 03:03:31 JST 2007 daip@hiro:/usr/obj/usr/src/sys/DAIKERNEL amd64
% cc -v
Using built-in specs.
Configured with: FreeBSD/amd64 system compiler
Thread model: posix
gcc version 3.4.6 [FreeBSD] 20060305

**** Debian Linux Sarge
% uname -a
Linux colinux 2.6.10-co-0.6.2 #5 Sat Feb 5 10:19:16 IST 2005 i686 GNU/Linux
% gcc -v
Reading specs from /usr/lib/gcc/i486-linux/3.4.4/specs Configured with: ../src/configure -v --enable-languages=c,c++,java,f77,pascal,objc,ada,treelang --prefix=/usr --libexecdir=/usr/lib --with-gxx-include-dir=/usr/include/c++/3.4 --enable-shared --with-system-zlib --enable-nls --without-included-gettext --program-suffix=-3.4 --enable-__cxa_atexit --enable-libstdcxx-allocator=mt --enable-clocale=gnu --enable-libstdcxx-debug --enable-java-gc=boehm --enable-java-awt=gtk --disable-werror i486-linux
Thread model: posix
gcc version 3.4.4 20050314 (prerelease) (Debian 3.4.3-13)
***** Windwos 2000にはcygwin環境もしくはMinGW環境を使用してイン ストールできます。
cygwin環境はhttp://sources.redhat.com/cygwin から入手できます。
MinGW環境はhttp://www.mingw.org/ から入手できます。
- 浮動小数点数の精度について。x86系cpuとgccを組み合わせたとき のgccのあやしい振舞いについて、 川合さんが詳しく考察しています。
浮動小数点数の処理については、入出力の精度に不満を残す 処理系が多いようです。

例: (/ 1448997445238699 (expt 10 15)) ≒ 1.448997445238699と(* 6525704354437806 (expt 2 -52))。

Shiro Kawaiさんから、"Printing Floating-Point Numbers Quickly and Accurately", Robert G. Burger and R. Kent Dybvig, Proceedings of the SIGPLAN '96 Conference on Programming Language Design and Implementation. という論文の存在を教えてい ただきました。この論文では、浮動小数点数の高速で正確な出力を 実装するアルゴリズムが述べられており、Chez Schemeを使用してア ルゴリズムの正当性が確認されています。R. Kent Dybvig氏がChez Schemeの開発に主として携わっていることから、Chez Schemeにはこ のアルゴリズムが実装されているようです。
ただしここに紹介する処理系はformatのような拡張出力モジュールを 備えているか、あるいはSLIBのprintfが使えますので、出力精度を気 にする必要はないと思います。

SCMについて

SCMを使うためには、Schemeで書かれたライブラリ SLIBをあらかじめインストールして、SCMから呼び出せるようにして おく必要があります。

SLIB

SLIBhttp://swissnet.ai.mit.edu/~jaffer/SLIB.html から入手できます。現在のバージョンは「3a4(2006-10-12)」です。Aubrey Jafferさんが保 守しています。SCMSLIBは密接に結び付いていますので、 奇妙な動作を避けるためにも、両方を同時に更新しておいた方がいいと思い ます。

slib3a4.zipを入手したら、コンパイルするscmのソースを置くディレクトリ と同じディレクトリ、例えばscmを$HOME/scmに展開するとすれば、$HOMEに 展開します。


    % unzip slib3a4 -d $HOME
  
SLIBライブラリは、指定したディレクトリの下の、slibディレクト リに展開されます。scmは実行ファイルが配置されたディレクトリを基準に してslibディレクトリを検索しますから、環境変数'SCHEME_LIBRARY_PATH' を指定する必要は現在では特にないようです。ただしSCHEME_LIBRARY_PATH が設定されていれば、そのディレクトリからライブラリが検索されます。

SCMのインストール

  1. 入手したscm5e3.zipを適当なディレクトリ、例えば$HOMEに展開します。 SCMは、指定したディレクトリの下のscm ディレクトリに展開されます。
    
        unzip scm5e3 -d $HOME
        
  2. SCMのコンパイルにとりかかる前に、次を修正しておいた方がいいかもし れません。

    • bignumデータ形式の使用

      SCMのbignumデータ形式を有効に使うためには、scmfig.hで定義され ているNUMDIGS_MAXを20000桁程度にし ておいた方がいいと思います。自前のMakefileでコンパイルする場合 は、コンパイラに-DNUMDIGS_MUX=20000を渡してもいいです。

    • SCMセッションのプロンプトに表示する文字列もこのファイルで変更 できます。
      
      	    % diff -C3 scmfig.h.orig scmfig.h
      	    *** scmfig.h.orig       Tue Jun 28 07:43:45 2005
      	    --- scmfig.h    Tue Jun 28 07:43:55 2005
      	    ***************
      	    *** 392,398 ****
      	      # ifdef SICP
      	      #  define PROMPT "==> "
      	      # else
      	    ! #  define PROMPT "> "
      	      # endif
      	      #endif
      		        
      	    --- 392,398 ----
      	      # ifdef SICP
      	      #  define PROMPT "==> "
      	      # else
      	    ! #  define PROMPT "scm5e2> "
      	      # endif
      	      #endif
        
                  ***************
                  *** 437,443 ****
                    # define BIGLO(x) ((x) & (BIGRAD-1))
                    /* NUMDIGS_MAX is the maximum number of digits for BIGNUMS */
                    # ifndef NUMDIGS_MAX
                  ! #  define NUMDIGS_MAX 1000
                    # endif
                    #endif
        
                  --- 437,443 ----
                    # define BIGLO(x) ((x) & (BIGRAD-1))
                   /* NUMDIGS_MAX is the maximum number of digits for BIGNUMS */
                    # ifndef NUMDIGS_MAX
                  ! #  define NUMDIGS_MAX 20000
                    # endif
                    #endif
                

  3. scm5e2からBツリーの一種wb、ディスクベース連想配列を使用したデー タベースライブラリも標準的にコンパイルできるようになりましたので、 http://swissnet.ai.mit.edu/~jaffer/WB からそのソースコード(wb1c3.zip)も入手しておくと便利です。 入手したらそれも同じディレクトリに展開します。
    
              unzip wb1c3 -d $HOME
            
  4. scmlitを作成します。
    
          make scmlit (FreeBSDではgmake scmlit -- GNU makeを使用)
          
    - scmlitは、Schemeで書かれたスクリプトファイルbuildを実 行するために作成します。
    scmlitはbuildスクリプトを実行するために必要な最小限のモジュー ルだけで構成されていますので、どのOSでもコンパイルできる可能性 が高くなっています。
  5. scmlitが作成できれば、Linuxの場合はmake allでコンパイルできま す。ただしデフォルトでは、r5rsマクロを組み込んだ実行イメージを ダンプしたSCMが作成されます。Linuxでイメージダンプファイルを使 いたくない場合とFreeBSDでは、Makefileとbuildスクリプトを使って 実行ファイルを作成できます。

    buildの引数には、-p freebsdのような短形式と、 --platform=freebsdのような長形式がありますが、ここでは短形式だ けを紹介します。詳しくはscmディレクトリに展開されるinfoファイ ルから、scm -- Installing SCM -- Building SCM -- Build Options を参照して下さい。

    • -h バッチスクリプトの形式
      バッチスクリプトの形式には、unix、dos、vms、amigados、system のいずれかを指定できます。
      -h systemを指定すると、実際にコンパイルが行なわれます。それ 以外の指定では、それぞれのOSに応じたバッチスクリプトが、標準 出力に出力されます。SCMは実際にはbuildを実行する環境からバッ チスクリプトの形式を判断しますので、systemを指定するのでなけ れば-hオプションは不要です。
    • -p プラットフォーム
      プラットフォームには、buildデータベースが持っているプラット フォーム名を指定します。ここで使用するのは、freebsd、linux、 os/2-emx、unix(cygwin)、gnu-win32(MinGW)のいずれかです。
      これによりbuildデータベースから、プラットフォーム固有のコンパイ ルオプションとリンクオプションが選択されます。
    • -t 作成するオブジェクトの種類(type)。
      • exe
        実行形式プログラム。
      • lib
        ライブラリモジュール。
      • dlls
        すべてのダイナミックリンクリンクライブラリオブジェクトファイル。
      • dll
        特定のダイナミックリンクライブラリオブジェクトファイル。
      デフォルトでは実行形式ファイルが作成されます。
    • -F 機能名 ...
      -Fオプションの機能名には、次の表に示す機能名を空白で区切って 指定します。表中*(アスタリスク)で印をつけた機能は上記コンパ イル環境でわたしがコンパイルできなかったものです。機能名に対 応する機能の詳細についてはscm.infoとソースファイルを参照して 下さい。

      コンパイル可能なSCMの機能(d = ダイナミックリンクライブ ラリ、o = できた、 * = できなかった、 / = テストしていない)
      機能名 機能の概要 動的ロード可 FreeBSD Debian Linux cygwin MinGW
      arrays 多次元配列 o o o o
      array-for-each arrayのマッピング o o o o
      bignums 多倍長整数 o o o o
      cautious 手続きに渡される引数のチェック o o o o
      compiled-closure C関数の組み込み o o o o
      curses cursesライブラリの組み込み d o o o /
      debug デバグ用エラーチェック o o o o
      dump 実行イメージダンプ * o * *
      dynamic-linking dlライブラリの使用 o o * *
      edit-line ラインエディタ d o o o /
      engineering-notation エンジニアリング表記の使用 o o o o
      generalized-c-arguments 可変引数C関数の使用 d o o o o
      i/o-extensions ANSI CファイルI/O d o o o o
      inexact 浮動小数点数の組み込み o o o o
      macro r5rsマクロのサポート o o o o
      posix posix関数(pipe,fork,uname etc.) d o o o /
      unix posix以外のunix関数(symlink,nice,mknod etc.) d o o * /
      record レコードデータ型 d o o o o
      regex 正規表現 d o o * /
      rev2-procedures r2rs手続き d o o / /
      sicp (eq? () #f)==>#t, etc / / / /
      socket BSDソケット d o o o *
      tick-interrupts ticks, ticks-interrupt, alarm etc. o o o o
      wb wb、ディスクベースBツリー連想配列プリミティブ d o o o o

      - dynamic-linkingはlibdlを使用します。libdlは、Linuxでは 独立したライブラリファイルですが、FreeBSDではlibcに入って います。
      - i/o-extensionsはスタティックに組み込むことも、dlとして ロードすることもできます。別のライブラリがこれの使用を前提 にしている場合があるので、読み込まれていないと(load "ライ ブラリ")が失敗することがあります。(require '機能)を使用すれ ば、必要に応じてioext.soが先にダイナミックロードされます。
      - edit-lineは、外部ライブラリlibreadlineを使用します。ター ミナルモードでインタラクティブにSCMを使いたいとき に便利です。libreadlineの日本語対応が不適切だったりして ラインエディタを使いたくない場合は、edit-lineを使わなけ ればいいです。emacsやxemacsのサブプロセスとして SCMを呼び出したときはedit-lineは使用されず、emacs の編集機能でインタラクティブに操作することができます。

    スクリプトとして使用するときは実行ファイルは小さいほど読み込みが 速く行なわれるので、ダイナミックリンクライブラリの受け口を取り付 けた最小のscmを作っておいて、必要に応じてライブラリを動的にロー ドする方が小回りが効いて便利です。

    buildを-Fなしで実行した場合は、デフォルトでarrays、bignums、inexactだけを指定したscmが作成されます。
    
          buildの実行例1
            ./build -h system -p freebsd 
          
    この指定によって汎用的で最小のScheme処理系が作成されます が、拡張機能を動的にロードできないので不便を感ずることがあります。

    次は1にダイナミックリンクライブラリの受け口を取り付けたscmを 作成する場合です。-Fを指定したときには指定した機能だけが組み込まれ るようになるので、必要な機能をすべて明示的に指定する必要がありま す。

    
          buildの実行例2
            ./build -h system -p freebsd -F arrays bignums inexact dynamic-linking
          
    scm5e1以来Makefileを使ったスクリプトbuildへの引数の受渡しが少 し細かくなって、コンパイルが楽になっています。
    • Makefile経由でのbuildへの引数の渡し方。

      Makefileの中では変数、SCMLIT(実行ファイルscmlitの呼び出し)、 SCMEXE(実行ファイルscmの呼び出し)、BUILD(スクリプトbuildの呼び 出し)が定義されています。それぞれ新しい定義をMakefileに渡して コンパイラ、リンカを呼び出せば、より細かい制御ができます。
      • Linux固有のコンパイラオプションを使用してコンパイル をより細かく制御して実行イメージをダンプした実行ファイルを作 成する場合(FreeBSDでの実行イメージダンプをわたしははまだでき ていません)。

        	    make dscm5 BUILD="./build -h system -p linux"
                  
        r5rsマクロ展開の実行イメージまでを含んだ実行ファイルscmが出 力されます。この実行ファイルはダンプ時に展開されていたscmセッ ションのイメージですので、イメージに含まれるライブラリを新た に読み込む必要はありませんが、ディスク上のファイルは1M近い巨 大なものになります。
      • dynamic-wind、values、syntax-rulesに必要な変数とコー ドはコンパイルするけれども、実行イメージダンプを行なわない場 合。

        	      make scm5 BUILD="./build -h system -p linux"
        	    
      • 同じ実行ファイルをFreeBSDで生成する場合。

        6.2-STABLEの標準のCコンパイラには最適化の 程度を上げるとある種の変数が消失して、存在しない変数を参照す るエラーがまだ発生します。変数にvolatile修飾子を指定して最適 化プロセスから除外するか、変数の型を変更することで回避できま す。gcc 4.2.1では解消されていますが、次のようにすればこのバグを回避できます。
        	      gmake scm5 BUILD="./build -DGCC_SPARC_BUG --linker-options=-DGCC_SPARC_BUG -hsystem -pfreebsd" SCMLIT="./scmlit -no-init-file" SCMEXE="./scm -no-init-file"
        	    
        以前からscmを使っていてその環境設定ファイルScmInit.scmがホー ムディレクトリに存在すると、まだ実行環境が確定していないscm を実行したときにエラーが発生する場合がありますので、scmlitに もscmにもScmInit.scmを読み込ませないようにしています。
      • Windows 2000

        Cygwin環境とMinGW環境ではダイナミックロードの仕組みがわかりませんので、Makefileのscm5.opt:以降にコンパイル可能モジュール名を追加して、モジュールをスタティックリンクして使用しています。
        	      make scm5 BUILD="./build -hsystem -punix"(cygwin)
        	      make scm5 BUILD="./build -hsystem -pgnu-win32"(MinGW)
        	    

    いずれの場合にもMakefileによってr4rstest.scm、syntest1.scm(r5rsマ クロの動作の確認)、syntest2.scm(キーワードの再定義の確認)がロード され、実行されます。Linuxでは浮動小数点数の丸め誤差によるエラーが 表示されますが、コンパイルが正常に終了してプログラムコードが正しく 実行されていることがわかります。

  6. ダイナミックリンクライブラリを作成します。
    
           % make mydlls BUILD="./build -h system -p linux" (Debian Linux)
           % gmake mydlls BUILD="./build -h system -p freebsd" (FreeBSD)
          
  7. wbライブラリを作成します。
            % cd $HOME/scm
    
    	% ln -s $HOME/scm/scm.h $HOME/scm/scmfig.h $HOME/scm/scmflags.h ../wb
    
            % make db.so BUILD="./build -h system -p linux" (Debian Linux)
    
    	% gmake db.so BUILD="./build -h system -p freebsd" (FreeBSD)
          
  8. インストール

    インストールはMakefileの中で指定されているprefixディレクトリに行な われ、SCMが使用するライブラリのカタログファイル(implcatと slibcat)もそこに書き込まれますので、ディレクトリに対する書き込み権 限が必要です。スーパーユーザでインストールを実行するか、でなけれ ばご自分が権限を持つディレクトリをprefixに指定しておきます。

    
          % cd $HOME/scm
          % make install prefix=$HOME/ (最後にフォワードスラッシュが必要です)
          % cd ../slib
          % make install prefix=$HOME/ (slibライブラリパッケージをインストールします)
        

    実行ファイルが、Makefileの中で指定されているprefixディレクトリの下 のbinディレクトリに、Init$(VERSION).scmその他の初期化ファイルが $(prefix)/lib/scmディレクトリに、slibライブラリパッケージが $(prefix)/lib/slibにインストールされます。ダイナミックロードライブ ラリ(*.so)を作成していれば、それもlib/scmディレクトリにインストー ルされます。

    カタログファイルを更新するために(require 'new-catalog)を発行します。

          scm -no-init-file -e'(require (quote new-catalog))'
        
    Schemeの世界を楽しんで下さい。

-> ひとつ前へ

-> ホーム


犬飼 大