Scheme、Scheme処理系、SCM

-> 英語

-> フランス語

更新:2001年12月28日 金曜日 15:58:57 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は上に書いたような特徴からまず教育用の言語として普及 しましたが、それに止まるものではありません。このページもShiro Kawaiさん にならって、Schemeの実用面を充実させていくつもりです。

Scheme処理 系

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

わたしが使用しているOSは、これも使用頻度順に、FreeBSD 4.4-RELEASE、 Debian Linux Potato (kernel 2.2r1)、OS/2 Warp V4.0、Windows 2000、 Windows 98です。ユーザの数から見ると、これは丁度逆順になるようです。 UNIX/Linuxは融通性に富んでいて使いやすいものですが、設定が簡単でない のであまり普及していません。

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

わたし がインストールできたScheme処理系
OS SCM Gauche STk guile mit-scheme
FreeBSD 4.4 make configure && make configure && make configure && make binary
Debian Linux 2.2 make / configure && make configure && make binary
OS/2 Warp V4.0 make / * * binary
Windows 2000 make / * * /
Windows 98 make / * * /

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

- Windwos 2000とWindows 98にはcygwin環境を使用してイン ストールしました。
cygwin環境はhttp://sources.redhat.com/cygwin から入手できます。
- OS/2では、emx環境でインストールしました。gccの他にgnu make とbashが必要です。
emx環境はhttp://hobbes.nmsu.eduから入 手できます。
- Windows(MS-DOS)用のSCMについては、Jafferさんのサイト から、バイナリも入手できます。ただしバイナリを実行するためには、 次に述べるSLIBライブラリの他に、ソースコードとともに配布されてい る*.scmファイルが、(implementation-vicinity)になければなりません。
- 浮動小数点数について、Linuxでは発生しませんが、FreeBSD上の SCMで後に述べるr4rstest.scmを通すためには、仮数部のデフォルト の精度を拡張精度にする必要があります(SCM on FreeBSD)。
浮動小数点数の処理については、入出力の精度に不満を残す 処理系が多いようです(例: (/ 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にはこ のアルゴリズムが実装されているようです。

SCMについて

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

SLIB

SLIBhttp://swissnet.ai.mit.edu/~jaffer/SLIB.html から入手できます。最新のバージョンは「2d2」です。Aubrey Jafferさんが保 守しています。

slib2d2.zipを入手したら適当なディレクトリ、例えば'/usr/local/lib' に 展開します。


    % unzip slib2d2 -d /usr/local/lib
  
SLIBライブラリは、指定したディレクトリの下の、slibディレクト リに展開されます。このディレクトリを、セッションの環境設定ファイルで 環境変数'SCHEME_LIBRARY_PATH'に指定しておきます。あとでライブラリファ イル名を環境変数に連結する必要がありますので、最後にフォワードスラッ シュ(OS/2とWindowsでは円記号)が必要です。

SCMのインストール

  1. 入手したscm5d4.zipを適当なディレクトリ、例えば/homeに展開します。 SCMは、指定したディレクトリの下のscm ディレクトリに展開されます。
    
        unzip scm5d4 -d /home
        
  2. SCMのコンパイルにとりかかる前に、次の点に注意する必要があるかもし れません。

    • bignumデータ形式の使用

      SCMのbignumデータ形式を有効に使うためには、scmfig.hで定義され ているNUMDIGS_MAXを20000桁程度にし ておいた方がいいと思います。

    • FreeBSDでコ ンパイルする場合の注意

      FreeBSDではdouble(flonum)の仮数部のデフォルトの長さが53ビッ ト(IEEE 754)に設定されています。flonum を64ビットに設定 するためにはコンパイル単位に' floatingpoint.h'を#includeし、 main関数の開始時に引数FP_PEを渡して、fpsetprec()を呼び出す必要 があります(FreeBSD Q and A)。これにより、Intel CPUのレジスタが64ビット浮動小 数点演算用にリセットされます。ただし以後はdoubleの内部演算がす べて64ビットで行なわれるようになりますので、桁溢れ、あるいは拡 張精度をdoubleに丸めた結果の誤差が発生する可能性が高くなります。
      ご自分の責任でscmmain.cへのパッチ を当てるか、scmmain.cを編集して下さい。わたしの環境ではこれま で特に重大な問題は発生していません。SCMの開発バージョン にはすでにパッチがあてられています。次のリリースからはパッチを あてる必要はなくなります。
      FP_PEは'ieeefp.h'で宣言されており、floatingpoint.hをincludeす ると取り込まれます。
      
               ...
              /*
               * FP precision modes
               */
      	  typedef enum {
      	        FP_PS=0,        /* 24 bit (single-precision) */
      		FP_PRS,         /* reserved */
      		FP_PD,          /* 53 bit (double-precision) */
      		FP_PE           /* 64 bit (extended-precision) */
      	  } fp_prec_t;
      	  ...
              
  3. scmlitを作成します。
    
          make scmlit
          
    - scmlitは、Schemeで書かれたスクリプトファイルbuildを実 行するために作成します。
    scmlitはbuildスクリプトを実行するために必要な最小限のモジュー ルだけで構成されていますので、どのOSでもコンパイルできる可能性 が高くなっています。
    - OS/2-emxでコンパイルする場合、オリジナルのfindexec.cでは必要 なインクルードファイルが取り込まれませんので、パッチを当てるなり書き変えるなり して下さい。これはフル仕様のSCMをコンパイルする場合にも必要で す。
  4. 引数を渡して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用)のいずれかです。
      これによりbuildデータベースから、プラットフォーム固有のコンパイ ルオプションとリンクオプションが選択されます。
    • -F 機能名 ...
      -Fオプションの機能名には、次の表に示す機能名を空白で区切って 指定します。表中*(アスタリスク)で印をつけた機能は上記コンパ イル環境でわたしがコンパイルできなかったものです。機能名に対 応する機能の詳細についてはscm.infoとソースファイルを参照して 下さい。

      コンパイル可能なSCMの機能(o = できた, * = できなかった, / = テストしていない)
      機能名 機能の概要 FreeBSD Debian Linux OS/2- emx cygwin
      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ライブラリの組み込み o o o o
      debug デバグ用エラーチェック / / / /
      dump 実行イメージダンプ * * * *
      dynamic-linking dldライブラリの使用 * * * *
      read-line ラインエディタ o o / /
      engineering-notation エンジニアリング表記の使用 o o o o
      generalized-c-arguments 可変引数C関数の使用 o o o o
      i/o-extensions ANSI CファイルI/O o o o *
      inexact 浮動小数点数の組み込み o o o o
      macro r5rsマクロのサポート o o o o
      posix posix関数(pipe,fork,uname etc.) o o * o
      unix posix以外のunix関数(symlink,nice,mknod etc.) o o * *
      record レコードデータ型 o o o o
      regex 正規表現 o o * *
      rev2-procedures r2rs手続き o o / /
      sicp (eq? () #f)==>#t, etc o o / /
      socket BSDソケット o o * /
      tick-interrupts ticks, ticks-interrupt, alarm etc. o o o o

    
          buildの実行例1(FreeBSD)
            ./build -h system -p freebsd -F arrays array-for-each bignums \
            cautious compiled-closure generalized-c-arguments i/o-extensions \
    	inexact macro record
          
    この指定によりコンパイルとリンクが行なわれて、実行ファイルscmが作 成されるはずですが、組み込むモジュールによってはbuildデータベース が対応していないために、コンパイルかリンクで失敗する場合がありま す。
    その場合は直接コンパイルとリンクを行なわずに一旦実行スクリプトを 作成し、そのスクリプトファイルを編集して実行してみて下さい。次の ようにします。
    
          buildの実行例2(FreeBSD)
            ./build -p freebsd -F arrays array-for-each bignums \
            cautious compiled-closure curses generalized-c-arguments \
    	i/o-extensions inexact macro posix unix record \
    	regex socket tick-interrupts > makescm
    
    	chmod +x ./makescm
          
    次のようなスクリプトファイルが作成されます。
    
          #!/bin/sh
          # unix script created by SLIB/batch 
          # ================ Write file with C defines
          rm -f scmflags.h
          echo '#define IMPLINIT "Init5d4.scm"'>>scmflags.h
          echo '#define INITS init_ramap();init_gsubr();init_ioext();init_posix();init_record();init_rgx();init_sc2();init_socket();init_unix();'>>scmflags.h
          echo '#define ARRAYS'>>scmflags.h
          echo '#define BIGNUMS'>>scmflags.h
          echo '#define CAUTIOUS'>>scmflags.h
          echo '#define FLOATS'>>scmflags.h
          echo '#define MACRO'>>scmflags.h
          echo '#define CCLO'>>scmflags.h
          echo '#define TICKS'>>scmflags.h
          # ================ Compile C source files
          cc -O -c rope.c unif.c subr.c sys.c eval.c scl.c repl.c time.c script.c findexec.c scmmain.c scm.c continue.c unix.c socket.c sc2.c rgx.c record.c posix.c ioext.c gsubr.c ramap.c
          # ================ Link C object files
          mv -f scm scm~
          cc -o scm -lc -lgnuregex -lm ramap.o gsubr.o ioext.o posix.o record.o rgx.o sc2.o socket.o unix.o continue.o scm.o scmmain.o findexec.o script.o time.o repl.o scl.o eval.o sys.o subr.o unif.o rope.o
          
    ./makescmを実行すると、ファイルscmflags.hにマクロ定義が書き込ま れ、実行ファイルscmが作成されます。今の段階では次のメッセージが 表示されます。
    
          sys.c: In function `prinport':
          sys.c:311: warning: passing arg 1 of `lputs' makes pointer from integer without a cast
          posix.c: In function `l_ttyname':
          posix.c:322: warning: assignment makes pointer from integer without a cast
          sys.o: In function `ltmpnam':
          sys.o(.text+0x40c): warning: tmpnam() possibly used unsafely; consider using mkstemp()
          /usr/libexec/elf/ld: sys.o: warning: unresolvable relocation against symbol `tmpnam' from .text section
          
    tmpnamに関するwarning以外は無視して構いません。tmpnamに関する warningは、このコンパイル方法ではtmpnam関数がリンクされていない ことを示しています。libcにはtmpnamが含まれているので理由がわか らないのですが、リンカに渡すオプション-lcを削除するとtmpnam が使 えるようになります。
    FreeBSDのCコンパイラはgcc(4.2-RELEASEでは2.95.2)ですが、FreeBSDの CライブラリはGNUのものとは別物とのことです。
    FreeBSDではtmpfileとtmpnamについて、mkstemp()の使用を強く推奨し ていますので(tmpfile (3))、標準Cライブラリを指定したときにtmpnam をリンクしないようなメカニズムがどこかにあるのかもしれません。ご 存知の方がいたら、教えていただければありがたいです。

    ともかく上のスクリプトファイルを次のように編集して実行すれば、フ ル仕様のSCMが作成されます。例では最適化オプションを最大のO3にし て、SCMセッションに表示するプロンプトも指定しています。

    
          #!/bin/sh
          # unix script created by SLIB/batch 
          # ================ Write file with C defines
          rm -f scmflags.h
          echo '#define IMPLINIT "Init5d4.scm"'>>scmflags.h
          echo '#define INITS init_ramap();init_gsubr();init_ioext();init_posix();init_record();init_rgx();init_sc2();init_socket();init_unix();'>>scmflags.h
          echo '#define ARRAYS'>>scmflags.h
          echo '#define BIGNUMS'>>scmflags.h
          echo '#define CAUTIOUS'>>scmflags.h
          echo '#define FLOATS'>>scmflags.h
          echo '#define MACRO'>>scmflags.h
          echo '#define CCLO'>>scmflags.h
          echo '#define TICKS'>>scmflags.h
          echo '#define PROMPT "SCM> "'>>scmflags.h
          # ================ Compile C source files
          cc -O3 -c rope.c unif.c subr.c sys.c eval.c scl.c repl.c time.c script.c findexec.c scmmain.c scm.c continue.c unix.c socket.c sc2.c rgx.c record.c posix.c ioext.c gsubr.c ramap.c
          # ================ Link C object files
          mv -f scm scm~
          cc -o scm -lgnuregex -lm ramap.o gsubr.o ioext.o posix.o record.o rgx.o sc2.o socket.o unix.o continue.o scm.o scmmain.o findexec.o script.o time.o repl.o scl.o eval.o sys.o subr.o unif.o rope.o
          
    次はDebian Linuxの場合です。
    
          buildの実行例3(linux)
            ./build -p linux -F arrays array-for-each bignums \
            cautious compiled-closure curses generalized-c-arguments \
    	i/o-extensions inexact macro posix unix record \
    	regex socket tick-interrupts > makescm
          
    次のようなスクリプトファイルが作成されます。
    
          #!/bin/sh
          # unix script created by SLIB/batch 
          # ================ Write file with C defines
          rm -f scmflags.h
          echo '#define IMPLINIT "Init5d4.scm"'>>scmflags.h
          echo '#define INITS init_ramap();init_gsubr();init_ioext();init_posix();init_record();init_rgx();init_sc2();init_socket();init_unix();'>>scmflags.h
          echo '#define ARRAYS'>>scmflags.h
          echo '#define BIGNUMS'>>scmflags.h
          echo '#define CAUTIOUS'>>scmflags.h
          echo '#define FLOATS'>>scmflags.h
          echo '#define MACRO'>>scmflags.h
          echo '#define CCLO'>>scmflags.h
          echo '#define TICKS'>>scmflags.h
          # ================ Compile C source files
          gcc -O2 -c rope.c unif.c subr.c sys.c eval.c scl.c repl.c time.c script.c findexec.c scmmain.c scm.c continue.c unix.c socket.c sc2.c rgx.c record.c posix.c ioext.c gsubr.c ramap.c
          # ================ Link C object files
          gcc -rdynamic -o scm -lc -lregex -lm ramap.o gsubr.o ioext.o posix.o record.o rgx.o sc2.o socket.o unix.o continue.o scm.o scmmain.o findexec.o script.o time.o repl.o scl.o eval.o sys.o subr.o unif.o rope.o
          
    このスクリプトに実行ビットを立てて実行すると、Debian Linuxには libregexが存在しないのでリンクに失敗します。Debian Linuxをインス トールするために消してしまったのですが、Laser5 Linux 6.0 (Japanese Redhat Linux 6.0) kernel 2.2.5をインストールしていたと きにもlibregexはありませんでした。Linuxでは正規表現ライブラリは 標準ライブラリになっているようです。リンクオプションから-lregex を削除すればリンクもエラーなしで終了し、rgx.cモジュールは正しく 動作します。

    
          buildの実行例4(cygwin)
            ./build -p unix -F arrays array-for-each bignums \
            cautious compiled-closure curses generalized-c-arguments \
    	inexact macro posix unix record tick-interrupts > makescm
    
    	chmod +x makescm ; ./makescm
          
    os/2では、makefileを作成してコンパイル しました。

    無論どのOSでも、Makefileを作成してコンパイルすることもできます。 わたし自身は、version 4e3以来のMakefileを次のように書き変えたも のを使うこともあります。

    Makefileはscmとlibscm.aの作成に使用し、Makefileでコンパイルする ときは空のscmflags.hを作成しています(rm scmflags.h ; touch scmflags.h)。

  5. r4rstest.scmを 実行して、作成したSCMの動作を確認します。
    
        ./scm r4rstest
        SECTION(2 1)
        SECTION(3 4)
         #<;primitive-procedure boolean?>;
         ...
       Passed all tests
    
       To fully test continuations, Scheme 4, and DELAY/FORCE do:
       (test-cont) (test-sc4) (test-delay)
       SCM> (test-cont) (test-sc4) (test-delay)
       ...
       Passed all tests
       ...
       (exit)
       
    わたしの環境(Pentium Pro 200 MHz, 128 MB memory)のFreeBSD、Debian Linux、Windows98--cygwinではすべてのテストに合格しましたが、環境に よっては次のように表示される場合もあり得ます。
    
       ...
       errors were:
       (SECTION (got expected (call)))
       ((6 5 6) (#f #t (mult-float-print-test #f)))
       ((6 5 6) (#f #t (mult-float-print-test #f)))
       ((6 5 6) (#f #t (mult-float-print-test #f)))
       ((6 5 6) (#f #t (mult-float-print-test #f)))
       ((6 5 6) (#f #t (mult-float-print-test #f)))
       ...
       
    最初の数字のリストはr4rsのセクション番号(上の例では§6.5.6 Numerical input and output--r5rsでは§6.2.6)で、組み込み手続き string->numberとnumber->stringの仕様に対応します。エラーは浮動小数 点数の入出力の精度によるもので、Schemeの本質的な動作には関係 しませんので、通常のプログラミングで問題になることはないと思います。

-> ひとつ前へ

-> ホーム


犬飼 大