[冒頭] [目次] [見出し] [ ? ]

Gauche ユーザリファレンス

本書は、R7RS準拠のScheme処理系、Gaucheのリファレンスマニュアルです。 Gaucheのバージョン0.9.5に対応します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1. はじめに

本書はGauche Scheme systemに関する、ユーザのためのガイドと リファレンスです。なるべくGaucheの実装を正確に記述することを 目的として、時には背景となる設計上の選択も含めて説明しています。

したがって、既にSchemeを知っていてGaucheの全機能を活用して プログラムを書きたいという読者を対象にしています。 Schemeに触れるのが初めての読者は、何らかのチュートリアルを併用すると 良いでしょう。私もいずれチュートリアルめいたものを書くつもりです。

このマニュアルでは、GaucheのScheme言語としての面のみを扱います。 GaucheはC言語から呼べる汎用リスト処理ライブラリとしての側面も持っていますが、 それは別ドキュメントで解説する予定です。Gaucheを組み込み言語として使いたい、 もしくはGaucheをC言語で拡張したいという読者はそのドキュメントが必要となります。

Scheme言語としての側面に関しては、なるべくこのマニュアル一冊でリファレンスとしての 用が足りるように記述しました。標準に定められている関数でも、こちらのマニュアルだけで 使うのに必要な事項を知ることができます。但し、このマニュアルは標準文書の替わりには なりません。記述を短くするために理論的な背景などは省略してあります。 オリジナルのドキュメントへのポインタを参考文献の項に上げておきましたので、 必要に応じて参照して下さい。

もしあなたがこのマニュアルをオフラインで読んでいるなら、 時々下記のURLをチェックしてみて下さい。更新があるかもしれません。

 
http://practical-scheme.net/gauche/.

なお、本マニュアルの日本語版製作には、 佐藤 誠 氏、山下 伸夫 氏の協力を頂きました。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.1 Gaucheの概要

GaucheはScheme言語のスクリプトエンジンです。 Schemeプログラムを読み込み、直ちにコンパイルして仮想マシンで実行します。 Scheme言語の標準である、"Revised^7 Report on the Algorithmic Language Scheme" (R7RS)に準拠しています。また、SRFI (http://srfi.schemers.org) に規定されている数多くのライブラリを サポートしています。

Gaucheは、プログラマやシステム管理者がこなす日常の雑事を 効率よくSchemeで書けるようにすることを目的として設計されています。

世の中には多くのSchemeの実装がありますが、 それぞれの実装には長所と短所があります。 Gaucheが長所とするべく重点を置いているのは次のようなポイントです。

立ち上りが速いこと

Gaucheが想定している用途のひとつは、プロダクション環境でちょろっと 10行スクリプトをでっちあげて、それが非常に頻繁に呼ばれるようなケースです。 CGIスクリプトなどもそうです。 Gaucheは頻繁に使われる共通の機能については、豊富な組み込み関数として、 あるいはプリコンパイルされ素早くロードできるSchemeライブラリとして提供しています。

マルチコアの完全な活用

Gaucheは多くのプラットフォームでネイティブスレッドをサポートしています。 内部は全てプリエンプティブな並行スレッドを意識してかかれており (つまり、「巨大なひとつのロック」はありません)、 複数のコアを存分に活用できるようになっています。

マルチバイト文字列

文字列が1バイトキャラクタのみを扱っていれば良かった時代は過ぎ去りました。 現代のプログラミングシステムは、様々なエンコーディングによるマルチバイト文字/文字列を 自然に扱える必要があります。 Gaucheは内部的に文字列を全て、コンパイル時に選択したエンコーディングの マルチバイト文字列として扱います。後から付け足したライブラリレベルでの マルチバイト文字列のサポートよりも、一貫性がありロバストな文字列操作が可能になっています。 詳しくはマルチバイト文字列を参照してください。

統合されたオブジェクトシステム

CLOSライクなメタオブジェクトプロトコルを備えた強力なオブジェクトシステム が組み込んであります。STklosやGuileのオブジェクトシステムとかなり互換性があります。

システムインタフェース

Schemeは計算機の詳細の多くを抽象化しますが、プログラムを書いていると、 それらの高レベル層をバイパスして地下室に降りて作業しなければならないような時が あります。GaucheはPOSIX.1システムコールのほとんどを組み込みでサポートします。 また、ネットワーキングモジュールなど他のシステム関連モジュールは通常、 高レベルの抽象的なインタフェースと低レベルのシステムコールに近いインタフェースを 両方提供します。

強化された入出力

本物のアプリケーションはI/O無しでは成り立ちません。 SchemeはI/Oをポートとして簡潔に抽象化していますが、 標準のSchemeには最低限の操作しか定義されていません。 Gaucheはポートオブジェクトを入出力のための統合された抽象化オブジェクトと考え、 それを通して下位のI/Oシステム層にアクセスするユーティリティ関数を提供しています。 入出力を参照して下さい。

拡張された言語

Gaucheは単なるScheme実装というだけでなく、言語レベルでいくつかの拡張が施されています。 例えば、遅延シーケンスによって、あたかもそれが(遅延して実体化される点を除いて) 普通のリストであるかのように振るまう遅延データ構造を扱うことが可能になります。 あらゆるリスト処理を使用できるという意味で、遅延シーケンスは、 例えばストリーム(srfi-41)のようにライブラリレベルで 実装した遅延構造とは異なります。 これによって、遅延アルゴリズムをよりふんだんに使用したプログラミングが可能になります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.2 表記について


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.2.1 項目の形式

このマニュアルでは、各項目は次のようなフォーマットで表記されています。

カテゴリ: foo arg1 arg2

[spec] fooの説明

カテゴリ は項目fooの種別を示します。 次のようなカテゴリがあります。

FunctionScheme手続き
Special Form特殊形式 (R7RSでは「構文」)
Macroマクロ
Moduleモジュール
Classクラス
Generic Functionジェネリックファンクション
Methodメソッド
Reader Syntaxリーダによって解釈される構文要素
Parameterパラメータ。特定のプロトコルに従う手続きで、動的環境の操作に使われるもの。 詳しくはgauche.parameter - パラメータ参照。
Generic applicationGaucheでは、手続きでないオブジェクトをあたかの手続きのように 引数に「適用」することができます(詳しくは適用可能なオブジェクトを参照してください)。 このエントリは、オブジェクトが引数に適用された時のふるまいを説明します。

手続き、特殊形式、マクロに関しては、エントリの後に引数の仕様が示されます。 引数リストの中には次のような表記が現れることがあります。

arg …

ゼロ個以上の可変個の引数。

:optional x y z
:optional (x x-default) (y y-default) z

省略可能な引数の表記。この例では3つまでの省略可能な引数が取られることを示します。 2番目の形式ではxyについて省略された場合の既定値も示しています。 この形式の引数表記は、Schemeに対するGaucheの独自拡張です。 完全な仕様については手続きを作るを参照してください。

:key x y z
:key (x x-default) (y y-default) z

キーワード引数の表記。この例ではxyzの3つのキーワード引数を 取ることが示されます。2番目の形式ではxyについて省略された場合の 既定値も示しています。 この形式の引数表記も、Schemeに対するGaucheの独自拡張です。 完全な仕様については手続きを作るを参照してください。

:rest args

残りの引数全てがリストになってargsに束縛されることを示します。 この形式の引数表記も、Schemeに対するGaucheの独自拡張です。 完全な仕様については手続きを作るを参照してください。

説明が項目の行に続きます。もし項目が特定の標準に準拠している場合は、 もととなった標準が次のような形式で示されます。

[R7RS]
[R7RS+]
[R6RS]
[R6RS+]
[R5RS]
[R5RS+]

この項目はそれぞれR7RS, R6RS, R5RSで定義されるように動作します。 "[R7RS+]"のようにプラス記号がついている場合は、 R7RSの仕様に上位互換な形で機能が追加されていることを示します。

R7RSはほぼR5RSの上位互換であり、R6RSとも多くの機能を共有しているので、 R5RS、R6RSの表示はそれらがR7RSに含まれていない場合のみ記されます。

[SRFI-n]
[SRFI-n+]

この項目はSRFI-nに定義されるように動作します。"[SRFI-n+]"とマークされている場合は SRFI-nの仕様に上位互換な形で追加の機能が実装されていることを示します。

[POSIX]

この項目はPOSIXのAPIを反映していることを示します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

1.2.2 名前と名前空間

R6RSから、Schemeプログラムのトップレベル定義を複数の名前空間に分けて 書くことができるようになりました。Schemeの言語標準では名前空間を ライブラリと呼んでいます。GaucheはR6RSより前から名前空間を サポートし、モジュールと呼んできました。本マニュアルでは 従来どおりモジュールという呼称を使います。

(註: RnRSライブラリは、厳密に言えばGaucheのモジュールよりも抽象的な概念です。 RnRSでは処理系がライブラリを様々な方法で実装できるように規程しています。 Gaucheではたまたま、モジュールという仕組みを使ってRnRSライブラリのの概念が 実現されているということです。ポータブルなR7RSライブラリを書く時は、 Gauche特有のモジュールのセマンティクスに依存しないように気をつけてください。 特に、RnRSライブラリはGaucheのモジュールよりも静的です。 例えばRnRSの規格内では、既に存在するライブラリに定義を追加することはできません。)

時に、同じ名前に対して異なるモジュールで別々の定義が与えられることがあります。 それらを区別する必要がある時は、本マニュアルでは名前の前にモジュール名と ハッシュ記号をつけて表記します。例えば、gauche#lambdagaucheモジュールで定義されたlambdaを指します。 これはあくまで説明のための表記で、 ソースコードにgauche#lambdaと書けるわけではないことに 注意してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2. 主要な概念

本章では、Gauche動作の背景となるいくつかの概念を説明します。 Gaucheの動作を正確に理解する助けになると思います。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.1 標準への準拠

Gaucheは、次に説明する少しの例外を除き、オプショナルな構文や手続きも含め "Revised^7 Report of Algorithmic Language Scheme" (R7RS) に準拠しています。

GaucheはR7RS以前から開発されているため、既存のGaucheソースコードの多くはR7RSの プログラムやライブラリの構造に従っていないことに注意してください。 Gaucheは、伝統的なGaucheのモジュール形式やスクリプトも、R7RS形式のプログラムや ライブラリも、両方解釈することができます。 R7RSがどのようにGaucheに統合されているかについては、 ライブラリモジュール - R7RS統合を参照してください。

Gaucheはまた、以下のSRFIをサポートしています。

SRFI-0, 機能ベースの条件展開

組み込みです。機能条件式参照。

SRFI-1, リストライブラリ

モジュールsrfi-1でサポートされます。srfi-1 - リストライブラリ参照。 SRFI-1の手続きのうちいくつかは組み込みになっています。

SRFI-2, AND-LET*: 局所束縛をともなう AND、ガード付 LET* 特殊フォーム

組み込みです。変数束縛参照。

SRFI-4, 一様な数値ベクタ型

モジュールgauche.uvectorsrfi-4の上位互換手続きを提供します。 同モジュールにはSRFI-4の手続きに加え、 算術演算やジェネリックなインタフェースが定義されています。gauche.uvector - ユニフォームベクタ参照。

SRFI-5, シグネチャとrest引数に互換性のあるlet形式

モジュールsrfi-5でサポートされます。 srfi-5 - シグネチャとrest引数に互換性のあるlet形式参照。

SRFI-6, 基本文字列ポート

SRFI-6の手続きは組み込みになっています。文字列ポート参照。

SRFI-7, 機能ベースプログラム設定言語

オートロードされるマクロとしてサポートされています。 srfi-7 - 機能ベースプログラム設定言語参照。

SRFI-8, receive: 多値束縛

構文receiveは組み込みになっています。変数束縛参照。

SRFI-9, レコード型の定義

モジュールgauche.recordでサポートされます。gauche.record - レコード型参照。

SRFI-10, Sharp-comma外部フォーム

組み込みです。読み込み時コンストラクタ参照。

SRFI-11, 多値を受け取るための構文

モジュールsrfi-11でサポートされます。srfi-11 - Let-values参照。

SRFI-13, 文字列ライブラリ

モジュールsrfi-13でサポートされます。srfi-13 - 文字列ライブラリ参照。 (SRFI-13の手続きのいくつかは組み込みになっています。)

SRFI-14, 文字集合のライブラリ

文字集合と基本的なSRFI-14手続きは組み込みになっています。 文字集合参照。SRFI-14の完全なサポートはモジュールsrfi-14 で提供されています。srfi-14 - 文字集合ライブラリ参照。

SRFI-16, 可変長引数手続き構文 (case-lambda)

組み込みです。手続きを作る参照。

SRFI-17, 一般化された set!

組み込みです。代入参照。

SRFI-18, マルチスレッドのサポート

いくつかのSRFI-18の機能は組み込みであり、、残りのAPIは gauche.threadsモジュールで提供されます。gauche.threads - スレッド参照。

SRFI-19, 時間データの型と手続き

時間のデータ型はGauche組み込みです(時間参照)。 SRFI-19の完全なサポートはモジュールsrfi-19で提供されています。 srfi-19 - 時間のデータ型と手続き参照。

SRFI-22, UNIX 上の Scheme スクリプトの実行

サポートされています。Schemeスクリプトを書く参照。

SRFI-23, エラー報告機構

組み込みです。例外の通知参照。

SRFI-25, 多次元配列のプリミティブ

モジュールgauche.arrayが、SRFI-25の上位互換と なっています。gauche.array - 配列参照。

SRFI-26, カリー化をともなわないパラメータの特殊化記法

オートロードされるマクロとして定義されています。手続きを作る参照。

SRFI-27, ランダムビットのソース

モジュールsrfi-27でサポートされます。srfi-27 - ランダムビットのソース参照。

SRFI-28, 基本フォーマット文字列

Gauche組み込みのformatがSRFI-28のものの上位互換に なっています。出力参照。

SRFI-29, 地域化

モジュールsrfi-29でサポートされます。 srfi-29 - 地域化参照。

SRFI-30, ネストした複数行コメント

ネイティブのリーダでサポートされています。字句構造参照。

SRFI-31, 再帰評価用の特殊フォーム rec

オートロードされるマクロとして定義されています。変数束縛参照。

SRFI-34, プログラムの例外処理

組み込みです。例外参照。 (但し、Gaucheはraiseに関してはsrfi-18のセマンティクスを文字通り 実装していて、それはsrfi-34と若干異なります。将来はsrfi-34に合わせるかもしれません。)

SRFI-35, コンディション

組み込みです。コンディション参照。

SRFI-36, I/O コンディション

部分的にサポートされています. コンディション参照。

SRFI-37, args-fold: プログラム引数処理

モジュールsrfi-37でサポートされます。 srfi-37 - args-fold プログラム引数処理参照。

SRFI-38, 共有されるデータの外部表現

組み込みです。データの読み込み出力参照。

SRFI-39, パラメータオブジェクト

モジュールgauche.parameterでサポートされます。 gauche.parameter - パラメータ参照。

SRFI-40, ストリームライブラリ

モジュールutil.streamでサポートされています。 See section util.stream - ストリームライブラリ.

SRFI-42, 先行評価的内包表記

モジュールsrfi-42でサポートされます。 srfi-42 - 先行評価的内包表記参照。

SRFI-43, ベクタライブラリ

モジュールsrfi-43でサポートされます。 srfi-43 - ベクタライブラリ(旧式)参照。

SRFI-45, 反復的 Lazy アルゴリズムのための基本関数

組み込みです。 See section 遅延評価.

SRFI-46, 基本的なsyntax-rulesの拡張

組み込みです。 See section 衛生的マクロ.

SRFI-55, requireの拡張

オートロードマクロとしてサポートされます。 srfi-55 - require の拡張参照。

SRFI-60, 整数に対するビット操作

ほとんどの手続きは組み込みになっています。ビット演算参照。 完全なサポートはsrfi-60モジュールで提供されます。 srfi-60 - 整数に対するビット操作参照。

SRFI-61, より汎用的なcond

組み込みです。条件式参照。

SRFI-62, S式コメント

ネイティブのリーダでサポートされています。字句構造参照。

SRFI-69, 基本的なハッシュテーブル

モジュールsrfi-69でサポートされます (srfi-69 - 基本的なハッシュテーブル参照)。 Gaucheにも組み込みのハッシュテーブルサポートがありますが(ハッシュテーブル参照)、 いくつかの手続きにおいて異なる名前を使っています。

SRFI-78, 軽いテストフレームワーク

モジュールsrfi-78でサポートされます。 Gaucheは既に独自のテストフレームワークを持っているので(gauche.test - 単体テスト参照)、 このモジュールは主としてサードパーティ製のモジュールがsrfi-78をテストに 使っている場合をサポートするためのものです。

SRFI-87, case節での=>

組込みです。条件式参照。

SRFI-95, ソートとマージ

組み込みです。ソートとマージ参照。

SRFI-98, 環境変数にアクセスするためのインタフェース

モジュールsrfi-98でサポートされます。srfi-98 - 環境変数へのアクセス参照。

SRFI-99, ERR5RS レコード

モジュールgauche.recordでサポートされます。gauche.record - レコード型参照。

SRFI-106, 基本的なソケットインタフェース

モジュールsrfi-106でサポートされます。srfi-106 - 基本的なソケットインタフェース参照。

SRFI-111, ボックス

モジュールsrfi-111でサポートされます。srfi-111 - ボックス参照。

SRFI-112, 環境の問い合わせ

モジュールsrfi-112でサポートされます。 srfi-112 - 実行環境の問い合わせ参照。

SRFI-113, セットとバッグ

モジュールsrfi-113でサポートされます。 srfi-113 - setとbag参照。

SRFI-114, 比較器

いくつかの機能は組み込みです(基本的な比較器参照)。srfiの完全な仕様はモジュールsrfi-114でサポートされます(srfi-114 - 比較器参照)。

SRFI-117, リストを元にしたキュー

data.queue上に実装されたモジュールsrfi-117でサポートされます。 (srfi-117 - リストを元にしたキュー参照)

SRFI-118, 簡単な可変長文字列

モジュールsrfi-118でサポートされます。 (srfi-118 - 簡単な可変長文字列参照)

SRFI-121, ジェネレータ

Gaucheのgauche.generatorがsrfi-121のスーパーセットになっています。 (gauche.generator - ジェネレータ参照)

SRFI-128, 比較器 (縮小版)

組み込みです。基本的な比較器参照。

SRFI-131, ERR5RSレコード構文 (簡易版)

このsrfiはsrfi-99のサブセットで、gauche.recorddefine-record-typeでカバーされます。gauche.record - レコード型参照。

SRFI-133, ベクタライブラリ (R7RS互換)

モジュールsrfi-133でサポートされます。 srfi-133 - ベクタライブラリ参照。

SRFI-134, 変更不可な両端キュー

モジュールdata.idequeがsrfi-134と互換です。 data.ideque - 変更不可な両端キュー参照。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.2 マルチバイト文字列

従来、文字列は単なるバイトの配列として扱われてきました。 そのため一文字が複数バイトを占めるようになっても 文字列は単純な文字の配列であると考えられがちですが、 Gaucheにおいては、そうではありません。

Gaucheは内部的にマルチバイト文字列をサポートします。 すなわち、文字列中の文字が占めるバイト数は一定していません。 Schemeの文字列プリミティブのセマンティクスは保たれているので、 詳細を気にしないでもプログラムは書けますが、 下にあげるいくつかの点を知っておいたほうが良いでしょう。

文字列オブジェクトは型タグと文字列本体へのポインタを保持しています。 文字列本体は「copy-on-write」方式で管理されます。すなわち、substring や正規表現を使って部分文字列を切り出したり、あるいは単に文字列をコピーした場合、 文字列オブジェクトそのものは別につくられますが、文字列本体は共有されます。 文字列が破壊的に変更される場合にのみ、文字列本体がコピーされます。

したがって、make-stringである大きさの文字列をあらかじめアロケート しておき、string-set!で順に埋めて行くようなアルゴリズムは Gaucheでは非常に効率が悪くなります。そのようなアルゴリズムは使わない方が良いでしょう。 (そのようなアルゴリズムはマルチバイト文字列とも相性が良くありません)。 文字列を順に埋めて行く場合はstring portsを使うのが 効率の良い方法です (文字列ポート参照)。

string-scan (文字列を扱うその他の手続き参照) や正規表現 (正規表現参照) など文字列を検索するプリミティブは、 インデックスを介さずに一致した文字列を直接返すことができます。

Gaucheのコンパイル時に、文字の内部エンコーディングを選択することができます。 実行時に手続きgauche-character-encodingを使うか、 コンパイル時に機能識別子(プラットフォーム依存の機能参照)を使うことで、 よってどの内部エンコーディングでコンパイルされたかを知ることが出来ます。 今のところ、以下のエンコーディングがサポートされています。

utf-8

UnicodeのUTF-8エンコーディング。これがデフォルトです。 Gaucheがこの内部エンコーディングでコンパイルされている場合は 機能識別子gauche.ces.utf8が定義されます。

euc-jp

ASCII、JIS X 0201カナ、JIS X 0212及びJIS X 0213:2000文字集合のEUC-JP エンコーディング。 Gaucheがこの内部エンコーディングでコンパイルされている場合は 機能識別子gauche.ces.eucjpが定義されます。

sjis

JIS X 0201カナ及びJIS X 0213:2000文字集合のShift-JISエンコーディング。 ソースコードの互換性のため、文字コード0から0x7fの範囲はJIS X 0201 roman ではなくASCIIにマップされます。 Gaucheがこの内部エンコーディングでコンパイルされている場合は 機能識別子gauche.ces.sjisが定義されます。

none

8ビット固定長の文字エンコーディング。文字コード0から0x7fの範囲はASCIIと みなします。文字列をどのエンコーディングとして解釈するかはアプリケーション次第です。 Gaucheがこの内部エンコーディングでコンパイルされている場合は 機能識別子gauche.ces.noneが定義されます。

他の文字エンコーディングからの変換は特別なポートを使って行われます。 gauche.charconv - 文字コード変換 を参照して下さい。

ソースプログラムのエンコーディングを指定する方法については 次の章で説明します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.3 マルチバイトスクリプト

リテラル文字列や文字以外にも、コメント、シンボル名、 リテラル正規表現など様々な箇所で、us-ascii以外の文字を 使うことができます。

デフォルトでは、GaucheはSchemeプログラムをGaucheの内部文字エンコーディングで 書かれているものとして扱います。これは、自分で書いたスクリプトを自分の 環境で走らせるだけなら十分ですが、別の文字エンコーディングを使うように コンパイルされた環境でスクリプトを走らせたい場合に問題となります。

そこで、Gaucheは、次のようなコメントがプログラムソースコードの 2行目までに現れた場合、ソースコードの残りの部分が<encoding-name>で 指定されるエンコーディングで書かれているものとして、必要ならば 適切なエンコーディング変換を行います。

 
;; coding: <encoding-name>

より正確には、1行目または2行目のコメントで、 正規表現#/coding[:=]\s*([\w.-]+)/にマッチするものがあった場合に、 最初の部分マッチがエンコーディング名として認識されます。 複数のマッチがあった場合は最初のものが有効になります。 このメカニズムを利用するためには、最初の2行以内にus-ascii以外の文字を 含めないようにして下さい。

例えば次の例では、Gaucheはスクリプトがeuc-jpで書かれているものと 認識します。coding指定の周囲の"-*-"は、Emacsが バッファのエンコーディングを適切に設定するのに使われます。

 
#!/usr/bin/gosh
;; -*- coding: euc-jp -*-

... script written in euc-jp ...

内部では、この特殊なコメントの処理は特別なポートによって 行われています。詳細はコーディング認識ポートを参照して下さい。 また、この処理を行わないようにする方法については Schemeファイルのロードを参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.4 大文字小文字の区別

Lisp系の言語は歴史的にシンボルの大文字小文字を区別していません。 しかしSchemeはR6RSからその伝統より離脱し、シンボルを読む際に大文字小文字を区別 するようになりました。(但し、R5RSの範囲内でも処理系内部では 大文字小文字を区別することになってます。 ここで問題としているのは読み込み時の動作です。)

Gaucheの入出力ルーチンも、デフォルトではシンボルの大文字小文字を区別します。 ただし、古いコードとの互換性のために、リーダを大文字小文字を区別しないモードにすることが できます。

#!fold-caseリーダディレクティブを使う

Gaucheはプログラムを読み込む時に、トークン#!fold-caseを見ると大文字小文字を 区別しないモードに切り替わります。トークン#!no-fold-caseは その逆の効果、すなわち大文字小文字を区別するモードへと切り替えます。 これらのトークンは、それが読まれたポートからの以降の読み込みに影響を 与えます。切り替えるトークンが再び読まれるか、EOFに出会うまで影響は継続します。 #!構文についてより詳しくは字句構造を参照してください。 この動作はR6RS及びR7RSで規定されています。

-fcase-foldコマンドライン引数を使う

また、goshインタプリタの起動時に-fcase-foldコマンドライン引数を 与えると、Gaucheは大文字小文字を区別しないモードで動作します (Gaucheを起動する参照)。 このモードでは、リーダはシンボルの読み込みの際に大文字を小文字に変換します。 大文字を含んでいるシンボルに関しては、‘|’ 文字でエスケープされて書き出されます (シンボル参照)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.5 統合されたオブジェクトシステム

GaucheはCLOSに類似した、STklosスタイルのオブジェクトシステムを持っています。 あなたが何らかのオブジェクト指向(OO)言語を使ったことがあれば、 基本的な使い方は簡単にわかるでしょう。

 
;; x, y座標を持つpointクラスを定義
(define-class point ()
  ((x :init-value 0)
   (y :init-value 0))
  )

(define-method move ((p point) dx dy)
  (inc! (slot-ref p 'x) dx)
  (inc! (slot-ref p 'y) dy))

(define-method write-object ((p point) port)
  (format port "[point ~a ~a]"
          (slot-ref p 'x)
          (slot-ref p 'y)))

しかし、主流のオブジェクト指向言語に慣れてはいるがCLOSスタイルのオブジェクトシステムに 慣れていないプログラマは、Gaucheのオブジェクトシステムの詳細を見てゆくと 奇妙に感じることがあるのではないかと思います。 ここではGaucheのオブジェクトシステムの代表的な性質を簡単に述べておきます。 詳しくはオブジェクトシステムの章を参照して下さい。

全てはオブジェクトである (それが気になる人には)

このような主張は他のOO言語でも見たことがあるでしょう。 Gaucheもその例にもれず、実行時にクラスを得たりメソッドを呼び出したりといった 操作が任意のオブジェクトに対して出来るという意味で、全てはオブジェクトです。 また組込みクラスを含めたどんなクラスに対しても新しいメソッドを定義することができます。

しかし、CLOS系のパラダイムでは、全てがオブジェクトかどうかという議論はあまり 問題ではありません。というのは次のような性質があるからです。

メソッドは全ての引数によってディスパッチされる

C++、Objective C、Python、RubyなどのOO言語では、メソッドは特定のクラスに 所属しています。Gaucheではメソッドはクラスに従属しません。

例えば、数値だけを要素に持つベクタークラス<num-vector>と 行列クラス<num-matrix>を定義したとします。 プログラマは、以下のようなあらゆる場合についてメソッドproductを 別々に定義することができます。

 
  (product <num-vector> <num-matrix>)
  (product <num-matrix> <num-vector>)
  (product <num-vector> <num-vector>)
  (product <num-matrix> <num-matrix>)
  (product <number>     <num-vector>)
  (product <number>     <num-matrix>)
  (product <number>     <number>)

これらの各メソッドは、<num-vector>クラスや<num-matrix> クラスに所属するわけではありません。

メソッドがクラスに所有されているわけではないので、既に存在するクラスに対していつでも 独自のメソッドを定義することができます(但し、いくつかの組込みクラスの既定のメソッドには 変更できないものがあります)。上の例で既にこれは示されています。プログラマは メソッドproductを組込みクラス<number>に対して定義することが できます。これが、全てはオブジェクトであるかどうかはCLOSスタイルのオブジェクトシステム では問題にならないと言った理由です。

少し詳しいことを言えば、メソッドはジェネリック関数に属しており、 ジェネリック関数が適切なメソッドを選ぶ役割を果たします。

クラスはインスタンスでもある

デフォルトでは、クラスは<class>というクラスのインスタンスであり、 ジェネリック関数は<generic>というクラスのインスタンスです。 しかし、<class>を継承したクラスを定義することにより、 オブジェクトの初期化がどのように行われるかとか、スロットがどのように アクセスされるかといった動作をカスタマイズすることができます。 また、<generic>を継承したクラスを定義することにより、 適用可能なメソッドがどのように選択されて、どのような順序で適用されるか といったことがカスタマイズ可能です。このメカニズムはメタオブジェクトプロトコル と呼ばれています。メタオブジェクトプロトコルは、言語をその言語そのもので拡張する方法と 言えるでしょう。

例として、ディストリビューションに含まれるlib/gauche/mop/singleton.scmlib/gauche/mop/validator等があります。src/libobj.scmを 読めば、クラス自身がGaucheでどのように定義されているかがわかります。 メタオブジェクトプロトコルの更に詳しいことについては、 MOPが参考になります。

クラスは名前空間を作らない

メインストリームのOO言語ではクラスが名前空間を作ることがよくあります。 CLOSスタイルのオブジェクトシステムはそうではありません。 Gaucheでは、名前空間はオブジェクトシステムとは直交する、 モジュールシステムによって管理されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.6 モジュールシステム

Gaucheは大きなソフトウェアをモジュール化して開発するための、 単純なモジュールシステムを備えています。

高レベルのインタフェースを使うのはユーザにとっては非常に簡単です。 モジュールfooが提供する機能を使いたければ、 プログラム中で(use foo)と表記するだけです。 このフォームはマクロで、コンパイル時に解釈されます。 通常は、fooの機能を実装したファイルをロードし、 その外部APIを呼び出したモジュール中にインポートします。

useメカニズムは、名前空間の分離とファイルローディングという 二つの独立な低レベルのメカニズムの上に実装されています。 これらの低レベルメカニズムはそれぞれ別々に使うこともできます。

useメカニズムは非遷移的です。すなわち、 モジュールBがモジュールAを’use’し、さらにモジュールCがモジュールBを ’use’している場合、モジュールCからはモジュールA内の束縛は見えません。 BとAはis-a関係ではないためです。 例えばモジュールAが低レベルの操作を実装し、 モジュールBがその上に高レベルの抽象的なインタフェースを実装しているとしましょう。 モジュールCがモジュールBを’use’しているということは、 CはBの提供する抽象レイヤに興味があるということです。 もしCがさらに低レベルの層にもアクセスしたいならば、CはAを明示的に’use’しなければ なりません。

しかし、別の形の関係が必要な場合もあります。例えば既存のモジュールAに ちょっと新しいインタフェースを付け加えたモジュールBをAの拡張として 提供したいという場合です。この時、BとAはis-a関係であり、Bを’use’ しているモジュールからA内の束縛も見えるのが自然です。 Gaucheでは、これをモジュールの継承と呼び、 extendフォームにより実現しています。

以下のセクションでモジュールの機能について詳しく説明しています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

2.7 コンパイル

Gaucheは、Schemeフォームをひとつづつ読み込んでは評価するという意味では インタプリタです。しかし内部では、Gaucheはひとつひとつのフォームを中間形式に コンパイルして仮想マシンで実行しています。

組み込みの構文とマクロはコンパイル時に認識されて展開されます。 よく使われる組み込み関数は、コンパイル時にグローバルな束縛が置き換わっていない場合に、 インライン展開されます。

プログラマは通常、コンパイラの動作を気にする必要はほとんどありませんが、 いくつかの点に注意する必要があります。

loadは実行時に評価される

loadはGaucheでは一般の関数なので、実行時に評価されます。 ロードされるファイル中でマクロを定義している場合、そのマクロは そのloadの呼び出しを含むトップレベルフォームが評価された後で 有効になります。例えば、foo.scmがマクロfooを定義しているとして、 次のような用法を考えてみてください。

 
;; in ``foo.scm''
(define-syntax foo
  (syntax-rules () ((_ arg) (quote arg))))

;; in your program
(begin (load "foo") (foo (1 2 3)))
  ⇒ error, bad procedure: `1'

(load "foo")
(foo (1 2 3)) ⇒ '(1 2 3)

beginloadfooの呼び出しを囲んだ場合、 コンパイラはまずbegin全体をコンパイルします。その時点でマクロ fooは定義されていませんから、これは評価時にエラーになります。 一方、後者ではloadが評価されてからfooがコンパイルされるので、 問題は起きません。

このような混乱を避けるために、別のプログラムファイルを読み込む必要がある時は requireuseを使うことを勧めます。これらは構文であり、 コンパイラに認識されます。

require はコンパイル時に評価される

上記の裏返しですが、requireuseはコンパイル時に 解釈されます。したがって、ifなどの条件文のボディにこれらのフォームを 置いておいても、指定されたファイルは条件にかかわらず読み込まれてしまいます。 どうしてももし条件によって読み込むかどうかを変えたい場合は、loadを使うか、 条件判断自体をマクロで行うようにしてください。 (例えば、cond-expandフォーム等を使って。(機能条件式参照))


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3. Gaucheでのプログラミング


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.1 Gaucheを起動する

Gaucheは独立したSchemeインタプリタとしても、組み込みのSchemeライブラリとしても 使うことができます。Gaucheのディストリビューションには、goshという インタプリタが附属しています。

Program: gosh [options] [scheme-file arg …]

Gaucheのインタプリタです。 scheme-fileが与えられなければ、 goshはインタラクティブに動作します。すなわち、標準入力からScheme式を読み込み、 それを評価して結果をプリントするという動作を、EOFを読むか明示的に終了させられるまで 続けます。

もしscheme-fileが与えられておらず、しかし入力が端末でない場合、 goshはread-eval-printループに入りますが、入力待ちの際にプロンプトを 表示しません。これはScheme式をパイプでgoshに処理させる場合に便利です。 -bまたは-iオプションでこの動作を強制的にonまたはoffできます。

scheme-fileが与えられた場合、goshはそれをSchemeプログラムとして ロードし、終了します。この動作に関してはSchemeスクリプトを書くを参照して 下さい。

コマンドラインオプション

goshには以下のようなコマンドラインオプションがあります。 ‘-’ で始まらない最初のコマンドライン引数がスクリプトファイルと認識されます。 スクリプトファイル名が ‘-’ で始まっている可能性がある場合は、ダミーオプション ‘--’ をスクリプトファイル名の前に置いて下さい。

Command Option: -I path

pathをロードパスのリストの最初に加えます。このオプションは複数指定できます。

Command Option: -A path

pathをロードパスのリストの末尾に加えます。このオプションは複数指定できます。

Command Option: -q

goshが起動時にシステムの初期化ファイルをロードしないようにします。

Command Option: -V

goshのバージョンを表示して終了します。

Command Option: -u module

起動後、インタラクティブなread-eval-printループに入る前、もしくはscheme-file をロードする前にmoduleを“use”します。つまりそのモジュールがロードされ インポートされます。(useの詳細についてはモジュールの定義と選択を参照して下さい。) このオプションは複数指定できます。

Command Option: -l file

起動後、インタラクティブなread-eval-printループに入る前、もしくはscheme-file をロードする前にfileをロードします。ロードの詳細についてはSchemeファイルのロード を参照して下さい。このオプションは複数指定できます。

Command Option: -L file

-lオプションと同様にfileをロードしますが、 fileが見つからなかった場合はエラーを報告せずに黙って無視します。 このオプションも複数指定できます。

Command Option: -e scheme-expression

起動後、インタラクティブなread-eval-printループに入る前、もしくはscheme-file をロードする前にscheme-expressionを評価します。評価は interaction-environment中で行われます(eval と repl参照)。 このオプションは複数指定できます。

Command Option: -E scheme-expression

オプション-eとほぼ同じですが、scheme-expressionは それが括弧で囲まれているかのように読まれます。 このオプションは複数指定できます。例:

 
% gosh -umath.const -E"print (sin (* pi/180 15))" -Eexit
0.25881904510252074
Command Option: -b

バッチ。入力が端末であってもプロンプトを出さないようにします。

Command Option: -i

インタラクティブ。入力が端末で無くてもプロンプトを出すようにします。

Command Option: -m module

スクリプトファイルが与えられた場合に、main手続きを 探すモジュールを指定します。デフォルトではuserモジュールが探されます。 スクリプトの実行について詳しくはSchemeスクリプトを書くを見てください。

moduleで指定されたモジュールが、スクリプトファイルのロード後にも 存在しない場合は、エラーが報告されます。

このオプションは、スクリプトとしても使えるようなSchemeモジュールを書く際に便利です。

Command Option: -f compiler-option

このオプションはコンパイラとランタイムの動作に影響を与えます。 今のところ、次のオプションのみがcompiler-optionとして有効です。

no-inline

一切のインライン展開を行いません。このオプションは以下の no-inline-globals no-inline-locals および no-inline-constants を同時に指定したのと等価です。

no-inline-globals

大域(global)関数のインライン展開を展開を行ないません。

no-inline-locals

局所(local)関数のインライン展開を展開を行ないません。

no-inline-constants

定数のインライン展開を行ないません。

no-post-inline-pass

インライン展開後に再び最適化パスを走らせるのを抑止します。

no-lambda-lifting-pass

lambda lifting最適化パスを抑止します。

no-source-info

デバッグのためのソースファイル情報を保持しません。メモリの使用量は小さくなります。

load-verbose

ファイルがロードされる時にそれを報告します。 正確にどのファイルがどういう順序でロードされているかを調べるのに便利です。

include-verbose

ファイルがincludeされる時にそれを報告します。 正確にどのファイルがどういう順序でincludeされているかを調べるのに便利です。

warn-legacy-syntax

文字列リテラル中に古い形式の16進数エスケープ形式があったら警告します。 リーダー字句モード を参照して下さい。

case-fold

シンボルの大文字小文字を区別しません。 大文字小文字の区別 を参照して下さい。

test

"../src" と "../lib" を、初期化ファイルを読む前に ロードパスに加えます。これは、作成されたgoshをインストールせずに ソースツリーの中で実行してみるのに便利です。

Command Option: -p profiler-option

プロファイラを有効にします。以下のようなprofiler-optionが 今のところサポートされています。

time

関数中で費された時間と、各関数が呼ばれた回数を記録して報告します。

load

各モジュールをロードするのにかかった時間を記録して報告します。 スクリプトの起動時間をチューンするのに便利です (実経過時間が報告されます)。

詳しくはプロファイラを使うを参照して下さい。

Command Option: -r standard-revision

goshを、指定されたリビジョンのScheme標準環境で起動します。 現在のバージョンではstandard-revisionとして7のみがサポートされます。

デフォルトでは、gosh起動時のモジュールはuserモジュールで、 これはgaucheモジュールを継承しています。すなわち、特に何も指定しないでも Gaucheのコア関数は全て使えるようになっています。

正しいR7RSプログラムは常にdefine-libraryフォームか R7RS式のimportフォームで始まり、Gaucheはそれらを見つけると 自動的にR7RS環境へと切り替えるので、特になにもしなくても R7RSスクリプトやライブラリを使うことはできます。 しかし、R7RS Schemeを勉強中のユーザにとっては、最初に入る環境が R7RSでないと混乱してしまうかもしれません。

-r7オプションが与えられると、goshは起動時のモジュールを r7rs.userモジュールにします。これはr7rsモジュールを継承した もので、importdefine-libraryだけが定義されています。

-r7をつけたgoshを対話REPLとして起動した場合は、簡便のために (scheme r5rs)を除く全てのR7RS-smallライブラリが importされた状態になっています。

GaucheがR7RSをどのようにサポートしているかの詳細については ライブラリモジュール - R7RS統合を参照してください。

(註: -r7オプションは、リーダ字句モードをstrict-r7に 替えることはしません。strict-r7モードをデフォルトにすると、 多くのGaucheコードをロードすることができなくなるからです。リーダ字句モードについては リーダー字句モードを参照してください。)

Command Option: --

このオプションに出会うと、goshはオプションの解析を止めて、その次の引数を 無条件にscheme-fileであると見倣します。scheme-fileがマイナス記号で 始まっている場合に必要です。

オプション-I, -A, -l, -u, -e 及び -E は、それらがコマンドライン引数として 出現した順に処理されます。例えば、-Iにより追加されるロードパスは それ以降の-lや-uオプションに影響を与えますが、それ以前のものには影響を 与えません。

環境変数

以下の環境変数を認識します。

Environment variable: GAUCHE_LOAD_PATH

この環境変数によって、追加するロードパスを指定できます。 パスは ’:’ で区切ります。 この変数によって指定されたパスはシステムのデフォルトのロードパスの前に 連結されます。

GaucheがロードするSchemeファイルを見つける方法について詳しくは Schemeファイルのロードを参照してください。

Environment variable: GAUCHE_DYNLOAD_PATH

この変数によって、動的にロードするオブジェクト用の追加ロードパスを 指定できます。パスは ’:’ で区切ります。 この変数によって指定されたパスはシステムのデフォルトのロードパスの前に 連結されます。

Gaucheが動的にロードするオブジェクトファイルを探す方法については ダイナミックライブラリのロードを参照してください。

Environment variable: GAUCHE_AVAILABLE_PROCESSORS

sys-available-processors でシステムのプロセッサ数を 取得できます(環境の問い合わせ参照)。 ライブラリやプログラムの中には、その数に基づいて並行に走るスレッド数を 調整するものがあります。けれども、テストやベンチマークでその数を 変えたいと思うことがあるかもしれません。例えば、8コア上では自動的に 8スレッド使うプログラムがあったとして、並列化の効果を見るためにその プログラムを1,2,4スレッドで走らせてベンチマークを取る、といった場合です。 この環境変数は、sys-available-processorsが返す値を上書きします。

Environment variable: GAUCHE_KEYWORD_DISJOINT
Environment variable: GAUCHE_KEYWORD_IS_SYMBOL

この二つの環境変数は、キーワードがシンボルとして扱われるかどうかに影響します。 詳しくはキーワードを参照してください。

Environment variable: TMP
Environment variable: TMPDIR
Environment variable: TEMP
Environment variable: USERPROFILE

これらはsys-tmpdirの返り値に影響を与えます。プラットフォームによって 参照される環境変数は異なります。パス名を参照してください。

Environment variable: GAUCHE_SUPPRESS_WARNING

システムの警告(WARNING: ...)を抑止します。気軽に使うべきではありません。 どうしても必要な場合のみ使ってください。

Windows特有の実行ファイル

Windowsネイティブ環境(mingw)では、インタプリタとしてふたつの 実行ファイルがインストールされます。 gosh.exeはWindowsコンソールアプリケーションとしてコンパイルされ、 普通のgoshのように、標準入出力を第一の通信手段とします。 もう一つの実行ファイルgosh-noconsole.exeはWindows非コンソールアプリケーション としてコンパイルされています。こちらは起動時にコンソールに接続されません。 標準入力はNULデバイスに接続されます。標準出力と標準エラー出力は 特殊なポートに接続され、最初に書き込みがあった時点で新たなコンソールが作られて 出力されます。(このトリックはSchemeポート経由の出力のみで動きます。 低レベルのCライブラリが標準出力や標準エラー出力に直接書き出したデータは捨てられます)。

gosh-noconsole.exeの目的は、Windows上でのスクリプティングです。 Schemeスクリプトがもしgosh.exeに関連付けられていたとしたら、 Explorerからそのスクリプトを起動するたびに、必ず新しいコンソールウィンドウが 開くでしょう。そのコンソールはスクリプト終了時に消えてしまうので、 多くの場合、ユーザには何が出力されているかを見る機会がなく、 したがってほとんど役に立ちません。Schemeスクリプトをgosh-noconsole.exeに 関連づけておけば、この煩わしいコンソールの出現を抑制できます。

Windows版のオフィシャルのインストーラを使ってGaucheをインストールしたなら、 Schemeスクリプト(‘*.scm’)は既にgosh-noconsole.exeに 関連づけられているので、ExplorerからSchemeスクリプトをダブルクリックすれば Schemeプログラムを走らせることができます。 ‘C:\Program Files\Gauche\examples’の下にいくつかサンプルが あります


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.2 インタラクティブな開発

スクリプトファイルが与えられなかった場合、 goshはインタラクティブなread-eval-printループ(REPL)に入ります。

インタプリタを終了するには、EOF文字(Unix端末では通常Control-D)をタイプするか、 (exit)を評価します。

インタラクティブセッションでは、goshgauche.interactiveモジュールをロードします (gauche.interactive - インタラクティブセッション参照)。 このモジュールはまた、ユーザーのホームディレクトリに‘.gaucherc’という ファイルがあればそれをロードします。 インタラクティブデバッグに便利な設定をそこに書いておくことができます。 (Gauche release 0.7.3から、‘.gaucherc’はgoshがスクリプトモードで 起動された時は読まれなくなりました。)

.gauchercは常にuserモジュールへとロードされます (gosh-r7オプションつきで起動されていてもそうです)。 .gauchercを自動で読み込む、という機能自体がGauche特有の機能ですから、 そこでポータビリティを考慮する必要はないわけです。

goshをEmacs内部で走らせることをお勧めします。 EmacsはSchemeサブプロセスを操作するための豊富な機能を持っています。 次の行を‘.emacs’に加えておくと、<M-x run-scheme> で Emacsのバッファ内でgoshが走ります。

 
(setq scheme-program-name "gosh -i")

対話環境でマルチバイト文字を使う場合は、端末の文字エンコーディングをgoshの 内部エンコーディングと合わせるようにして下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.2.1 REPLでの開発

REPLに入ると、Gaucheはプロンプトを出してScheme式の入力を待ちます。

 
gosh>

完全なScheme式を入力してENTERをタイプすると、そのS式の評価結果が表示されます。

 
gosh> (+ 1 2)
3
gosh> 

REPLセッションは、過去3回分の評価結果をグローバル変数 *1*2*3 に束縛します。これらのヒストリ変数を 使って、以前の結果を後続の式の中で使えます。

 
gosh> *1
3
gosh> (+ *2 3)
6

Scheme式が複数の値を返した場合 (多値参照)は、各値が順に表示されます。

 
gosh> (min&max 1 -1 8 3)
-1
8
gosh> 

式が多値を返しても、変数*1*2*3 に束縛されるのは最初の値のみです。しかし別のグローバル変数 *1+*2+*3+に、全ての値をリストにしたものが 束縛されています。

 
gosh> *1
-1
gosh> *2+
(-1 8)

(上の例で、*1を評価した時点でヒストリがひとつずれてしまっていることに 注意してください。(min&max 1 -1 8 3)の結果を見るためには *2+を参照する必要があります。)

手続き*historyはヒストリ変数の値を表示します。

 
gosh> (*history)
*1: (-1 8)
*2: -1
*3: -1
gosh> 

特別な場合として、式の評価がゼロ個の値を返した場合は、ヒストリ変数は更新されません。 *history手続きはゼロ個の値を返すので、ヒストリを見るだけでヒストリが 進んでしまうということはありません。

 
gosh> (*history)
*1: (-1 8)
*2: -1
*3: -1
gosh> (values)
gosh> (*history)
*1: (-1 8)
*2: -1
*3: -1

最後に、評価途中で捕捉されないエラーが発生した場合は、エラーコンディションオブジェクトが グローバル変数*eに束縛されます。

 
gosh> (filter odd? '(1 2 x 4 5))
*** ERROR: integer required, but got x
Stack Trace:
_______________________________________
  0  (eval expr env)
        At line 173 of "/usr/share/gauche-0.9/0.9.3.3/lib/gauche/interactive.scm"
gosh> *e
#<error "integer required, but got x">

(エラースタックトレースの表示はインストールの状況によって異なる場合があります。)

REPLプロンプトではまた、よくある仕事のために、特別なトップレベルコマンドを 入力することもできます。トップレベルコマンドはScheme式ではありませんし、S式でさえ ありません。むしろ、伝統的な行指向のシェルコマンドのように動作します。

トップレベルコマンドは通常のScheme式と区別するために、コンマで始まります。 どういったコマンドが使えるかを見るには、,helpとタイプしてリターンを 入力してみてください。

 
gosh> ,help
You're in REPL (read-eval-print-loop) of Gauche shell.
Type a Scheme expression to evaluate.
A word preceeded with comma has special meaning.  Type ,help <cmd> 
to see the detailed help for <cmd>.
Commands can be abbreviated as far as it is not ambiguous.

 ,a|apropos  Show the names of global bindings that match the regexp.
 ,cd         Change the current directory.
 ,doc|info   Show info document for an entry of NAME.
 ,d|describe Describe the object.
 ,history    Show REPL history.
 ,h|help     Show the help message of the command.
 ,pwd        Print working directory.
 ,source     Show source code of the procedure if it's available.

それぞれのコマンド特有のヘルプを見るには、コンマを含まないコマンド名を helpコマンドに与えてください。

 
gosh> ,help d
Usage: d|describe [object]
Describe the object.
Without arguments, describe the last REPL result.

,d (あるいは,describe)トップレベルコマンドは与えられたSchemeオブジェクト、 または何もオブジェクトが与えられなければ直前の結果のオブジェクトについて、 その説明を表示します。ちょっと試してみましょう。

 
gosh> (sys-stat "/home")
#<<sys-stat> 0x2d6adc0>
gosh> ,d
#<<sys-stat> 0x2d6adc0> is an instance of class <sys-stat>
slots:
  type      : directory
  perm      : 493
  mode      : 16877
  ino       : 2
  dev       : 2081
  rdev      : 0
  nlink     : 9
  uid       : 0
  gid       : 0
  size      : 208
  atime     : 1459468837
  mtime     : 1401239524
  ctime     : 1401239524

上の例では、まず(sys-stat "/home")を評価して、結果として <sys-stat>オブジェクトが返ってきました。続く,dコマンドによって その<sys-stat>オブジェクトの詳細が表示されています。

表示される情報はオブジェクトの型に依存します。型によっては、追加の情報が 表示される場合もあります。例えば正確な整数をdescribeすると、 いくつかの異なる解釈が示されます。

 
gosh> ,d 1401239524
1401239524 is an instance of class <integer>
  (#x538537e4, ~ 1.3Gi, 2014-05-28T01:12:04Z as unix-time)
gosh> ,d 48
48 is an instance of class <integer>
  (#x30, #\0 as char, 1970-01-01T00:00:48Z as unix-time)

シンボルをdescribeすると、分かっている束縛が示されます。

 
gosh> ,d 'filter
filter is an instance of class <symbol>
Known bindings for variable filter:
  In module `gauche':
    #<closure (filter pred lis)>
  In module `gauche.collection':
    #<generic filter (2)>

手続きをdescribeした場合、もし分かっていればそのソースコード上の 場所も表示されます(Defined at ...の行):

 
gosh> ,d string-interpolate
#<closure (string-interpolate str :optional (legacy? #f))> is an
instance of class <procedure>
Defined at "../lib/gauche/interpolate.scm":64
slots:
  required  : 1
  optional  : #t
  optcount  : 1
  locked    : #f
  currying  : #f
  constant  : #f
  info      : (string-interpolate str :optional (legacy? #f))
  setter    : #f

他のトップレベルコマンドも見てみましょう。,infoコマンドは 手続き、変数、構文、モジュールもしくはクラス名が与えられると、そのドキュメントを 表示します。(テキストはシステムにインストールされたGaucheのinfoドキュメントから 検索されます。もしエラーが出た場合は、infoドキュメントが正しくインストール されているかどうか確認してください。)

 
gosh> ,info append
 -- Function: append list ...
     [R7RS] Returns a list consisting of the elements of the first LIST
     followed by the elements of the other lists.  The resulting list is
     always newly allocated, except that it shares structure with the
     last list argument.  The last argument may actually be any object;
     an improper list results if the last argument is not a proper list.

gosh> ,info srfi-19
 -- Module: srfi-19
     This SRFI defines various representations of time and date, and
     conversion methods among them.

     On Gauche, time object is supported natively by '<time>' class
     (*note Time::).  Date object is supported by '<date>' class
     described below.

gosh> ,info <list>
 -- Builtin Class: <list>
     An abstract class represents lists.  A parent class of '<null>' and
     '<pair>'.  Inherits '<sequence>'.

     Note that a circular list is also an instance of the '<list>'
     class, while 'list?' returns false on the circular lists and dotted
     lists.
          (use srfi-1)
          (list? (circular-list 1 2)) => #f
          (is-a? (circular-list 1 2) <list>) => #t

,a (または,apropos) は、与えられた名前や正規表現に マッチするグローバルな識別子を表示します。

 
gosh> ,a filter
filter                         (gauche)
filter!                        (gauche)
filter$                        (gauche)
filter-map                     (gauche)

註: aproposコマンドは現在のプロセスにロードされている名前のみから 検索します。一方、infoコマンドは現在のプロセスにロードされているかどうか とは関係なく、infoドキュメントから検索します。

註: gosh-qオプション (初期化ファイルをロードしない) で 起動した場合もREPLに入りますが、そこではヒストリ変数などは使えません。 REPLの便利機能はgauche.interactiveモジュールで実装されていますが、 -qオプションをつけるとgauche.interactiveがロードされないからです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.3 Schemeスクリプトを書く

goshのコマンドラインにSchemeプログラムのファイル名が渡された場合、 goshuserモジュールをカレントモジュールとし、 それ以降のコマンドライン引数のリストをグローバル変数*argv*に束縛して、 Schemeプログラムをロードします。もしscheme-fileの最初の行が“#!”で始まって いたら、その行は無視されます。これにより、Unix系のシステムで実行可能なSchemeスクリプト を書くことが出来ます。

典型的なGaucheスクリプトの最初の行は次のようなものです。

 
#!/usr/local/bin/gosh
  または,
#!/usr/bin/env gosh
  または,
#!/bin/sh
:; exec gosh -- $0 "$@"

後の2つは「シェルトランポリン」テクニックを用いて、goshがPATHにあるディレクトリの どこかにあれば起動できるようにしています。3番目の方法は、 goshにいくつかコマンドラインオプションを渡したい時に便利です。

ファイルが正常にロードされたら、goshは userモジュールに ‘main’ という手続きが定義されているかどうか調べ、 定義されていればそれを呼びます。mainには、スクリプトへの引数のリストが 唯一の引数として渡されます。リストの最初の要素はスクリプトファイル名です。

mainが整数の値を返したら、goshはその値を終了ステータスとして終了します。 mainが整数以外の値を返した場合はgoshは終了ステータス70 (EX_SOFTWARE)で終了します。このふるまいはSRFI-22と互換です。

mainが定義されていなければgoshはロード後にそのままステータス0で 終了します。

シェルスクリプトやPerlスクリプトと同じように、スクリプトのボディに直接 実行される式を書くこともできますが、なるべく ‘main’ を使った方法を 使うことをお薦めします。そうすると、スクリプトをインタプリタにインタラクティブに ロードしてデバッグすることもできます。

-mコマンドラインオプションを使えば、userモジュール以外の モジュールで定義されたmain手続きをスクリプトのメイン関数として 呼ぶことができます。Schemeモジュールを、Schemeスクリプトとしても使えるように したい場合に便利です。

例えば、fooというSchemeモジュールを書いて、その中で main関数を定義しておきます。このmain関数はexportしないでおきます。 このファイルがモジュールとしてロードされた場合、このmain関数は 外からは見えないので何もしません。しかし、gosh-m fooオプションを 与えて、このファイルをスクリプトファイルとして指定すれば、ファイルをロードした後に main手続きが呼ばれます。その中には、 テストだとかモジュールのサンプルアプリケーションを書いておくことができるでしょう。

コマンドライン引数を受け取る標準的な方法は、main関数の引数としてですが、 他にもコマンドライン引数にアクセスする方法が提供されています。詳しくは コマンドライン引数を参照してください。

ではいくつか簡単な例を示しましょう。最初の例はUnixのcat(1)コマンドを模するものです。 エラー処理やコマンドラインオプションの処理は行っていません。

 
#!/usr/bin/env gosh

(define (main args)   ;entry point
  (if (null? (cdr args))
      (copy-port (current-input-port) (current-output-port))
      (for-each (lambda (file)
                  (call-with-input-file file
                    (lambda (in)
                      (copy-port in (current-output-port)))))
                (cdr args)))
  0)

次のスクリプトは簡単なgrepコマンドです。

 
#!/usr/bin/env gosh

(define (usage program-name)
  (format (current-error-port)
          "Usage: ~a regexp file ...\n" program-name)
  (exit 2))

(define (grep rx port)
  (with-input-from-port port
    (lambda ()
      (port-for-each
       (lambda (line)
         (when (rxmatch rx line)
           (format #t "~a:~a: ~a\n"
                   (port-name port)
                   (- (port-current-line port) 1)
                   line)))
       read-line))))

(define (main args)
  (if (null? (cdr args))
      (usage (car args))
      (let ((rx (string->regexp (cadr args))))
        (if (null? (cddr args))
            (grep rx (current-input-port))
            (for-each (lambda (f)
                        (call-with-input-file f
                          (lambda (p) (grep rx p))))
                      (cddr args)))))
  0)

また、gauche.parseopt - コマンドライン引数の解析を使うと手軽にコマンドラインオプション を処理することができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.4 デバッグ

Gaucheにはまだデバッグをサポートする機能があまり実装されていません。 デバッギングのインタフェースに関して良いアイディアがあればお寄せください。

今のところ、作者は必要な時は古典的な「プリントスタブ」方式を使っています。 Gaucheのリーダには、中間の結果を出力するために、 #?で始まるいくつかの構文が用意されています。

構文#?=exprは、exprを評価する前にまずその式自体を 表示し、評価後にその結果(複数の場合もあります)を表示します。

 
gosh> #?=(+ 2 3)
#?="(stdin)":1:(+ 2 3)
#?-    5
5
gosh> #?=(begin (print "foo") (values 'a 'b 'c))
#?="(stdin)":2:(begin (print "foo") (values 'a 'b 'c))
foo
#?-    a
#?+    b
#?+    c
a
b
c

註: デバッグスタブが原始スレッド(gauche.threads - スレッド参照)以外のスレッドで評価された場合、 スタブの出力には、どのスレッドから出力されたかを示す番号がつけられます。 次の例では、#<thread ...>およびプロンプトが原始スレッドのREPLの 出力ですが、続く#?=[1]...#?-[1]...make-threadにより作られたスレッドからのデバッグ出力です。 この番号はデバッグ時にスレッドを区別するためだけのもので、スレッド毎に異なりますが、 それ以外の意味はありません。

 
gosh> (use gauche.threads)
gosh> (thread-start! (make-thread (^[] #?=(+ 2 3))))
#<thread #f (1) runnable 0xf51400>
gosh> #?=[1]"(standard input)":1:(+ 2 3)
#?-[1]    5

構文#?,(proc arg …)は特に手続き呼び出しのための ものです。procを呼ぶ直前に引数の値を表示し、procから返ってきたら 戻り値(複数の場合もあります)を表示します。

 
gosh> (define (fact n)
        (if (zero? n)
            1
            (* n #?,(fact (- n 1)))))
fact
#?,"(standard input)":4:calling `fact' with args:
#?,> 4
#?,"(standard input)":4:calling `fact' with args:
#?,> 3
#?,"(standard input)":4:calling `fact' with args:
#?,> 2
#?,"(standard input)":4:calling `fact' with args:
#?,> 1
#?,"(standard input)":4:calling `fact' with args:
#?,> 0
#?-    1
#?-    1
#?-    2
#?-    6
#?-    24
120

内部的には、構文#?=x#?,xはそれぞれ単に (debug-print x)および(debuf-funcall x)と読まれ、 実際の表示についてはマクロdebug-printdebug-funcallが 処理します。詳しくはデバッグ補助を参照してください。

特別な構文を用意した理由は、(1)目的の式にデバッグスタブを付加するのに、 式全体を余分な括弧でくくらなくて良いのですぐできる (2)デバッグスタブをエディタで探したり取り除いたりするのが極めて簡単、というものです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.5 プラットフォーム依存の機能

GaucheではOSが提供するAPIに近い低レベルAPIを提供するようにしています。 しかし、システムごとに扱いの違うものがあります。たとえば、POSIXでは symlinkは必須ではありませんので、システムによっては sys-symlink (ディレクトリ操作参照)がありません。 UNIX系のシステム関数は少なからずWindowsでは使えません。

プラットフォーム間でポータブルなプログラムを書くために、Gaucheでは 頻繁にcond-expand を使います(機能条件式参照)。 拡張された機能識別子(feature-identifier)が提供されており、これ を使って特定の機能が利用可能かどうかチェックできます。たとえば、 symlinkがあるシステムでは機能識別子gauche.sys.symlinkが 定義されます。したがって、以下のようにsys-symlinkが利用できるか どうかによって、コードをスイッチするようなプログラムを書けます。

 
(cond-expand
 (gauche.sys.symlink
   ... code that uses sys-symlink ...)
 (else
   ... alternative code ...)
 )

Cのシステムプログラミングに詳しいなら、上のコードは以下のCのイディオム と同じだとみなせます。

 
#if defined(HAVE_SYMLINK)
... code that uses symlink ...
#else
... alternative code ...
#endif

このような機能識別子はたくさんあり、それぞれの識別子についてはこのマニュ アル中のその機能に依存した手続きの項目で説明しています。特に重要なもの を以下にリストアップしておきます。

gauche

この機能識別子は常に定義されています。Gauche以外のSchemeの実装とも互換 性のあるコードを書くときに使えます。

gauche.os.windows

Windowsネイティブプラットフォームで定義されます。cygwinでは この機能識別子は定義されません (下記も参照)。

gauche.os.cygwin

Cygwin上で定義されます。

gauche.sys.threads

Gaucheがスレッドをサポートするようにコンパイルされている場合に定義されます。 詳細はgauche.threads - スレッドを参照してください。

gauche.sys.pthreads
gauche.sys.wthreads

スレッドがサポートされている時に、下位の実装を示すために定義されます。 詳細はgauche.threads - スレッドを参照してください。

gauche.net.ipv6

GaucheがIPv6をサポートするようにコンパイルされている場合に定義されます。

gauche.ces.utf8
gauche.ces.eucjp
gauche.ces.sjis
gauche.ces.none

これらの機能識別子のうちのどれかひとつが、Gaucheの内部文字エンコーディングに応じて 定義されます。内部エンコーディングについて詳しくは マルチバイト文字列を参照してください。

cond-expandはマクロなので、cond-expand自身がトップレベル にあれば、節の本体はトップレベルで展開されます。これはトップレベルの定 義をスイッチできるということです。

 
(cond-expand
 (gauche.os.windows
  (define (get-current-user)
    ... get current username ...))
 (else
  (define (get-current-user)
    (sys-uid->user-name (sys-getuid)))))

あるいは条件によってモジュールを使いわけられます。

 
(cond-expand
 (gauche.os.windows
   (use "my-windows-compatibility-module"))
 (else))

トップレベルの束縛をチェックするような旧いテクニック (global-variable-bound?を使う, モジュールイントロスペクション参照) は実行時に効果を持つので、コンパイル時に解釈されるuse を切り替えるのはうまくいきません。 可能なかぎり、cond-expandを使うことを推奨します。

現時点では機能識別子集合はGaucheのコンパイル時に固定されます。つまり、 Cのプリプロセッサを使った条件分岐よりも柔軟性に劣ります。この機能を拡 張してあとから機能識別子を追加できるようにする計画ですが、この機能拡張 はコンパイルと実行が細切れに入れ替わる場合のセマンティクスを複雑にしてしまう可 能性があり、現在その影響を慎重にみきわめている最中です。

いくつか注意事項があります。

機能識別子は変数ではありません。cond-expandfeature-requirement部の内部でのみ利用可能です(機能要求の定義全般 については機能条件式を見てください)。

srfi-0の定義により、機能要求が満されず、かつelse節がない 場合にはcond-expandはエラーをあげます。常にelseを書いて おくのがよいでしょう。 上の例のように、else節でなにもしないとき でも、空のelse節を書くのがよいでしょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.6 プロファイリングとチューニング

自分のスクリプトのスピードが十分に出ないというときには、 性能を改善するポイントとして考えられる点がいくつかあります。

どんなときでも、実行時間を食いつぶしているコード部分を見つけるというの を先ず最初にやるのがよいでしょう。Gauche にはこの作業を補助する基本的 なツールが2つあります。組込のサンプリングプロファイラ(これについては次 の節で説明します)を使えば各手続きでどれほどの時間がかかり、その手続き が何回呼ばれたかを表示できます。gauche.time モジュール (gauche.time - 時間の計測) ではコードの中の特定の部分の実行にかかる時間を 測定するためのAPIが提供されています。

最適化というのは特殊化ということでもあります。もっともよく使われる 実行のパターンを探して、そこを効率よく実行する専用のパスを設けることで す。Gauche 自身も例外ではありません。したがって、Gaucheが効率よく実行 できるパターンがいくつかありますし、また一方では効率よく実行できないパ ターンもあります。次の節では、パフォーマンスに関するヒント では Gauche が効 率良く実行できるパターンにコードをあわせるチョットしたコツを教えましょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.6.1 プロファイラを使う

0.8.4 から Gauche は組込みのプロファイラを備えています。これは 現時点ではまだ実験的なもので、Linux 上でしかテストしていません。すべてのプ ラットフォームで利用できるわけではありませんし、シングルスレッドの アプリケーションでしか動きません。

非対話環境でこのプロファイラを使うには、gosh のコマンドラインオプショ ンとして -ptimeを指定してください。

 
% gosh -ptime your-script.scm

your-script.scm’ の実行完了後、Gauche は各関数についてその呼び出し回数 および消費時間を示した表を印字します。この表は総消費時間の順でソートさ れています。

 
Profiler statistics (total 1457 samples, 14.57 seconds)
                                                    num    time/    total
Name                                                calls  call(ms) samples
---------------------------------------------------+------+-------+-----------
combinations*                                       237351  0.0142   337( 23%)
(lset-difference #f)                               1281837  0.0020   256( 17%)
(make-anchor make-anchor)                          3950793  0.0005   198( 13%)
member                                             4627246  0.0004   190( 13%)
filter                                              273238  0.0030    81(  5%)
every                                              1315131  0.0004    59(  4%)
(lset-difference #f #f)                            1281837  0.0004    54(  3%)
(make-entry make-entry)                             730916  0.0005    40(  2%)
(clear? #f)                                         730884  0.0005    33(  2%)
(initialize #f)                                     599292  0.0005    32(  2%)
fold                                                237307  0.0013    30(  2%)
acons                                               806406  0.0004    29(  1%)
clear?                                               33294  0.0084    28(  1%)
(combinations* #f)                                  805504  0.0002    15(  1%)
(make-exit make-exit)                               730884  0.0002    15(  1%)
lset-difference                                     237318  0.0006    15(  1%)
reverse!                                            475900  0.0001     6(  0%)
(fold <top> <top> <list>)                           237323  0.0003     6(  0%)
procedure?                                          238723  0.0002     4(  0%)
pair?                                               237307  0.0001     3(  0%)
 :
 :

時間プロファイラは統計的標本化をおこなっていることに注意してください。 プロファイラは10ミリ秒ごとにプロセスに割込んで、その時点で実行されてい る関数を記録します。ナノ秒オーダの関数呼出しごとの個別の実行時間に比べる と、このサンプリングレートはかなり粗いものです。しかしながら、プロ グラムの実行時間が長ければ、各関数ごとの標本分布は関数ごとの消費時間を ほぼ反映しているだろうと期待できます。

数字はあくまで近似にすぎないこと を心にとめておいてください。ひとつの関数あたりの標本数はプログラムが扱 うデータが違えば、すぐに変化してしまうことがあります。 また、今のところGCにかかる時間はGCがトリガされた関数の実行時間に 算入されてしまっていることに注意して下さい。これによって、あまり 重要でない関数がリストの上位に浮かびあがってくることがあります。 一般的なパターンを知るには、プログラムをいろいろなデータで走らせて みると良いでしょう。

一方、関数呼び出し回数のカウントは正確なものです。これは Gauche は 実際の呼出しごとにカウントしているからです。

Schemeでは基本的にすべての関数は無名なので、プロファイル結果の’name’ フィールドはヒントにすぎません。トップレベルで束縛されている関数につい ては通常それが最初に束縛されたグローバル変数名が印字されます。内部関数 については関数の入れ子構造を反映して名前のリストが印字されます。 メソッドは、名前と特定化子のリストとして印字されます。

プロファイラはそれ自身にオーバヘッドがあります。通常は、処理時間が 20-30% 増加します。プロファイラを選択的にオンにしたい場合や、 停止しないサーバプログラムを走らせていて、そのサーバを停止することなく、 統計を取りたいような場合には、プログラムからプロファイラ APIを呼ぶこと ができます。詳細については プロファイラAPI を参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.6.2 パフォーマンスに関するヒント

「論より run 」これがパフォーマンスチューニングの第一法則です。 Scheme のような高級言語では、何がパフォーマンスに強い影響を 与えるかはことのほかその実装に大きく依存し、ある処理系では とても安価な操作が別の処理系ではとても高価になり得ます。 Gauche にもそのようなパフォーマンスに関する実装特有の特徴があり、 それらのうちのいくつかを知っておくことは、ベンチマークの結果の どこに着目すべきかを知るうえで助けになるでしょう。

「ソースコードの2割が実行時間の8割を消費する」というのも古くから 言われています。実際の実行時間に大した影響を及ぼさないところを 下手にいじくってプログラムをわかりづらくすることは避けましょう。 これからいくつかのヒントを述べますが、これらのことを四六時中気にして プログラミングしなければならないということではありません。 むしろ、出来るだけ明瞭でわかりやすいプログラムを心がけ、ループの一番深いところ (もっとも時間を消費するところ)でこれらのトリックを使うのが良いでしょう。

Ports: SRFI-18 (スレッド) の仕様を満たすために Gauche のすべての入出力基本関数はポートをロックします。 このオーバーヘッドは小単位(例えばバイト毎)の入出力を行なう アプリケーションでは無視できないでしょう。 入出力基本関数は通常呼びだし毎にポートをロックし、そこからの 下位レベルの入出力はロックのオーバーヘッドの影響を受けずに 行なわれます。ですから readread-uvector などのより大きな単位で入出力を行なう基本関数では問題と なることが少なくなります。 (注意:これらの基本関数が常にポートをロックしつづけることを 保証するものではないことに注意してください。また、ポートのロックは 競合がほとんど発生しない場合に最適化されています。 ポートへのアクセスが複数のスレッドで競合する可能性がある場合は、 アプリケーション側でmutexを明示的に用いて競合を避けてください。)

ポートロックが実際に問題となった場合、二つばかり対処策が考えられます。 (1) より大きな単位で入出力を行なう。(2) with-port-locking (ポートとスレッド 参照)を使ってより広範囲でポートをロックする。

文字列: 多バイト文字列の取扱いのため、Gauche では文字列の 変更とインデックスによるアクセスが特に高価な操作となります。 これは意図的な設計です。 Gauche ではこの二つの操作を避けたプログラミングを 推奨しています。 文字列の中の文字を順にアクセスするには (インデックスを使わずに)文字列ポート(文字列ポート参照)を使うと より明瞭かつ効率的なプログラムとなり、一方サーチして部分文字列をとり出す といった操作には、多彩な高レベル関数が用意されています。 (例えば 文字列を扱うその他の手続き正規表現srfi-13 - 文字列ライブラリ 等を参照。) バイト列を表現するのに 文字列を使っていたなら、代わりにユニフォームベクタ(gauche.uvector - ユニフォームベクタ 参照)を使いましょう。

深い再帰: Gauche の仮想機械(VM)は効率的な ローカルフレーム割り当てのためにスタックを使っています。 再帰が深くなって(プログラムにもよりますが、大体数百回から千回) スタックがオーバーフローするとスタックの内容をヒープに退避するという オーバーヘッドが生じます。 あるデータ量を越えたところでパフォーマンスの 低下が見られたならば、深い再帰がないか調べてみて下さい。

Generic functions: Generic function の持つ動的な性質の ため、これらの呼び出しは通常の手続き呼び出しより遅くなります。 実行時のディスパッチのオーバヘッドだけでなく、 VM コードへのコンパイル時にたいして最適化が行えないためです。 パフォーマンスのために Generic function の利用をどんな場合にも避ける という必要はありませんが、もしある一つの関数が実行時間の大部分を 占めていて、その関数が Generic function を内部で呼び出しているなら それを使わないように変更してみる価値はあるでしょう。

組込み関数の再定義: Gauche のコンパイラはいくつかの組込み関数を(それらが再定義されていなければ) インライン展開します。 基本関数を再定義するのは時には便利ですが、 限られた範囲にとどめておいた方がよいでしょう。 やり方は、再定義をどこか別のモジュールに集めておき、どうしても再定義 バージョンが必要なときに限ってそのモジュール use するというように しておけばよいでしょう。

クロージャの作成: クロージャを作成するとそれが持つ環境がヒープに コピーされます。 オーバーヘッドは小さいですが、何百万回も呼ばれるような ループの中で作成されれば無視できなくなるでしょう。そんな疑いがあれば その関数を逆アセンブルしてみましょう。 Gauche のコンパイラはクロージャの 簡単な解析を行ない生成をなるべく避けるようになっています。そのような場合 局所関数の本体はインライン展開されています。逆アセンブルのコードに CLOSURE命令が含まれていれば、残念ながらクロージャが生成されます。

これらのヒント集は完全でないし、Gauche の改良とともに変わっていくでしょう。 ですから、固定された特徴だとは思わないで下さい。 このヒント集は今後、折を見て実装に対応させて更新してゆきます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.7 Gaucheのモジュールを書く

Gaucheのライブラリはモジュール毎に整理されています。Gaucheはどんな形式でも、 有効なScheme式が書かれてさえいればロードすることができますが、 Gaucheのライブラリは一定の形式を保って書かれています。 Gauche用にまとまったSchemeコードを書いたら、それをモジュール形式にしておくと 再利用がしやすくなるでしょう。

通常ひとつのファイルで ひとつのモジュールを定義しますが、モジュールを複数のファイルに分けることもできます。 まずひとつのファイルでモジュールを定義する方法を説明します。 Gaucheのライブラリで使われているテンプレートは次のような形式です。

 
;; モジュールのインタフェースの定義
(define-module foo
  (use xxx)
  (use yyy)
  (export foo1 foo2 foo3)
  )
;; モジュール本体
(select-module foo)

… module body …

このファイルは“foo.scm”という名で、*load-path*にあるディレクトリの いずれかに置かれなければなりません。

まず、define-moduleフォームがモジュールfooを作成します。 define-moduleフォーム内で、このモジュールが依存している他のモジュール を ‘use’ マクロを使ってロードし、このモジュールがエクスポートするシンボルを ‘export’ 構文を使って指定します。(これらの構文の詳細はモジュールの定義と選択を 参照して下さい)。

これらのuseフォームやexportフォームは必ずしもdefine-module フォームの先頭に置く必要はありませんが、このようにファイルの最初の方に固めておくことで、 fooが依存しているモジュール群やfooが提供するシンボル等が 一覧しやすくなります。

次の、‘select-module’ フォームにより、それ以降のScheme式が モジュールfooの中で評価されます。モジュールの内容を全てdefine-moduleの中に 書いてしまうことも出来ますが、最後の括弧を閉じ忘れやすいことや、エディタのインデントが 狂うことなどから、あまりお薦めしません。

select-moduleフォームの後には、モジュールの内容であるScheme式を自由に書けます。 それらはモジュールfooの中で評価されます。モジュール内で定義するトップレベルの 束縛のうち、明示的に‘export’で指定されたシンボルのみが、このモジュールをインポートする他の モジュールから見えます。

これで、他のプログラムは ‘(use foo)’ とするだけでこのモジュールの機能が 利用可能になります。もしモジュールをサイト全体で利用できるようにしたければ、gosh内で

 
(gauche-site-library-directory)

を評価して得られるディレクトリにfoo.scmを置いて下さい。

モジュールの名前空間のトップに新たなモジュールを追加するのに気が引ける場合は、 モジュールを階層的に構成することもできます。Gaucheのモジュールの多くは既に階層的に 構成されています。具体的なモジュール例はライブラリモジュール - 概要を参照して下さい。 例えば、text.trモジュールは “text/tr.scm” ファイルに 実装されています。パス名の区切り文字‘/’がモジュール名ではピリオドになることに 注意して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

3.8 拡張パッケージの使用

パッケージの構築とインストール

Gauche にはまとまったライブラリが付属していますが、 Gauche をプロダクション環境で利用するのにはとても十分とはいえません。 利用可能な追加ライブラリもいくつもあります。こうしたライブラリを 拡張パッケージ、あるいは単にパッケージといいます。それぞれの パッケージは追加機能を提供するひとつあるいはそれ以上のモジュールを 提供します。ほとんどのパッケージは別のCのライブラリへのバインディングを 提供しています。たとえば、グラフィックスライブラリとかデータベース クライアントなどです。もし、パッケージがいくぶんでもCのコードを含む 場合は、利用するマシン上で、既にインストールされている Gauche システムを 使って、そのコードをコンパイルする必要があります。

通常、パッケージは圧縮 tarball の形式になっています。標準的には、 「gzの解凍 + tarの展開 + configure + make + make install」とやればよいように なっています。パッケージのドキュメントを読めば、必要に応じて、 configure スクリプトに与えるコマンドラインオプションで ライブラリを調整できるようになっています。

Gauche 0.8 より、gauche-package というユーティリティスクリプトが インストールされるようになっています。これはパッケージの構築と インストールを自動化します。

Package-1.0.tar.gz’ というパッケージをダウンロードしてきた としましょう。もし、このパッケージが慣例に従っていれば、やることは

 
$ gauche-package install Package-1.0.tar.gz

とタイプするだけです。 これで、gzip圧縮ファイルの解凍、tarアーカイブファイルの展開、 ‘Package-1.0’ サブディレクトリへの移動、configure スクリプトの 実行、make、make install が行われます。デフォルトでは、 gauche-package はカレントディレクトリに tarball を展開します。 これをカスタマイズファイル(後述)で変更することができます。

ファイルをインストールするのに特別な権限が必要な場合には、 --install-as というオプションを使うと、make install 部分が、sudo を使って実行されます。

 
$ gauche-package install --install-as=root Package-1.0.tar.gz

上手くいかない場合には、gauche-package build Package-1.0.tar.gz とやってパッケージの構築だけすることもできます。この場合、手で ‘Package-1.0’ ディレクトリに移動して、make install を 実行します。

設定オプションを -C あるいは --configure-options というコマンドライン引数で与えられます。たとえば、

 
$ gauche-package install -C "--prefix=/usr/local" Package-1.0.tar.gz

もしパッケージがこの新しい記述ファイルを採用しているなら、 以前に指定した設定オプションを記憶していて、そのパッケージを 再度インストールするときには、自動的にそれを再利用します。 (パッケージ開発者の方は、Gauche のソースツリーにある、 ‘examples/spigot/README’ ファイルをチェックアウトすれば、 どのように Gauche のパッケージマネージメントシステムがやっているかを 見られます。)

tarball がローカルディレクトリにない場合でも、ダウンロードしてくる URL を知っているなら、その URL を直接 gauche-package に与える ことができます。gauche-packagehttp および ftp を理解し、wgetncftpget のどちらかを使って、その tarball をダウンロードし、configure および make を実行します。

 
$ gauche-package install http://www.example.com/Package-1.0.tar.gz

gauche-package のカスタマイズ

gauche-package プログラムは ‘~/.gauche-package’ があれば、 それを読みます。このファイルにはパラメータの連想リストが含まれて いなければなりません。こんな感じです。

 
(
 (build-dir . "/home/shiro/tmp")
 (gzip      . "/usr/local/bin/gzip")
 (bzip2     . "/usr/local/bin/bzip2")
 (tar       . "/usr/local/bin/gtar")
)

以下は、認識されるパラメータのリストです。 設定ファイルにプログラムが与えられていなければ、gauche-packagePATH を探します。

build-dir

tarball が展開されるディレクトリ。もし、URL が与えられれば、 ダウンロードされたファイルはこのディレクトリに置かれます。

bzip2

bzip2 プログラムへのパス

cat

cat プログラムへのパス

make

make プログラムへのパス

ncftpget

ncftpget プログラムへのパス

rm

rm プログラムへのパス

sudo

sudo プログラムへのパス

tar

tar プログラムへのパス

wget

wget プログラムへのパス


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4. 基本的な構文


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.1 字句構造

GaucheはR7RS Schemeのパーザをいくつかの点で拡張しています。 また、歴史的経緯から、デフォルトの字句構文の中に、R7RSの仕様と相反するものが いくつかあります。リーダのモードを設定することで、 R7RS互換にすることもできます。

hash-bang指示子

#!で始まるトークンはリーダに特別な影響を与えます。 R7RSではそういった指示子がふたつ定義されています。 シンボルのリード時に大文字小文字を無視するかしないかを 切り替える、#!fold-case#!no-fold-caseです。

Gaucheで使える指示子の一覧については、下のHash-bangトークンを参照してください。

角括弧

Gaucheは、R6RSの[]()と同様に扱う構文規則を採用しています。 どちらの括弧も全く等価ですが、それぞれの開き括弧と閉じ括弧は対応していなければなりません。 括弧を混ぜることに眉をひそめる熟練のLisperもいますが、 括弧の種類を使い分けるのは、括弧の役割を視覚的に識別するのに役に立ちます。

一般的な慣習は、関数とマクロ呼び出し以外のグルーピングには[]を使う、 というものです。ただしそのようなグルーピングがネストする場合は、外側には()を 使います。例:

 
(cond [(test1 x) (y z)]
      [(test2 x) (s t)]
      [else (u v)])

(let ([x (foo a b)]
      [y (bar c d)])
  (baz x y))

この記法は、必ず従わなければいけないというものではありません。 R7RSではこの構文を採用せず、[]は言語拡張用に取ってあるので、 ポータブルなR7RSプログラムを書く場合は()だけを使うのが良いでしょう。 (リーダがstrict-r7モードの時は、[]が使われていたら エラーが通知されます。詳しくはリーダー字句モード参照。)

エディタのSchemeモードの中には、)をタイプするとそれがどちらの 種類の括弧を閉じるかに応じて自動的に])を選んで挿入してくれる ものがあります (EmacsのQuackなど)。この記法を使うときは そういったモードを活用すると良いでしょう。

シンボル名

シンボル名はデフォルトで大文字小文字を区別します (大文字小文字の区別参照)。 数字や’+’, ’-’ で始まるシンボル名も、 トークンが有効な数値リテラルを構成しない限り許されます。 また、他の変な文字も ’|’ でシンボルを囲むことによってシンボル名に含めることが できます (例:’|this is a symbol|’)。 シンボルも参照して下さい。

数値リテラル

不正確な実数の整数部または小数部のいずれかが0の場合、それを省略することが できます。例:30., .25, -.4。 数値のリーダは、パディングの’#’を認識します。 複素数表記は直交座標形 (例:1+0.3i) でも極座標形 (例:3.0@1.57) でも 認識されます。 非正確な実数は、正の無限大、負の無限大、非数(NaN)を含み、 それらはそれぞれ+inf.0-inf.0、および+nan.0と 表記されます。(-nan.0も非数(NaN)として読まれます)。

Gaucheの拡張機能として、プレフィクス(#e, #x等)つき数値リテラル中の 数字の中に_を自由に挿入することができます。_は単に無視されます。 桁を適切に区切って読みやすくするのに便利です(例: #b1100_1010_1111_1110)。

Gaucheはまた、Common Lisp風の基数を明示した数値リテラルも認識します。 例えば3進数の120は#3r120と書けます(10進数で15)。2進数から36進数まで 認識されます。10進数より上ではa-zA-Zのアルファベットを使えます。

複素数の極座標記法において、Gaucheではサフィックスpiをつけることにより 偏角をπの係数で表すことができます。Schemeの構文では偏角はラジアンですが、 πは浮動小数点数では近似値でしか扱えないので、偏角ゼロ以外で切りの良い数値を 表現することができません。

 
gosh> 2@3.141592653589793
-2.0+2.4492935982947064e-16i

piサフィックスを使うと切りの良い数値が書けます。

 
gosh> 2@1pi
-2.0
gosh> 2@0.5pi
0.0+2.0i
gosh> 2@-0.5pi
0.0-2.0i
16進数文字エスケープ

いくつかのリテラル中では、文字をその文字コードの16進数表記で記入できます。 具体的には、文字、文字セット、文字列、シンボル、正規表現リテラルです。

R7RSは文字列および縦棒で囲まれたシンボル中では\xNNNN;、文字単独では #\xNNNNというエスケープ構文を採用しました。16進数の桁数は可変で、 文字コードはUnicodeコードポイントです。

Gaucheは歴史的に2種類のエスケープを使ってきました。 \u\xです。 uはUnicodeコードポイントに、xは内部エンコーディングによる 文字コードに使っていました。また、文字リテラル以外では 16進数の桁数は固定で、したがってR7RSのような終端文字(;)を必要としませんでした。

0.9.4からは、\xエスケープはそれが正しいR7RSの16進エスケープと解釈できる 限りはそう読まれます。R7RSの構文に合致しない場合は、以前のGaucheの構文とみなされます。

滅多にないことですが、R7RSとも以前のGauche構文ともみなすことができて、しかし解釈に違いが 生じるような場合というのが存在します。そのようなリテラルを含む昔のソースを 現在のGaucheで読むと、予想外の動作が起きるかもしれません。 その場合は、リーダのモードを以前との互換モードに変更できます。 詳細はリーダー字句モードを参照してください。

拡張された#構文

#’ で始まる特殊構文がたくさん定義されています。 下の表を参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.1.1 #構文

以下の表はシャープ構文のリストです。

#![R6RS][R7RS][SRFI-22] これは、スクリプトのインタプリタ指定行(shebang)の 開始を示すか、リーダのモードに影響を与える特別なトークンとして読まれます。 下の‘hash-bang token’の節を参照してください。
#"補間された文字列を先導します。文字列の補間参照。
##, #$, #%, #&, #'未使用。
#([R7RS] リテラルベクタ表記の開始です。
#)未使用。
#*二重引用符がこれに続くときは、不完全な文字列を表します。文字列参照。
#+未使用。
#,[SRFI-10] リーダ構築子構文を開始します。
#-, #.未使用。
#/リテラルの正規表現を先導します。正規表現参照。
#0#9#n#, #n=: [SRFI-38] 共有サブストラクチャの定義と参照。
#nR, #nr: 基数を明示した数値リテラル。
#:インターンされていないシンボル。シンボル参照。
#;[SRFI-62] S式コメント。続くS式をひとつ読んで捨てます。
#<読み取り不可能オブジェクトを先導します。
#=, #>未使用。
#?デバッグマクロを先導します。デバッグ参照。
#@未使用。
#a未使用。
#b[R7RS] 2進数接頭辞。
#c未使用。
#d[R7RS] 10進数接頭辞。
#e[R7RS] 正確数接頭辞。
#f[R7RS] 真理値の偽、あるいは SRFI-4 のユニフォームベクタを先導します。gauche.uvector - ユニフォームベクタ参照。 R7RSでは真理値の偽として#f#falseが使えます。
#g, #h未使用。
#i[R7RS] 不正確数接頭辞。
#j, #k, #l, #m, #n未使用。
#o[R7RS] 8進数接頭辞。
#p, #q, #r未使用。
#s[SRFI-4] SRFI-4 のユニフォームベクタを先導します。gauche.uvector - ユニフォームベクタ参照。
#t[R7RS] 真理値の真。R7RSでは真理値の真として#t#trueが使えます。
#u[SRFI-4] SRFI-4 のユニフォームベクタを先導します。gauche.uvector - ユニフォームベクタ参照。 R7RSもバイトベクタに#u8のプリフィクスを使いますが、これは u8ユニフォームベクタと互換です。
#v, #w未使用。
#x[R7RS] 16進数接頭辞
#y, #z未使用。
#[リテラルの文字集合を先導します。文字集合参照
#\[R7RS] リテラルの文字を先導します。文字参照。
#], #^, #_未使用。
#`文字列補間の古い構文です。新しいコードでは#"を使ってください。
#{未使用。
#|[SRFI-30] ブロックコメントを先導します。コメントは対応する ’|#’ で終ります。
#}, #~未使用。

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.1.2 Hash-bangトークン

文字シーケンス#!は、どこでどのように現れるかによって2つの全く異なる 意味を持ちます。

ファイルが#!/もしくは#! (ハッシュ、バング、空白) から始 まっている場合、読み込みルーチンはそれをスクリプトのインタプリタ指定行 (shebang) とみなし、最初の行末までを無視します。 (実際は、ソースはファイルでなくても構いません。リーダはポートの先頭かどうかで 判断します。)

その場合以外では、#!identifierがひとつのトークンとして読ま れ、特別な意味を持ちます。この種のトークンはデータとして読まれるのでは なく、リーダに対して特別な指定を与えることもあります。

デフォルトでは、以下のトークンが認識されます。

#!fold-case
#!no-fold-case

リーダが大文字小文字を区別するかどうかを切り替えます。 #!fold-caseに出会うとリーダは大文字小 文字を区別しないモードになり、#!no-fold-caseに出会うとその逆にな ります。(大文字小文字の区別も参照してください。)

#!r6rs

このトークンはR6RSで導入され、プログラムがR6RSに準拠しているこ とを示します。GaucheはR6RSに準拠していませんが、今のところこのトー クンに出会っても警告を出すだけで読み込みは継続します。

#!r7rs

リーダをR7RSに準拠したstrict-r7モードにします。 詳細はリーダー字句モードを参照してください。

#!gauche-legacy

リーダをGauche 0.9.3およびそれ以前のものと互換なlegacyモードにします。 詳細はリーダー字句モードを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.2 リテラル

Special Form: quote datum

[R7RS] datumそれ自身を返します。

 
(quote x) ⇒ x
(quote (1 2 3)) ⇒ (1 2 3)
Reader Syntax: 'datum

[R7RS] (quote datum)の略記です。

 
'x ⇒ x
'(1 2 3) ⇒ (1 2 3)

注: RnRSは、リテラル式の値を変更するのはエラーであるとしています。 しかしGaucheはペアとベクタについてはそれが定数であるかどうかをチェックしておらず、 set-car!vector-set!等の破壊的手続きによってそれらの値を 変更してもエラーは報告されません。そうした場合の動作は不定です。 文字列に関しては定数かどうかのチェックを行っており、定数文字列を変更しようとすると エラーが報告されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.3 手続きを作る

Special Form: lambda formals body …
Special Form: ^ formals body …

[R7RS+] この式は評価されると手続きを生成します。この式が評価された時点の環境が手続き中に保持されます。 手続きが呼ばれると、記憶された環境に引数の束縛を追加した環境中でbody が順に評価され、 最後の式の値が返されます。

^lambdaの短い別名です。これはGauche独自の拡張です。

 
(lambda (a b) (+ a b))
  ⇒ 二つの引数を加算する手続き

((lambda (a b) (+ a b)) 1 2) ⇒ 3

((^(a b) (+ a b)) 1 2)       ⇒ 3

Gaucheはまた、lambdaが取るformalsの構文を拡張し、 省略可能引数やキーワード引数を簡単に指定できるようにしています。 同じ機能は純粋なR7RSでも、可変長引数を自力で解析することで実現可能ですが、 コードはより長く冗長になってしまいます。ポータブルなコードを書いているのでなければ 拡張構文を使った方が良いでしょう。

Formals は以下のいずれかの形でなければなりません。

Macro: ^c body …

(lambda (c) body …)の短縮表記です。 cには#[_a-z]に含まれる任意の一文字が使えます。

 
(map (^x (* x x)) '(1 2 3 4 5)) ⇒ (1 4 9 16 25)
Macro: cut expr-or-slot expr-or-slot2 …
Macro: cute expr-or-slot expr-or-slot2 …

[SRFI-26] 手続きを簡潔に書ける便利なマクロです。 いわゆる部分適用を実現するために使えます。

expr-or-slotは式またはシンボル<>でなければなりません。 最後のexpr-or-slotはシンボル<...>であっても構いません。 cutは、そのフォーム中にある<>と同じ数の引数を取る lambdaフォームに展開されます。そのフォームのボディには次の式が置かれます。

 
  (expr-or-slot expr-or-slot2 …)

但し、各<>は対応する仮引数に置き換えられます。 もしシンボル<...>が与えられていた場合、展開されたlambdaフォームは 可変長引数を取る手続きとなり、作成される手続きに与えられたすべての引数が expr-or-slotを呼び出すのに使われます(下の4番目の例を参照のこと)。

 
(cut cons (+ a 1) <>)  ≡ (lambda (x2) (cons (+ a 1) x2))
(cut list 1 <> 3 <> 5) ≡ (lambda (x2 x4) (list 1 x2 3 x4 5))
(cut list)             ≡ (lambda () (list))
(cut list 1 <> 3 <...>)
   ≡ (lambda (x2 . xs) (apply list 1 x2 3 xs))
(cut <> a b)           ≡ (lambda (f) (f a b))

;; Usage
(map (cut * 2 <>) '(1 2 3 4))
(for-each (cut write <> port) exprs)

cutecutとほぼ同じですが、expr-or-slotに与えられた フォームが手続きを作る前に評価される点が異なります。

 
(cute cons (+ a 1) <>)
   ≡ (let ((xa (+ a 1))) (lambda (x2) (cons xa x2)))

Gaucheには他にも二つほど、部分適用を簡潔に書く方法があります。 下の$マクロ、及び組み込み手続きをpa$です (コンビネータ参照)。

Macro: $ arg …

関数適用をチェインするマクロです。Haskellの$にヒントを得ました (意味は異なりますが)。 マクロ引数arg …中に$が出現すると、それが 関数の最後の引数の区切りとなります。例えば次のコードでは、 関数fの最後の引数が(g c d …)となります。

 
  ($ f a b $ g c d ...)
  ≡ (f a b (g c d ...))

$はチェインすることができます。

 
  ($ f a b $ g c d $ h e f ...)
  ≡ (f a b (g c d (h e f ...)))

$のかわりに$*が現れた場合は、最後の引数ひとつだけでなく 「残りの引数全部」を示します。

 
  ($ f a b $* g c d ...)
  ≡ (apply f a b (g c d ...))

  ($ f a b $* g $ h $* hh ...)
  ≡ (apply f a b (g (apply h (hh ...))))

さらに、もし引数リストが$または$*で終わっていた場合は、 式全体が「残りの引数(リスト)」を受け取ることを期待する手続きとなります。

 
  ($ f a b $ g c d $ h e f $)
  ≡ (lambda (arg) (f a b (g c d (h e f arg))))
  ≡ (.$ (cut f a b <>) (cut g c d <>) (cut h e f <>))

  ($ f a b $ g c d $ h e f $*)
  ≡ (lambda args (f a b (g c d (apply h e f args))))
  ≡ (.$ (cut f a b <>) (cut g c d <>) (cut h e f <...>))

関数的にコードを書いていると、関数呼び出しが深くネストすることが多くなります。 しかしSchemeの構文はそのようなネストとあまり相性が良くありません。 閉じ括弧が式の最後にずらずらと積み重なりますし、また通常のインデントルールでは コードが右のカラムにどんどん伸びていってしまいます。次の同等な二つの式を比べてみてください。

 
(intersperse ":"
             (map transform-it
                  (delete-duplicates (map cdr
                                          (group-sequence input)))))

($ intersperse ":"
   $ map transform-it
   $ delete-duplicates
   $ map cdr $ group-sequence input)

これはあくまで好みの問題ですし、こういった構文糖衣は濫用されがちなので 気をつけてください。けれどもスパイスのように、控えめな隠し味として使うと、 しばしばとても有用です。

コーナーケースとして、引数リストに$$*も現れなかった場合は、 単なる関数呼び出しになります。

 
  ($ f a b c) ≡ (f a b c)
Macro: case-lambda clause …

[R7RS][SRFI-16] それぞれの clause は (formals expr …) という形式 でなければなりません。ここで、formalslambda の仮引数 リストです。

この式は、評価されると可変数の引数をとり、lambda 式から結果として できる手続きと同じレキシカルスコープをもつ手続きになります。この手続きが いくつかの引数とともに呼ばれると、その引数が formals と一致する 最初の clause が選択されます。この引数の一致というのは、 lambda 式の formals に対応するものとして指定されるものです。 formals の変数は実引数に束縛され、 expr … がその 環境内で評価されます。

実際の引数がどの clauseformals にも一致しなければエラーです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.4 代入

Special Form: set! symbol expression
Special Form: set! (proc arg …) expression

[R7RS][SRFI-17] まずexpressionが評価されます。最初の形式では、symbolへの束縛が expressionの結果を指すように変更されます。 もしsymbolがローカルに束縛されていない場合は、グローバルな変数symbolが 存在していなければなりません。そうでなければエラーが報告されます。

2番目の形式はSRFI-17に定義されている「一般化されたset!」です。 これは構文的な装飾であり、実際は以下のように解釈されます。

 
((setter proc) argexpression)

CommonLispのsetfとは、setter手続きに渡される引数の順序が異なることに注意して下さい。

例:

 
(define x 3)
(set! x (list 1 2))
x                    ⇒ (1 2)

(set! (car x) 5)
x                    ⇒ (5 2)
Macro: set!-values (var …) expr

複数の変数を同時に更新します。Exprは与えられた変数と同じ 数だけの値を生成しなけばなりません。各値が対応するvarに セットされます。

 
(define a 0)
(define b 1)
(set!-values (a b) (values 3 4))
a ⇒ 3
b ⇒ 4
(set!-values (a b) (values b a))
a ⇒ 4
b ⇒ 3
Function: setter proc

[SRFI-17] 手続きprocのsetter手続きを返します。 procがsetter手続きを持たない場合の動作は未定義です。

ある関数fのsetter手続きgとは、もし(g a b … v)のように 呼ばれた場合、次の(f a b …)vを返すようになる手続きのことです。

ある手続きにsetter手続きを関連付けるには、setter自身のsetter手続きを使うことが できます。

 
(set! (setter f) g)

特定の手続きにsetter手続きを「ロック」することができます。システム既定のsetter手続き、 例えばcarに対するset-car!等はロックされていて、上記のような方法で 変更することは出来ません。ユーザ定義手続きにsetter手続きをロックするには下記の getter-with-setterを使います。

procが手続きでない場合は、object-applyジェネリックファンクションの setterが返されます。これにより、適用可能オブジェクトが一般化されたset! でもうまく動作します。詳しくは適用可能なオブジェクトを参照して下さい。

Function: has-setter? proc

procがsetter手続きを持っている場合は#tを返します。

Function: getter-with-setter get set

[SRFI-17] 2つの手続き、getsetを取り、新しい手続きを返します。 新しい手続きはgetと同じ動作をし、そのsetter手続きはsetにロックされて います。

この手続きは、SRFI-17によれば、setter手続きのインライン展開を可能にするための ものですが、Gaucheではまだそのような最適化は実装されていません。

Gaucheでは、一般化されたset!と同じセマンティクスを持ついくつかのマクロが定義 されています。これらはset!を使った形に展開されます。

Macro: push! place item

itemplaceの値をコンスし、その結果を再びplaceにセットします。 placeset!と同様に、 変数か(proc arg …)という形式でなければなりません。 このマクロの戻り値は未定義です。

 
(define x (list 2))
(push! x 3)
x ⇒ (3 2)

(push! (cdr x) 4)
x ⇒ (3 4 2)

placeがリストの場合、だいたいこんなふうに展開されます。

 
(push! (foo x y) item)
 ≡
 (let ((tfoo foo)
       (tx x)
       (ty y))
   ((setter tfoo) tx ty (cons item (tfoo tx ty))))

註:Common Lispのpushマクロは引数を逆の順番で取ります。 push!は他の副作用を持つ形式との互換性を考えてこの順番としました。 Perlのpush関数はpush!マクロと同じ引数順ですが、 itemはシーケンスの末尾に追加されます (Perlのunshiftの方が push!の動作に近いです)。 Perlのpushオペレータの動作が必要ならQueueが使えます (data.queue - キュー参照)。

Macro: pop! place

placeの値を取り出し、そのcdrplaceにセットします。 元の値のcarを返します。

 
(define x (list 1 2 3))
(pop! x) ⇒ 1
x ⇒ (2 3)

(define x (vector (list 1 2 3)))
x ⇒ #((1 2 3))
(pop! (vector-ref x 0)) ⇒ 1
x ⇒ #((2 3))

註:この動作はCommon Lispのpopと同じです。 Perlのpopはシーケンスの末尾から値を取ります。 Perlならshiftpop!の動作に近いです。

Macro: inc! place :optional delta
Macro: dec! place :optional delta

placeの値を評価します。それは数値にならなければなりません。 その値にdeltaが加算(inc!)もしくは減算(dec!)され、 結果がplaceに格納されます。deltaの既定値は1です。

Common Lispのincfdecfに似ていますが、 戻り値を使うことは出来ません。

Macro: update! place proc

push!等のマクロの一般化された形式です。 procは一つの引数を取り、一つの値を返す手続きでなければなりません。 placeの値がprocに渡され、procの結果がplaceに格納されます。

 
(define a (cons 2 3))
(update! (car a) (lambda (v) (* v 3)))
a ⇒ (6 . 3)

(update! (cdr a) (cut - <> 3))
a ⇒ (6 . 0)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.5 条件式

Special Form: if test consequent alternative
Special Form: if test consequent

[R7RS] まずtestを評価し、それが真の値を返したらconsequentを評価します。 そうでなければalternativeを評価します。もしalternativeが与えられて いなければ未定義の値を返します。

 
(if (number? 3) 'yes 'no) ⇒ yes
(if (number? #f) 'yes 'no) ⇒ no

(let ((x '(1 . 2)))
  (if (pair? x)
      (values (car x) (cdr x))
      (values #f #f)))
  ⇒ 1 and 2
Special Form: cond clause1 clause2 …

[R7RS][SRFI-61] 各clause節は次のいずれかの形式でなければなりません。

 
(test expr …)
(test => expr)
(test guard => expr)
(else expr expr2 …)

最後の形式は最後の節のみに許されます。

最初の節から順にtestが評価され、それが真の値を返すまで次の節のtestが 評価されます。testが真の値を返したら、それが最初の形式であれば その節のexprが順に評価され、最後の評価値が戻り値となります。 それが2番目の形式であれば、exprがまず評価されます。 exprは引数をひとつ取る手続きを返さねばなりません。 続いて、testの結果がその手続きに渡され、その手続きの戻り値がcond形式の 評価値となります。

3番目の形式はSRFI-61で定義されています。この形式では、testは 任意の数の値に評価されることができます。それらの値がまず guardに渡され、もしguardが真の値を返したら、同じ引数がexprに 適用されて、その戻り値がcond形式の評価値となります。 guard#fを返した場合は次の節へと評価が進みます。 guardexpr は、testが返すのと同数の引数を取れなければいけません。

もし全てのテストが偽の値を返し、最後の節が4番目の形式(else節)でなければ、未定義の値が返されます。

最後の節がelse節で、他の全てのテストが失敗した場合、else節のexpr が順に評価され、その最後の値がcond形式の値となります。

 
(cond ((> 3 2) 'greater)
      ((< 3 2) 'less)) ⇒ greater
(cond ((> 3 3) 'greater)
      ((< 3 3) 'less)
      (else 'equal)) ⇒ equal
(cond ((assv 'b '((a 1) (b 2))) => cadr)
      (else #f)) ⇒ 2
Special Form: case key clause1 clause2 …

[R7RS][SRFI-87] keyは任意の式です。clauseは以下の形式でなければなりません。

 
((datum ...) expr expr2 …)
((datum ...) => proc)

ここで、各datumはSchemeオブジェクトの外部表現であり、全てのdatumは 異なっていなければなりません。最後のclauseには次の形式を持つelse節が許されます。

 
(else expr expr2 …)
(else => proc)

まずkeyが評価され、その結果がそれぞれのdatumと比較されます。 keyの値とeqv?(等価参照)を使って一致するdatum が見つかれば、対応するexprが順に評価され、その最後の値がcaseの 値となります。=>を含む節はSRFI-87で定義されています。これらの節では、 keyの結果がprocに渡され、その結果がcaseの値となります。

もし一致するdatumが見つからない場合、else節が与えられていれば そのexprが順に評価され、最後の値が返されます。else節がなければcase節 の値は未定義です。

 
(case (* 2 3)
  ((2 3 5 7) 'prime)
  ((1 4 6 8 9) 'composite)) ⇒ composite

(case (car '(c d))
  ((a) 'a)
  ((b) 'b)) ⇒ undefined

(case (car '(c d))
  ((a e i o u) 'vowel)
  ((w y) 'semivowel)
  (else 'consonant)) ⇒ consonant

(case 6
  ((2 4 6 8) => (cut + <> 1))
  (else => (cut - <> 1))) ⇒ 7

(case 5
  ((2 4 6 8) => (cut + <> 1))
  (else => (cut - <> 1))) ⇒ 4
Macro: ecase key clause1 clause2 …

このフォームはほぼcaseと同等ですが、else節が与えられず、 keyの値がclause中のどのdatumとも一致しなかった場合の 動作だけが異なります。caseではそのような場合は未定義値が返されますが、 ecaseはエラーを報告します。

このマクロはCommon Lispから採られました。想定外の値が渡されることを 念のために検出したい、という場合に便利です。

 
(ecase 5 ((1) 'a) ((2 3) 'b) ((4) 'c))
 ⇒ ERROR: ecase test fell through: got 5, expecting one of (1 2 3 4)
Special Form: and test …

[R7RS] test式が順に評価されます。最初に偽の値を返したところで評価が止まり、 偽の値が返されます。残りの式は評価されません。 もし全ての式が真の値を返した場合は、最後の式の値が返されます。 式が与えれない場合は#tが返されます。

 
(and (= 2 2) (> 2 1)) ⇒ #t
(and (= 2 2) (< 2 1)) ⇒ #f
(and 1 2 'c '(f g))   ⇒ (f g)
(and)                 ⇒ #t
Special Form: or test …

[R7RS] test式が順に評価されます。最初に真の値を返したところで評価が止まり、 その値が返されます。残りの式は評価されません。 もし全ての式が偽の値を返した場合は、偽の値が返されます。 式が与えれない場合は#fが返されます。

 
(or (= 2 2) (> 2 1)) ⇒ #t
(or (= 2 2) (< 2 1)) ⇒ #t
(or #f #f #f)        ⇒ #f
(or (memq 'b '(a b c))
    (/ 3 0)) ⇒ (b c)
Special Form: when test expr1 expr2 …
Special Form: unless test expr1 expr2 …

[R7RS] まずtestが評価されます。それが真の値(unlessの場合は偽の値)を返した場合、 引続きexpr1およびexpr2 …が順に評価され、最後の評価値が返されます。 そうでなければ、未定義の値が返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.6 変数束縛

Special Form: let ((var expr) …) body …
Special Form: let* ((var expr) …) body …
Special Form: letrec ((var expr) …) body …
Special Form: letrec* ((var expr) …) body …

[R7RS] 各変数varexprの値に束縛されているローカルな環境を作成し、 その中でbody …を評価します。varはシンボルでなければ ならず、重複があってはなりません。body …の最後の式の値が このフォームの値となります。

これらの4つのフォームの違いは、exprが評価される時点のスコープと順序にあります。 letexprletフォームに入る前の環境において評価します。 各exprの評価される順序は不定で、コンパイラは最適化のために自由に順序を 変更することがあります。 一方、let*exprを現れた順に、 それ以前のvarが束縛された環境において評価してゆきます。

letrecは全てのvarが仮想的に不定の値に束縛された環境において 各exprを(順不同で)評価します。 letrecは相互再帰的なローカル関数を定義する場合に必要です。 最後に、letrec*letrecと同じスコープ規則を使い、 さらにexprを現れる順に評価するものです。

 
(define x 'top-x)

(let  ((x 3) (y x)) (cons x y)) ⇒ (3 . top-x)
(let* ((x 3) (y x)) (cons x y)) ⇒ (3 . 3)

(let ((cons (lambda (a b) (+ a b)))
      (list (lambda (a b) (cons a (cons b 0)))))
  (list 1 2))  ⇒ (1 2 . 0)

(letrec ((cons (lambda (a b) (+ a b)))
         (list (lambda (a b) (cons a (cons b 0)))))
  (list 1 2))  ⇒ 3

ひとつのexprを評価するために、それまでに現れたvarの値を参照する 必要がある時は、letrec*を使わなければなりません。 下の例では、abの値を計算するためにcubeの値を 使っているので、letrec*にする必要があります。 (上の例との違いに注意してください。上の例では、listの値を計算する 時には、同時に束縛されているconsの値は必要とされません。consの値が 必要になるのはlistが実際に適用される時です。)

 
(letrec* ((cube (lambda (x) (* x x x)))
          (a (+ (cube 1) (cube 12)))
          (b (+ (cube 9) (cube 10))))
  (= a b)) ⇒ #t

この例は現在のGaucheではletrecを使ってもたまたま動作しますが、 将来にわたって動作し続けることは保証されません。 letrecを使うなら評価順に依存しないように、プログラマが気をつけないといけません。 振り返ってみれば、letrec*だけが提供されてた方が単純だったでしょう。 生憎、Schemeの歴史の中ではletrecの方がずっと前からあったので、いまさら 取り除くこともできないのです。また、letrec*に出来ない最適化が letrecだと可能になることもあります。

Macro: let1 var expr body …

変数が一つしか無い場合の便利なマクロです。次のように展開されます。

 
(let ((var expr)) body …)
Macro: if-let1 var expr then
Macro: if-let1 var expr then else

このマクロは次のようなイディオムを簡素化します。

 
(let1 var expr
  (if var then else))
Macro: rlet1 var expr body …

このマクロは次のようなイディオムを簡素化します。

 
(let1 var expr
  bodyvar)
Macro: and-let* (binding …) body …

[SRFI-2] 簡単に言うと、このフォームはlet*のように動作しますが、 bindings中の式が#fに評価されたらそこで評価を打ち切り #fを返します。

bindingは以下のいずれかの形式でなければなりません。

(variable expression)

expressionが評価されます。それが真の値を返したら、その値がvariable に束縛され、次のbindingへと進みます。もうbindingが無ければ body …が評価されます。もしexpression#fを返したら、 評価を打ち切り、and-let*から#fを返します。

(expressionx)

この形式ではvariableが省略されています。Expressionが評価され、 その結果は評価を続行するか打ち切るかを判断するためにのみ使われます。

bound-variable

この形式ではbound-variableは束縛変数を示す識別子でなければなりません。 その変数の値が偽でなければ評価を続行します。

いくつか例を挙げます。次のコードは連想リストalistからkeyを 探し、見つかったらその値を返します。

 
(and-let* ((entry (assoc key alist))) (cdr entry))

もしargが正確な整数の文字列表現だった場合はnumを返し、そうでなければ 0を返します:

 
(or (and-let* ((num (string->number arg))
               ( (exact? num) )
               ( (integer? num) ))
      num)
    0)

以下のコードはとあるサーバーのポート番号をいくつかの可能性 (環境変数、設定ファイル…)の中から探す仮想的なコードです。

 
(or (and-let* ((val (sys-getenv "SERVER_PORT")))
      (string->number val))
    (and-let* ((portfile (expand-path "~/.server_port"))
               ( (file-exists? portfile) )
               (val (call-with-input-string portfile port->string)))
      (string->number val))
    8080) ; default
Macro: and-let1 var test exp1 exp2 …

testを評価し、それが#fでなければvarをその値に束縛して exp1 exp2 …を評価します。戻り値は最後の式の値です。 test#fだった場合は単に#fを返します。

これはand-let*if-let1を使って次のとおり書くこともできます。 しかし、このパターンを書くことがあまりに多いため、専用のマクロを用意する価値が あると判断しました。

 
(and-let1 var test
  exp1
  exp2 …)

≡

(and-let* ([var test])
  exp1
  exp2 …)

≡

(if-let1 var test
  (begin exp1 exp2 …)
  #f)
Macro: fluid-let ((var val) …) body …

動的スコープの変数をエミュレートするマクロです。 varfluid-letフォームを含むスコープで定義されている 変数でなければなりません。valは式です。 fluid-letはまずvalを評価し、 valvarに動的スコープで束縛してbody … を評価します。

マルチスレッド環境下では、varの値の変化は全てのスレッドから見えます。 このフォームは主として他の処理系のコードを移植する際の利便性のために 追加されました。スレッドローカルな動的状態を実現するには、 パラメータオブジェクト(gauche.parameter - パラメータ参照)を 使って下さい。

 
(define x 0)

(define (print-x) (print x))

(fluid-let ((x 1))
  (print-x))  ⇒ ;; prints 1
Special Form: receive formals expression body …

[SRFI-8] この構文により、多値を受け取ることができます。 formalsはシンボルのリストです。不完全なリストであっても構いません。 expressionが評価され、返された値がlambda形式の引数の束縛と 同じようにしてformals内の変数と束縛され、その環境下でbody …が 評価されます。

 
(define (divrem n m)
  (values (quotient n m) (remainder n m)))

(receive (q r) (divrem 13 4) (list q r))
  ⇒ (3 1)

(receive all (divrem 13 4) all)
  ⇒ (3 1)

(receive (q . rest) (divrem 13 4) (list q rest))
  ⇒ (3 (1))

なお、多値call-with-valuesreceiveと等価な手続き的インタフェースです。 多値を複数の変数に同時に束縛するには、define-values (定義参照) が使えます。 また、SRFI-11 (srfi-11 - Let-values) のlet-valueslet*-valuesletのような形式で多値を扱うことができます。

Macro: rec var expr
Macro: rec (name . vars) expr …

[SRFI-31] 再帰的な参照のある式の評価を行うマクロです。

最初の形式は、varexprの結果に束縛される状態でexprを 評価します。 2番目の形式は以下の形式と等価です。

 
(rec name (lambda vars expr …))

例:

 
;; constant infinite stream
(rec s (cons 1 (delay s)))

;; factorial function
(rec (f n)
  (if (zero? n)
      1
      (* n (f (- n 1)))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.7 順次実行

Special Form: begin form …

[R7RS] formを順に評価し、最後の値を返します。

Beginletのように新たなスコープを作成するわけではありません。 すなわち、一般的にはform …の先頭に「内部のdefine (internal define)」を 置くことはできません。意味的には、beginはまるでform …が beginを囲むコンテクスト中に展開されているかのように振舞います。 例えば、トップレベルに次のような式があった場合、それは2つのトップレベルのdefineと 同等です。

 
(begin (define x 1) (define y 2))

よりトリッキーな例:

 
(let ()
  (begin
    (define x 2)
    (begin
      (define y 3)
    ))
  (+ x y))

  ≡

(let ()
  (define x 2)
  (define y 3)
  (+ x y))
Macro: begin0 exp0 exp1 …

exp0, exp1, … を評価し、exp0の結果を返します。 この名前はMzSchemeから取られました。これはCommonLispで prog1と呼ばれているものです。

beginと違って、これは新たなスコープを構成します。 begin0フォームは次のように展開されるからです。

 
(receive tmp exp0
  exp1 …
  (apply values tmp))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.8 繰り返し

Special Form: do ((variable init [step]) …) (test expr …) body …

[R7RS]

  1. init …を評価し、variable …をそれぞれの結果へと 束縛します。以降のステップはvariable …が束縛された環境で評価されます。
  2. testを評価します。真の値が得られたら、 expr …を順に評価し、最後のexprの結果を返り値とします。
  3. そうでなければ、body …を(副作用のために)評価します。
  4. それからstep …を評価し、それぞれの結果へ 新たなvariable …を束縛し、ステップ2から繰り返します。

次の例はループを10回繰り返し、その間iの値をjへと 累積して最後に返すコードです。

 
(do ((i 0 (+ i 1))
     (j 0 (+ i j)))
    ((= i 10) j)
  (print j))
 ⇒ 45 ; また、jの中間結果が途中で出力される

stepが省略された場合は、variableの元の値がそのまま持ち越されます。 exprが一つも無い場合は、testが真の値を返した時点でその値がそのまま do式の値となります。

do構文は括弧を多用するので、角括弧を併用してまとまりを視覚的に 強調するのを好む人もいます。よくある書き方は、それぞれの変数の束縛と、 テスト節全体に角括弧を使うという書き方です。

 
(do ([i 0 (+ i 1)]
     [j 0 (+ i j)])
    [(= i 10) j]
  (print j))

註:Common Lisp (や、多くの言語の“forループ”) とは違って、 繰り返しの度に新しいvariableが束縛されます。 次の例では5回ループして、それぞれiを閉じ込むクロージャのリストを 作っています。各クロージャを呼び出せば、それぞれが自身が作られた時のiを 閉じ込んでいることがわかります。

 
(define closures
  (do ([i 0 (+ i 1)]
       [c '() (cons (^[] i) c)])
      [(= i 5) (reverse c)]
    ))

((car closures))  ⇒ 0
((cadr closures)) ⇒ 1
Special Form: let name ((var init) …) body …

[R7RS] 「名前付きlet」と呼ばれるlet式のバリエーションです。 この構文は、次の手続きを作り、それをnameに束縛して、 init …を引数として呼び出します。

 
(lambda (var …) body …)

この構文自体は繰り返しとは直接に関係ありませんが、 名前付きletのポイントは、上のlambda式がnameのスコープ内で作られることです。 すなわち、bodyからnameを再帰的に呼ぶことができます。したがって この構文は再帰によるループを書くのに非常に良く使われます (そのため、次の例のように作られる手続きをloopと名付けることが良く行われます)。

 
(let loop ([x 0] [y '()])
  (if (= x 10)
    y
    (loop (+ x 1) (cons x y))))
 ⇒ (9 8 7 6 5 4 3 2 1 0)

もちろん名前付きletは必ずループに使わなければならないということはありません。 nameを非末尾再帰呼び出ししても良いですし、他の高階関数に渡しても構いません。 名前付きletがあるのは、ローカルな再帰関数を定義する際の共通するパターンをうまく とらえているためです。doよりも名前付きletを好んで使うSchemerもいます。 名前付きletの方が柔軟性が高いからです。

以下の書き換え規則が、名前付きletのセマンティクスを正確に説明します。 letrecの使い方がちょっとひねってあるのは、bodyprocの スコープに含まれるけれどinitはそうではない、ということを表現するためです。

 
(let proc ((var init) …) body …)
 ≡
((letrec ((proc (lambda (var …) body …)))
   proc)
 init …)
Macro: dotimes ([variable] expr [result]) body …
Macro: dolist ([variable] expr [result]) body …

Common Lispからの輸入です。完全なフォームは以下のように展開されます。

 
(dotimes (variable expr result) body …)
 ≡
(do ((limit expr)
     (variable 0 (+ variable 1)))
    ((>= variable expr) result)
  body …)

(dolist (variable expr result) body …)
 ≡
(begin
  (for-each (lambda (variable) body …) expr)
  (let ((variable '())) result))

(dolistresultの評価時にvariable()に 束縛しているのはCLとの互換性のためです。)

result、もしくはresultvariable両方を省略することが できます。つまり、最初の引数が2要素であれば、それはvariableexprだということです。その場合、これらのフォームの結果は未定義です。

もしresultvariableの両方が省略されたら、 exprで決定される回数だけ単にbody …を繰り返し実行します。

 
;; print "a" 10 times.
(dotimes (10) (print "a")) 

;; print "a" (length lst) times.
(dolist (lst) (print "a"))

srfi-42 - 先行評価的内包表記も参照してください。 より複雑な繰り返しを記述できます。

Macro: while expr body …
Macro: while expr => var body …
Macro: while expr guard => var body …

varは識別子でguardは一つの引数をとる手続きです。

最初の形式ではまずexprが評価され、もしそれが真値を返したら body … が評価されます。そしてexprが真値を返す 限り繰り返されます。

2番目の形式では、body … で有効な変数varを 作成し、exprの結果に束縛します。

最後の形式では、expr の返り値が guard に渡されこの 結果が真値である限り body … が繰り返されます。 変数 varexpr の結果に束縛されます。

while 自体の返り値は不定です。

 
(let ((a '(0 1 2 3 4)))
  (while (pair? a)
    (write (pop! a)))) ⇒ prints "01234"

(let ((a '(0 1 2 3 #f 5 6)))
  (while (pop! a) integer? => var
    (write var))) ⇒ prints "0123"
Macro: until expr body …
Macro: until expr guard => var body …

while の条件を逆にしたものです。 つまり、 最初の形式ではexprが真値を返すまで body … を 繰り返します。2番目の形式ではexprの結果がguard に渡され それが真値を返すまで繰り返します。 Varexpr返り値に 束縛されます。

(guard を省いた2番目の形式ではvarは常に#fへ 束縛されるため、あまり意味がありません。)

until自体の返り値は不定です。

 
(let ((a '(0 1 2 3 4)))
  (until (null? a)
    (write (pop! a)))) ⇒ prints "01234"

(until (read-char) eof-object? => ch
  (write-char ch))
 ⇒ reads from stdin and writes char until EOF is read

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.9 準クオート(Quasiquote)

Special Form: quasiquote template

[R7RS] 準クォートは固定部分と変数部分の両方を持つような構造を構成するのに便利 です。詳細は以下の説明を参照してください。

Reader Syntax: `template

[R7RS] `x(quasiquote x)として読み込まれます。

Special Form: unquote datum …
Special Form: unquote-splicing datum …

[R7RS] これらの構文は準クォートされたtemplate内にあるときにだけ意味を持ち ます。R5RSではこれらの構文が準クォートの外側で現われたときの意味に ついては何も言及していません。Gaucheではそのような場合にはエラーを通知します。 そのようなunquoteunquote-splicingが現れるのは、 通常どこかで準クォートを忘れているからです。

R5RSはunquoteunquote-splicingに一つだけ引数を取ることを 許しています。(unquote)(unquote x y)のようなフォームが 準クオートの中に現れた場合の動作は定義されていません。 R6RSではこれらの場合も定義されており、Gaucheもそれに倣っています。

Reader Syntax: ,datum
Reader Syntax: ,@datum

[R7RS] ,xおよび,@xは、それぞれ(unquote x)および (unquote-splicing x)として読み込まれます。

準クォートの基本

(foo bar x y)のようなリストを構成したいとしましょう。 ここではfooおよびbarはシンボルで、xおよびyは 実行時に定まる値とします。(説明のために、変数xおよびyがそ れらの値を持っているものとします。) ひとつの方法はlist関数を明 示的に呼ぶことです。

 
(let ((x 0) (y 1))
  (list 'foo 'bar x y)) ⇒ (foo bar 0 1)

同じことを準クォートを使うと以下のようになります。

 
(let ((x 0) (y 1))
  `(foo bar ,x ,y))  ⇒ (foo bar 0 1)

ふたつの記法の違いは、前者では結果に書いたとおりを入れ込みたいところで クォートを使い、後者ではクォートしたくないところにunquotesを使 うことです。

ほとんどが固定部分でその中に変数部分が散在するような場合には準クォート を使った方が単純で読みやすくなります。

そういうわけで、旧来のマクロでは準クォートが頻繁につかわれていました。 旧来のマクロは基本的にマクロの引数として与えられた変数部分からプログラ ム断片を生成する手続だからです。簡単なmy-ifマクロの定義とそれが condに展開されるようすを見てみましょう。

 
(define-macro (my-if test then else)
  `(cond (,test ,then)
         (else ,else)))

(macroexpand '(my-if (< n 0) n (- n)))
  ⇒ (cond ((< n 0) n) (else (- n)))

マクロ定義内の2つのelseに注目してください。ひとつはアンクォート されていませんので、出力にはそのまま現われます。もうひとつの方はといえ ば、こちらはアンクォートされていますので、その場所にマクロの引数が入り ます。

もちろんマクロとは関係のないところでも準クォートは使えます。準クォート は構造のあるデータを構築する汎用的な方法です。ほとんどが変数部分である ような構造でも準クォートを好んで使うプログラマもいます。準クォートを使っ た方が簡潔に書けるからです。さらにGaucheでは準クォート形式に対する実行 時アロケーションができるだけ少くなるようにしていますので、準クォートを 使った方が効率がいいはずです。この点については後述の「準クォートはどの くらい静的か」を見てください。

スプライシング

(unquote-splicing expr)が準クォート形式の中で使われていれ ば、exprは評価されてリストになるものでなければならず、それをとり まくコンテキストで継ぎ合わされます。例を見ると簡単にわかります。

 
(let ((x '(1 2 3)))
  `(a ,@x b)) ⇒ (a 1 2 3 b)

(let ((x '(1 2 3)))
  `(a ,x b)) ⇒ (a (1 2 3) b)

(let ((x '(1 2 3)))
  `#(a ,@x b)) ⇒ #(a 1 2 3 b)

アンクォート版とアンクォートスプライシング版を比べてください。スプライ シングはベクタに対しても機能します。

複数の引数を取るunquote

unquoteunquote-splicingが複数の引数を取る場合は、 あたかも各引数がそれぞれunquoteまたはunquote-splicingされている かのように解釈されます。

 
;; This is the same result as `(,(+ 1 2) ,(+ 2 3) ,(+ 3 4))
`((unquote (+ 1 2) (+ 2 3) (+ 3 4)))
  ⇒ (3 5 7)

;; This is the same result as
;;   `(,@(list 1 2) ,@(list 2 3) ,@(list 3 4))
`((unquote-splicing (list 1 2) (list 2 3) (list 3 4)))
  ⇒ (1 2 2 3 3 4)

;; Edge cases
`((unquote))          ⇒ ()
`((unquote-splicing)) ⇒ ()

複数のフォームをスプライスできない位置に現れた ゼロ個もしくは複数の引数を取るunquote/unquote-splicingフォームは エラーとなります。

 
;; Multiple arguments unquotes are error in non-splicing context
`(unquote 1 2)          ⇒ error
`(unquote-splicing 1 2) ⇒ error

省略記法の,x,@xは単一引数のフォームとしてしか使えないので、 ゼロ個もしくは複数の引数を取る場合は unquoteunquote-splicingを陽に表記しなければなりません。 わざわざそんなコーディングをする必要はまず無いでしょう。 この機能は、ネストしたアンクオートしているフォーム、 ,,@,@,@をサポートするためにあります。 R5RSの仕様では、これらのフォームで、内側のunquote-splicingがゼロ個もしくは複数個の フォームへと展開された場合を処理できません。

準クォートはどのくらい静的か

準クォート形式が変数部分を含む場合、実行時には、明示的な形式が使われた ときと同じことがおこります。`(,x ,y)(list x y)のように 評価されます。しかし、Gaucheでは準クォート形式が固定部分を持つ場合には 実行時アロケーションができるだけ少くなるようにします。

まず、`(a b c)のように準クォート形式に変数部分がない場合、全体 は静的にアロケートされます。構造の末尾が固定部分の場合にも静的にアロケー トされます。たとえば、`((,x a b) (,y c d))(list (cons x '(a b)) (cons y '(c d)))のように機能します。

さらにアンクォート式が定数式の場合,Gaucheはそれを準クォートの固定部分 に埋め込みます。たとえば、(define-constant x 3)のように定義した としましょう。この場合`(,x ,(+ x 1))は定数'(3 4)のように コンパイルされます。(define-constant形式については、 定義を参照してください。)

一般的には、準クォート形式のどの部分が固定データとしてコンパイルされど の部分がされないのかを特定することは困難です。それゆえ、準クォートの返 す構造の一部が新規にアロケートされていることを前提としたコードを書いて はいけません。いいかえると、そのような構造を変更するのは避けるべきです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.10 定義

Special Form: define variable expression
Special Form: define (variable . formals) body …

[R7RS+] この形式はトップレベル (ローカルな束縛が無い状態) とローカルスコープがある状態とで 別の意味を持ちます。

トップレベルでは、この形式は変数variableに対するグローバルな束縛を定義します。 最初の形式では、expressionが評価され、その結果が変数variableの値となります。

 
(define x (+ 1 2))
x ⇒ 3
(define y (lambda (a) (* a 2)))
(y 8) ⇒ 16

2番目の形式は手続きを定義するための構文的な修飾で、以下の形式と同じです。

 
(define (name . args) body …)
  ≡ (define name (lambda args body …))

このフォームがローカルスコープの中に現われた場合、ローカル変数の束縛となります。 (内部define)。

内部defineはlambdaやその他のローカル束縛を作る構文の、本体部分の先頭に置けます。 これらは、下に示すようにletrec*フォームと等価です。

 
(lambda (a b)
  (define (cube x) (* x x x))
  (define (square x) (* x x))
  (+ (cube a) (square b)))

 ≡

(lambda (a b)
  (letrec* ([cube (lambda (x) (* x x x))]
            [square (lambda (x) (* x x))])
    (+ (cube a) (square b))))

内部defineは実質的にletrec*フォームなので、 相互再帰する内部関数を書けますし、また同じスコープで先に導入された定義を 使って定義される値を計算することもできます。しかし、 内部defineフォームの後に定義される値を使うことはできません。 そういったプログラムを書いてもGaucheは直ちにエラーを報告しませんが、 あとでおかしな結果が出ることがあります。

 
(lambda (a)
  (define x (* a 2))
  (define y (+ x 1))  ; yの値を計算するのにxを使ってよい
  (* a y))

(lambda (a)
  ;; odd?の中からeven?を参照するのはok。odd?が定義される時点ではeven?
  ;; の値は使われず、odd?が呼ばれた時に初めて使われるから。
  (define (odd? x) (or (= x 1) (not (even? (- x 1)))))
  (define (even? x) (or (= x 0) (not (odd? (- x 1)))))
  (odd? a))

(lambda (a)
  ;; これはダメ。yを定義する時点でxの値を使わないとならないので。
  ;; ただし、すぐにはエラーとならないかもしれない。
  (define y (+ x 1))
  (define x (* a 2))
  (* a y))

束縛を作るフォームのボディー内で、内部defineは同じレベルにあるすべての式より前に 現れなければなりません。例えば次のコードは、defineフォームの 前に式(print a)があるので不正です。

 
(lambda (a)
  (print a)
  (define (cube x) (* x x x))  ; error!
  (cube a))

束縛を作るフォームのボディー中に、式を置かず内部defineだけを書いておくのも不正ですが、 Gaucheは特にエラーを出しません。

beginは新しいスコープを作らないことに注意してください(順次実行参照)。 beginの中に現われるdefineは、あたかもbeginとそれを囲む 括弧が無いかのように振舞います。すなわち、以下の2つの形式は等価です。

 
(let ((x 0))
  (begin
    (define (foo y) (+ x y)))
  (foo 3))
 ≡
(let ((x 0))
  (define (foo y) (+ x y))
  (foo 3))
Macro: define-values (var …) expr
Macro: define-values (var var1 … . var2) expr
Macro: define-values var expr

[R7RS] まずexprが評価され、続いて各値がvarに順に束縛されます。 最初の形式では、expr

 
(define-values (lo hi) (min&max 3 -1 15 2))

lo ⇒ -1
hi ⇒ 15

二番目の形式では、exprvar var1 …に対応する数か それ以上の値を生成しなければなりません。余った値はリストになってvar2に 束縛されます。

 
(define-values (a b . c) (values 1 2 3 4))

a ⇒ 1
b ⇒ 2
c ⇒ (3 4)

最後の形式では、exprの生成する全ての値がリストにまとめられ、varに 束縛されます。

 
(define-values qr (quotient&remainder 23 5))

qr ⇒ (4 3)

define-valuesdefineが許されるところならどこでも使えます。 つまり、内部defineにdefine-valuesを混ぜて使えるということです。

 
(define (foo . args)
  (define-values (lo hi) (apply min&max args))
  (define len (length args))
  (list len lo hi))

(foo 1 4 9 3 0 7)
 ⇒ (6 0 9)

srfi-11 - Let-valuesも参照してください。

Special Form: define-constant variable expression
Special Form: define-constant (variable . formals) body …

このフォームはトップレベルでしか使えません。 トップレベルのdefineと同じように動作しますが、 コンパイラはvariableの値が今後変更されないものとして 最適化されたコードを生成します。

variableの値をset!で変更しようとするとエラーとなります。 variableを再定義することは許されますが、警告が表示されます。

“内部define-constant” にあたるものはありません。宣言が無くても コンパイラはどのローカル束縛が変更されないかを検出して最適化できるからです。

Special Form: define-in-module module variable expression
Special Form: define-in-module module (variable . formals) body …

この形式はトップレベルでしか使えません。 variableのグローバルな束縛をmodule中に作成します。 moduleはモジュール名を表すシンボルか、モジュールオブジェクトで なければなりません。moduleがシンボルの場合、その名前を持つ モジュールが既に存在している必要があります。

expressionは現在のモジュール中で評価されます。

2番目の形式は次の形式の構文的修飾です。

 
(define-in-module module variable (lambda formals body …))

註: シンボルが現在のモジュール中で定義されているか(グローバルな束縛を持つか) を調べるには、global-variable-bound?が使えます (モジュールイントロスペクション参照)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.11 インクルード

Special Form: include filename …
Special Form: include-ci filename …

[R7RS] filename …で指定されるファイルをコンパイル時に読み、 その内容がbeginで囲まれてインクルードフォームの位置に替わりに 置かれているかのように解釈します。 includeフォームはファイルの内容をそのまま読みますが、 include-ciフォームは、あたかもファイル先頭に#!fold-caseが 指定されたかのように、シンボルの大文字小文字を区別しないモードで読みます (大文字小文字の区別参照)。

読み込まれるファイルに文字エンコーディングを指定するコメントがあればそれは そのファイルを読んでいる間有効になります(マルチバイトスクリプト参照)。

filenameが絶対パスならば、そのパスに正確に合致するファイルが探されます。 相対パスなら、まずincludeフォームが 置かれていたファイルからの相対で探され、見つからなければ *load-path*にあるディレクトリからの相対パスが順に試されます。

例: ファイル‘a.scm’には次のコードが書かれているとします。

 
(define x 0)
(define y 1)

このファイルを別ソースファイルからインクルードすることができます:

 
(define (foo)
  (include "a.scm")
  (list x y))

この場合、元のソースファイルはあたかも次のように書かれていたかのように解釈されます。

 
(define (foo)
  (begin
   (define x 0)
   (define y 1))
  (list x y))

(註: バージョン0.9.4では、includeは渡されたパス名が ./../で始まっていた場合は特別な動作をしていました。 コンパイラのカレントディレクトリからの相対でファイルを探していたのです。 これは意図した動作というより、loadとファイル探索ルーチンを共有していた ために生じた仕様でした。しかしloadが常に実行時の動作であるのに対し、 includeでカレントディレクトリからの相対パスを考える意味はほとんどありません。 そこで0.9.5では仕様を変更しました。)

Gaucheには、他のファイルからソースを読んでくる方法がいくつかあります。 以下にインクルードとの比較を説明します。

require (useextendも内部でrequireを呼んでいます)
load

通常、他のファイルに書かれたソースファイルを取り込むのに適した方法は require (もしくはuseextend) です。 includeフォームは、requireでは実現が難しいトリックのために使います。 例えば、第三者がR5RS向けに書いたコードを、Gaucheのモジュールシステムでラップしたい場合、 includeを使う 次のような内容の小さなファイルをそのコードのファイルと同じディレクトリに置いておけば、 第三者のコードを一切変えることなく、(use third-party-module) で コードをロードできます。

 
(define-module third-party-module
  (export proc ...)
  (include "third-party-source.scm"))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.12 機能条件式

cond-expandマクロ

しばしば、実装やプラットフォームがどういった機能を提供しているかに 応じてコードを切り替えたいことがあります。例えば、ネットワークが使えるか どうかで振る舞いを変えるとか、ほぼポータブルなコードの一部だけに 特定の実装に依存するコードを入れたいとかいう場合です。

C言語なら、#ifdefのようなプリプロセッサディレクティブを、 Common Lispなら、#+#-のようなリーダマクロを使うところです。 Schemeでは、こういう時にはcond-expandを使います。

Macro: cond-expand (feature-requirement command-or-definition …) …

[SRFI-0][R7RS] プラットフォームがfeature-requirementに示される機能をサポートしていれば、 このマクロはcommand-or-definition … へと展開されます。

feature-requirement は以下のような構文でなければなりません。

 
feature-requirement
  : feature-identifier
  | (and feature-requirement …)
  | (or  feature-requirement …)
  | (not feature-requirement)
  | (library library-name)

このマクロは feature-requirement を順にテストし、そのひとつが 満たされたら、対応する command-or-definition … に展開されます。

最後の節のfeature-requirementの部分にはシンボルelseを 置くこともできます。他の節のfeature-requirementが満たされなかった場合に その節が展開されます。

条件を満たす節もelse節も無かった場合、cond-expandは エラーを通知します。この仕様は、 必要な機能を欠いている実行環境を早めに検出するためです。 もしチェックする機能がオプショナル、つまりそれが無くてもプログラムの実行自体は 可能であるというものであるなら、最後に空のelse節を忘れないようにしてください。

 
(cond-expand
  [feature expr]  ; some optional feature
  [else])

feature-identifier は機能を示すシンボルです。その機能が現在のプラットフォームで サポートされているなら、それは、feature-requirement を満たします。 より複雑な条件を構成するために、feature-requirement のブール代数による 組み合わせを用いることが出来ます。

(library library-name)という形式はR7RSで追加されたもので、 指定されるライブラリが使える場合に条件が満たされます。 これはR7RSの形式なので、ライブラリ名はリスト形式で指定します (gauche.netのかわりに(gauche net)等)。

例えば、Gauche、Chicken Scheme、ChibiSchemeに依存するコードを 入れたいとしましょう。現代の多くのScheme実装は、自分自身を示す feature-identifierを定義しているので、条件付きのコードは次のとおり書けます。

 
(cond-expand
 [gauche  (gauche-specific-code)]
 [(or chicken chibi) (chicken-chibi-specific-code)]
 [else    (fallback-code)]
 )

cond-expandの条件はマクロ展開時に全て処理され、 不採用だった節のコードは捨てられる、という事実は重要です。 このおかげで、例えば実装によって認識されないマクロ呼び出しや言語拡張を command-or-definitionに含めることができます。 また、グローバルな束縛を条件によって定義したりしなかったりすることができます。

これをcondと比較してみましょう。condは条件を実行時に調べます。 もし条件節の中に、あるプラットフォームではサポートされないマクロ呼び出しを 書いた場合、たとえその節が現実には決して実行されないものであるとしても、 マクロ展開時にエラーになってしまうかもしれません。 また、condを使って条件的にグローバルな束縛を定義することはできません。

ただし、マクロ展開した結果をどうにかして保存しておいて、 別のプラットフォームでそれを走らせる、ということをする場合は注意が必要です。 条件選択はマクロ展開をするプラットフォームの機能に応じて行われ、 それはコードを実行するプラットフォームの機能とは一致しないかもしれません (これはもちろん、クロスコンパイルで常に問題となることです。)

Gaucheで使えるfeature identifier一覧は下を見てください。

Gauche特有のfeature identifier

gauche
gauche-X.X.X

プログラムをGaucheで実行していることを示します。ポータブルなプログラムの中に Gauche特有のコードを埋め込むのに便利です。 X.X.XはGaucheのバージョンで(例: gauche-0.9.4)、 特定のGaucheのバージョンのみに依存したコードを入れるのに使えます。 (処理系のバージョンつきの機能識別子はR7RSで示唆されているものですが、 バージョンを比較する機能がないとあまり有用ではないでしょう。 将来はそのような機能が追加されるかもしれません。)

gauche.os.windows
gauche.os.cygwin

それぞれ、WindowネイティブプラットフォームとCygwin/Windowsプラットフォームを 示します。どちらも定義されていなければ、Unix系と考えて構いません。 (CygwinもUnix系と言えなくはありませんが、他のUnix系に比べて 色々違いがあるので、機能識別子を用意してあります。)

gauche.ces.utf8
gauche.ces.eucjp
gauche.ces.sjis
gauche.ces.none

これらのうちのいずれか一つが、Gaucheの内部エンコーディングに合わせて 定義されます。詳しくはマルチバイト文字列を参照してください。

gauche.net.tls

TLS/SSLサポートがあれば定義されます。

gauche.net.ipv6

IPv6サポートが組み込まれていれば定義されます。 ただし、これがGaucheがIPv6サポート込みでビルドされたことを示すだけです。 OSがIPv6サポートをオフにしている場合、IPv6機能を使おうとするとシステムエラーが 投げられます。

gauche.sys.threads
gauche.sys.pthreads
gauhce.sys.wthreads

マルチスレッドがサポートされていればgauche.sys.threadsが定義されます (gauche.threads - スレッド参照)。また、マルチスレッドは内部ではプラットフォームによって POSIXスレッドかWindowsスレッドを利用しますが、前者の場合は gauche.sys.pthreadsが、後者ではgauche.sys.wthreadsが 併せて定義されます。

gauche.sys.sigwait
gauche.sys.setenv
gauche.sys.unsetenv
gauche.sys.clearenv
gauche.sys.getloadavg
gauche.sys.getrlimit
gauche.sys.lchown
gauche.sys.getpgid
gauche.sys.nanosleep
gauhce.sys.crypt
gauche.sys.symlink
gauche.sys.readlink
gauche.sys.select
gauche.sys.fcntl
gauche.sys.syslog
gauche.sys.setlogmask
gauche.sys.openpty
gauche.sys.forkpty

これらシステムの機能が使えるのなら対応する機能識別子が定義されます。

R7RSで定義されたfeature identifiers

r7rs

実装がr7rs準拠であることを示します。

exact-closed

数値計算が正確な値について閉じている、つまり、正確な数を ゼロ以外の正確な数で割った結果が常に正確な数になることを示します。

ieee-float

内部的にIEEE浮動小数点数を使っていることを示します。

full-unicode

Unicodeの全ての範囲をサポートしていることを示します。

ratios

有理数のサポートがあることを示します。

posix
windows

プラットフォームによってどちらかが定義されます。

big-endian
little-endian

プラットフォームによってどちらかが定義されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13 モジュール

この章では、GaucheのモジュールのセマンティクスとAPIを述べます。 Gaucheで使われているモジュールの書法についてはGaucheのモジュールを書くも 併せて参照して下さい。

R7RSプログラムでは、モジュールに相当するものは「ライブラリ」と呼ばれ、 Gaucheとは異なる構文で定義します。詳しくはR7RSライブラリ形式を参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13.1 モジュールのセマンティクス

モジュールは、シンボルを束縛へとマップするオブジェクトで、 グローバル変数の解決に影響を与えます。

CommonLispのパッケージは名前からシンボルへのマッピングを行いますが、 Gaucheでは同じ名前を持つシンボルは原則としてeq?です (例外はインターンされていないシンボルです。シンボル参照)。 しかし、Gaucheのシンボルは「値」のスロットを持っていません。 モジュールによってシンボルに対応する束縛が見付けられ、値はそこに 格納されています。 モジュールが違えば同じシンボルは別々の束縛へとマップされ、違う値を 返します。

 
;; 二つのモジュールAとBを作成し、グローバル変数'x'をその中で定義
(define-module A (define x 3))
(define-module B (define x 4))

;;  #<symbol 'x'> ---[module A]--> #<binding that has 3>
(with-module A x) ⇒ 3

;;  #<symbol 'x'> ---[module B]--> #<binding that has 4>
(with-module B x) ⇒ 4

モジュールは、自身が持つ一部または全ての束縛を他のモジュールからも 使えるようにexportすることができます。あるモジュールXが他の モジュールYをimportすると、 モジュールYでexportされている束縛が元のモジュールXから見えるようになります。 モジュールはいくつでも他のモジュールをimportすることができます。

 
(define-module A
  (export pi)
  (define pi 3.1416))

(define-module B
  (export e)
  (define e 2.71828))

(define-module C
  (import A B))

(select-module C)
(* pi e) ⇒ 8.539748448

また、モジュールは継承することもできます。 既存のモジュールを継承したモジュールに新しい束縛を足してexportすることにより、 既存のモジュールを拡張することができます。新しいモジュールの内部からは、 継承元のモジュールの束縛が(exportされていないものも含め)全て見えます。 (新しく作られるモジュールはデフォルトでgaucheモジュールを継承しています。 新しいモジュールからgaucheの組込み手続き等が使えるのはそのためです)。 外からは、新しいモジュールには元のモジュールの全てのexportされた束縛と 新たに追加されexportされた束縛が見えます。

 
;; Module A defines and exports deg->rad.
;; A binding of pi is not exported.
(define-module A
  (export deg->rad)
  (define pi 3.1416)   ;; not exported
  (define (deg->rad deg) (* deg (/ pi 180))))

;; Module Aprime defines and exports rad->deg.
;; The binding of pi is visible from inside Aprime.
(define-module Aprime
  (extend A)
  (export rad->deg)
  (define (rad->deg rad) (* rad (/ 180 pi))))

;; Module C imports Aprime.
(define-module C
  (import Aprime)
  ;; Here, both deg->rad and rad->deg are visible,
  ;; but pi is not visible.
  )

コンパイル中のどの時点でも、「カレントモジュール」が一意に決定され、 グローバル変数の束縛はそのカレントモジュールを起点に探されます。 その変数の束縛が見付かれば、変数参照の式はその束縛へアクセスするコードとして コンパイルされます。もしコンパイラが束縛を見付けられなかった場合、 変数参照の式はカレントモジュールでマークされ、束縛の解決はランタイムへと 先送りされます。すなわち、ランタイムにその変数が使われる時点で 再びマークされていたモジュールから束縛の探索が行われます (ランタイムでのカレントモジュールからでは無いことに注意)。 束縛が見付かれば、束縛へアクセスするコードがコンパイルされたコード列に 挿入されます。見付からなければ’undefined variable’エラーが報告されます。

グローバル変数に対して適切な束縛がひとたび発見されれば、 その束縛へのアクセスはコンパイルされたコードに埋め込まれ、 その変数の束縛の探索は二度と行われません。

definedefine-syntax等の定義を行う特殊形式は カレントモジュールに束縛を挿入します。これは、importしたり継承したりしている モジュールの同名の束縛をシャドウします。

グローバル変数の束縛の解決は次の手順で行われます。 まずカレントモジュールが探されます。次に、importしているモジュールが importされた逆の順番に並べられ、それぞれについてその モジュールおよびそのモジュールの先祖(継承されているモジュール)が順に探されます。 importは遷移的ではありません;importされたモジュールがimportしているモジュール… というふうに再帰的に辿ることはしません。 最後に、カレントモジュールの先祖が順に探されます。

この順序は、複数のモジュールで同じ名前が定義され、あなたのモジュールが その両方をインポートしている場合に重要になります。 その名前があなたのモジュールで 定義されていないとして、もしモジュールAがまずimportされ、 次にBがimportされている場合、あなたのコードはBの 束縛を見ることになります。

AをimportしてBをimportした後に再びAをimport した場合、後のimportの方が効力を持ちます。すなわち、Aの束縛が 見えることになります。

もし、二つのモジュールが同名の束縛をエクスポートしており、 その両方にアクセスしたければ、一方もしくは両方の名前にプレフィクスを つけることができます。詳しくはモジュールの使用を参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13.2 モジュールとライブラリ

モジュールは実行時データ構造です。実行時に任意の名前のモジュールを 手続き的に作成することができます。

しかしほとんどのライブラリは、固有の名前空間を生成するために モジュールを用います。これにより、どの束縛をライブラリ使用者に 見せるかを制御できます。(ここでの「ライブラリ」は、R7RSのライブラリだけでなく より広い意味で使っています。)

通常ライブラリは1つ以上のSchemeソースファイル形式で提供されます。 したがって、ファイル名をモジュール名に対応づける(またはその逆の) 規約にしておけば便利です。そうすれば、たとえば、ライブラリーファイルを ロードしたり、use マクロを使ってモジュールを一動作で、 インポートしたりできます。

当分の間、Gauche はこの対応づけのための単純なルールを使用します。すなわち、 モジュール名は、例えば gauche.mop.validator のように ‘.’ (ピリオド)記号で階層的に区切って構成されます。このようなモジュールが 要求されても、現在の実行時環境に存在しない場合には、Gauche は ピリオド記号をディレクトリ区切りに変換して gauche/mop/validator のようにモジュール名からパス名に変換します。その後、 gauche/mop/validator.scm をロードパスから探します。

これが単にデフォルトの振る舞いであることに注意してください。 理論上、1つのSchemeソース・ファイルは多数のモジュールを含むことがあります。 あるいは、1つのモジュール実装は多数のファイルにまたがることもありえます。 将来、特別なケースのために、この対応付けをカスタマイズするフックを 用意するかもしれません。したがって、モジュールおよびライブラリーファイルを 扱うルーチンを書く場合には、上記のデフォルトルールを盲目的に適用しないで ください。Gaucheは module-name->pathpath->module-name という 2つの対応づけ手続き(詳細に関しては、モジュールイントロスペクション参照) を用意しています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13.3 モジュールの定義と選択

Special Form: define-module name body …

nameはシンボルでなければなりません。 名前nameを持つモジュールが存在しなければまず作成します。 それから、body … をモジュールname中で評価します。

Special Form: select-module name

名前nameを持つモジュールをカレントモジュールとします。 その名前を持つモジュールが無ければエラーとなります。

select-moduleがSchemeファイルの中で用いられた場合、 その効果はそのファイルの終了までに限られます。select-moduleを中で呼んでいる ファイルをloadやrequireしても、呼んだ側のカレントモジュールは影響を受けません。

Special Form: with-module name body …

名前nameを持つモジュールをカレントモジュールとした状態でbody … を順に評価し、最後の結果を返します。該当するモジュールが存在しなければエラーとなります。

Special Form: current-module

コンパイル時点でのカレントモジュールに評価されます。 これは手続きではなく特殊形式です。 Gaucheではモジュールはコンパイル時に静的に決定されます。

 
(define-module foo
  (export get-current-module)
  (define (get-current-module) (module-name (current-module))))

(define-module bar
  (import foo)
  (get-current-module)) ⇒ foo ; not bar

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13.4 モジュールの使用

Special Form: export spec …

[R7RS] カレントモジュールから、specで指定される束縛をexportします。 exportされた束縛は、カレントモジュールをimportしたモジュール中で見えるようになります。

specは次のどちらかの形式でなければなりません。nameexport-name はシンボルです。

name

名前nameを持つ束縛がexportされます。

(rename name exported-name)

名前nameを持つ束縛が、exported-nameという別名でexportされます。

註: Gaucheのexportは単なるスペシャルフォームで プログラムの途中に書くこともできますが、 R7RSのexportはライブラリ宣言の一部で define-libraryフォームの直下にしか書けません。 詳しくはR7RSライブラリ形式を参照してください。

Special Form: export-all

カレントモジュール中の全ての束縛をexportします。

Special Form: import import-spec …

import-specで指定されるモジュールがexportしている 束縛のすべてもしくはいくつかを、カレントモジュール中で使えるようにします。 import-specは以下の形式です。

 
<import-spec> : <module-name>
              | (<module-name> <import-option> ...)

<import-option> : :only (<symbol> ...)
                | :except (<symbol> ...)
                | :rename ((<symbol> <symbol>) ...)
                | :prefix <symbol>

<module-name> : <symbol>

module-nameで指定される名前のモジュールは このフォームがコンパイルされる時点までに存在していなければなりません。

モジュールのimportは遷移的ではありません。 つまりmodule-nameで指定されたモジュールがその内部でimport しているモジュールは自動的にカレントモジュールにはimportされてません。 モジュールの独立性を保つための設計です。この性質により、 ライブラリモジュールの作者はいくら他のモジュールを importしようとも利用者の名前空間を不意に汚染してしまう心配はありません。 (利用者からはそのモジュールでexportしている名前しか見えないからです。)

import-optionは束縛がどのようにインポートされるかを制御します。 :onlyがある場合、<symbol> …に挙げられた名前を 持つ束縛のみがインポートされます。:exceptがある場合は逆に、 エクスポートされている束縛のうち挙げられた名前を持つもの以外が インポートされます。:renameは各2要素のリストの最初の名前 を持つ束縛が2番目の名前へとリネームされます。 :prefixがあると、元の名前の前に 指定されるシンボルが付加された名前で束縛が見えるようになります。 import-optionが指定されなければ、module-nameのすべての エクスポートされた束縛がプレフィクス無しでインポートされます。

 
(define-module M (export x y)
  (define x 1)
  (define y 2)
  (define z 3))

(import M)

x ⇒ 1
z ⇒ エラー。 zはMからエクスポートされなていない

(import (M :only (y)))

x ⇒ エラー。xは:onlyリストに含まれない。

(import (M :except (y)))

y ⇒ エラー。yは:exceptにより除外されている。

(import (M :prefix M:))

x ⇒ error
M:x ⇒ 1
M:y ⇒ 2

一つ以上のインポートオプションが与えられた場合、 それは出現順に処理されます。すなわち、:prefixが 最初に現れた場合、その後に来る:only:except はプレフィックスつきの名前を使って指定しなければなりません。

註: R7RSにもimportフォームがありますが、若干構文と意味が異なります。 3つのimport形式を参照してください。

Macro: use name :key only except rename prefix

モジュールのインポートと必要に応じてファイルのロードを合わせて行う、 便利なマクロです。基本的に、(use foo) は以下のふたつのフォームと 等価です。

 
(require "foo")
(import foo)

すなわち、まず名前“foo”を持つライブラリファイルが(まだロードされて いなければ)ロードされ、その中で定義されているモジュールfooをカレントモジュールに インポートします。

キーワード引数only, except, prefiximportにインポートオプションとして渡されます。

 
(use srfi-1 :only (iota) :prefix srfi-1:)

(srfi-1:iota 3) ⇒ (0 1 2)

ファイルのロードとモジュールとは直交する概念ですが、 実用的にはモジュール毎にファイルを分割するのが便利です。 必ずしもそうする必要は無く、requireimport を別々に 使っても構いません。が、Gaucheに附属してくるライブラリはすべて、 use マクロで使えるように書かれています。

もしモジュールが一つのファイルに収めるには大きすぎる場合、一つのメインファイルと いくつかのサブファイルに分けることも出来ます。メインファイルの中でモジュールを 定義し、サブファイルをまとめてロードするか、オートロードを設定します。

実際は、与えられたモジュール名からファイルのパス名を得るのに 手続きmodule-name->pathが使われます。デフォルトの変換規則は、 モジュール名name中のピリオド‘.’を‘/’に置換 するというものです。例えば(use foo.bar.baz)

 
(require "foo/bar/baz")
(import foo.bar.baz)

となります。これはあまりScheme風ではありませんが、便利ではあります。 将来、このマッピングルールをカスタマイズする機構が導入されるかもしれません。

useされるファイルがトップレベル定義を持つ場合、ファイル内でモジュールが 明示されてることが必要です (通常はdefine-module/select-moduledefine-libraryが使われます)。 もし、“Attempted to create a binding in a sealed module: module: #<module gauche.require-base>”というエラーが出たら、 useしたファイルがモジュール指定を持っていないということです。 詳しくはrequireとprovideを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13.5 モジュールの継承

export-importメカニズムは、次のような場合をうまく処理できません。

このような場合にモジュールの継承が使えます。

Macro: extend module-name …

カレントモジュールが、module-name …に挙げられたモジュールを 継承するようにします。それまでの継承の情報は捨てられ、module-name … から計算される継承情報が有効になります。

新たに作られるモジュールはデフォルトでgaucheモジュールを継承しています。 例えばそのモジュールに(extend scheme)というフォームを入れた場合、 その時点でそのモジュールはschemeモジュール(R5RSで定義された束縛 のみを含む)を直接継承するようになります。したがって、そのフォームの後で ’import’ やその他gauche特有の束縛はそのモジュール内では 使えなくなります。

module-nameに挙げられたモジュールがまだ存在しなかった場合、 extenduseと同じメカニズムを使ってファイルをロードすることを 試みます。

モジュールは複数のモジュールを継承することができます。 丁度、クラスが複数のクラスを継承できるのと同じようにです。 多重継承の場合、次のようにしてモジュール間の優先順位が決められます。

各モジュールはmodule precedence listというモジュールのリストを 持っています。そこにリストされた順に束縛が探されます。 モジュールが複数のモジュールを多重継承した場合、継承される各モジュールの module precedence listを、次に挙げる制約を満たすようにマージ したものが新たなmodule precedence listとなります: (1) あるmodule precedence listでモジュールAがモジュールBより前に現れていたら、 結果のmodule precedence listでもAはBより前に現れる: (2) モジュールAがモジュールBよりextendフォームで前に現れていたら、 結果のmodule precedence listでもAはBより前に現れる。 この条件を満たすようなmodule precedence listが構成できない場合はエラーとなります。

例えばあなたがライブラリを3つのモジュール、 mylib.basemylib.utilmylib.systemに分けて 書いたとしましょう。次のように書けば、これらのモジュールを 一つのmylibモジュールに見せることができます。

 
(define-module mylib
  (extend mylib.system mylib.util mylib.base))

このライブラリモジュールのユーザは (use mylib) とするだけで 全てのサブモジュールのexportされた束縛を利用することができるようになります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13.6 モジュールイントロスペクション

この節では、実行時にモジュールを操作する手続きをリストします。 これらの手続きにより、例えばモジュールの内部を調べたり、手続き的に 新しいモジュールを作成したり、特定のモジュールやライブラリの存在を 調べたりすることができます。ただし、モジュールは第一にコンパイル時の 構造であることを忘れないでください。実行時にモジュールをいじくるのは、 十分にモジュールの構造を理解した上で行ってください。

Builtin Class: <module>

モジュールクラスです。

Function: module? obj

objがモジュールなら真の値を返します。

Function: find-module name

名前がシンボルnameであるようなモジュールを返します。 その名前をもつモジュールが存在しなければ、#fを返します。

Function: make-module name :key if-exists

シンボルの名前nameを持つモジュールを作成して返します。 その名前を持つモジュールが既に存在していた場合、その動作は if-existsキーワード引数で指定されます。 if-exists引数が:errorである場合(デフォルト)、 エラーが報告されます。それが#fである場合は単に#fが返されます。

モジュールを実行時に動的に生成することは、通常のスクリプトでは あまり必要とはされません。既に書かれたプログラムの解釈においては、 モジュールは名前で指定されている必要があるからです。 構文define-moduleimportextendwith-module 等はモジュールそのものではなくモジュール名を取ります。 これは、モジュールが本質的にコンパイル時の構造であるためです。 しかし、動的に作られるモジュールが有用な場合もあります—プログラムそのものが、 動的に作られる場合です。evalにモジュールを渡して、 そのような動的に作られたプログラムがそのモジュールの中で コンパイルされ評価されるようにできます。

また、name#fを渡すことで無名のモジュールを作ることもできます。 無名のモジュールはfind-moduleで探すことはできませんし、 他のモジュールからimportすることもextendされることも できません(importextendはモジュール名を必要とするからです)。 無名のモジュールは、一時的に隔離された名前空間を動的に作りたい時に 便利です。例えばネットワークで接続されたプログラムから送られた式を その中で評価して、コネクションが終了したら名前空間ごと捨ててしまうという ような場合です。無名のモジュールはシステムの内部辞書に登録されないので、 モジュールへの参照が無くなればガベージコレクトされます。

R7RSでは、environment手続きによって一時的なモジュールを 作ることもできます。scheme.eval - R7RS evalを参照してください。

Function: all-modules

現在存在する全ての名前付きモジュールのリストを返します。 無名のモジュールは含まれません。

Function: module-name module
Function: module-imports module
Function: module-exports module
Function: module-table module

モジュールオブジェクトのアクセスメソッドです。 moduleの名前(シンボル)、moduleがインポートしているモジュールのリスト、 エクスポートしているシンボルのリスト、そして シンボルから束縛へのマップを行うハッシュテーブルを返します。

もしmoduleが全てのシンボルをエクスポートしている場合は、module-exports#tを返します。

モジュールオブジェクト以外が渡された場合はエラーになります。

Function: module-parents module
Function: module-precedence-list module

モジュールの継承に関する情報を返します。 module-parentsmoduleが直接継承しているモジュールのリストを 返します。module-precedence-listmoduleのmodule precedence list (モジュールの継承参照) を返します。

Function: global-variable-bound? module symbol

symbolのグローバルなバインディングがmoduleから 可視であれば、真を返します。moduleはモジュールオブジェクトか 既存のモジュール名を示すシンボルでなければなりません。

註: 以前、この手続きの機能はsymbol-bound?という手続きで 実現されていました。symbol-bound?は非推奨となり、新しいコードは global-variable-bound?を使わねばなりません。 この変更の理由は、symbol-bound?がカレントモジュールをデフォルトと しており、またその名前からも、グローバルな束縛値があたかも (CommonLispのように)シンボルそのものの属性であるかのような誤解を招いて いたからです。そのせいで、特にコンパイル時と実行時でカレントモジュールが 異なるような場合に多くの混乱が生じていました。 新しい名前とAPIは、グローバルな束縛値についてモジュールに問い合わせている ということを明確にしています。

Function: global-variable-ref module symbol :optional default

モジュールmoduleから可視の、シンボルsymbolのグローバルな 束縛値を返します。moduleはモジュールオブジェクトか 既存のモジュール名を示すシンボルでなければなりません。 symbolに対する可視のグローバル束縛が無い場合は、 default引数があたえられていればその値を返し、 無ければエラーを通知します。

Function: module-name->path symbol

モジュール名symbolを、パス名の一部(requireprovideが 使うような)へと変換します。

Function: path->module-name string

module-name->pathの逆関数です。

特定のライブラリやモジュールがシステムにインストールされて使える状態にあるか 調べたりする場合は、ライブラリの操作を参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

4.13.7 組み込みモジュール

Gauche起動時にいくつかのモジュールがあらかじめ定義されています。

Builtin Module: null

このモジュールはR5RSで述べられている"null environment"に相当します。 R5RSの構文要素への束縛だけを含んだモジュールです。

Builtin Module: scheme

このモジュールはnullモジュール内の束縛全てに加えて、 R5RSで定義されている全ての手続きの束縛を含みます。

select-moduleによって一度nullschemeモジュールに 入ると、そこから他のモジュールに移ることはできなくなることに注意してください。 これらのモジュールからは、あらゆるモジュール操作構文が不可視だからです。

Builtin Module: gauche

このモジュールはschemeモジュール内の全ての束縛に加え、 Gaucheの組込み手続きや構文が含まれています。

Builtin Module: user

このモジュールはユーザコードがコンパイルされる既定のモジュールです。 gaucheモジュール内の全ての束縛がインポートされています。

Builtin Module: gauche.keyword
Builtin Module: keyword

GaucheがGAUCHE_KEYWORD_IS_SYMBOLモードで実行されている時は、 キーワード (:で始まるシンボル) が自動的にこれらのモジュールの中で 自分自身に束縛されます。(詳しくはキーワードを参照してください。)

keywordモジュールはキーワードの自己束縛をエクスポートせず、 gauche.keywordモジュールはエクスポートします。前者は主に 内部的に使うためのもので、プログラマが知る必要があるのは後者です。

デフォルトのモジュール継承を使っている場合、キーワードモジュールは 継承チェインの中に含まれているので、このモジュールを陽に使用する必要はありません。 gaucheを継承しないモジュールを書いていて、キーワードをクオートせずに 使いたい場合にこのモジュールをインポートしてください。 例えばR7RSプログラムやライブラリは、(import (gauche keyword)) もしくは(import (gauche base)) (後者はgauche.keywordを 継承しています)しない限り、キーワードにクオートが必要です。

次のR7RSプログラムはgauche.baseをimportすることで、 Gaucheの組み込みの識別子とともに自己束縛されたキーワードが使えるようになります。

 
;; R7RS program
(import (scheme base)
        (gauche base))  ; import gauche builtins and keywords

;; You can use :directory without quote, for it is bound to itself.
(sys-exec "ls" '("ls" "-l") :directory "/")

ただ、もうちょっと凝ったインポートをする場合は、 GAUCHE_KEYWORD_IS_SYMBOLがセットされている状態ではキーワードも 単なるインポートされるシンボルにすぎないということを覚えておいてください。 例えば次のコードではGaucheの組み込み識別子をgauche/というプレフィクス つきでインポートしています。この場合、gauche.baseの継承経由で インポートされているキーワードもプレフィクスがつけられることになります。 キーワードをいちいちプレフィクスつき、もしくはクオートつきで書きたくなければ、 gauche.keywordを別にインポートしましょう。

 
;; R7RS program
(import (scheme base)
        (prefix (gauche base) gauche/) ; use gauche builting with gauche/ prefix
        (gauche keyword))              ; imports keywords

;; Without importing gauche.keyword,
;; you need to write ':directory
(gauche/sys-exec "ls" '("ls" "-l") :directory "/")

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5. マクロ

Lisp系言語のマクロは、他の言語に見られるマクロ(例えばCプリプロセッサマクロ)とは 大きく異なります。Lispのマクロは元の言語の構文を拡張する手段なのです。 例えば、Gaucheの構文を若干変えることで他のScheme実装用に書かれたSchemeプログラムを 走らせたり、あるいは自分の問題解決に適した「ミニ言語」をデザインすることさえできます。

Gaucheは、名前の衝突を避ける安全なマクロを簡単に書ける、「衛生的な(hygienic)」 マクロをサポートします。これは、従来のLispマクロは知っていても衛生的なマクロを 初めて見る人には、やや難解に見えるかもしれません。 衛生的マクロに馴染みが無い読者向けに、紹介の節を用意しました(なぜ衛生的マクロか)。 既に衛生的マクロを知っている読者はその節は読み飛ばして構いません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.1 なぜ衛生的マクロか

Lispのマクロは、ソースコードをプログラムによって変換するものです。 マクロ変換器(macro transformer)が、ソースコードの部分木を受け取り、 加工したソースコードの部分木を返します。

伝統的なLispマクロでは、入力となるソースコードも、出力されるコードも、単なるS式でした。 Gaucheはそのタイプのマクロもdefine-macro形式でサポートしています。 例えば、whenは伝統的マクロで次のとおり書けます。

 
(define-macro (when test . body)
  `(if ,test (begin ,@body)))

このマクロが(when (zero? x) (print "zero") 'zero)のように使われたとすれば、 上記の変換器はそれを(if (zero? x) (begin (print "zero") 'zero))と 書き換えます。一見問題なさそうですね。

けれども、beginifが通常とは違う意味で束縛されている環境で whenが使われたらどうなるでしょう。

 
(let ([begin list])
  (when (zero? x) (print "zero") 'zero))

展開結果は次の通りになります。

 
(let ([begin list])
  (if (zero? x) (begin (print "zero") 'zero)))

これでは意図した通りには動きません。展開された結果の中のbeginが ローカル変数と解釈されてしまいます。

これは変数捕捉の一形態です。Lispのマクロによる変数捕捉というと、 別の形態、すなわちマクロにより導入される一時変数がマクロに渡された式内の 変数を意図せずに捕捉してしまうことが話題に上ることが多いのですが、 そちらはgensymを使って一時変数を決して衝突しない名前にすることで 簡単に回避できます。

しかし上の例のような変数捕捉はgensymでは回避できません。外側の (let ([begin list]) ...)の部分はマクロを書く人には制御できない からです。マクロ作成者が、この衝突を避けるために出来ることは何もありません。 せいぜい、マクロ使用者がそんな使い方をしないように祈るだけです。 もちろん、beginを再束縛するなんて誰もやろうとは思わないかもしれませんが、 同様の衝突はあなたのライブラリが提供するものも含めあらゆるグローバル変数について 起こり得るのです。

異なるLisp方言はそれぞれ異なる方法でこの問題に対処してきました。 Common Lispは、ある意味プログラマの常識に頼ります。マクロ作成者は ライブラリのパッケージを分けることで、偶然名前が衝突してしまう危険性を 減らせますが、マクロ使用者が同じパッケージの名前を再束縛することを防げるわけではありません。 (Common Lispの仕様ではCL標準のシンボルをローカルに再束縛した場合の 動作は未定義とされていますが、ユーザが提供するライブラリについては 何も決められていません。)

Clojureは、名前空間プレフィクスによって直接トップレベル変数を参照する方法を 導入したので、同名のローカル変数束縛をバイパスして意図するトップレベル変数を確実に 参照できます (また、Clojureのquasiquoteは高機能で、自由変数を自動的に プレフィクスつきのトップレベル変数へと変換してくれます。) この方法はローカルマクロが存在しない限りはうまくいきます。 ローカルマクロがあると、後の例で見るように、複数の同名のローカル変数束縛を 区別する必要が出てきます。Clojureの方法はローカル変数束縛とトップレベル変数束縛を 区別できるだけです。Clojureにはローカルマクロが無いのでそれでよいのですが、 Schemeは一様で直交する定理を重視するので、レキシカルスコープを持つローカル関数があるなら、 レキシカルスコープを持つローカルマクロもやっぱり欲しいわけです。

レキシカルスコープを持つローカルマクロを見てみましょう。説明のために、 ローカルなマクロ束縛を書けるlet-macroという形式があると仮定します。 (実際にはlet-macro形式はありません。マクロ変換器の指定方法が やや異なるlet-syntaxletrec-syntaxという形式があります。 ただ、ここではdefine-macroと似たような形で例を示す方がわかりやすいので、 そのようなlet-macroがあるものとして説明します。)

 
(let ([f (^x (* x x))])
  (let-macro ([m (^[expr1 expr2] `(+ (f ,expr1) (f ,expr2)))])
    (let ([f (^x (+ x x))])
      (m 3 4))))    ; [1]

ローカルな識別子mは、二つの式を引数として取り、S式を返すマクロ変換器に 束縛されます。従って、[1]の(m 3 4)(+ (f 3) (f 4))へと展開されます。上の式を展開結果を使って 書き直してみます (展開後はlet-macroフォームはもはや必要ないので 展開結果には含めていません)。

 
(let ([f (^x (* x x))])
  (let ([f (^x (+ x x))])
    (+ (f 3) (f 4))))  ; [2]

さてここで問題です。展開結果に現れた[2]のフォーム内のfは、どちらの fを参照すべきでしょう。上の式を文字通り解釈するなら、 より内側にある(^x (+ x x))への束縛となります。 けれども、Schemeのスコープ規則にしたがえば、 外側のコードは、内側にどんなコードが来るかに関わらず意味が決まって欲しいわけです。

 
(let ([f (^x (* x x))])
  (let-macro ([m (^[expr1 expr2] `(+ (f ,expr1) (f ,expr2)))])
    ;; ここに書かれたコードがうっかり外側のコードに影響を与えてしまう
    ;; のは避けたい。
    ))

マクロ作成者は内側のletfをシャドウしてしまうことを 知らないかもしれません(内側のフォームは他のコードをincludeしている かもしれませんし、また他の人が、ローカルマクロが外側のfを参照することに 気づかずに内側のコードを変更してしまうかもしれません。)

let-macroの中に置かれるコードが何であれローカルマクロが動作するためには、 マクロの展開結果から「外側のf」を確実に参照する方法が必要です。 基本的なアイディアは、 マクロ変換器mにより挿入される名前(f+)に 「印」をつけて、二つのfを区別するというものです。

例えば、フォーム全体を書き直して、対応するローカル変数がユニークな名前を持つように リネームしたらどうでしょう:

 
(let ([f_1 (^x (* x x))])
  (let-macro ([m (^[expr1 expr2] `(+ (f_1 ,expr1) (f_1 ,expr2)))])
    (let ([f_2 (^x (+ x x))])
      (m 3 4))))

こうしておけばナイーブな展開でもスコープが正しく保たれます。つまり、 mの展開結果に現れるf_1は内側のf_2と衝突しません。

 
(let ([f_1 (^x (* x x))])
  (let ([f_2 (^x (+ x x))])
    (+ (f_1 3) (f_1 4))))

(ラムダ計算において、レキシカルスコープを保ったまま高階関数を扱う際に 似たようなリネーム戦略を見たことがあるかもしれません)

上の例ではマクロの定義時に現れるf(リネーム後はf_1)が マクロの使用時に現れるf (リネーム後はf_2) によって シャドウされることを避ける話でした。

一方、もう一つのタイプの変数捕捉 (より頻繁に話題に上る、gensymで回避できる捕捉) は、マクロ使用時の変数がマクロ定義時に導入される束縛によりシャドウされてしまう という問題です。これについても、同じリネーム戦略が使えます。 次の例を見てみましょう。

 
(let ([f (^x (* x x))])
  (let-macro ([m (^[expr1] `(let ([f (^x (+ x x))]) (f ,expr1)))])
    (m (f 3))))

ローカルマクロはfの束縛を導入しています。 一方、マクロの使用時(m (f 3))に、fへの参照が含まれています。 後者のfは外側のfを指すべきです。なぜならマクロを使っている フォームは字句上、マクロ定義のletの外側にあるからです。

fをレキシカルスコープによってリネームすれば次のようになるでしょう。

 
(let ([f_1 (^x (* x x))])
  (let-macro ([m (^[expr1] `(let ([f_2 (^x (+ x x))]) (f_2 ,expr1)))])
    (m (f_1 3))))

これだと展開しても二つのfはきちんと区別されます。

 
(let ([f_1 (^x (* x x))])
  (let ([f_2 (^x (+ x x))])
    (f_2 (f_1 3))))

以上が、衛生的マクロの原理です (まあ、だいたいは)。 ただし、実際の実装では、すべてを一気にリネームすることはありません。 後者の例のようなケースで注意すべき点があります。後者の例では静的に ff_2にリネームしましたが、より複雑な場合にマクロ展開器が 再帰的に自分を呼ぶことがあり、その場合にはマクロの展開ごとに挿入されるfを 別のものとして扱う必要があります。 従って、マクロの展開とリネームは協調して動作しなければなりません。

それを実装する戦略はいくつか考えられます。そして、Scheme標準は実装を どれかひとつの戦略に縛ってしまうことを良しとしません。 結果的に、標準はマクロシステムが満たすべき性質を、二つの簡潔な 文で示すに止まります:

マクロ展開器が識別子(変数かキーワード)の束縛を挿入した場合、 識別子はそのスコープ内で実効的にリネームされ、 他の識別子との衝突を避けられる。

マクロ展開器が識別子の自由参照を挿入した場合、その識別子は展開器が定義された場所から 見える束縛を参照し、マクロが使われる場所を囲むローカル束縛には影響されない。

これを読んだだけでは、これらの性質をいかにして実現するかは すぐにはわからないかもしれません。そして既存の衛生的マクロ(syntax-rulesなど)は このいかにしての部分を隠しています。それが、 衛生的マクロをとっつきにくく思う理由の一つかもしれません。 これはある意味、継続に似ています。継続の仕様はごく簡潔に述べられていて、 最初に読んだときにはどう動くかさっぱりわからないかもしれません。 しかし経験を積んで使うのに慣れた後でもう一度元の説明を読むと、 必要十分なことが書いてあるとわかるのです。

この節ではいかにして衛生的マクロがこれらの性質を 実現しているかについての詳細には触れませんでしたが、 衛生的マクロが何をして、何のために必要かについて ある程度示せたのではないかと思います。 以降の節では、Gaucheがサポートする衛生的マクロシステムについて 例を交え紹介してゆきます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.2 衛生的マクロ

マクロ束縛

以下のフォームはtransformer-specで作られるマクロ変換器と nameの束縛を作ります。外側のスコープにnameの束縛があれば、 それはシャドウされます。

トップレベル束縛の場合、nameに他のモジュールからインポートされたり 継承されている束縛があれば、それをシャドウすることになります (モジュール参照)。 (註:モジュール内でのトップレベル束縛がインポートした束縛をシャドウするのは Gaucheの拡張です。R7RSではインポートした束縛の再定義はしてはいけないことに なっているので、ポータブルなコードでは避けて下さい)。

同じスコープで同じ名前を複数回束縛した場合の動作は未定義です。

transformer-specsyntax-rulesフォーム、 er-macro-transformerフォーム、あるいは他のマクロキーワードか 構文キーワードです。これについては後述します。

Special Form: define-syntax name transformer-spec

[R7RS] トップレベルで使われた場合、このフォームはトップレベルのnametransformer-specで定義されるマクロ変換器に束縛します。

lambdalet等の本体の宣言部分に使われた場合 (内部define-syntax)、 その本体内のスコープでnameを束縛します。 内部defineがletrec*に変換されるのと同じように、 内部define-syntaxはletrec-syntaxへと変換されます。

Special Form: let-syntax ((name transformer-spec) …) body
Special Form: letrec-syntax ((name transformer-spec) …) body

[R7RS] ローカルマクロを定義します。各nameが 対応するtransformer-specで定義されるマクロ変換器へと束縛された 環境を作りbodyを評価します。 let-syntaでは、transformer-speclet-syntaxを 囲むスコープ内でtransformer-specを評価するのに対し、 letrec-syntaxではnameの束縛がなされた環境で transformer-specを評価します。つまりletrec-syntaxは 相互再帰的なマクロを定義できます。

Transformer specs

transformer-specは、マクロ展開器へと評価される特別な式です。 マクロ変換器はコンパイル時に実行されるため、他の式とは異なった段階で評価されます。 そのためにいくらか制限があります。

現在のところ、以下の上げる式しか許されていません。

  1. syntax-rulesフォーム。これは「高レベル」マクロと呼ばれ、 パターンマッチングのみによってマクロを定義します。 これはSchemeとは異なる一種の宣言的言語で、 マクロの段階や衛生の問題をボンネットの下に隠してしまいます。 ある種のマクロはsyntax-rulesでより簡単に書けます。 詳しくはSyntax-rules pattern langaugeを参照してください。
  2. er-macro-transfomerフォーム。 これはexplicit renaming(ER)マクロを定義します。 ERマクロでは、必要な衛生を保ちながら、任意のSchemeコードを使って変換を書けます。 伝統的なLispのマクロは、ERマクロでリネームを使わない特別な場合と考えられます。 詳しくはExplcit-renamingマクロ変換器を参照してください。
  3. マクロキーワードか構文キーワード。これはGauche独自の拡張で、 既存のマクロキーワードや構文キーワードの別名を定義するものです。
     
    (define-syntax si if)
    (define écrivez write)
    
    (si (< 2 3) (écrivez "oui"))
    

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.2.1 Syntax-rules pattern langauge

Special Form: syntax-rules (literal …) clause1 clause2 …
Special Form: syntax-rules ellipsis (literal …) clause1 clause2 …

[R7RS] This specifies a macro transformer by pattern matching.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.2.2 Explcit-renamingマクロ変換器

Special Form: er-macro-transformer procedure-expr

procedure-exprからマクロ変換器を作ります。 作られたマクロ変換器は、define-syntaxlet-syntaxletrec-syntaxにより構文キーワードに束縛されなければなりません。 マクロ変換器の他の用途は定義されていません。

procedure-exprは3つの引数、formrenameid=?を 取る手続きへと評価される式です。

form引数には、マクロ呼び出しのS式そのものが渡されます。 procedure-exprはマクロ展開の結果をS式として返します。 この点は、伝統的なマクロとよく似ています。実のところ、 renameid=?を無視すれば、セマンティクスは伝統的な(非衛生な)マクロと 同じになります。次の例を見てください (この例ではmatchを使っています。マクロの入力を分解するのにも 手軽なツールです。)

 
(use util.match)

;; Unhygienic 'when-not' macro
(define-syntax when-not
  (er-macro-transformer
    (^[form rename id=?]
      (match form
        [(_ test expr1 expr ...)
         `(if (not ,test) (begin ,expr1 ,@expr))]
        [_ (error "malformed when-not:" form)]))))

(macroexpand '(when-not (foo) (print "a") 'boo))
  ⇒ (if (not (foo)) (begin (print "a") 'boo))

衛生を気にする必要がない場合は、これでも十分です。 例えばマクロを自分で書いたコードの中だけで使い、 すべてのマクロ呼び出しを把握していて名前の衝突が起きないことを知っている場合です。 けれども、このwhen-notマクロを広く使えるようにするなら、 マクロの使われる場所での名前の衝突からの防御が必要です。 たとえば、次のとおり呼び出されたとしてもちゃんと動くようにしたい場合です。

 
(let ((not values))
  (when-not #t (print "This shouldn't be printed")))

procedure-exprに渡されるrename引数は、 シンボル(正確には、シンボルか識別子)を取り、それをマクロ定義時の環境を保持する ユニークな識別子へと実質的にリネームする手続きです。 リネームされた識別子はマクロ使用時の環境には影響を受けません。

大雑把なルールとして、マクロの出力に挿入する識別子はすべてrenameを通すことを 徹底すれば、衛生は保たれます。when-notマクロの例では、 マクロの出力にifnotbeginを挿入していますから、 衛生的なバージョンは次のとおり書けます。

 
(define-syntax when-not
  (er-macro-transformer
    (^[form rename id=?]
      (match form
        [(_ test expr1 expr ...)
         `(,(rename 'if) (,(rename 'not) ,test)
            (,(rename 'begin) ,expr1 ,@expr))]
        [_ (error "malformed when-not:" form)]))))

でもこれは面倒ですし読みづらいですね。そこでGaucheでは、 補助マクロquasirenameを用意しています。これはquasiquoteのように 動作しますが、フォーム中の識別子をリネームしてゆきます。詳しくは後述の quasirenameのエントリを参照してください。quasirenameを使うと 衛生的なwhen-notはこうなります:

 
(define-syntax when-not
  (er-macro-transformer
    (^[form rename id=?]
      (match form
        [(_ test expr1 expr ...)
         (quasirename rename
           (if (not ,test) (begin ,expr1 ,@expr)))]
        [_ (error "malformed when-not:" form)]))))

シンボルをリネームせずに挿入すれば、意図的に衛生を破ることができます。 次のコードはアナフォリック(前方照応的)なwhenを定義しています。 つまり、テスト式の結果が、expr1 exprs … からitという 変数で参照できるということです。 itの束縛はマクロ呼び出し箇所には無かったもので、 マクロ展開器により挿入されるので、これは非衛生マクロになります。

 
(define-syntax awhen
  (er-macro-transformer
    (^[form rename id=?]
      (match form
        [(_ test expr1 expr ...)
         `(,(rename 'let1) it ,test     ; 'it' is not renamed
             (,(rename 'begin) ,expr1 ,@expr))]))))

quasirenameを使う場合、itがリネームされないようにするには ,'itと書きます。

 
(define-syntax awhen
  (er-macro-transformer
    (^[form rename id=?]
      (match form
        [(_ test expr1 expr ...)
         (quasirename rename
           (let1 ,'it ,test
             (begin ,expr1 ,@expr)))]))))

使用例を見てみましょう。

 
(awhen (find odd? '(0 2 8 7 4))
  (print "Found odd number:" it))
 ⇒ prints Found odd number:7

最後に、procedure-exprid=?引数はふたつの引数を取り、 それらがともに識別子であって、しかも同じ束縛を参照するか束縛されていないか、という 場合に限り#tを返します。 これはリテラル構文キーワード(condcaseフォームのelse等) を比較するのに使えます。

下のif=>マクロはifと同じように動作しますが、 (if=> test => procedure)のように呼ばれた場合、あ testが偽でない場合に、その結果を引数にしてprocedureを呼び出します。 シンボル=>は衛生的に比較されます。つまり、マクロ定義時と同じ束縛を 参照している場合にのみ有効となります。

 
(define-syntax if=>
  (er-macro-transformer
    (^[form rename id=?]
      (match form
        [(_ test a b)
         (if (id=? (rename '=>) a)
           (quasirename rename
             (let ((t ,test))
               (if t (,b t))))
           (quasirename rename
             (if ,test ,a ,b)))]))))

(rename '=>)とすることで、マクロ定義時における=>の束縛を 参照する識別子を手に入れ、id=?でそれをマクロ引数から渡された式と 比較しています。

 
(if=> 3 => list)  ⇒ (3)
(if=> #f => list) ⇒ #<undef>

;; 第二引数が=>でなければ、if=>は通常のifと同じ:
(if=> #t 1 2)     ⇒ 1

;; 下の例ではマクロ呼び出しでの=>の束縛がマクロ使用時の束縛と違っているため、
;; => はリテラルと認識されず、if=> は通常のifとして振る舞う。
(let ((=> 'oof)) (if=> 3 => list)) ⇒ oof
Macro: quasirename renamer form

form中の「リテラル」な部分 (unquoteunquote-splicingの外側) に現れるシンボルや識別子がrenameによってリネームされることを除いて、 準クオートのように動作します。

例えば次のフォームは:

 
(quasirename r (a ,b c "d"))

次のとおり書くのと同じです:

 
(list (r 'a) b (r 'c) "d")

この手続きはマクロ専用というわけではありません。 renamerはシンボルか識別子を取る手続きであれば何でも構いません。

 
(quasirename (^[x] (symbol-append 'x: x)) (+ a ,(+ 1 2) 5))
  ⇒ (x:+ x:a 3 5)

ただ、ERマクロを書く際にとても便利なのは確かです。次の2つを比べてみてください。

 
(use util.match)

;; using quasirename
(define-syntax swap
  (er-macro-transformer
    (^[f r c]
      (match f
        [(_ a b) (quasirename r
                   (let ((tmp ,a))
                     (set! ,a ,b)
                     (set! ,b tmp)))]))))

;; not using quasirename
(define-syntax swap
  (er-macro-transformer
    (^[f r c]
      (match f
        [(_ a b) `((r'let) (((r'tmp) ,a))
                     ((r'set!) ,a ,b)
                     ((r'set!) ,b (r'tmp)))]))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.3 伝統的なマクロ

Special Form: define-macro name procedure
Special Form: define-macro (name . formals) body …

変換子が procedure である大域マクロ name を定義します。 2番目のフォームは、以下のフォームの簡易記法です。

 
(define-macro name (lambda formals body …))

コンパイラが (name arg …) というフォームを見つけると、 arg … を引数として procedure を呼び出します。 procedure が戻ると、コンパイラは元のフォームの場所に返されたフォームを 挿入し、再度それをコンパイルします。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.4 マクロの展開

Function: macroexpand form
Function: macroexpand-1 form

form がリストで、その最初の要素が大域的にマクロに束縛された 変数であるならば、macroexpand-1はそのマクロ変換子を実行し、 展開されたフォームを返します。そうでなければ、form をそのまま 返します。

macroexpand は、form が展開できなくなるまで macroexpand-1 を繰り返します。

これらの手続きは、大域的に定義されたマクロを展開するために使うことが できます。

Function: macroexpand-all form

form中にあるマクロを全て展開します。結果の中に残るのは、 関数呼び出しとGaucheの組み込み構文だけになります。 formは現在のモジュール中のトップレベルにあるものと解釈されます (現在のモジュールについてはモジュールのセマンティクスを参照してください。 この概念はちょっとややこしいです。コンパイル時と実行時で 現在のモジュールが異なる場合があるからです。)

form中で導入されるローカル変数は全て、衝突を避けるためにリネームされます。 ローカル変数が全て固有の名前を持つようになるので、letフォームはすべて letrecで表されます(letによる束縛が他の束縛をシャドウしないと わかっていれば、letletrecに置き換えても意味は変わりません)。

注意: もしform内で呼ばれているマクロが、他のモジュール中にあるグローバル 変数への参照を挿入した場合、現在の実装ではその情報は失われてしまいます。 いくつか、その問題を修正する方法は考えられるのですが(例えば他のモジュール中に グローバル変数参照は識別子オブジェクトのまま残しておくとか、 with-moduleフォームに変換するとか、 そういったケースのための特殊構文を導入するとか)、今のところどうするか 決まっていません。なので、現在のふるまいにあまり依存しないようにしてください。 今のところ、この手続きは、マクロの展開結果をインタラクティブに確かめる用途に 限って使うのが安全です。

 
(macroexpand-all
 '(letrec-syntax
      [(when-not (syntax-rules ()
                   [(_ test . body) (if test #f (begin . body))]))]
    (let ([if list])
      (define x (expt foo))
      (let1 x 3
        (when-not (bar) (if x))))))
 ⇒ (letrec ((if.0 list)) 
     (letrec ((x.1 (expt foo)))
       (letrec ((x.2 '3)) 
        (if (bar) '#f (if.0 x.2)))))
Special Form: %macroexpand form
Special Form: %macroexpand-1 form

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

5.5 マクロユーティリティ

Macro: syntax-error msg arg …
Macro: syntax-errorf fmt arg …

これらはerrorerrorf (例外の通知参照) と ほぼ同じですが、実行時ではなくマクロ展開時(すなわち、コンパイル時)に エラーを通知するところが異なります。

これらの手続きは、マクロの誤った使い方を、 マクロ展開ルーチンの出す複雑なエラーではなく、 分かりやすい方法でユーザーに通知するのに使えます。 そのため、arg …はまず下に述べるunwrap-syntaxに渡されて 内部の構文的束縛情報を取り除いた後でこれらの手続きに渡されます。

 
(define-syntax my-macro
  (syntax-rules ()
    ((_ a b)   (foo2 a b))
    ((_ a b c) (foo3 a b c))
    ((_ . ?)
     (syntax-error "malformed my-macro" (my-macro . ?)))))

(my-macro 1 2 3 4)
  ⇒ error: "malformed my-macro: (mymacro 1 2 3 4)"
Function: unwrap-syntax form

formの内部的な構文情報を取り除きます。 健全なマクロの実装のために、マクロ拡張ルーチンはマクロ内のシンボルを 識別子(identifier)に置き換えます。識別子はそれが現れる構文的環境を 捕捉します。それはコンパイラに取っては必要な情報ですが、 エラーメッセージに現れるとユーザーを混乱させるもとにもなります。 そこで、この手続きではform中の識別子をもとのシンボルに 置換しなおします。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6. 組み込みライブラリ


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.1 型とクラス

Schemeは動的な強い型付けの言語です。つまり、全ての値は 実行時に自分の型を知っていて、その型が値に適用できる操作を決定します。

Gaucheでは、クラスによって型を記述します。クラス自身もオブジェクトであり、 実行時に取り扱うことができます。新たなクラスを作成することで、 既存の型とは異なるユーザ定義型を使うことができます。

R6RSから、Schemeにも新しい型を定義する機能が標準で備わりました。 define-record-typeを使います。Gaucheでも、 gauche.recordモジュールを使うことでレコード型を定義できます。 gauche.record - レコード型を参照してください。内部的にはレコード型は クラスで実装されています。

この節ではもっとも基本的な型システムへのインタフェースを説明します。 新たなクラスを定義し、そのインスタンスを作る方法については オブジェクトシステムを参照してください。

あらかじめ定義されているクラスはグローバル変数に束縛されています。Gaucheでの 慣習として、クラスを格納している変数は<string>のように <>で囲まれた名前を持ちます。 (この<>は文法的には何ら特別な意味を持ちません。 普通に変数名として使える文字です)。 組み込みの型に対応するクラスについては、この章の中で順に紹介してゆきます。 まずは次のいくつかのクラスから始めましょう。

Builtin Class: <top>

全ての型のスーパータイプを表現するクラスです。 つまり、どんなクラスXに対しても(subtype? X <top>)#tであり、 どんなオブジェクトxに対しても(is-a? x <top>)#tです。

Builtin Class: <bottom>

全ての型のサブタイプを表現するクラスです。 どんなクラスXに対しても(subtype? <bottom> X)#tであり、 どんなオブジェクトxに対しても(is-a? x <bottom>)#fです。

<bottom>型のインスタンスは存在しません。

註: <bottom>は全ての型のサブタイプですが、そのクラス順位リスト (class precedence list, CPL)には<bottom><top> 以外のクラスは含まれていません。全ての型を線形に並べることは常に可能である とは限らず、まだそうであったとしても新たなクラスの定義や既存のクラスの 再定義のたびに<bottom>のCPLを検査してアップデートすることは 高くつくでしょう。subtype?is-a?といった手続きは <bottom>を特別扱いしています。

<bottom>の使いどころのひとつは、applicable?手続きです。 Procedure class and applicabilityを参照のこと。

Builtin Class: <object>

このクラスは、ユーザ定義されたクラスのスーパータイプを表現するクラスです。

Function: class-of obj

objのクラスを返します。

 
(class-of 3)         ⇒ #<class <integer>>
(class-of "foo")     ⇒ #<class <string>>
(class-of <integer>) ⇒ #<class <class>>

註: Gaucheでは、ユーザ定義クラスを再定義することができます。 新たな定義でインスタンスの構造が変更された場合、以前のクラスから作られた インスタンスにclass-ofを適用すると、インスタンスが新しいクラスに 適合するようにアップデートされます。詳しくはクラスの再定義を参照して ください。インスタンスアップデートを避けるにはcurrent-class-ofを 使います(インスタンスへのアクセス参照)。

Function: is-a? obj class

objclassのインスタンスであるか、classのサブクラスの インスタンスである場合に、真を返します。

 
(is-a? 3 <integer>)   ⇒ #t
(is-a? 3 <real>)      ⇒ #t
(is-a? 5+3i <real>)   ⇒ #f
(is-a? :foo <symbol>) ⇒ #f

註:objのクラスが再定義されていた場合、is-a?はインスタンスアップデートを トリガします。クラスの再定義を参照してください。

Function: subtype? sub super

クラスsubがクラスsuperのサブクラスであれば(subsuperが 同じである場合も含め)#tを、そうでなければ#fを返します。

(subtype?という名前はCommon Lispのsubtypepから来ています)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2 等価性と比較

ふたつのオブジェクトを比較するというのは簡単な話のように思えるかもしれません。 しかし詳しく見てゆくと、一筋縄ではいかない特別なケースが出てきます。 ふたつの手続きが等しいとはどういうことでしょうか。 複素数を何らかの基準で順番に並べるにはどうすばいいでしょう。 一般的な回答は無く、目的によるとしか言えません。 そこで、Schemeでは(Gaucheも)、いくつかの選択肢を用意しておき、 さらに必要ならばユーザが独自の定義を与えることもできるようにしています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.1 等価

Schemeには等価性を判定する汎用的な述語が3つあります。 また、これらの他に、いくつかの型はその型同士で使える比較手続きを持っています。

Function: eq? obj1 obj2

[R7RS] 最も高速で、細かい区別ができる述語です。 obj1obj2が同一(identical)のオブジェクト、 すなわち、メモリ上の同じオブジェクトを表している場合に#tを返します。 特に、二つのシンボルが同一かどうかを比較するのにeq?が使えます。 ヒープアロケートされるオブジェクトに対しては、ポインタ比較と考えても良いでしょう。

真偽値もeq?で比較できますが、文字と数値はたとえ値が同じオブジェクトであっても 互いにeq?になるかどうかはわかりません。 同一性比較で文字や数値も扱う必要がある場合は、下のeqv?を使いましょう。

 
(eq? #t #t)               ⇒ #t
(eq? #t #f)               ⇒ #f
(eq? 'a 'a)               ⇒ #t
(eq? 'a 'b)               ⇒ #f
(eq? (list 'a) (list 'a)) ⇒ #f
(let ((x (list 'a)))
  (eq? x x))              ⇒ #t
Function: eqv? obj1 obj2

[R7RS] obj1obj2がともに正確な数値、もしくはともに(NaN以外の)不正確な数値である場合、 (= obj1 obj2)が真であれば#tが、偽であれば#fが 返されます。 obj1obj2がともに文字である場合、 (char=? obj1 obj2)が真であれば#tが、偽であれば#fが 返されます。 それ以外の場合は、Gaucheではeqv?eq?と同じです。

 
(eqv? #\a #\a)             ⇒ #t
(eqv? #\a #\b)             ⇒ #f
(eqv? 1.0 1.0)             ⇒ #t
(eqv? 1 1)                 ⇒ #t
(eqv? 1 1.0)               ⇒ #f
(eqv? (list 'a) (list 'a)) ⇒ #f
(let ((x (list 'a)))
  (eqv? x x))              ⇒ #t

NaNの比較には少々奇妙なところがあります。数値比較は、引数に一つでもNaNが 含まれていれば失敗します。したがって(= +nan.0 +nan.0)は常に#fと なります。けれども、(eq? +nan.0 +nan.0)(eqv? +nan.0 +nan.0)#tを返すことがあるかもしれません。

Function: equal? obj1 obj2

[R7RS+] obj1obj2がリストやベクタなどの複合型である場合、 equal?は再帰的に対応する要素同士をequal?で比較してゆきます。 そうでなければ、equal?eqv?と同じようにふるまいます。

もしobj1obj2が互いにeqv?でなく、 組み込み型でもなく、かつ両者のクラスが等しい場合、equal?は ジェネリックファンクションobject-equal?を呼びます。 object-equal?にメソッドを定義することにより、 ユーザ定義のデータ型に対するequal?の振るまいを拡張することができます。

 
(equal? (list 1 2) (list 1 2)) ⇒ #t
(equal? "abc" "abc")           ⇒ #t
(equal? 100 100)               ⇒ #t
(equal? 100 100.0)             ⇒ #f

註: この手続きは、obj1obj2がともにペアやベクタを介した 循環構造を持っている場合もR6RSやR7RSに規定されるように値を返します。 ただし、循環構造がユーザ定義データ型を間に挟んでいる場合は 終了しない可能性があります。

Generic Function: object-equal? obj1 obj2

equal?が未知のオブジェクトに対して呼ばれた場合、 このジェネリックファンクションが呼ばれます。自分で定義したクラスに対して このメソッドを定義することにより、equal?で等価判定が行えるように なります。メソッドは、obj1obj2が等価ならば#tを、 そうでなければ#fを返さねばなりません。 オブジェクトの各要素に対して再帰的に等価判定を行いたい場合は、 object-equal?を直接呼ぶのではなく、equal?を各要素に対して 呼ぶようにして下さい。

 
(define-class <foo> ()
  ((x :init-keyword :x)
   (y :init-keyword :y)))

(define-method object-equal? ((a <foo>) (b <foo>))
  (and (equal? (slot-ref a 'x) (slot-ref b 'x))
       (equal? (slot-ref a 'y) (slot-ref b 'y))))

(equal? (make <foo> :x 1 :y (list 'a 'b))
        (make <foo> :x 1 :y (list 'a 'b)))
  ⇒ #t

(equal? (make <foo> :x 1 :y (make <foo> :x 3 :y 4))
        (make <foo> :x 1 :y (make <foo> :x 3 :y 4)))
  ⇒ #t
Method: object-equal? (obj1 <top>) (obj2 <top>)

このメソッドは、ユーザ定義クラスの2つのインスタンスがequal?であるかを 調べる際に、そのクラスに特殊化したメソッドが無かった場合を捕まえるものです。

このメソッドが呼ばれると、まず登録されたデフォルト比較器の中にobj1obj2を共に処理できるものがあるかどうかを調べ、 あればその比較器の等価述語を使って比較します。デフォルト比較器が引数を処理できなければ #fを返します。デフォルト比較器については 用意されている比較器を参照してください。特に default-comparatorcomparator-register-default!の 項目を見てください。

註: もし、このメソッドと全く同じスペシャライザを 指定してobject-equal?メソッドを定義すると、それはこのメソッドを 置き換えることになり、default-comparatorの動作が壊れることになります。 将来のGaucheのバージョンではそのような再定義は禁止される予定です。 うっかりそういう再定義を行わないように気をつけてください。

しばしば、ふたつの複合型オブジェクトに関して、両者がトポロジー的に等しいこと、 すなわち一方が共有する部分構造を持っている場合にもう一方も同じように部分構造を 共有しているかどうかを調べたいことがあります。equal?はその目的には 使えません。モジュールutil.isomorphの提供するisomorphic?が その目的に使えます。(util.isomorph - 同型判定参照)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.2 比較

等価性はオブジェクトが等しいかどうかしか判断しませんが、しばしば オブジェクトの順序関係を調べたい場合もあります。 ここでも、唯一の「万能順序」があるわけではありません。例えばある複素数が 別の複素数より大きいか小さいかというのは数学的には意味をなしませんが、 数値を含むオブジェクトのリストをソートして一貫性のある結果を得たい場合は 何らかの適当な順序づけを決めておくことは役に立ちます。

Function: compare obj1 obj2

一般的な比較手続きです。obj1obj2より小さければ-1を、 等しければ0を、大きければ1を返します。

obj1obj2が比較できない場合はエラーとなりますが、 compareは多くのSchemeオブジェクト間に全順序を定義しているので、 様々なオブジェクトの比較に使うことができます。この全順序は、 srfi-114で定義されているものの上位互換です。

組み込みオブジェクトのうち、自然な順序が定義されているものはそれに従って順序づけられます (例えば実数はその数値の大小で、文字はchar<等を使って比較されます)。 また便宜上、通常の意味で大小が定義されないオブジェクトについても ある程度表面的な順序が定義されます。例えば複素数は、まず実部の大小によって 順序づけられ、次に虚部の大小によって順序づけられます。 すなわち、1+i2-i22+iはこの順番に並びます。

#f#tより前に来ます。

リストは辞書順で比較されます。すなわち、まず共通のプリフィクスを除きます。 一方が()でもう一方がそうでなければ、()である方が先にきます。 どちらの尾部も空でなければ、尾部の先頭要素同士を比べます。 (この定義により、空リストは全てのリストの「最小」のものになります)。

(ユニフォームベクタを含む)ベクタは、まず長さで比較され、 長さが同じ場合は要素同士が左から比較されます。 リストや文字列とは異なることに注意。

 
(compare '(1 2 3) '(1 3))
  ⇒ -1  ; (1 2 3) is smaller
(compare '#(1 2 3) '#(1 3))
  ⇒ 1   ; #(1 3) is smaller
(compare "123" "13")
  ⇒ -1  ; "123" is smaller

二つのオブジェクトがともに<object>のサブクラスであれば、 ジェネリックファンクションobject-compareが呼ばれます。

二つのオブジェクトが異なる型で、少なくとも一方が<object>でない時は、 両者の型により順序が決まります。srfi-114は組み込み型について 次の順序を定めています。

  1. Empty list.
  2. Pairs.
  3. Booleans.
  4. Characters.
  5. Strings.
  6. Symbols.
  7. Numbers.
  8. Vectors.
  9. Uniform vectors (u8 < s8 < u16 < s16 < u32 < s32 < u64 < s64 < f16 < f32 < f64)
  10. 他のオブジェクトすべて
Generic Function: object-compare obj1 obj2

このジェネリックファンクションを特殊化することで compare手続きをユーザ定義クラスに対して動作するように拡張できます。

このメソッドは以下のいずれかの値を返さねばなりません。 -1 (obj1obj2より前)、 0 (obj1obj2は等しい)、 1 (obj1obj2より後)、 #f (obj1obj2は順序づけできない)。

Method: object-compare (obj1 <top>) (obj2 <top>)

このメソッドは、ユーザ定義クラスの2つのインスタンスをcompareする 際に、そのクラスに特殊化したメソッドが無かった場合を捕まえるものです。

このメソッドが呼ばれると、まず登録されたデフォルト比較器の中にobj1obj2を共に処理できるものがあるかどうかを調べ、 あればその比較器の比較手続きを使って両者の順序を判定します。 デフォルト比較器が引数を処理できなければ #f(「比較できない」)を返します。デフォルト比較器については 用意されている比較器を参照してください。特に default-comparatorcomparator-register-default!の 項目を見てください。

註: もし、このメソッドと全く同じスペシャライザを 指定してobject-compareメソッドを定義すると、それはこのメソッドを 置き換えることになり、default-comparatorの動作が壊れることになります。 将来のGaucheのバージョンではそのような再定義は禁止される予定です。 うっかりそういう再定義を行わないように気をつけてください。

Function: eq-compare obj1 obj2

とある全順序に従ってobj1obj2を比較し、前者が小さければ-1、 等しければ0、前者が大きければ1を返します。obj1obj2は どんなSchemeオブジェクトでも良く、また型が違っていても構いません。 次の二つの性質が保証されます。

これらの性質以外には、順序についていかなる意味もありません。

この手続きは、任意のSchemeオブジェクトをとにかく順序づけたい (一貫してさえいれば、実際の順序は何でも良い)、という場合に使います。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.3 ハッシュ

ハッシュ関数は等価判定述語と関係が深いので、ここで説明します。

Function: eq-hash obj
Function: eqv-hash obj

この2つはそれぞれeq?および eqv?と一緒に使うのに適した ハッシュ関数です。返り値のハッシュ値は、 システムおよびプロセスに依存する値です。動作しているプロセスの境界を 超えてもちまわることはできません。

注意: eq-hash をつかって、数をハッシュしてはいけません。 2つの数はたとえその値が等しくても eq? であることは保証されて いません。

Function: default-hash obj

[SRFI-128+] equal?と一緒に使うのに適したハッシュ関数です。

obj が、数値、真理値、文字、シンボル、キーワード、文字列、リスト、 ベクタのいずれかならば、そのハッシュ値を求めるのには内部ハッシュ関数を 使います。 obj が、それ以外であれば、 hash は総称関数 object-hash を呼んで、そのハッシュ値を計算します。

返されるハッシュ値はhash-saltの値にも依存します。hash-salt はプロセスが走る度に異なる値をとります。

Function: portable-hash obj salt

時に、「ポータブル」なハッシュ値が必要になることがあります。ここでポータブルとは、 プロセスを何度実行しても、また異なるプラットフォームで実行しても、同じオブジェクト に対して常に同じ値となることです。そのようなハッシュ値は、 プロセスの外に保存したり他のプロセスと共有するオブジェクトと一緒に使えます。

この手続きはobjについてその性質を持つハッシュ値を計算して返します。 つまり、同じオブジェクトと同じソルト値が渡されれば同じハッシュ値となる、ということです。 ここで、「同じオブジェクト」とは、だいたい外部表現が同じになるものと 考えて構いません。equal?であるもの同士は同じです。 また、オブジェクトをwriteで書き出してreadで読み込んだものは 元のオブジェクトと同じになります。

このことから、read/write不変性をもたないオブジェクトはportable-hash では扱えません。objがそのようなオブジェクトを含まないことは 呼び出し側で保証する必要があります。

saltは非負のfixnumで、ハッシュ関数に変化をつけます。一貫した結果を 得るには同じソルト値を使わなければなりません。

objが数値、真理値、文字、シンボル、キーワード、文字列、リスト、 ベクタのいずれでもない場合、ハッシュ値を計算するのに 総称関数 object-hashが呼ばれます。

Function: legacy-hash obj

バージョン0.9.4まで、Gaucheではhashと呼ばれるハッシュ関数が equal?ハッシュテーブル用のハッシュ計算と ポータブルなハッシュ値の計算の両方を兼ねていました。 しかしそれには問題がありました。

  1. ハッシュ関数にソルトを与えることができなかったため、 外部からのデータによる衝突攻撃が可能だった
  2. ハッシュ関数自体があまり良くなかった(特に浮動小数点数)
  3. 多倍長整数と浮動小数点数のハッシュ値計算で、 異なるアーキテクチャで異なる値を返してしまうバグがあった。

既にhashで計算されたハッシュ値を利用しているデータがあるため、 以前のhashで提供していたハッシュ関数をlegacy-hashとして 保存することにしました。古いデータにアクセスする場合に使ってください。 (hash関数自体もデフォルトでは legacy-hashと同様に振る舞いますが、ちょっとした仕掛けがあります。 下の説明を参照)。

新たに書くコードでポータブルなハッシュ値が必要なら portable-hashを使ってください。

Generic Function: object-hash obj rec-hash

この総称関数に対するメソッドを定義することにより、ユーザ定義された 型のオブジェクトはハッシュ値を持つことができ、equal?型のハッシュ テーブルで利用できるようになります。

メソッドは正確な非負整数を返さなければなりません。また、 互いにequal?であるオブジェクト同士に対しては同じハッシュ値を 返さなければなりません。 さらに、objがポータブル(「ポータブル」の意味については 上のportable-hashの説明を参照)の場合には、 ハッシュ値は実行中のプラットフォームやプロセスの状態に依存してはなりません。

メソッドが obj の要素のハッシュ値を必要とする場合には、 それらに対して、rec-hashを呼び出してください。そうすれば 適切なハッシュ関数が再帰的に呼び出されます。例えばobject-hashportable-hash経由で呼び出されている場合、 rec-hashを呼べば同じソルト値でポータブルなハッシュ値が計算されます。

objがいくつかの要素を持っている場合、各要素のハッシュ値を まとめるにはcombine-hash-valueを呼びます。

 
(define-class <myclass> () (x y))

;; user-defined equality function
(define-method object-equal? ((a <myclass>) (b <myclass>))
  (and (equal? (ref a 'x) (ref b 'x))
       (= (abs (ref a 'y)) (abs (ref b 'y)))))

;; user-defined hash function
(define-method object-hash ((a <myclass>) rec-hash)
  (combine-hash-value (rec-hash (ref a 'x))
                      (rec-hash (abs (ref a 'y)))))
Method: object-hash (obj <top>) rec-hash
Method: object-hash (obj <top>)

これら二つのメソッドはシステムで定義されており、 後方互換性およびdefault-comparatorの正しい振る舞いを保証しています。 全く同じスペシャライザを持つメソッドを定義することでこれらを置き換えてしまわないように 注意してください。将来のバージョンでは、これらのメソッドを置き換えるのは エラーになる予定です。

Function: combine-hash-value ha hb

二つのハッシュ値hahbを組み合わせたハッシュ値を返します。 次の性質が保証されます:(= ha1 ha2) かつ (= hb1 hb2) ならば (= (combine-hash-value ha1 hb1) (combine-hash-value ha2 hb2))。 これはユーザ定義のobject-hashメソッドを書くのに便利です。

Function: hash obj

この関数は非推奨となりました。

equal?ハッシュとして使える、objのハッシュ値を返します。 デフォルトでは、この関数はlegacy-hashと同じ値を返します。 しかし、この関数がdefault-hashportable-hashから (object-hashを経由して)呼ばれた場合は、呼び出したハッシュ関数へと 再帰します。

この振る舞いは既存のコードを動かすためのものです。0.9.5より前は、 hashが、ポータブルなハッシュ値にもequal?ハッシュテーブルにも 使えるハッシュ値を計算する唯一の関数でした。object-hashメソッドは ハッシュ値を計算したいオブジェクトだけを引数に取り、もしそのオブジェクトが 指す他のオブジェクトのハッシュ値が欲しいなら、hashを再帰呼び出ししていました。

0.9.5から、object-hashはいくつかのハッシュ関数から呼び出されるようになり、 第2引数に再帰すべきハッシュ関数を取るようになりました。 けれども既存のコードを動かなくしてしまうわけにはいきません。そこで、 object-hashのデフォルトメソッド(2引数でマッチするメソッドが 無い場合に呼ばれるもの)が、1引数のobject-hashを呼び出すように 定義されています。既存のobject-hashhashを呼び出していれば、 正しいハッシュ関数へと再帰するというわけです。 新たに書くコードはこの振る舞いに依存してはいけません。 2引数のobject-hashメソッドを定義してください。

Function: boolean-hash bool
Function: char-hash char
Function: char-ci-hash char
Function: string-hash str
Function: string-ci-hash str
Function: symbol-hash sym
Function: number-hash num

[SRFI-128] これらは特定の型のオブジェクト専用のハッシュ関数です。 Gaucheでは、これらの関数は単に引数の型チェックをして(必要なら大文字小文字の 区別をなくした後)、組み込みのdefault-hashを呼び出しているだけです。 これらはsrfi-128で定義されているために提供されていますが、 特にポータビリティが必要でなければdefailt-hash (あるいは、等価述語によってはeq-hasheqv-hash)を呼んでしまう方が簡単だし高速です。

大文字小文字を区別しない、char-ci-hashおよびstring-ci-hashは、 引数にそれぞれchar-foldcasestring-foldcaseを適用してから hashに渡します。 (char-foldcaseについては文字を、 string-foldcaseについてはFull string case conversionを 参照してください。)

Function: hash-bound
Function: hash-salt

[SRFI-128] どちらも、正確な非負整数へと展開されます。

(註: SRFI-128はこれらをマクロと定義しています。処理系が ランタイムオーバヘッドを避けられるようにするためです。 Gaucheでは、呼び出しのオーバヘッドはそれほど問題にならないという 立場から、どちらも手続きにしています。)

ユーザ定義のハッシュ関数は、 結果を0と(hash-bound)の間に限定してもハッシュの質を落とさないことが 保証されます。(ユーザ定義のハッシュ関数は(hash-bound)を気にする 必要はありません。ハッシュテーブルが必要な時にモジュロを取ります)。

ユーザ定義のハッシュ関数はまた、(hash-salt)を計算に組み入れることが できます。展開されるソルト値は、Schemeプロセスの実行ごとに異なる値を 取ったり、ハッシュテーブルごとに異なる値を取るかもしれません。 これは、コリジョン攻撃を回避するためのものです。 組み込みのハッシュ関数は既にソルト値を考慮しているので、 ユーザ定義ハッシュ関数がプリミティブ型のハッシュ値を組み合わせるだけの 場合は、ソルト値を気にする必要はありません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.4 基本的な比較器

等価性判定と大小比較は色々なデータ構造のパラメータになっています。 treemapはキーの大小を比較します。ハッシュテーブルはキーが等しいかどうかを調べ、 またハッシュ関数と等価性判定に一貫性が無ければなりません。

ジェネリックなデータ構造を作りたければ、こういった比較についてのバリエーションを 抽象化する必要があります。そのために導入されたのが比較器(comparator)です。 これは、比較について関係の深いいくつかの手続きをまとめた構造体です。

比較器を定義したSRFIは二つあります。現在の最新はSRFI-128で、新規コードは そちらを使うべきです。GaucheはSRFI-128のAPIを全て組み込みで提供します。 古く、より複雑なのがSRFI-114で、Gaucheはそちらは主に後方互換性のために サポートしています。重要なのは、Gaucheの組み込みの<comparator>オブジェクトは SRFI-128の比較器としてもSRFI-114の比較器としても使えるということです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.4.1 比較器クラスとコンストラクタ

Builtin Class: <comparator>

以下の手続きをひとまとめにするレコードです。

型検査述語

オブジェクトがこの比較器によって扱えるかどうかを調べます。

等価述語

二つのオブジェクトが等価かどうかを判定し、等価なら#tを、そうでなければ#f を返します。

順序手続き

二つのオブジェクトの大小関係を判定し、 最初のオブジェクトが二つ目のオブジェクトより小さければ#tを、 そうでなければ#fを返します。

比較手続き

二つのオブジェクトの大小関係を判定し、 最初のオブジェクトの方が小さければ-1を、 二つが等しければ0を、 最初のオブジェクトの方が大きければ1を返します。

ハッシュ関数

オブジェクトのハッシュ値を返します。

SRFI-128の比較器は順序手続きを使い、 SRFI-114の比較器は比較手続きを使います。 Gaucheの<comparator>は両方をサポートするために、 自動的に欠けている手続きを補います。つまり、順序手続きを与えてSRFI-128のインタフェースで 比較器を作った場合、Gaucheは自動的に比較手続きを生成しますし、 比較手続きを与えてSRFI-114のインタフェースで比較器を作った場合は 順序手続きが自動生成されます。

比較器は、順序/比較手続き、ハッシュ関数、あるいはその両方を欠いていることもあります。 比較器が順序づけやハッシュに使えるかどうかは、それぞれ comparator-ordered?comparator-hashable?で調べることができます。

Gauche組み込みのデータ型は、コンストラクタで比較器を取ることができます (例:ハッシュテーブル(ハッシュテーブル参照)、 ツリーマップ(ツリーマップ参照))。 また、ソートとマージの手続きも比較器を受け取ることができます (ソートとマージ参照。

Function: make-comparator type-test equal order hash :optional name

[SRFI-128+] 与えられたtype-testequalorderhashの 各関数をまとめて、新たな比較器を作って返します。

各関数の役割については上の<comparator>の説明を参照してください。

註: SRFI-128とSRFI-114は両方ともmake-comparatorを定義していますが、 SRFI-128がorder手続きを取るところでSRFI-114はcompare手続きを 取ります。SRFI-128の方が推奨されるので、Gaucheでは組み込みのmake-comparator をSRFI-128に合わせ、SRFI-114の方はmake-comparator/compareという 名前にしてあります。

各引数は、手続きでなく真偽値を取ることもできます。その場合、既に定義された 手続きが使えるので便利です。但し、引数に真偽値を渡した場合でも、 アクセサは補われた手続きの方を返します。 (例えばtype-test引数に対応するアクセサは comparator-type-test-procedureですが、これは常に手続きを返します。)

type-test引数は、#tもしくは1引数の手続きでなければなりません。 手続きの場合、それは渡されたオブジェクトが比較器の他の手続きに渡せるかどうかを 判定する述語です。#tの場合、どんなオブジェクトに対しても#tを 返す手続きが補われます。

equal引数は二つの引数を取りその等価性を判定する 述語でなければなりません。

order手続きは#fもしくは二つの引数を取り真偽値を返す手続きで なければなりません。手続きの場合、最初の引数が二番目の引数より厳密に手前に ある場合にのみ#tを返します。#fが渡された場合は、 作られる比較器は順序づけには使えません。

hash引数は#fか、一つの引数を取り非負の正確な整数をハッシュ値と して返す手続きでなければなりません。 #fが渡された場合は、この比較器ではハッシュ値を取れないことを意味します。 この場合、呼ばれたらエラーを投げる手続きが補われます。

最後の、省略可能な引数nameはGaucheの拡張です。 どんなオブジェクトでも構いませんが、通常はシンボルが渡されます。 これは比較器を出力する時に使われるだけですが、デバッグには役に立ちます。

Function: make-comparator/compare type-test equal compare hash :optional name

これはSRFI-114比較器のコンストラクタです。SRFI-114ではこれが make-comparatorと呼ばれていますが、名前の衝突を避けるために別の名前を用意しました。 (use srfi-114)した場合は、SRFI-114のコンストラクタが元の名前 で使えるようになります(組み込みのmake-comparatorがシャドウされます。) これは後方互換性のために提供されています。 新たなコードはSRFI-128のmake-comparatorを使ってください。

以下の点を除き、上のmake-constructorとほぼ同じです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.4.2 比較器にまつわる述語とアクセサ

Function: comparator? obj

[SRFI-128] objが比較器である場合に#tを、そうでなければ#fを返します。

Method: object-equal? (a <comparator>) (b <comparator>)

二つの比較器が等価かどうかをequal?で調べた場合、 比較はこのメソッドを介して、各スロットが等価かどうかで判定されます。 比較器abが別々に作られたものであっても、各スロットの内容が 等価であればequal?とみなされます。

これはGaucheの拡張です。SRFI-128は比較器同士の等価性については何も定義していません。 けれども、時には等価判定が便利なことがあります。

 
(equal? (make-comparator #t equal? #f hash 'foo)
        (make-comparator #t equal? #f hash 'foo))
  ⇒ #t

;; 次の式は、無名手続きがどのようにアロケートされるかによって、#tにも#fにも
;; なり得る。
(equal? (make-comparator (^x x) eq? #f #f)
        (make-comparator (^x x) eq? #f #f))
Function: comparator-flavor cmpr

cmprがsrfi-128コンストラクタで作られた場合はシンボルorderingを、 srfi-114コンストラクタで作られた場合はシンボルcomparisonを返します。

アプリケーションは通常両者を区別する必要はありません。一方の比較器は もう一方の比較器としても使うことができるからです。 比較器の実装に特化した最適化をしたい場合にのみ、区別が必要です。

Function: comparator-ordered? cmpr
Function: comparator-hashable? cmpr

[SRFI-128] 比較器cmprが、オブジェクトの順序づけあるいはハッシュに使える 場合にそれぞれ#tを返します。

Function: comparator-type-test-procedure cmpr
Function: comparator-equality-predicate cmpr
Function: comparator-ordering-predicate cmpr
Function: comparator-hash-function cmpr

[SRFI-128] 比較器の型検査述語、等価述語、順序手続きおよびハッシュ関数をそれぞれ返します。

これらの手続きは常に手続きを返します。make-comparatororderhash#fを渡した場合でも、エラーを投げるだけの 手続きが返されます。

Function: comparator-comparison-procedure cmpr

[SRFI-114] これはSRFI-114の手続きですが、SRFI-128比較器でも時々便利なことがあります。 cmprの型検査述語を満たす二つのオブジェクトを取り、-1、0、1のいずれかを 返す手続きを返します。二つのオブジェクトのうち最初の方が小さければ-1が、 等しければ0が、大きければ1が返されます。 比較器は大小比較可能でなけばなりません(順序手続きが渡されるか、 あるいはSRFI-114コンストラクタで比較手続きが渡されるか)。

Function: comparator-test-type cmpr obj
Function: comparator-check-type cmpr obj

[SRFI-128] objが比較器cmprで扱えるオブジェクトかどうかを、 cmprの型検査述語を適用して調べます。 comparator-test-typeは結果の真偽値を返します。 comparator-check-typeobjが扱えないオブジェクトである場合に エラーを投げます。

Function: =? cmpr obj obj2 obj3 …
Function: <? cmpr obj obj2 obj3 …
Function: <=? cmpr obj obj2 obj3 …
Function: >? cmpr obj obj2 obj3 …
Function: >=? cmpr obj obj2 obj3 …

[SRFI-128] 比較器cmprを使ってオブジェクトを比較します。 obj, obj2, obj3 …は全てcmprの 型検査述語を満たさなければなりません。 3つ以上のオブジェクトが渡された場合、比較の順番は規定されていません。

<?<=?>?>=?を使うためには 比較器は順序比較可能でなければなりません。

Function: comparator-hash cmpr obj

[SRFI-128] 比較器cmprのハッシュ関数を使ってobjのハッシュ値を計算して返します。 比較器はハッシュ計算可能でなければなりません。また、objは 比較器の型検査述語を満たさなければなりません。

Function: comparator-compare cmpr a b

[SRFI-114] ふたつのオブジェクトabの順序を調べ、 aが先 (小さい) なら -1を、 等しければ0を、 aが後 (大きい) なら 1を返します。 abはともにcmprの型検査述語を満たさねばなりません。

単純な比較なら<?等の手続きでできますが、 時に3分岐の比較が便利なこともあります。そのため、srfi-114から この手続きを採用しました。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.4.3 用意されている比較器

Variable: default-comparator

[SRFI-114] この変数は、様々な場所でオブジェクトを比較する際のデフォルトとして使われる 比較器に束縛されています。

デフォルト比較器は次のとおり定義されており、 ほぼすべてのSchemeオブジェクト間の比較が出来ます。

 
(define default-comparator
  (make-comparator/compare #t equal? compare default-hash 'default-comparator))

定義からわかるように、等価述語、比較手続き、そしてハッシュ計算はそれぞれ equal?comparedefault-hashで処理されます。 組み込みオブジェクト、及び二つの異なる型の間の比較についてはそれらが処理します。

ユーザ定義クラスのオブジェクトについては、 上記手続きがそれぞれジェネリック関数 object-equal?object-compareobject-hashを 呼び出します。これらのメソッドを定義することにより、 default-comparatorの定義域も自動的に拡張されます。

srfi-128は、default-comparatorを拡張する別の方法を定義しています。 下のcomparator-register-default!の項を参照してください。

Function: comparator-register-default! comparator

[SRFI-128] これは、default-comparator(make-default-comparatorが 返す比較器)の振る舞いをユーザプログラムが拡張できるようにする、 SRFI-128が定める方法です。

Gaucheでは、object-equal?object-compareobject-hashに 特殊化したメソッドを定義することでも、デフォルト比較器を拡張できることに 留意してください。詳しくは上のdefualt-comparatorの説明を参照のこと。

実際、Gaucheではこれらのジェネリックファンクションを使って、 登録されたデフォルト比較器を処理しています。これらのジェネリックファンクションには、 あらかじめ<top>に特殊化されたメソッドが定義されており、 default-comparatorがユーザ定義クラスのオブジェクトに使われ、 かつそのクラスに特殊化されたメソッドが定義されていない場合を捕まえます。 そして、登録された比較器から与えられた引数に適用可能なものを探し、 見つかればそれを使います。

この手続きがグローバルな副作用を持つことに顔をしかめる人もいるかもしれません。 SRFI-128では、デフォルトの比較器や登録された比較器が既にカバーしている定義域と 重複する定義域を持つ比較器を登録することを禁じています。 言い換えれば、comparator-register-default!で定義できるのは、 デフォルトの比較器や既に登録された比較器が処理できないオブジェクトを扱う 比較器だけです。従って、新たな比較器を登録することによる副作用は、 それまでdefault-comparatorが扱えなかったオブジェクトを 扱えるようになるというだけで、既に扱えていたオブジェクトに対する default-comparatorの動作が変わってしまうということはありません。

尤も、現実にその条件を強制することは不可能です。 もし、既にデフォルトの比較器の定義域(および、Gaucheのメソッドにより拡張された定義域) と重なるような定義域を持つ比較器を登録してしまったら、その時点でプログラムの 移植性は損なわれます。現在のバージョンでは、comparator-register-default! で登録された比較器は最も低い優先度を持ちますが、その動作をあてにすべきではありません。

Variable: eq-comparator
Variable: eqv-comparator
Variable: equal-comparator

[SRFI-114] それぞれ、等価述語にeq?eqv?equal?を使う 組み込みの比較器です。全てのSchemeオブジェクトを受け付けます。 対応するハッシュ関数(eq-comparatoreq-hasheqv-comparatoreqv-hashequal-comparatorhash)を備えています。 eq-comparatorのみ、eq-compareを使った順序づけが可能です (eq-compareについては比較参照)。

eq-comparatoreqv-comparatorはそれぞれ、 make-eq-comparator and make-eqv-comparatorが返す比較器とは 等価でないことに注意してください。後者の手続きはSRFI-128で規定されていますが、 ハッシュ関数にはdefault-hashを使うように指定されています。 default-hasheq-hasheqv-hashより重く、 ループのある構造は扱えず、また変更可能なオブジェクトの同一性のみに基づいてハッシュ値を 計算するのには使えません。 こういったdefault-hashの制限を避けたい場合のために eq-comparatoreqv-comparatorを用意しました。

Variable: boolean-comparator
Variable: char-comparator
Variable: char-ci-comparator
Variable: string-comparator
Variable: string-ci-comparator

[SRFI-114] それぞれ、真偽値、文字、文字列を比較する比較器です。 *-ci-*がついている手続きは大文字小文字の区別をしません。 全て、適切なハッシュ関数も備えています。

文字列の大文字小文字の区別をしない比較は、Unicodeのfull-string case conversion に従います (Full string case conversion参照)。

Variable: exact-integer-comparator
Variable: integer-comparator
Variable: rational-comparator
Variable: real-comparator
Variable: complex-comparator
Variable: number-comparator

[SRFI-114] それぞれ、正確な整数、整数、有理数、実数、複素数、そして全ての数値を比較する比較器です。 Gaucheではnumber-comparatorcomplex-comparatorと同じです。

等価性は=により判定されます。 正確な整数、整数、有理数および実数の比較器では、比較手続きは数値の大小に基づいて 判定されます。複素数の比較においては、まず実部の大小が比べられ、実部が等しい場合に 虚部の大小が比べられます。

これらの比較器はNaNを扱いません。 NaNを扱いたい場合は、srfi-114モジュールのmake-inexact-real-comparator 手続きを使って、NaNの比較方法を指定する必要があります。 詳しくはsrfi-114 - 比較器を参照してください。

Variable: pair-comparator
Variable: list-comparator
Variable: vector-comparator
Variable: uvector-comparator
Variable: bytevector-comparator

[SRFI-114] ペア、リスト、ベクタ、ユニフォームベクタ、バイトベクタ(u8vectorの別名)を それぞれ比較するための比較器です。 各データ型の要素についてはデフォルトの比較器(default-comparator)が 使われます。

リストは辞書順で比較されますが(例:(1 2 3)(1 3)より前)、 ベクタの仲間はまず長さが短いものが先に来て、それから内容で比較されることに注意してください (#(1 3)#(1 2 3)より前)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.2.4.4 比較器を組み合わせる

Function: make-default-comparator

[SRFI-128] デフォルト比較器を返します。Gaucheでは常に唯一のdefault-comparatorが 返されます。

Function: make-eq-comparator
Function: make-eqv-comparator

[SRFI-128] eq?およびeqv?を等価述語に使う比較器をそれぞれ返します。 これらの関数が返す比較器のハッシュには、default-hashを使うことが SRFI-128で規定されていますが、それにはいくつか欠点があります。 変更可能なオブジェクトの同一性に基づいたハッシュ関数が欲しい場合は使えませんし、 循環するオブジェクトでは発散します。大きな構造をハッシュしようとすると遅くなります。 可能ならば、eq-comparatoreqv-comparatorを 使うことを推奨します(用意されている比較器)参照。)

Function: make-reverse-comparator cmpr

[SRFI-114] 与えられた比較器cmprと同じ型検査述語、等価述語、ハッシュ関数を持ち、 大小比較だけが逆になっているような比較器を返します。

Function: make-key-comparator cmpr test key

何らかの構造体があって、その比較に際してはその中の一つの要素を見るだけで良い、 という場合に使える手続きです。

比較器を作って返します。型検査述語にはtestが使われます。 等価述語、比較手続き、ハッシュ関数については、それらの引数にkeyを適用して 得られた値を、比較器cmprの対応する手続きに渡すような手続きとなります。 cmprが比較手続きやハッシュ関数を欠いている場合は、 返される比較器も同様に比較手続きやハッシュ関数を欠いたものとなります。

次の例では、ツリーマップusersは userレコードのusernameスロットだけを見て比較を行います。

 
(use gauche.record)

(define-record-type user #t #t
  username      ; string
  password-hash ; string
  comment)      ; string

(define users   ; table of users, managed by tree-map
  (make-tree-map
    (make-key-comparator string-comparator user? user-username)))
Function: make-tuple-comparator cmpr1 cmpr2 …

リスト(x1 x2 …)の各要素をそれぞれcmpr1 cmpr2 … で比較するような比較器を返します。 例えば(make-tuple-comparator c1 c2 c3)が返すのは、 3要素のリストを受け入れ、最初の要素がc1で、二番目の要素がc2で、 最後の要素がc3で比較されるような比較器です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3 数値

Gaucheは次のタイプの数値をサポートしています。

多倍長の正確な整数

メモリの許す限りの精度が扱えます。

多倍長の正確な非整数の有理数

分母と分子は正確な整数で表現されます。 メモリの許す限りの精度が扱えます。

浮動小数点の不正確な実数

実装に使われるC言語のdouble型で表現されます。通常IEEE 64bit浮動小数点数です。

浮動小数点の不正確な複素数

実部と虚部はそれぞれ浮動小数点の不正確な実数で表現されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3.1 数値クラス

Builtin Class: <number>
Builtin Class: <complex>
Builtin Class: <real>
Builtin Class: <rational>
Builtin Class: <integer>

数値オブジェクトのクラス階層を構成します。<complex><number>を継承し、<real><complex>を継承し、 <rational><real>を継承し、 <integer><rational>を継承します。

これらのクラスはR7RSで定義されている数値の階層とは必ずしも対応しません。 特に、<integer>クラスのインスタンスはR7RSでいうexact integerのみになります。

 
(integer? 1)        ⇒ #t
(is-a? 1 <integer>) ⇒ #t
(is-a? 1 <real>)    ⇒ #t

(integer? 1.0)        ⇒ #t
(is-a? 1.0 <integer>) ⇒ #f
(is-a? 1.0 <real>)    ⇒ #t

(class-of (expt 2 100)) ⇒ #<class <integer>>
(class-of (sqrt -3)) ⇒ #<class <complex>>

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3.2 数値に関する述語

Function: number? obj
Function: complex? obj
Function: real? obj
Function: rational? obj
Function: integer? obj

[R7RS] objがそれぞれ数、複素数、実数、有理数、整数ならば#tを返します。 Gaucheでは、数の集合は複素数の集合と同一であり、有理数の集合は +inf.0, -inf.0, +nan.0を除いた実数の集合と同一 (浮動小数点数が有限精度であるため)です。

 
(complex? 3+4i)   ⇒ #t
(complex? 3)      ⇒ #t
(real? 3)         ⇒ #t
(real? -2.5+0.0i) ⇒ #t
(real? #e1e10)    ⇒ #t
(integer? 3+0i)   ⇒ #t
(integer? 3.0)    ⇒ #t

(real? +inf.0)     ⇒ #t
(real? +nan.0)     ⇒ #t
(rational? +inf.0) ⇒ #f
(rational? +nan.0) ⇒ #f

R6RSでは正確性についてより厳密な定義がなされ、特に 非正確なゼロを虚数部に持つ複素数は実数ではないと規定されています。 Gaucheは今のところ正確な複素数を持たず、虚部がゼロの複素数は 自動的に実数に変換されます。従ってR6RSのコードのうち、 (real? 1+0.0i)#fであることを当てにしているものは うまく動かないでしょう。

Function: real-valued? obj
Function: rational-valued? obj
Function: integer-valued? obj

[R6RS] Gaucheではこれらの手続きはそれぞれreal?rational?、 およびinteger?の別名となっています。これらはR6RSコードとの互換性のために 提供されています。

R6RSで規定されている、これらの手続きと-valuedがついていない手続きの違いは、 これらの手続きは非正確なゼロの虚部を持つ複素数に対しても#tを返すという ことです。Gaucheではゼロの虚部を持つ複素数は実数とみなしているので、 この違いは現れません。

Function: exact? obj
Function: inexact? obj

[R7RS] objがそれぞれ正確な数、不正確な数ならば#tを返します。

 
(exact? 1)       ⇒ #t
(exact? 1.0)     ⇒ #f
(inexact? 1)     ⇒ #f
(inexact? 1.0)   ⇒ #t

(exact? (modulo 5 3)) ⇒ #t
(inexact? (modulo 5 3.0)) ⇒ #f
Function: exact-integer? obj

[R7RS] (and (exact? obj) (integer? obj))と等価ですが、より高速です。

Function: zero? z

[R7RS] 数値zがゼロに等しければ#tを返します。

 
(zero? 1)        ⇒ #f
(zero? 0)        ⇒ #t
(zero? 0.0)      ⇒ #t
(zero? 0.0+0.0i) ⇒ #t
Function: positive? x
Function: negative? x

[R7RS] 実数xがそれぞれ正または負なら#tを返します。 非実数を渡すとエラーになります。

Function: finite? z
Function: infinite? z
Function: nan? z

[R7RS] 実数に対して、それが有限、無限、NaNであればそれぞれ#tを返します。

非実数の複素数に対しては、finite?は実数部と仮数部がともに 有限なら#tを、infinite?は実数部と仮数部の少なくともどちらかが 無限なら#tを、nan?は実数部と仮数部の少なくともどちらかが NaNなら#tを返します。(これはR6RSとは非互換です。R6RSでは これらの手続きは引数に非実数の数が与えられた場合はエラーを通知しなければならないからです。)

R7RSではこれらの手続きは(scheme inexact)ライブラリにあります。

Function: odd? n
Function: even? n

[R7RS] 整数nがそれぞれ奇数または偶数なら#tを返します。 非整数を渡すとエラーになります。

 
(odd? 3)     ⇒ #t
(even? 3)    ⇒ #f
(odd? 3.0)   ⇒ #t
Function: fixnum? n
Function: bignum? n

nが正確な整数で、かつ内部的にそれぞれfixnumもしくはbignum で表現されているときに#tを返します。 通常のSchemeプログラムは整数の内部表現を気にする必要はありません。 これらの手続きは、ある種の最適化を行う低レベル手続きのために用意されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3.3 数値の比較

Function: = z1 z2 z3 …

[R7RS] 与えられた全ての数値zが等しければ#tを返します。

 
(= 2 2)          ⇒ #t
(= 2 3)          ⇒ #f
(= 2 2.0)        ⇒ #t
(= 2 2.0 2.0+0i) ⇒ #t
(= 2/4 1/2)      ⇒ #t
Function: < x1 x2 x3 …
Function: <= x1 x2 x3 …
Function: > x1 x2 x3 …
Function: >= x1 x2 x3 …

[R7RS] 与えられた全ての実数xがそれぞれ単調増加、単調非減少、単調減少、単調非増加している 場合に#tを返します。

Function: max x1 x2 …
Function: min x1 x2 …

[R7RS] 与えられた実数のうち、それぞれ最大と最小のものを返します。 引数にNaNが含まれていた場合は、NaNが返されます。

コレクションからの選択と探索find-minfind-maxも参照。

Function: min&max x1 x2 …

与えられた実数から、最大値と最小値の二つの値を返します。

コレクションからの選択と探索find-min&maxも参照。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3.4 数値の演算

Function: + z …
Function: * z …

[R7RS] 与えられた数の和と積をそれぞれ返します。引数が与えられない場合、 (+) は 0、 (*) は 1 となります。

Function: - z1 z2 …
Function: / z1 z2 …

[R7RS] 一つだけ数値が与えられた場合、それぞれnegationと逆数を返します。

2つ以上の数値が与えられた場合、それぞれ次の結果を返します。

 
z1 - z2 - z3z1 / z2 / z3
 
(- 3)       ⇒ -3
(- -3.0)    ⇒ 3.0
(- 5+2i)    ⇒ -5.0-2.0i
(/ 3)       ⇒ 1/3
(/ 5+2i)    ⇒ 0.172413793103448-0.0689655172413793i

(- 5 2 1)     ⇒ 2
(- 5 2.0 1)   ⇒ 2.0
(- 5+3i -i)   ⇒ 5.0+2.0i
(/ 14 6)      ⇒ 7/3
(/ 6+2i 2)    ⇒ 3.0+1.0i

註:0.8.8までGaucheは正確な有理数をサポートしておらず、 それ以前は除数と被除数がともに正確な数であっても商が整数にならなければ 結果は非正確な数へと変換されていました。今のGaucheはそうではありません。

既存のコードが以前のGaucheのふるまいを当てにしていた場合、 速度が大きく低下する可能性があります。正確な有理数の演算は 浮動小数点数の演算よりはるかに遅いからです。 (正確な結果を得たいのでない場合は)下に述べる/.を使うと良いでしょう。

Function: +. z …
Function: *. z …
Function: -. z1 z2 …
Function: /. z1 z2 …

+*-/と同様ですが、引数は不正確な 数へと変換され、結果も常に不正確な数となります。 正確数による計算が不要で、bignumや正確な有理数をうっかり使ってしまう ことによるオーバヘッドを避けたい時に便利です。

Function: abs z

[R7RS+] 実数のzに対しては、その絶対値を返します。 複素数のzに対しては、そのmagnitudeを返します。 複素数を扱うのはGaucheの拡張です。

 
(abs -1)   ⇒ 1
(abs -1.0) ⇒ 1.0
(abs 1+i)  ⇒ 1.4142135623731
Function: quotient n1 n2
Function: remainder n1 n2
Function: modulo n1 n2

[R7RS] 整数n1を整数n2で割った商(quotient)および余り(remainder, modulo) を返します。 n1n2の両方が正確な数値の時のみ、戻り値は正確な数値になります。

remaindermodulo はどちらかの引数が負の時に異なる値を返します。 Remainder R と商 Q とは次の関係があります。

 
  n1 = Q * n2 + R

ここで商について abs(Q) = floor(abs(n1)/abs(n2)) ですから、Rの符号は常にn1と同じになります。

一方、moduloはn2が正の時はn1の符号に関わらず期待したように動作します (例: (modulo -1 n2) == n2 - 1)。 n2が負の場合は次の式によって正の場合にマップできます。

 
  modulo(n1, n2) = -modulo(-n1, -n2)

したがって、moduloの結果の符号は常にn2の符号と同じになります。

 
(remainder 10 3)    ⇒ 1
(modulo 10 3)       ⇒ 1

(remainder -10 3)   ⇒ -1
(modulo -10 3)      ⇒ 2

(remainder 10 -3)   ⇒ 1
(modulo 10 -3)      ⇒ -2

(remainder -10 -3)  ⇒ -1
(modulo -10 -3)     ⇒ -1
Function: quotient&remainder n1 n2

整数n1を整数n2で割った商(quotient)および余り(remainder) を同時に計算し、2つの値として返します。

Function: div x y
Function: mod x y
Function: div-and-mod x y
Function: div0 x y
Function: mod0 x y
Function: div0-and-mod0 x y

[R6RS] これらはR6RSで導入された整数除算手続きです。 quotient, modulo, remainderと違い、 これらの手続きは整数でない値を取ることもできます。 被除数xには任意の実数、除数yにはゼロ以外の実数が許されます。

divは整数nを、modは実数mを返し、 それらは以下の関係を満たします。

例:

 
(div 123 10)     ⇒ 12
(mod 123 10)     ⇒ 3

(div 123 -10)    ⇒ -12
(mod 123 -10)    ⇒ 3

(div -123 10)    ⇒ -13
(mod -123 10)    ⇒ 7

(div -123 -10)   ⇒ 13
(mod -123 -10)   ⇒ 7

(div 123/7 10/9) ⇒ 15
(mod 123/7 10/9) ⇒ 19/21
;; 123/7 = 10/9 * 15 + 19/21

(div 14.625 3.75) ⇒ 3.0
(mod 14.625 3.75) ⇒ 3.375
;; 14.625 = 3.75 * 3.0 + 3.375

xが非負の整数、yが整数である場合は、 divmodの結果はquotientremainder の結果に一致します。しかしxが負になると結果は異なります。

div-and-moddivmodを同時に計算し、 結果を二つの値で返します。

div0mod0は、mの値域が異なる以外は同じです。

 
(div0 123 10)   ⇒ 12
(mod0 123 10)   ⇒ 3

(div0 127 10)   ⇒ 13
(mod0 127 10)   ⇒ -3

(div0 127 -10)  ⇒ -13
(mod0 127 -10)  ⇒ -3

(div0 -127 10)  ⇒ -13
(mod0 -127 10)  ⇒ 3

(div0 -127 -10) ⇒ 13
(mod0 -127 -10) ⇒ 3

div0-and-mod0div0mod0を同時に計算し、 結果を二つの値で返します。

R6RSとR7RSの除算・剰余操作を可視化したものが http://blog.practical-scheme.net/gauche/20100618-integer-divisions にあります。

Function: gcd n …
Function: lcm n …

[R7RS] 与えられた整数の、最大公約数と最小公倍数をそれぞれ返します。

引数は整数でなければなりませんが、正確数である必要はありません。 引数が一つでも非正確なら、結果は非正確数になります。

Function: continued-fraction x

有限の実数xの正規連分数展開を、遅延シーケンスで返します。 xが無限大、NaN、または非実数の場合はエラーが報告されます。 返り値は遅延シーケンスなので、係数は必要になる度に計算されます。

 
(continued-fraction 13579/2468)
  ⇒ (5 1 1 122 1 9)

(+ 5 (/ (+ 1 (/ (+ 1 (/ (+ 122 (/ (+ 1 (/ 9))))))))))
  ⇒ 13579/2468

(continued-fraction (exact 3.141592653589793))
  ⇒ (3 7 15 1 292 1 1 1 2 1 3 1 14 3 3 2 1 3 3 7 2 1 1 3 2 42 2)

(continued-fraction 1.5625)
  ⇒ (1.0 1.0 1.0 3.0 2.0)
Function: numerator q
Function: denominator q

[R7RS] 有理数qの分子と分母をそれぞれ返します。

Function: rationalize x ebound

[R7RS] 実数xを、誤差ebound以内で近似した最も単純な有理数qを返します。

Gaucheは不正確な有理数をサポートしないので、引数の少なくとも一方が不正確数だと 結果の有理数が浮動小数点数へと変換され不正確数として返されることに注意してください。

 
(rationalize 1234/5678 1/1000) ⇒ 5/23

(rationalize 3.141592653589793 1/10000)
  ⇒ 3.141509433962264
(rationalize (exact 3.141592653589793) 1/10000)
  ⇒ 333/106
(rationalize (exact 3.141592653589793) 1/10000000)
  ⇒ 75948/24175

;; Some edge cases
(rationalize 2 +inf.0) ⇒ 0
(rationalize +inf.0 0) ⇒ +inf.0
(rationalize +inf.0 +inf.0) ⇒ +nan.0
Function: floor x
Function: ceiling x
Function: truncate x
Function: round x

[R7RS] 引数xは実数でなければなりません。floorceilingはそれぞれ xを越えない最大の整数と、xを下回らない最小の整数を返します。 truncatexの小数部をゼロの方向に向かって切捨てた整数を返します。 roundxに最も近い整数を返します。xの 小数部が0.5ぴったりだった場合はroundは最も近い偶数を返します。

Schemeの一般的な規則に従い、xが不正確数であれば結果も不正確となります。 例えば(round 2.3)の結果は2.0です。 もし、不正確な数を丸めて正確な整数を得たいのであれば、結果にexactを 適用するか、以下に示すfloor->exactなどの手続きを使ってください。

Function: floor->exact x
Function: ceiling->exact x
Function: truncate->exact x
Function: round->exact x

これらは、頻出する(exact (floor x)) 等を簡潔に書くための 手続きです。

Function: clamp x :optional min max
 
 min if x < min
 x   if min <= x <= max
 max if max < x

を返します。もしminまたはmaxが省略されるか#fが与えられたら、 それぞれ -∞ もしくは +∞ と解釈されます。 与えられた全ての数値が正確な整数の場合に限り正確な整数を返します。

 
(clamp 3.1 0.0 1.0) ⇒ 1.0
(clamp 0.5 0.0 1.0) ⇒ 0.5
(clamp -0.3 0.0 1.0) ⇒ 0.0
(clamp -5 0)        ⇒ 0
(clamp 3724 #f 256) ⇒ 256
Function: exp z
Function: log z
Function: log z1 z2
Function: sin z
Function: cos z
Function: tan z
Function: asin z
Function: acos z
Function: atan z
Function: atan y x

[R7RS] 超越関数です。複素数も扱えます。 R7RSではこれらの手続きは(scheme inexact)モジュールにあります。

2引数のlogはR6RSで追加されたもので、z2を底としたz1の対数を 返します。

2引数のatanは 実数xyに対して (angle (make-rectangular x y))を返します。

Function: sinh z
Function: cosh z
Function: tanh z
Function: asinh z
Function: acosh z
Function: atanh z

双曲線関数です。複素数も扱えます。

Function: sqrt z

[R7RS] 複素数zの平方根のひとつを返します。枝刈りの定義はCommon Lispと同じです。 実数に対しては正の平方根を返します。

zが正確な実数の平方である場合、戻り値は正確数となります。

 
(sqrt 2)      ⇒ 1.4142135623730951
(sqrt -2)     ⇒ 0.0+1.4142135623730951i
(sqrt 256)    ⇒ 16
(sqrt 256.0)  ⇒ 16.0
(sqrt 81/169) ⇒ 9/13
Function: exact-integer-sqrt k

[R7RS] 非負の正確な整数kに対し、次の式を満たす 非負の正確な整数srを返します。

 
k = (+ (* s s) r)
k < (* (+ s 1) (+ s 1))
 
(exact-integer-sqrt 782763574)
  ⇒ 27977 and 51045
Function: expt z1 z2

[R7RS] 複素数z1, z2に対して、 z1^z2 (z1z2乗)を返します。

Function: expt-mod base exponent mod

(modulo (expt base exponent) mod) を効率よく計算します。

次の例は、メルセンヌ素数M_74207281 (2^74207281 - 1) の最後の10桁を 求めます。

 
(- (expt-mod 2 74207281 #e1e10) 1)
 ⇒ 1086436351
Function: gamma x
Function: lgamma x

ガンマ関数と、ガンマ関数の絶対値の自然対数です。

註:数学的にはこれらの関数は複素領域で定義されますが、現在の実装は実数引数だけを サポートします。

Function: fixnum-width
Function: greatest-fixnum
Function: least-fixnum

[R6RS] これらの手続きは、fixnumで表現できる数値の幅 (wとします)、 fixnumで表現できる最大の整数値 (2^(w-1) - 1)、および fixnumで表現できる最小の整数値 (- 2^(w-1)) を それぞれ返します。性能が重要になる部分では数値がfixnumの範囲に収まるかどうかを 気にかける必要があります。

これらの名前はR6RSで定義されました。Common LispとChezSchemeには most-positive-fixnumおよびmost-negative-fixnumがあります。

註: 0.9.5以前のfixnum-widthには本来より1小さい値を返すバグがありました。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3.5 数値の変換

Function: make-rectangular x1 x2
Function: make-polar x1 x2

[R7RS] 二つの実数x1x2から複素数を作ります。 make-rectangularx1 + ix2 を返します。 make-polarx1e^(ix2) を返します。

R7RSではこれらの手続きは(scheme complex)ライブラリにあります。

Function: real-part z
Function: imag-part z
Function: magnitude z
Function: angle z

[R7RS] 複素数zを取り、実数を返します。 real-partimag-partzの実数部と虚数部をそれぞれ返し、 magnitudeanglezの絶対値と偏角をそれぞれ返します。

R7RSではこれらの手続きは(scheme complex)ライブラリにあります。

Function: decode-float x

与えられた有限の浮動小数点数xに対して、 3つの正確な整数からなるベクタ #(m, e, sign) を返します。ここで、

 
  x = (* sign m (expt 2.0 e))
  sign は 1, 0 または -1.

です。

特例として、x+inf.0もしくは-inf.0の時は m#tに、 x+nan.0の時はm#fになります。

このAPIはChezSchemeから取られました。

 
(decode-float 3.1415926)
 ⇒ #(7074237631354954 -51 1)
(* 7074237631354954 (expt 2.0 -51))
 ⇒ 3.1415926

(decode-float +nan.0)
 ⇒ #(#f 0 -1)
Function: encode-float vector

これはdecode-floatの逆関数です。vectordecode-floatが返す形式の3要素のベクタでなければなりません。

 
(encode-float '#(7074237631354954 -51 1))
 ⇒ 3.1415926

(encode-float '#(#t 0 1))
 ⇒ +inf.0
Function: fmod x y
Function: modf x
Function: frexp x
Function: ldexp x n

[POSIX] これらの手続きは、浮動小数点数を分解したり合成するのに使えます。 fmodは実数xを実数yで割った余りを返します。すなわち、 x/yを0に近いほうの整数に丸めた値をnとするとき、 x-n*yを返します。 modfxの少数部と整数部を返します。 frexpは実数xを、仮数部と指数部に分解して返します。すなわち、 xが0でない限り、 x = fraction * 2^exponent かつ 0.5 <= |fraction| < 1.0 であるところのfractionexponentを返します。 (xが0であれば、fractionexponentはともに0となります。) ldexpfrexpの逆関数で、x * 2^n を返します。

 
(fmod 32.1 10.0)  ⇒ 2.1
(fmod 1.5 1.4)    ⇒ 0.1
(modf 12.5)       ⇒ 0.5 and 12.0
(frexp 3.14)      ⇒ 0.785 and 2
(ldexp 0.785 2)   ⇒ 3.14
Function: exact z
Function: inexact z

[R7RS] それぞれ数zの正確な表現および不正確な表現を返します。 正確数をexactに渡した場合や、不正確数をinexactに渡した場合は 渡した値がそのまま返されます。

Gaucheは、虚部がゼロでない正確な複素数や、正確な無限大/NaNをサポートしないので、 これらの数がexactに渡された場合はエラーとなります。

 
(inexact 1)    ⇒ 1.0
(inexact 1/10) ⇒ 0.1

不正確で有限な実数がexactに渡された場合は、 浮動小数点数演算の精度の中で最も簡単な表現となる正確な有理数が返されます。

 
(exact 1.0)     ⇒ 1
(exact 0.1)     ⇒ 1/10
(exact (/ 3.0)) ⇒ 1/3

あらゆる有限の不正確な実数xについて、 (inexact (exact x)) は常に元の数xeqv?になります。

(その逆、つまり正確数 n(exact (inexact n)) は 必ずしも等しくならないことに注意してください。 ある不正確数へと写像される正確数は無数にあり得るためです。)

誤差の許容範囲を指定して非正確な実数を正確な有理数に直したい場合は、 rationalizeおよびreal->rationalを参照してください。

Function: exact->inexact z
Function: inexact->exact z

[R5RS] それぞれ、正確数を非正確に変換、あるいはその逆を行います。

不正確な数をexact->inexactに渡したり、または正確な数を inexact->exactに渡した場合、Gaucheではそれをエラーにせず 引数をそのまま返します。従ってこれらはGaucheでは 上のinexactおよびexactとそれぞれ等価です。 R5RS実装の中には、非正確数をexact->inexactに渡したりすると エラーになるものもあり得るので、ポータブルなコードを書く場合は 注意してください。

一般的には、exactおよびinexactを使う方が 簡潔で良いでしょう。こちらの手続きはR5RSプログラムとの互換性の ために用意されています。

Function: real->rational x :optional hi lo open?

指定されたエラー範囲の中で、有限な実数xの正確な有理数表現のうち 最も簡単なものを返します。ratinalizeexactはこの手続きを 下請けとして呼び出しています。

返される有理数は次の条件を満たします。

 
(<= (- x lo) r (+ x hi))   ; when open? is #f
(<  (- x lo) r (+ x hi))   ; otherwise

hiloは非負でなければなりません。

hiloの両方、もしくは一方が省略された場合、それはxの値に応じて 決定されます。xが正確数の場合、省略されたhi/loはゼロとみなされます。 xが非正確数の場合は、xの浮動小数点数表現の精度に基づいて hi/loが決定されます。後者の場合、open?の値もまた、 xの値で決まります。浮動小数点数の「偶数丸め」規則に対応して、 xの仮数部が奇数であればopen?は真、そうでなければ偽となります。 従って、real->rationalにひとつの有限の数値だけを渡した場合、 その動作はexactと同じになります。

 
(real->rational 0.1) ⇒ 1/10

エラー範囲にゼロを渡すと、引数の浮動小数点数の正確な数値表現 (つまり、(* sign mantissa (expt 2 exponent))を正確値で計算した結果) が返されます。

 
(real->rational 0.1 0 0) ⇒ 3602879701896397/36028797018963968

(hilo引数を与えて open?を省略した場合は、 閉じた範囲と考えます。)

Function: number->string z :optional radix use-upper?
Function: string->number string :optional radix

[R7RS+] これらの手続きは数値とそれをradix進数で表現する文字列とを変換します。 radixは2から36の間でなければなりません。省略された場合は10進数とみなされます。

number->stringは数値zを取り文字列を返します。 zが正確な整数以外の場合、radixは10でなければなりません。 10より大きいradixに関しては、小文字のアルファベットが桁として使われます。 但し、省略可能な引数use-upper?に真の値が与えられた場合は大文字のアルファベットが 使われます。use-upper?引数はGaucheの拡張です。

string->numberは文字列stringを取り数値を返します。 不正確な数値は10進数表記しか認識されません。与えられた文字列が数値を構成しない 場合は#fが返されます。

Generic Function: x->number obj
Generic Function: x->integer obj

数値への強制型変換手続きです。 objをそれぞれ数値もしくは正確な整数と解釈して返します。 数値と文字列に対してのメソッドは定義されています (文字列はstring->numberを使って変換されます。 数値として解釈できない文字列に関しては0が返されます)。 その他のobjについては単に0が返されます。 x->integerは、objが数値として表現できるけれど正確な整数では ない場合、roundinexact->exactによって正確な整数に変換します。

他のクラスはこのメソッドを定義することにより、独自の変換関数を提供することができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3.6 ビット演算

これらの手続きは整数を半無限のビットベクタとして扱います。 正の整数の場合、数値の左側に無限に0が続いていると見倣されます。 負の整数の場合、数値は2の補数で表現され、左側に無限に1が続いていると見倣されます。

これらの操作の名前に関しては、Scheme界には大きく二つの流儀があります。 GaucheはSLIBの元々の“logical”モジュールに合わせました。 その起源はCommon Lispです。 もう一つの流儀では、arithmetic-shiftといった、より長く記述的な名前を使います。

SRFI-60 (see section srfi-60 - 整数に対するビット操作) は両方の流儀の名前を定義しており、 さらにいくつかの追加の手続きも用意しています。他のScheme向けに書かれた コードをGaucheに移植する際には有用でしょう。

Function: ash n count

[SRFI-60] 整数nを左に整数countビットだけシフトします。 countが負であれば、-countだけnを右にシフトします。

 
; Note: 6  ≡ [...00110], and
;       -6 ≡ [...11010]
(ash 6 2)   ⇒ 24  ;[...0011000]
(ash 6 -2)  ⇒ 1   ;[...0000001]
(ash -6 2)  ⇒ -24 ;[...1101000]
(ash -6 -2) ⇒ -2  ;[...1111110]
Function: logand n1 …
Function: logior n1 …
Function: logxor n1 …

[SRFI-60] 整数n1 …の、それぞれビット論理積、 ビット論理和、ビット排他的論理積を返します。 ゼロ個の引数が与えられた場合は、logand-1を、 logiorlogxor0を返します。

Function: lognot n

[SRFI-60] 整数nのビット否定を返します。

Function: logtest n1 n2 …

[SRFI-60] ≡ (not (zero? (logand n1 n2 …)))

Function: logbit? index n

[SRFI-60] 整数nindex目のビットが1ならば#tを、0ならば#fを 返します。

Function: bit-field n start end

[SRFI-60] 整数nstartビット目(含む)からendビット目(含まない)までを 取り出した数値を返します。start < end でなければなりません。

Function: copy-bit index n bit

[SRFI-60] bitが真の値なら、整数nindexビット目をセットした数値を 返します。 bitが偽の値なら、整数nindexビット目をリセットした数値を 返します。

Function: copy-bit-field n from start end

[SRFI-60] 正確な整数を返します。この整数の各ビットはstart番目のビット(この ビットを含む)からend番目のビット(このビットは含まない)の部分以外 はnと同じで、この区間のビットは、正確な数fromの下位 (end-start)ビットのコピーです。

 
(number->string (copy-bit-field #b10000000 -1 1 5) 2)
  ⇒ "10011110"

(number->string (copy-bit-field #b10000000 #b010101010 1 7) 2)
  ⇒ "11010100"

註:この手続きのAPIはSLIBから採られましたが、採用当時の引数の順序は (copy-bit-field n start end from) でした。SRFI-60の 議論中に、一貫性のために引数順が変更され、SLIBの新しいバージョンも それに追従しています。Gaucheではこの引数順の変更に最近まで気づいていなかったため、 0.9.4以前のバージョンではcopy-bit-fieldが古い引数順で定義されています。 この手続きを使っていたコードは0.9.4以降のGaucheで使う際に修正しなければなりません。 もしどちらのバージョンのGaucheでも動かす必要がある場合は、 回避策としてcopy-bit-fieldの定義を自前で持つのが良いでしょう。

 
(define (copy-bit-field to from start end)
  (if (< start end)
    (let1 mask (- (ash 1 (- end start)) 1)
      (logior (logand to (lognot (ash mask start)))
              (ash (logand from mask) start)))
    from))
Function: logcount n

[SRFI-60] nが正なら、nのビットのうち1の個数を返します。n が負なら、nの2に関する補数のビットのうち0の個数を返します。

 
(logcount 0)      ⇒ 0
(logcount #b0010) ⇒ 1
(logcount #b0110) ⇒ 2
(logcount #b1111) ⇒ 4

(logcount #b-0001) ⇒ 0  ;; 2の補数:  ....111111
(logcount #b-0010) ⇒ 1  ;; 2の補数:  ....111110
(logcount #b-0011) ⇒ 1  ;; 2の補数:  ....111101
(logcount #b-0100) ⇒ 2  ;; 2の補数:  ....111100
Function: integer-length n

[SRFI-60] 正確な整数nを表現するのに必要な最小ビット数を返します。負の数は2 の補数で表現されるものと仮定しています。符号用のビットは考慮しません。

 
(integer-length 255)  ⇒ 8
(integer-length 256)  ⇒ 9

(integer-length -256)  ⇒ 8
(integer-length -257)  ⇒ 9
Function: twos-exponent n

nが2の冪乗(expt 2 k) (k >= 0) ならその冪 k を、 そうでなければ#fを返します。

Function: twos-exponent-factor n

(expt 2 k)nの約数であるような最大のkを返します。 言い換えれば、nのLSBから連続するゼロビットの数を返します。 ただしnがゼロならゼロを返します。

これは次の式でも計算できますが、この手続きはnがbignumの場合にも 中間結果を作らないので若干速いです。

 
(- (integer-length (logxor n (- n 1))) 1)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.3.7 エンディアン

Schemeの世界では、数値がマシン上でどのように表現されているかを気にする 必要はほとんどありません。しかし、外の世界とバイナリ表現でデータを やりとりする場合はそれが問題になります。

binary.ioモジュール(binary.io - バイナリI/O参照)や write-uvector/read-uvector!(gauche.uvector - ユニフォームベクタ参照) 等、Gaucheのバイナリ入出力手続きはエンディアンを指定する 省略可能なendian引数を取ります。

エンディアンの指定には次のシンボルを使います。

big-endian

ビッグエンディアン。32ビット整数値#x12345678#x12 #x34 #x56 #x78 のようなバイトシーケンスとして出力されます。

little-endian

リトルエンディアン。32ビット整数値#x12345678#x78 #x56 #x34 #x12 のようなバイトシーケンスとして出力されます。

arm-little-endian

これはlittle-endianのバリエーションで、ARMプロセッサの 特定のモードで使われるものです。倍精度浮動小数点数以外はlittle-endian と同様に動作します。倍精度浮動小数点数については、ふたつのリトルエンディアンワードが ビッグエンディアンの順で書き出されます (例えばマシンレジスタ上でのビットパターンが #x0102030405060708の場合、出力は #x04 #x03 #x02 #x01 #x08 #x07 #x06 #x05となります)。

endian引数が省略された場合は、これらの手続きはパラメータdefault-endianの 値を使います。

Parameter: default-endian

これは、バイナリ入出力ルーチンでendian指定が省略された場合に 共通して使われるエンディアンを指定する動的パラメータです (gauche.parameter - パラメータ参照)。 このパラメータの初期値はシステムのネイティブエンディアンです。

システムのネイティブエンディアンは次の手続きで得られます。

Function: native-endian

システムのエンディアンを示すシンボルを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.4 論理値

Builtin Class: <boolean>

論理値のクラスです。#t#fのみがこのクラスのインスタンスです。

Function: not obj

[R7RS] obj#fの時のみ#tを返し、それ以外の場合は#fを 返します。

Function: boolean? obj

[R7RS] objが論理値である場合に#tを返します。

Function: boolean obj

obj#fであるとき#fを返し、それ以外の場合は#tを 返します。値を論理値に変換したい場合に便利です。

Function: boolean=? a b c …

[R7RS] 全ての引数は論理値でなければなりません。全ての引数が等しい場合に #tが、そうでなければ#fが返ります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.5 未定義値

Gaucheで作業していると、しばしば未定義値 #<undef> に出会うことがあるでしょう。

 
gosh> (if #f #t)
#<undef>

この値は、値そのものに意味が無いこと、あるいは とりたててほかにふさわしい値が無いことを意味します。

未定義値と未束縛の変数を混同しないようにしてください。 #<undef>は通常の第一級の値であり、変数に束縛しておくこともできます。 未束縛の変数は文字通り、変数が束縛されていない、すなわち値を持たないことを意味します。

しかし、場合によっては、変数に特定の値が提供されていないことを 示すのに#<undef>が使われることもあります。 例えば、既定値をもたない省略可能引数に実引数が提供されなかった 場合、引数の値は#<undef>となります (手続きを作る参照)。 これは実際に#<undef>が引数として渡された場合と区別がつかない ことに注意してください。#<undef>を受け取ってわかることは、 せいぜいその値にたいした意味がないということくらいです。 #<undef>に大きな意味を持たせすぎないようにしましょう。

以上の注意を念頭においた上で、未定義値を扱うには 次の手続きが利用できます。

Function: undefined? obj

objが未定義値である場合に限り#tを返します。

Function: undefined

未定義値を返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6 ペアとリスト

ペアとリストはSchemeにおける最も基本的なデータ構造のひとつです。 Gaucheのコアは全ての標準のリスト手続きと、多くのScheme実装に見られる 便利な手続きのいくつかを提供します。それらで足りない場合は さらに多くの手続きがsrfi-1 - リストライブラリutil.combinations - 組み合わせ といったモジュールで提供されています。 また、リストに限らないジェネリックなシーケンス/コレクションの操作が gauche.collection - コレクションフレームワークgauche.sequence - シーケンスフレームワークで提供されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6.1 pairクラスとnullクラス

Builtin Class: <list>

リストを表す抽象クラスで、クラス<null>とクラス<pair>の親クラスです。 クラス<sequence>を継承します。

循環リストやドットリストも<list>クラスのインスタンスですが、 list?は偽の値を返すことに注意して下さい。

 
(use srfi-1)
(list? (circular-list 1 2)) ⇒ #f
(is-a? (circular-list 1 2) <list>) ⇒ #t
Builtin Class: <null>

空リストのクラスです。()がこのクラスの唯一のインスタンスです。

Builtin Class: <pair>

ペアのクラスです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6.2 リストに関する述語

Function: pair? obj

[R7RS] objがペアなら#tを、そうでなければ#fを返します。

Function: null? obj

[R7RS] objが空リストなら#tを、そうでなければ#fを返します。

Function: null-list? obj

[SRFI-1] obj が空リストなら#tを、ペアなら#fを返し、 それ以外のときはエラーを報告します。

リスト終了条件を検査する時に、正しいリスト以外を除外したい場合に null?の代わりに使えます。

Function: list? obj

[R7RS] objが正しいリストなら#tを、そうでなければ#fを返します。 この手続きはobjがドットリストや循環リストなら#fを返します。

下に説明する、 proper-list?circular-list?dotted-list? といった手続きも参照してください。

Function: proper-list? x

[SRFI-1] x が真性リストであれば #t を返します。

Function: circular-list? x

[SRFI-1] x が循環リストであれば #t を返します。

Function: dotted-list? x

[SRFI-1] x が有限の大きさで、空リストで終端していないリストなら #t を返します。これには、ペアではなく、空リストもない値(たとえば シンボルや数値)のような長さ0のドットリストと考えられるものを含みます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6.3 リストの作成

Function: cons obj1 obj2

[R7RS] obj1obj2のペアを作成します。

 
(cons 'a 'b) ⇒ (a . b)
Function: make-list len :optional fill

[R7RS][SRFI-1] 長さlenの正規のリストを返します。引数fillが与えられていれば、各要素は fillになります。そうでなければ各要素の値は不定です。

 
(make-list 5 #t) ⇒ (#t #t #t #t #t)
Function: list obj …

[R7RS] 要素がobj …であるリストを作成します。

 
(list 1 2 3) ⇒ (1 2 3)
(list) ⇒ ()
Function: list* obj1 obj2 …
Function: cons* obj1 obj2 …

[SRFI-1] listとほぼ同じですが、最後の引数が作成されるリストの最後のペアのcdrになります。 二つの手続きは全く同じです。Gaucheはもともとlist*を持っていましたが、 SRFI-1がcons*という名前を定義しました。

 
(list* 1 2 3) ⇒ (1 2 . 3)
(list* 1) ⇒ 1
Function: list-copy list

[R7RS][SRFI-1] listの浅いコピーを行います。 listが循環リストの場合、この手続きは停止しません。

Function: iota count :optional (start 0) (step 1)

[SRFI-1] startから始まり、stepずつ増加する、 count 個の要素からなる数値のリストを返します。countは 非負の整数でなければなりません。startstepが ともに正確数であれば、結果は正確数のリストになります。そうでなければ 結果は非正確数のリストです。

 
(iota 5)        ⇒ (0 1 2 3 4)
(iota 5 1 3/7)  ⇒ (1 10/7 13/7 16/7 19/7)
(iota 5 0 -0.1) ⇒ (0 -0.1 -0.2 -0.3 -0.4)

この手続きはリストを最後まで作って返します。リストが短ければ 十分速いですが、何万という数をリストにしたければ、遅延リストを使った方が 良いかもしれません。liotaを見てください(遅延シーケンス参照)。

Macro: cond-list clause …

条件によりエントリを追加することによりリストを構築します。 それぞれのclauseは条件と式を持ちます。 条件が真であれば、関連する式の結果が結果のリストの構築に使われます。 条件が偽であれば、何も挿入されません。

clauseは、以下のフォームのうちの1つでなければなりません。

(test expr …)

testが評価され、それが真ならばexpr …が評価され、 戻り値が結果の一部となります。exprが与えられなければ、 testの結果が偽でなければその結果が使われます。

(test => proc)

testが評価され、それが真ならばprocがその値とともに 呼ばれ、その戻り値が結果を構築するために使われます。

(test @ expr …)

(test expr …)のように動作しますが、最後のexprの 値はリストでなければならず、それは結果のリストに(unquote-splicingのように) スプライスされます。

(test => @ proc)

(test => proc)のように動作しますが、procの戻り値 はリストでなければならず、それは結果のリストに(unquote-splicingのように) スプライスされます。

 
(let ((alist '((x 3) (y -1) (z 6))))
 (cond-list ((assoc 'x alist) 'have-x)
            ((assoc 'w alist) 'have-w)
            ((assoc 'z alist) => cadr)))
  ⇒ (have-x 6)

(let ((x 2) (y #f) (z 5))
  (cond-list (x @ `(:x ,x))
             (y @ `(:y ,y))
             (z @ `(:z ,z))))
  ⇒ (:x 2 :z 5)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6.4 リストへのアクセスと変更

Function: car pair
Function: cdr pair

[R7RS] それぞれpairのcarとcdrを返します。

Function: set-car! pair obj
Function: set-cdr! pair obj

[R7RS] pairのcarもしくはcdrをobjで置き換えます。

注: (setter car)set-car! であり、 (setter cdr)set-cdr! です。

Function: caar pair
Function: cadr pair

Function: cdddar pair
Function: cddddr pair

[R7RS] caar(car (car x)), cadr(car (cdr x)), 等々。

R7RSでは、2レベルよりも深いアクセサは(scheme cxr)ライブラリに含まれます。

対応するsetterも定義されています。

 
(let ((x (list 1 2 3 4 5)))
  (set! (caddr x) -1)
  x)
  ⇒ (1 2 -1 4 5)
Function: length list

[R7RS] 正規のリストlistの長さを返します。 listがドットリストならばエラーが起きます。 listが循環リストの場合、この関数は無限ループします。

Function: length+ x

[SRFI-1] x が真性リストなら、その長さを返します。 x がそうでなければ(たとえ循環リストであっても) #f を返します。

Function: length=? x k
Function: length<? x k
Function: length<=? x k
Function: length>? x k
Function: length>=? x k

それぞれ、リストxの長さがkと等しいか、k未満か、k以下か、kより大きいか、 k以上かの場合に#tを、そうでなければ#fを返します。 この関数はk要素までしかリストを辿らないので、特にxが遅延シーケンス (遅延シーケンス参照)である場合に、必要以上にリストを実体化しなくて済みます。

xはドットリストや循環リストでも構いません。 ドットリストの長さを数える際には最後のペアのcdrは無視します。 つまり、ペアでないオブジェクトの長さは0で、(a . b)の長さは1になります。 循環リストは無限の長さを持つとみなします。

 
(length<=? '(a b) 2)  ⇒ #t
(length<=? '(a b) 1)  ⇒ #f
(length<=? '()    0)  ⇒ #t

;; dotted list cases
(length<=? 'a       0)  ⇒ #t
(length<=? '(a . b) 0)  ⇒ #f
(length<=? '(a . b) 1)  ⇒ #t

註: これらの手続きの名前は混乱を招きやすいかもしれません。something<=?等の 名前は通常、同じ型のオブジェクト同士の比較に使われるからです。 残念ながら、今のところより良い名前を思いつけていません。

Function: take x i
Function: drop x i

[SRFI-1] take はリスト x の最初のi個の要素を返します。 drop はリスト x の最初のi個の要素を除いたリストを返します。

 
(take '(a b c d e) 2) => (a b)
(drop '(a b c d e) 2) => (c d e)

x はあらゆる値をとりえます。

 
(take '(1 2 3 . d) 2) => (1 2)
(drop '(1 2 3 . d) 2) => (3 . d)
(drop '(1 2 3 . d) 3) => d

dropxi 回 cdr 操作をおこなうのと全く 同じです。返される値は、x と共通の末尾を共有します。一方、 take は、引数のリストが長さ0でないリストなら必ず新しいリストの 領域を確保します。

i がリスト x の終端を超えたらエラーが発生します。 より寛容な手続きについては、下のtake*drop*を見てください。

あらゆる並びからの部分並びを抽出する汎用的な方法に関しては、 シーケンスのスライス にある subseq を参照してください。

Function: take* list k :optional (fill? #f) (padding #f)
Function: drop* list k

takedropのより寛容なバージョンです。 これらの手続きは、リストがkより短くてもエラーを通知しません。

その場合、take*はデフォルトではlistのコピーを 返します。もしfill?が真であれば、足りない要素の部分に paddingを追加してk要素にしたリストを返します。

一方、drop*はリストの長さが不足する場合は単に空リストを返します。

 
(take* '(a b c d) 3)       ⇒ (a b c)
(take* '(a b c d) 6)       ⇒ (a b c d)
(take* '(a b c d) 6 #t)    ⇒ (a b c d #f #f)
(take* '(a b c d) 6 #t 'z) ⇒ (a b c d z z)
(drop* '(a b c d) 3)       ⇒ (d)
(drop* '(a b c d) 5)       ⇒ ()

注意: 一般的な、いかなるシーケンスからのサブシーケンスの抽出については、 シーケンスのスライスsubseqを見て下さい。

Function: take-right lis k
Function: drop-right lis k

[SRFI-1] take-rightlis の最後の k個の要素 からなるリストを返します。 drop-rightlis の最後の k個の要素を 除いたリスト返します。

 
(take-right '(a b c d e) 2) => (d e)
(drop-right '(a b c d e) 2) => (a b c)

lis は有限リストであればOKです。

 
(take-right '(1 2 3 . d) 2) => (2 3 . d)
(drop-right '(1 2 3 . d) 2) => (1)
(take-right '(1 2 3 . d) 0) => d
(drop-right '(1 2 3 . d) 0) => (1 2 3)

take-right の返す値はいつでも lis の共通の末尾を共有します。 drop-right は、引数が長さが0でないリストなら、必ず新しいリストの 領域を確保します。

k がリスト lis の長さより大きければエラーが発生します。 より寛容なバージョンについては下のtake-right*, drop-right*を 参照してください。

Function: take-right* list k :optional (fill? #f) (padding #f)
Function: drop-right* list k

take*及びdrop*と同じですが、listの右端からカウントします。 これらはlistk要素より短い場合にもエラーを通知しません。 drop-right*はその場合は単に空リストを返します。 take-right*はその場合はデフォルトでlistそのものを 返しますが、fill?が真であればlistの右側に 足りない分だけpaddingを足したものを返します。その場合でも 結果の尾部はlistと共有されます。

Function: take! lis k
Function: drop-right! lis k

[SRFI-1] [SRFI-1] take および drop-right の その場で更新されるバージョンです。これらの 手続きは lis を破壊的に変更するかもしれません。

lis が循環リストなら、take! は期待されるものより短いリストを返す 可能性があります。

Function: list-tail list k :optional fallback

[R7RS] listk番目のcdrを返します。listは 正規のリストでもドットリストでも循環リストでも構いません。 (listがドットリストの場合、最後のcdrは無視されます)。

kの値が負であったりlistの長さ以上の場合、 fallback引数が与えられていればそれが返され、 そうでなければエラーが報告されます。

Function: list-ref list k :optional fallback

[R7RS+] listk番目の要素を返します。listは 正規のリストでもドットリストでも循環リストでも構いません。

もしkがリストの長さを超えていたり、負数であった場合は通常はエラーが起こります。 しかし、オプショナルな引数fallbackが与えられていた場合は、エラーは起きず fallbackが返されます。これはGaucheの拡張です。

Function: list-set! list k v

[R7RS] リストlistk番目の要素をvへと変更します。 kが0から(- リストの長さ 1)の範囲の正確な整数でない場合は エラーが報告されます。listが変更不可なリストであった場合、 エラーは報告されませんが、その後の振る舞いは不定となります。

Function: last-pair list

[SRFI-1] listの最後のペアを返します。listは 正規のリストかドットリストです。

 
(last-pair '(1 2 3))   ⇒ (3)
(last-pair '(1 2 . 3)) ⇒ (2 . 3)
(last-pair 1)          ⇒ error
Function: last pair

[SRFI-1] 空ではない有限リスト pair の最後の要素を返します。 これは、(car (last-pair pair)) と同等です。

 
(last '(1 2 3))        ⇒ 3
(last-pair '(1 2 . 3)) ⇒ 2
Function: split-at x i
Function: split-at! x i

[SRFI-1] split-at はリスト x をインデックス i の 位置で分割し、最初の i 個の要素からなるリストと、残りの末尾とを 返します。

 
(split-at '(a b c d e) 2) ⇒ (a b) (c d e)

split-at! はその場で更新されるバージョンです。 これは x を破壊的に更新するかもしれません。

Function: split-at* list k :optional (fill? #f) (padding #f)

SRFI-1のsplit-atの寛容なバージョンです。 take*drop*の結果を返します。

 
(split-at* '(a b c d) 6 #t 'z)
  ⇒ (a b c d z z) and ()
Function: slices list k :optional fill? padding

listを、それぞれの長さがkであるようなサブリスト(スライス)に 分割します。 listの長さがkの整数倍でない場合は、最後のスライスは take*と同じ方法で扱われます。つまり、デフォルトではkより 短いもの、あるいはfill?が真ならばpaddingが追加されます。

 
(slices '(a b c d e f g) 3)
  ⇒ ((a b c) (d e f) (g))
(slices '(a b c d e f g) 3 #t 'z)
  ⇒ ((a b c) (d e f) (g z z))
Function: intersperse item list

listの要素の間にitemを挿入します。 (引数の順番は、Haskellのintersperseと同じです。)

 
(intersperse '+ '(1 2 3))  ⇒ (1 + 2 + 3)
(intersperse '+ '(1))      ⇒ (1)
(intersperse '+ '())       ⇒ ()

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6.5 リストをたどる手続き

Function: map proc list1 list2 …

[R7RS+] 与えられたリストの各要素に対してprocを適用し、その結果をリストにして 返します。R7RSではprocの適用順序が定められていませんが、Gaucheでは 常にprocはリスト内の順番で呼ばれます。 複数のリストが与えられた場合、最も短いリストが終了した時点でprocの適用を 打ち切ります。

 
(map car '((a b) (c d) (e f))) ⇒ (a c e)

(map cons '(a b c) '(d e f))
  ⇒ ((a . d) (b . e) (c . f))

gauche.collectionモジュール(gauche.collection - コレクションフレームワーク参照) を使うと、mapがリスト以外のコレクション型に対しても動作するようになります。

Function: append-map f clist1 clist2 …
Function: append-map! f clist1 clist2 …

[SRFI-1] 機能的には以下と同等ですが、若干効率が良いです:

 
  (apply append (map f clist1 clist2 …))
  (apply append! (map f clist1 clist2 …))

引数のリストのうち少くともひとつは有限でなければなりません。

Function: map* proc tail-proc list1 list2 …

mapとほぼ同じですが、引数の最後のペアのcdrtail-procが 適用され、その返り値が結果のリストの最後のペアのcdrになります。 この手続きは正規でないリストを引数に取ることができます。 引数のリストがひとつだけの場合、tail-procの引数は必ずペアでないオブジェクトです。

 
(map* - / '(1 2 3 . 4)) ⇒ (-1 -2 -3 . 1/4)

(define (proper lis)
  (map* values
        (lambda (p) (if (null? p) '() (list p)))
        lis))

(proper '(1 2 3))     ⇒ (1 2 3)
(proper '(1 2 3 . 4)) ⇒ (1 2 3 4)

二つ以上のリストが与えられた場合は、最も短いリストがtail-procの呼び出しを 決めます。map*が最も短いリストの最後のペアに達した時点で、 その時のそれぞれのペアのcdrがtail-procへと渡されます。

 
(map* + vector '(1 2 3 4) '(1 2 . 3))
  ⇒ (2 4 . #((3 4) 3))

註: map*という名前は、正規でないリストを作り得るlist*/cons*からの 連想でつけられました (リストの作成SRFI-1 リスト操作関数参照)。

Function: for-each proc list1 list2 …

[R7RS] 手続きprocをリストの各エレメントに対して順に適用します。 procの結果は捨てられます。for-eachの戻り値は定義されていません。 複数のリストが与えられた場合、一番短いリストが終了した時点でfor-eachは終了します。

gauche.collectionモジュール(gauche.collection - コレクションフレームワーク参照) を使うと、for-eachがリスト以外のコレクション型に対しても動作するようになります。

Function: fold kons knil clist1 clist2 …

[SRFI-1] 基本的なリスト反復演算子です。単一のリスト clist1 = (e1 e2en) を与えられたときには、以下を返します。

 
(kons en … (kons e2 (kons e1 knil)) … )

n 本のリストが与えられた場合には、kons 関数は n+1 個の引数 をとる関数でなければなりません。それぞれのリストから要素をひとつずつと、 初期値 knil である「種」あるいは畳み込み状態とよばれるものです。 この畳み込み演算は、もっとも短いリストの要素がなくなったところで終了します。 与えられるリストの少くともひとつは有限でなければなりません。

例:

 
(fold + 0 '(3 1 4 1 5 9)) ⇒ 23 ;sum up the elements
(fold cons '() '(a b c d e)) ⇒ (e d c b a) ;reverse
(fold cons* '() '(a b c) '(1 2 3 4 5))
    ⇒ (c 3 b 2 a 1) ;n-ary case
Function: fold-right kons knil clist1 clist2 …

[R6RS][SRFI-1] 基本的な再帰演算子です。単一のリスト clist1 = (e1 e2en) を与えられたときには、以下を返します。

 
(kons e1 (kons e2 … (kons en knil)))

n 本のリストが与えられた場合には、kons 関数は n+1 個の引数 をとる関数でなければなりません。それぞれのリストから要素をひとつずつと、 初期値 knil である「種」あるいは畳み込み状態とよばれものです。 この畳み込み演算は、もっとも短いリストの要素がなくなったところで終了します。 与えられるリストの少くともひとつは有限でなければなりません。

例:

 
(fold-right cons '() '(a b c d e))
   ⇒ (a b c d e) ;copy list
(fold-right cons* '() '(a b c) '(1 2 3 4 5))
   ⇒ (a 1 b 2 c 3) ;n-ary case
Function: fold-left snok knil clist1 clist2 …

[R6RS] これは左結合のfoldの別バージョンです。一つのリスト clist1 = (e1 e2en) が与えられた場合、次の値を返します。

 
(snok (… (snok (snok knil e1) e2) …) en)

上のfoldと見比べてみてください。結合の順序は同じですが、 snokに渡される引数の順序が、foldにおいてkonsに渡される 引数の順序とは逆になっています。snokが可換な演算であれば、 foldfold-leftの結果は同じになります。

 
(fold-left + 0 '(1 2 3 4 5) ⇒ 15

(fold-left cons 'z '(a b c d))
  ⇒ ((((z . a) . b) . c) . d)

(fold-left (^[a b] (cons b a)) 'z '(a b c d))
  ⇒ (a b c d z)

二つ以上のリストが渡された場合、snokは現在のknil値および 入力リストclist1 clist2 … の対応する要素を引数として 呼び出されます。

 
(fold-left list 'z '(a b c) '(A B C))
  ⇒ (((z a A) b B) c C)

註:多くの関数型言語で左結合および右結合の畳み込み操作が定義されていますが、 それはfold-leftfold-rightに相当します。 (例: Haskellのfoldlfoldr。) Schemeでは、まずSRFI-1によってfoldfold-rightが 左/右結合の畳み込み演算として導入され、R6RSでfold-leftが定義されました。 (但し、R6RSのfold-leftではclist1 clist2 …の 長さがすべて同じでない場合の動作は未定義ですが、Gaucheでは 最短のリストが終了した時点でそれまでの結果が返されます。)

Function: reduce f ridentity list
Function: reduce-right f ridentity list

[SRFI-1] fold および fold-right の変形バージョンです。 f は二項演算子でなければなりません。 また、ridentityf の入力として許される あらゆる値 x について以下を満していなければなりません。

 
 (f x ridentity) ≡ x

これらの関数は実質的に foldfold-right と同じことを 行いますが、ridentityには上記の性質があるため、 fridentityには適用されません。 ridentityが使われるのはlistが空の場合だけです。

Function: filter pred list
Function: filter! pred list

[SRFI-1] 手続き predlist の各要素に適用され、 pred が真を返す要素のリストが返されます。

 
(filter odd? '(3 1 4 5 9 2 6)) ⇒ (3 1 5 9)

filter! はその場で更新されるバージョンです。結果を生成するために list を破壊的に変更するかもしれません。

Function: filter-map f clist1 clist2 …

map と似ていますが、真になる場合の値のみが保存されます。 引数として与えられるリストの少くともひとつは有限でなければなりません。

 
(filter-map (lambda (x) (and (number? x) (* x x)))
            '(a 1 b 3 c 7))
  ⇒ (1 9 49)
Function: remove pred list
Function: remove! pred list

[SRFI-1] 手続き predlist の各要素に適用され、 pred が偽を返す要素のリストが返されます。

 
(remove odd? '(3 1 4 5 9 2 6)) ⇒ (4 2 6)

remove! はその場で更新されるバージョンです。結果を生成するために list を破壊的に更新するかもしれません。

Function: find pred clist

[SRFI-1] clist の各要素に対して左から右に pred を適用し、 pred が真を返す最初の要素を返します。predを満たす要素が 無い場合は#fを返します。

Function: find-tail pred clist

[SRFI-1] clist の各要素に対して左から右に pred を適用し、pred が 真を返す場合、その car がその要素であるペアを返します。 predを満たす要素が無い場合は#fを返します。

Function: any pred clist1 clist2 …

[SRFI-1] clist の各要素に pred を適用し、predが偽でない 値を返したら直ちにその値を返します。 predが偽でない値を返す前にリストの要素を使いきってしまったら #fが返ります。

Function: every pred clist1 clist2 …

[SRFI-1] clist の各要素に pred を順に適用し、predが 偽を返した場合、直ちに偽を返します。全てのpredの適用が 偽でない値を返した場合は、最後に返された値が返されます。

Function: count pred clist1 clist2 …

[SRFI-1] n をゼロから与えられたリストのうち最も短いリストの 長さまでとして、pred 手続きを与えられたリストの n 番目の要素に それぞれ適用します。 pred が真を返した数が返ります。

 
(count even? '(3 1 4 1 5 9 2 5 6)) ⇒ 3
(count < '(1 2 4 8) '(2 4 6 8 10 12 14 16)) ⇒ 3

引数で与えられるリストの少くともひとつは有限でなければなりません。

 
(count < '(3 1 4 1) (circular-list 1 10)) ⇒ 2
Function: delete x list :optional elt=
Function: delete! x list :optional elt=

[SRFI-1] 以下と同等です。

 
  (remove (lambda (y) (elt= x y)) list)
  (remove! (lambda (y) (elt= x y)) list)

比較手続き elt= はデフォルトでは equal? です。

Function: delete-duplicates list :optional elt=
Function: delete-duplicates! list :optional elt=

[SRFI-1] list から重複した要素を取り除きます。list 中に等しい要素が 複数ある場合、一番左がわにある最初のものだけが残ります。これらの 生き残った要素間の順番は最初のリストの順番が保存されます。 比較手続き elt= のデフォルト値は、equal? です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6.6 他のリスト手続き

Function: append list …

[R7RS] 渡されたリストの要素を繋げたリストを返します。最後の引数の部分以外は新しいセルがアロケート されて使われます。最後の引数は正規のリストである必要がありません。その場合、結果は正規でない リストとなります。

Function: append! list …

[SRFI-1] 渡されたリストの要素を繋げたリストを返します。最後の引数以外のリストのセルは、結果を 作成するために再利用されるかもしれません。 最後の引数は正規のリストである必要はありません。

Function: concatenate list-of-lists
Function: concatenate! list-of-lists!

[SRFI-1] それぞれ、(apply append list-of-lists) および (apply append! list-of-lists) と同等ですが、 applyのオーバヘッドが無い分、わずかに効率的です。

Function: reverse list :optional (tail '())
Function: reverse! list :optional (tail '())

[R7RS+, SRFI-1+] listの要素を逆順に並べたリストを返します。 reverseは常に新たにリストをアロケートしますが、 reverse!は結果を作るためにlistのセルを再利用するかもしれません。 但し、listの先頭のセルがreverse!の結果でも先頭になるとは 限らないので、listが変更されることを当てにするのではなく、 reverse!の戻り値を利用する必要があります。

省略可能引数tailが与えられた場合、それは結果のリストの末尾に 付け足されます (tail部分はコピーされません)。 これは、既にある結果のリストに新たに作った結果を付け足すイディオムで 便利です。

 
(reverse '(1 2 3 4 5)) ⇒ (5 4 3 2 1)
(reverse '(1 2 3) '(a b)) ⇒ (3 2 1 a b)

tail引数はGauche独自の拡張で、 伝統的なSchemeのreverseにはありませんが、 次の対応を考えると、そう不自然なことではありません。

 
(reverse xs)      ≡ (fold cons xs '())
(reverse xs tail) ≡ (fold cons xs tail)
Function: append-reverse rev-head tail
Function: append-reverse! rev-head tail

[SRFI-1] [SRFI-1] 2引数のreverseおよびreverse!と同等です。 srfi-1との互換のために用意されています。

Function: memq obj list
Function: memv obj list
Function: member obj list :optional obj=

[R7RS][SRFI-1] listからobjを探します。もしlistn番目の要素が objと同一ならば、(list-tail list n)を返します。 memqは同一性の判定にeq?を、memveqv?を、 memberequal?を使います。 objlist中に見つからなければ#fが返されます。

memberに省略可能引数obj=が与えられた場合は、 その手続きがequal?のかわりにobjと要素を比較するのに使われます。

 
(memq 'a '(a b c))          ⇒ (a b c)
(memq 'b '(a b c))          ⇒  (b c)
(memq 'a '(b c d))          ⇒ #f
(memq (list 'a) '(b (a) c)) ⇒ #f
(memv 101 '(100 101 102))   ⇒ (101 102)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.6.7 連想リスト

Function: acons obj1 obj2 obj3

(cons (cons obj1 obj2) obj3) を返します。 連想リストの先頭に新しいエントリを加えるのに便利です。

(この手続きはSRFI-1ではalist-consという名前で定義されています。 srfi-1 - リストライブラリ参照)。

 
(acons 'a 'b '((c . d))) ⇒ ((a . b) (c . d))
Function: alist-copy alist

[SRFI-1] alist の新しい複製を返します。alist の背骨の部分、 およびキーと値を指す各セルは複製されます。

 
(define a (list (cons 'a 'b) (cons 'c 'd)))
a ⇒ ((a . b) (c . d))

(define b (alist-copy a))
b ⇒ ((a . b) (c . d))

(set-cdr! (car a) 'z)
a ⇒ ((a . z) (c . d))
b ⇒ ((a . b) (c . d))
Function: assq obj list
Function: assv obj list
Function: assoc obj list :optional key=

[R7RS][SRFI-1] listの各要素はペアでなければなりません。 これらの手続きは、listの要素であるペアのうち、そのcarが objと一致するペアを左から探して行きます。もし見付かればそのペアが、 見付からなければ#fが返されます。 assqは比較関数にeq?を、assveqv?を、 assocequal?をそれぞれ用います。

assocの省略可能引数が与えられた場合は、 その手続きがequal?のかわりに、 objと各キーとの同一性判定に使われます。

Function: alist-delete key alist :optional key=
Function: alist-delete! key alist :optional key=

[SRFI-1] alist から keyと同じキーをもつすべてのセルを削除します。 比較は key= で行います。これのデフォルト値は eqv? です。

その場で更新を行うバージョン alist-delete! は元の alist を変更してしまうことがあります。

Function: rassoc key alist :optional eq-fn
Function: rassq key alist
Function: rassv key alist

与えられるkeyalistのそれぞれの要素で、carの代わりに cdrにマッチするような逆になった連想リストです。 両方向の連想リストと理解すると簡単です。 rassocは、そのデフォルトがequal?である、オプションの 比較関数を取ります。rassqeq?rassveqv?を 使います。

Function: assoc-ref alist key :optional default eq-fn
Function: assq-ref alist key :optional default
Function: assv-ref alist key :optional default

これらの手続きは、他の*-ref手続きと対称的な連想リストへの アクセスを提供します。(assoc, assq, assvと 引数の順序が逆であることに注意しえください。*-ref手続きは、 コンテナを最初に、要素を次に取ります)。

これは、一般的な連想リストアクセスのパターンを提供します。

 
(assoc-ref alist key default eq-fn)
 ≡
  (cond [(assoc key alist eq-fn) => cdr]
        [else default])))

defaultが省略されると、#fが使われます。

assoc-refは、そのデフォルトがequal?である、オプションの 比較関数eq-fnを取ります。assq-refeq?を、 assv-refeqv?をそれぞれ使います。

Function: rassoc-ref alist key :optional default eq-fn
Function: rassq-ref alist key :optional default
Function: rassv-ref alist key :optional default

assoc-refの逆連想リストバージョンです。

 
(rassoc-ref alist key default eq-fn)
 ≡
  (cond ((rassoc key alist eq-fn) => car)
        (else default))))

オプショナル引数の意味は、assoc-refと同じです。

Function: assoc-set! alist key val :optional eq-fn
Function: assq-set! alist key val
Function: assv-set! alist key val

alist(key . val)のペアが追加された連想リストを返します。 alistがすでにkeyをキーとする要素を持っている場合、 その要素のcdrは破壊的にvalに変更されます。 alistkeyをキーとする要素を持っていない場合は、 新しいペアが作成され、alistの一番前に追加されます。 したがって、key-valペアが追加されたことを保証するために その戻り値を使うべきです。

assoc-set!は、そのデフォルトがequal?である、オプションの 比較関数eq-fnを取ります。assq-set!eq?を、 assv-set!eqv?を、それぞれ使います。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.7 シンボル

Builtin Class: <symbol>

シンボルを表すクラスです。

Reader Syntax: |name|

[R7RS] シンボルの定義では通常許されないような文字を使った妙な名前のシンボルを表記するのに 使う構文です。16進数エスケープ表記を含めることもできます。

 
;; 空白を名前に含むシンボル
'|this is a symbol| ⇒ |this is a symbol|

;; Unicodeコードポイントを、バックスラッシュとxからなるエスケープに続けて
;; 書くこともできます。セミコロンで終端します。
'|\x3bb;| ⇒ λ

インタプリタが大文字小文字を区別しないモードで走っている場合は、 大文字を含むシンボルを表記するときにも使えます (大文字小文字の区別参照)。

Reader Syntax: #:name

「インターンされていない」シンボルを表記します。インターンされていないシンボルは gensymstring->uninterned-symbolで作ることができます。

インターンされていないシンボルは、良く伝統的なマクロで 変数衝突を避けるために使われます。インターンされていないシンボルは 内部のシンボルのテーブルに登録されないため、同じ名前のシンボルでも eq?になりません。

 
(eq? '#:foo '#:foo) ⇒ #f
(eq? '#:foo 'foo) ⇒ #f

インターンされていないシンボルが含まれるS式を、シンボルの同一性を保って 表記するには、srfi-38記法が使えます。

 
(let1 s '#:foo (list s s))
  ⇒ prints (#0=#:foo #0#)

(let ((s '#:foo) (t '#:foo)) (list s t s t))
  ⇒ prints (#0=#:foo #1=#:foo #0# #1#)
Function: symbol? obj

[R7RS] objがシンボルなら#tを返します。

 
(symbol? 'abc)     ⇒ #t
(symbol? 0)        ⇒ #f
(symbol? 'i)       ⇒ #t
(symbol? '-i)      ⇒ #f
(symbol? '|-i|)    ⇒ #t
Function: symbol-interned? symbol

シンボルsymbolがインターンされていれば#tを、 そうでなければ#fを返します。シンボルでないものが渡された場合はエラーとなります。

Function: symbol=? a b c …

[R7RS] 全ての引数はシンボルでなければなりません。引数のどの二つの組み合わせを取っても それがeq?である時に限り、#tが返ります。

Function: symbol->string symbol

[R7RS] symbolの名前を文字列で返します。返される文字列は変更不可です。

 
(symbol->string 'foo) ⇒ foo
Function: string->symbol string

[R7RS] 文字列stringを名前に持つシンボルを返します。

 
(string->symbol "a") ⇒ a
(string->symbol "A") ⇒ A
(string->symbol "weird symbol name") ⇒ |weird symbol name|
Function: string->uninterned-symbol string

string->symbolと似ていますが、作られたシンボルはインターンされません。

 
(string->uninterned-symbol "a") ⇒ #:a
Function: gensym :optional prefix

インターンされてない、新しいシンボルを作って返します。 返されるシンボルは、他のシンボルとは決してeq?にならないことが保証されます。 prefixが与えられた場合は、それは文字列でなければならず、 それが作られるシンボルのプレフィクスに使われます。これは主としてデバッグを 容易にするためのものです。

Function: symbol-sans-prefix symbol prefix

symbolprefixは共にシンボルでなければなりません。 prefixsymbolの先頭部分にマッチした場合、 symbolからマッチ部分を取り除いた名前を持つシンボルを返します。 マッチしない場合は#fを返します。

 
(symbol-sans-prefix 'foo:bar 'foo:) ⇒ bar
(symbol-sans-prefix 'foo:bar 'baz:) ⇒ #f
Function: symbol-append interned? objs …
Function: symbol-append objs …

objsの文字列表記をつなげた名前を持つシンボルを返します。

最初の引数が真偽値の場合は、最初の形式と解釈されます。interned?引数は 結果のシンボルがインターンされているかどうかを指定します。

それ以外の引数はそれぞれ次のとおり変換されます:キーワードであれば (先頭の:を含む)名前に、それ以外はx->stringを適用した結果に。 (キーワードを特別扱いするのは、キーワード-シンボル統合の後でも動作が 変わらないようにするためです。詳しくはキーワードとシンボルの統合を 参照。)

これは、Biglooにある同名の手続きの上位互換になっています。Bigloo版は シンボルのみを引数に許し、また結果は常にインターンされます。

 
(string-append 'ab 'cd) ⇒ abcd
(string-append 'ab ':c 30) ⇒ ab:c30
(string-append #f 'g 100) ⇒ #:g100

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.8 キーワード

Builtin Class: <keyword>

キーワードは、自動的にクォートされる特別なシンボルのようなものです。 名前で渡す引数(キーワード引数)や、キーワード-値のリストで広く使われて います。

Gaucheにおけるキーワード引数のサポートについては手続きを作るを、 また独自にキーワード-値リストをパーズする方法については let-keywords マクロ(省略可能引数のパージング) も参照して下さい。

CommonLisp とは異なり、Gaucheではキーワードとシンボルを別の型として扱ってきました。 しかしこれはR7RSに違反しています。R7RSでは:で始まる識別子はシンボルとして 読まれるからです。そこで、Gaucheでもキーワードをシンボルのサブタイプとすることにしました。 すなわち、キーワードもシンボルと同様通常の変数として評価されるのですが、 たまたまその値がそれ自身に束縛されているように見えるのです。

この変更は後方互換性を壊す可能性があるので、現在はデフォルトでは有効になっていません。 環境変数GAUCHE_KEYWORD_IS_SYMBOLをセットした状態でgoshを 起動することにより、キーワードがシンボルとして扱われるようになります。

この「キーワードはシンボルのサブタイプ」が近い将来デフォルトの動作になる予定です。 開発者は、GAUCHE_KEYWORD_IS_SYMBOLをセットした状態で ライブラリやアプリケーションが壊れないかどうか確認しておいてください。 互換性を保つ方法については、 キーワードとシンボルの統合を参照してください。

Reader Syntax: :name

名前が name であるキーワードとして読み込まれます。 (先行する ’:’ は、キーワードの名前の一部ではないことに注意してください。)

Function: keyword? obj

obj がキーワードであれば、#t を返します。

Function: make-keyword name

名前が name であるキーワードを返します。nameには 文字列かシンボルが許されます。

 
(make-keyword "foo")  ⇒ :foo

(make-keyword 'foo)   ⇒ :foo
Function: keyword->string keyword

キーワード keyword の名前を文字列で返します。

 
(keyword->string :foo) ⇒ "foo"
Function: get-keyword key kv-list :optional fallback

キー-値のリストから値を取り出すのに便利な手続きです。 キー-値のリスト kv-list は偶数個の要素を持たなければなりません。 1つ目、3つ目、5つ目、… の要素はキーとして扱われ、 2つ目、4つ目、6つ目、… の要素は、その前の要素をキーとした値と なります。

この手続きは、キーの集合から key を探して、それが見つかれば、 対応する値を返します。 2つ以上のキーにマッチしたら、最左のものとなります。 マッチするキーがない場合、fallback が与えられていればそれを 返し、さもなければエラーを通知します。

kv-list が正しい偶数個の要素を持つリストでない場合は、エラーに なります。

キーワード-値リストの ‘キーワード’ と、key 引数は、実際には キーワードである必要はありません。いかなる Scheme オブジェクトで あっても良いです。キーの比較は、eq? によって行われます。

この手続きは、STk から導入されました。

 
(get-keyword :y '(:x 1 :y 2 :z 3))
  ⇒ 2
(get-keyword 'z '(x 1 y 2 z 3))
  ⇒ 3

(get-keyword :t '(:x 1 :y 2 :z 3))
  ⇒ #<error>
(get-keyword :t '(:x 1 :y 2 :z 3) #f)
  ⇒ #f
Macro: get-keyword* key kv-list :optional fallback

get-keyword と同様ですが、kv-listkey を 含まない場合にのみ fallback が評価されることだけが違います。

Function: delete-keyword key kv-list
Function: delete-keyword! key kv-list

kv-list から keyeq? であるキーをもつキーと値を 削除します。

delete-keywordkv-list を変更しません。しかし、 返されたリストは共通の末尾部分を共有します。

delete-keyword! は新しくアロケートされることはありません。 そして、破壊的に kv-list を変更する可能性があります。 最初のキーがマッチした場合元のリストは変更されないこともありえますが、 返り値のリストを使わなければいけません。

key にマッチするキーがない場合 kv-list が返ります。

 
(delete-keyword :y '(:x 1 :y 2 :z 3 :y 4))
 ⇒ (:x 1 :z 3)
Function: delete-keywords keys kv-list
Function: delete-keywords! keys kv-list

delete-keyworddelete-keyword!と似ていますが、 keysにオブジェクトのリストを指定できます。 kv-list中のキーがkeysのうちのどれかに一致すれば、 そのキーと続く値がkv-listから取り除かれます。

 
(delete-keywords '(:x :y) '(:x 1 :y 2 :z 3 :y 4))
 ⇒ (:z 3)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.8.1 キーワードとシンボルの統合

将来のGaucheでは、キーワードは「デフォルトで自分自身に束縛されている」という 性質を持つシンボルとなります。 表面的にはこの変更はたいした違いをもたらさないでしょう。 プログラム中に表記してあるキーワードはそれ自身に評価されるので、 今までどおりキーワード引数を渡すことができます。 今までと違ってキーワードを変数として使い、例えば(define :key 3)のように 新たな値に束縛することはできるようになりますが、その変更は そうしているモジュールの中だけに留まります。そもそも現在のGaucheで キーワードの束縛は許されていないので、この変更は後方互換です。 (キーワードを再定義する時はその効果をわきまえて使ってください)。

けれども、違いが現れるいくつかのわかりづらい場合が存在し、 注意していないと互換性を壊してしまうかもしれません。 現在のGaucheで使えて、さらに将来に渡っても壊れることがないような方法を これから説明します。

ライブラリやアプリケーションがこの変更後も動くかどうかを確かめるには、 環境変数GAUCHE_KEYWORD_IS_SYMBOLをセットして走らせてください。

(symbol? :key)は現在は#fを返すが、将来は#tを返す

キーワードかどうかの判定にはkeyword?を使えますが、 シンボルかキーワードかで動作を変えるコードでは、常にキーワードの検査を先にするように してください。

 
;; これは壊れる
(cond
  [(symbol? x) (x-is-symbol)]
  [(keyword? x) (x-is-keyword)])

;; これなら大丈夫
(cond
  [(keyword? x) (x-is-keyword)]
  [(symbol? x) (x-is-symbol)])

パターンマッチ中のリテラルキーワード

現在のバージョンでは、util.matchsyntax-rulesの パターンにキーワードが現れた場合、それはキーワード自身とのみマッチします。 しかしキーワードがシンボルのサブタイプになると、そういったキーワードはシンボルと 同じようにパターン変数として扱われます。

 
;; 現在のバージョン
(match '(a b) [(:key z) (list :key z)] [_ "nope"])
   ⇒ "nope"

;; キーワードとシンボルが統合された後
;; :keyは単なるパターン変数となる
(match '(a b) [(:key z) (list :key z)] [_ "nope"])
   ⇒ (a b)

syntax-rulesでも同じことが起きます。

どのバージョンでも動くコードにするには、マッチさせたいキーワードが リテラルであることを明示してください。

Gauche 0.9.5から、matchはパターンにクオートされていないキーワードが 現れると警告を発します。

キーワードの表示

(display :key)は今はkeyを表示します(コロン無し)が、 将来は:keyを表示するようになります。

keyを表示したい場合は(display (keyword->string :key))とすれば、 どのバージョンでも同じように動作します。

R7RSコードではクオートするかGaucheのモジュールをインポートする

統合後は、キーワード(:で始まるシンボル)はgauche.keywordモジュール の中で自分自身に束縛されます。

Gaucheコードはデフォルトでgaucheモジュールを継承し、 それがkeywordモジュールを継承しているので、 キーワードの束縛は自動的に見えるようになります。

しかしR7RSコードではgaucheモジュールは継承されないので、 :で始まるシンボルもデフォルトではただのシンボルです。 通常、Gaucheの組み込み機能を使うには(import (gauche base))としますが、 こうするとキーワードの自分自身への束縛もインポートされるようになっています (gauche.basegauche.keywordも継承しているからです)。 ただ、Gaucheの手続きとは別にキーワードをR7RSコード中で使いたい場合は 注意してください。自己束縛されているキーワードだけが欲しければ (import (gauche keyword))とする必要がありますし、 そうしない場合はキーワードに見えるシンボルでもクオートする必要があります。

 
(import (scheme base))

:foo ⇒ ERROR: unbound variable: :foo

(import (gauche base))

:foo ⇒ :foo

次の例では、R7RSライブラリfoo(gauche base)から copy-portのみをインポートしています。こういった場合、 :sizeキーワードをクオートなしで使うには、(gauhce keyword)も 別にインポートしなければなりません。 (あるいは、(gauche base)からインポートするシンボルのリストに :sizeも明示するか)。

 
(define-library (foo)
  (import (scheme base)
          (only (gauche base) copy-port)
          (gauche keyword))
  (export cat)        

  (begin
    (define (cat)
      (copy-port (current-input-port)
                 (current-output-port)
                 :size 4096))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.9 Identifier

Builtin Class: <identifier>

An identifier is an internal object to keep track of binding of variables by the compiler.

Usually it is hidden from Scheme world, but the higyenic macro expander inserts identifiers into its output, which is necessary for hygiene. But that makes reading macro expansion result difficult.

If you bothered by all the #<identifier ...> stuff in the macro output, remember a handy trick to pass the expansion result to unwrap-syntax; it converts all identifiers in the passed form to bare symbols. It does lose information—two different identifiers may be converted to a symbol with the same name—so you need some care to interpret the output, but usually the output gives a fairly good idea of what the macro is doing.

Currently, identifiers are disjoint from symbols. That might cause problems if you tweak macro output. The plan is to make identifiers just a special kind of symbols eventually, so do not assume too much about identifiers.

Function: identifier? obj
Function: identifier->symbol identifier
Function: unwrap-syntax form

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.10 文字

Builtin Class: <char>
Reader Syntax: #\charname

[R7RS] リテラルの文字オブジェクトを表現します。

リーダーは#\に出会うと、まず次の文字を読み込みます。それが文字 ()[]{}" \|;# のいずれかならば、その文字自身となります。 そうでなければ、単語を構成しない文字に出会うまで文字が続けて読み込まれます。 もし一文字しか読まれなければ、その文字自身となります。そうでなければ その単語が文字の名前として解釈されます。 それが有効な文字の名前でなければエラーとなります。

以下の文字の名前が認識されます。これらの文字の名前は大文字小文字を区別しません。

space

スペース (ASCII #x20)

newline, nl, lf

改行文字 (ASCII #x0a)

return, cr

復帰文字 (ASCII #x0d)

tab, ht

水平タブ (ASCII #x09)

page

フォームフィード、改ページ (ASCII #x0c)

escape, esc

エスケープ (ASCII #x1b)

delete, del

デリート (ASCII #x7f)

null

NUL文字 (ASCII #x00)

xN

N が16進表記の整数であるとき、Unicodeコードポイントが整数N であるような文字。これはR7RSの字句構文です。(下の互換性に関する注も見てください。)

uN

Nが4桁または8桁の16進数整数であるとき、UCSコードNであるような文字。 これはGaucheの古い構文です。新規のコードでは\xNを使ってください。 (下の互換性に関する注も見てください。)

 
#\newline ⇒ #\newline ; 改行文字
#\x0a     ⇒ #\newline ; 改行文字
#\x41     ⇒ #\A       ; ASCII文字 'A'
#\x3042   ⇒ #\あ      ; 平仮名「あ」
#\x2a6b2  ⇒ ; JISX0213 2-94-86

互換性に関する注: 0.9.4まで、Gaucheは\xNN構文を、 Unicodeコードポイントでなく内部文字エンコーディングで文字を表記するのに 使っていました。Gaucheの内部エンコーディングをutf-8もしくは noneでコンパイルされている場合は両者に違いはありません (noneの場合、U+00ffまでの文字しか扱うことができませんが、 扱える範囲内ではUnicodeと一致します)。 内部エンコーディングがeuc-jpsjisの場合、 \xNNで記されたASCIIの範囲外の文字の解釈は、 0.9.3.3およびそれ以前のGaucheとは異なることになります。

リーダのモードをleagcyにセットすれば(リーダー字句モード参照)、 #\xNNは以前と同じ解釈となり、互換性は保たれます (ただし、R7RSとは非互換になります)。 あるいは#\uNNNN表記や文字そのものを表記することで、 以前のGaucheでも新しいGaucheでも動作するようにできます。

Function: char? obj

[R7RS] objが文字なら#tを、そうでなければ#fを返します。

Function: char=? char1 char2 char3 …
Function: char<? char1 char2 char3 …
Function: char<=? char1 char2 char3 …
Function: char>? char1 char2 char3 …
Function: char>=? char1 char2 char3 …

[R7RS] 文字を比較します。比較は内部の文字エンコーディングで行われます。

Function: char-ci=? char1 char2 char3 …
Function: char-ci<? char1 char2 char3 …
Function: char-ci<=? char1 char2 char3 …
Function: char-ci>? char1 char2 char3 …
Function: char-ci>=? char1 char2 char3 …

[R7RS] 文字を、大文字小文字を区別せずに比較します。 比較はそれぞれの文字のfoldcaseの内部文字エンコーディングに基づいて行われます。 foldcaseについては下のchar-foldcaseを参照してください。

R7RSではこれらの手続きは(scheme char)ライブラリにあります。

Function: char-alphabetic? char
Function: char-numeric? char
Function: char-whitespace? char
Function: char-upper-case? char
Function: char-lower-case? char

[R7RS] 文字charがそれぞれアルファベット (Unicode文字カテゴリ LuLlLtLmLoNl)、 数字 (Unicode文字カテゴリ Nd)、 空白文字 (Unicode文字カテゴリ ZsZpZl)、 大文字 (Unicode文字カテゴリ Lu)、 小文字 (Unicode文字カテゴリ Ll)の時に真の値を返します。

R7RSではこれらの手続きは(scheme char)ライブラリにあります。

Function: char-general-category char

[R6RS] 文字charのUnicode general categoryを表す、以下のシンボルのいずれかを返します。

CcOther, Control
CfOther, Format
CnOther, Not Assigned
CoOther, Private Use
CsOther, Surrogate
LlLetter, Lowercase
LmLetter, Modifier
LoLetter, Other
LtLetter, Titlecase
LuLetter, Uppercase
McMark, Spacing Combining
MeMark, Enclosing
MnMark, Nonspacing
NdNumber, Decimal Digit
NlNumber, Letter
NoNumber, Other
PcPunctuation, Connector
PdPunctuation, Dash
PePunctuation, Close
PfPunctuation, Final quote
PiPunctuation, Initial quote
PoPunctuation, Other
PsPunctuation, Open
ScSymbol, Currency
SkSymbol, Modifier
SmSymbol, Math
SoSymbol, Other
ZlSeparator, Line
ZpSeparator, Paragraph
ZsSeparator, Space

Gaucheがeuc-jpかshift_jisエンコーディングでコンパイルされている場合、 Unicodeに対応するものがない文字がいくつかあります (各文字は、Unicode一文字+Unicode結合文字で表現できます)。 これらの文字については、以下のとおり暫定的にカテゴリを割り当てています。 将来のUnicodeにこれらの文字が取り込まれた場合は、Unicodeで決められる カテゴリに変更されるでしょう。

SJISEUCCatUnicode
82F5A4F7LoU+304B U+309A (Semi-voiced Hiragana KA)
82F6A4F8LoU+304D U+309A (Semi-voiced Hiragana KI)
82F7A4F9LoU+304F U+309A (Semi-voiced Hiragana KU)
82F8A4FALoU+3051 U+309A (Semi-voiced Hiragana KE)
82F9A4FBLoU+3053 U+309A (Semi-voiced Hiragana KO)
8397A5F7LoU+30AB U+309A (Semi-voiced Katakana KA)
8398A5F8LoU+30AD U+309A (Semi-voiced Katakana KI)
8399A5F9LoU+30AF U+309A (Semi-voiced Katakana KU)
839AA5FALoU+30B1 U+309A (Semi-voiced Katakana KE)
839BA5FBLoU+30B3 U+309A (Semi-voiced Katakana KO)
839CA5FCLoU+30BB U+309A (Semi-voiced Katakana SE)
839DA5FDLoU+30C4 U+309A (Semi-voiced Katakana TSU)
839EA5FELoU+30C8 U+309A (Semi-voiced Katakana TO)
83F6A6F8LoU+31F7 U+309A (Semi-voiced small Katakana FU)
8663ABC4LlU+00E6 U+0300 (Accented latin small ae)
8667ABC8LlU+0254 U+0300 (Accented latin small open o)
8668ABC9LlU+0254 U+0301 (Accented latin small open o)
8669ABCALlU+028C U+0300 (Accented latin small turned v)
866AABCBLlU+028C U+0301 (Accented latin small turned v)
866BABCCLlU+0259 U+0300 (Accented latin small schwa)
866CABCDLlU+0259 U+0301 (Accented latin small schwa)
866DABCELlU+025A U+0300 (Accented latin small schwa w/hook)
866EABCFLlU+025A U+0301 (Accented latin small schwa w/hook)
8685ABE5SkU+02E9 U+02E5
8686ABE6SkU+02E5 U+02E9
Function: char->integer char
Function: integer->char n

[R7RS] char->integerは文字charの内部エンコーディングに対応する 整数値を返します。integer->charは数値nと内部エンコーディングが 同じ文字を返します。有効な文字charに対して以下の式は常に真となります。

 
(eq? char (integer->char (char->integer char)))

註: R7RSでは、これらの手続きはUnicodeコードポイントを扱うとされています。 Gaucheの内部エンコーディングがutf-8noneであればそれに 合致します (noneの場合はU+00ffまでの文字がサポートされます)。 内部エンコーディングがeuc-jpsjisの場合は、 Unicodeコードポイントと文字の変換には下のchar->ucs/ucs->charを 使ってください。

対応する内部エンコーディングを持つ文字が無い数値をinteger->charに渡した 場合の結果は不定です。

Function: char->ucs char
Function: ucs->char n

それぞれ、文字charをUCSコードポイントを表す整数へ変換し、 またUCSコードポイントを表す整数nを文字へと変換する手続きです。

Gaucheの内部文字エンコーディングがUTF-8でコンパイルされている場合は、 これらの手続きはそれぞれchar->integerおよびinteger->char と等価です。

Gaucheの内部文字エンコーディングがUTF-8でない場合、 これらの手続きはgauche.charconvモジュールを使って 内部文字コードとUCSとの変換を行います (gauche.charconv - 文字コード変換参照)。 charが対応するUCSコードを持っていない場合、char->ucs#fを 返します。UCSコードnに対応する文字が内部エンコーディングで定義されていない 場合、変換ルーチンが代替文字を提供していればそれを、そうでなければ#f が返されます。

Function: char-upcase char
Function: char-downcase char
Function: char-titlecase char
Function: char-foldcase char

[R6RS][R7RS] charの大文字、小文字、titlecase、foldcaseを それぞれ返します。

変換は原則としてUnicodeで定義された文字対文字のマッピングに従います。 Unicodeに従って変換する先の文字が内部エンコーディングでサポートされていない 場合は、変換は行いません。内部エンコーディングが’none’の場合は、 文字をLatin-1 (ISO-8859-1) であるとみなします。Latin-1の small y with diaresis (U+00ff) をupcaseすると、 内部エンコーディングがutf-8の場合はcapital y with diaeresis (U+0178)にマップされますが、 内部エンコーディングがnoneであれば元の文字がそのまま返されます。

R7RSはchar-titlecaseを定義していません。それ以外の3つの手続きは (scheme char)ライブラリにあります。R6RSは全ての手続きを提供しています。

文字対文字のマッピングでは、一文字が複数文字に展開されるようなケースマッピングは 扱いません。有名な例はeszett (latin small letter sharp S, U+00df)で、 文字列のコンテキストでは二つの大文字のSへとマップされますが、 char-upcase #\ß は単に #\ß を返します。 完全なマッピングが必要なら、gauche.unicodeモジュールの string-upcase等が利用できます (Full string case conversion参照)。

Function: digit->integer char :optional (radix 10) (extended-range? #f)

文字charradix進数の数字を構成するのに有効な文字であれば、 対応する整数が、そうでなければ#fが返されます。

 
(digit->integer #\4) ⇒ 4
(digit->integer #\e 16) ⇒ 14
(digit->integer #\9 8) ⇒ #f

省略可能引数extended-range?に真の値が与えられると、 この手続きはASCIIの数字だけでなく、Ndカテゴリに属する すべての文字、例えば FULLWIDTH DIGIT ZEROからNINE (U+ff10 - U+ff19) も認識します。

R7RSにはdigit-valueという手続きが定義されていて、それは (digit->integer char 10 #t)と等価です。

Common Lispには同様の手続きがdigit-char-pというやや曖昧な名で定義されています。

Function: integer->digit integer :optional (radix 10) (basechar1 #\0) (basechar2 #\a)

digit->integerの逆です。整数integerradix進数 ひと桁で 表現するのに使われる文字を返します。integerがひと桁で表現できない数値の場合は #fが返されます。

 
(integer->digit 13 16) ⇒ #\d
(integer->digit 10) ⇒ #f

省略可能引数basechar1はゼロに相当する文字を指定します。 デフォルトは#\0です。ここに、例えばU+0660 (ARABIC-INDIC DIGIT ZERO) を与えれば、整数をアラビア・インド数字に変換できます。

もうひとるの省略可能引数basechar2は、10を越える整数に使う文字の起点を 指定します。デフォルトは#\aです。ここに#\Aを渡せば、 16進数表記で小文字のかわりに大文字を使うようになります。

Common Lispのdigit-charに当たります。

Function: gauche-character-encoding

コンパイル時に選択された内部文字エンコーディングを表すシンボルを返します。 返される値は以下のいずれかです。

euc-jp

EUC-JP

utf-8

UTF-8

sjis

Shift JIS

none

マルチバイトキャラクタをサポートしない(8ビット固定長文字)。

コンパイル時に内部エンコーディングによってコードを切り替えたい場合は、 機能識別子gauche.ces.*が使えます。 プラットフォーム依存の機能を見てください。

Function: supported-character-encodings

ネイティブなマルチバイトエンコーディングスキームでサポートされている 文字エンコーディングスキームの名前を表す文字列のリストを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.11 文字集合

Builtin Class: <char-set>

文字の集合を取り扱う、文字集合(キャラクタセット)のクラスです。 Gaucheは文字集合オブジェクトのサポートと、 ある文字がその集合に属するかどうかを調べる手続きを言語組み込みで持っています。

コレクションプロトコル (gauche.collection - コレクションフレームワーク参照) を実装しているので、 gauche.collectionで提供される標準のコレクションメソッドが使えます。

<char-set>のインスタンスは文字に関数のように適用することができ、 その文字が文字集合に属するかどうかを判定する述語として機能します。 下記のchar-set-contains?を参照してください。

文字集合に関する他の操作、例えば集合演算などは、SRFI-14モジュール (srfi-14 - 文字集合ライブラリ参照) で提供されています。

Reader Syntax: #[char-set-spec]

この構文で、リテラル文字集合を記述することができます。 char-set-specには集合に含める文字を列挙します。 次の特殊なシーケンスを含めることができます。

x-y

文字xと文字yの間の文字全て。xyも含みます。 xyよりも内部文字コードで比較して小さくなければなりません。

^

カレットがchar-set-specの最初に来た場合、以降に示される文字集合の 補集合がこの文字集合となります。

\xN;

UnicodeコードポイントがN(16進数表記)の文字。

\s

空白文字。

\S

空白でない文字。(\sの補集合)

\d

10進数の数字

\D

\dの補集合

\w

単語を構成する文字の集合(アルファベット、数字、アンダースコア)

\W

\Wの補集合

\\

バックスラッシュ文字

\-

マイナス文字

\^

カレット文字

[:alnum:] …

POSIX流文字集合表記。以下の文字集合名が認識されます: alnum, alpha, blank, cntrl, digit, graph, lower, print, punct, space, upper, xdigit

 
#[aeiou]     ; 母音文字'a', 'e', 'i', 'o', 'u'の集合
#[a-zA-Z]    ; アルファベット
#[[:alpha:]] ; アルファベット (POSIX表記)
#[\\\-]      ; バックスラッシュとマイナス文字
#[]          ; 空の文字集合
#[ぁ-ん]     ; 平仮名の集合
#[\x0d;\x0a;\x3000;]  ; リターン, 改行, 全角空白

互換性への注: 以前は、\xNN (2桁固定の16進数、終端のセミコロン無し) を文字と認識していました。 例えば#[\x0d\x0a]はリターン文字と改行文字セットを意味していました。 互換性のため、終端のセミコロンが見当たらない場合は古い構文もサポートされます。 しかし曖昧な場合もあります。#[\x0a;]は新しい構文では 改行文字のみのセットですが、古い構文では改行文字とセミコロンになります。

リーダのモードをleagcyにセットすると、常に古い構文で認識されます。 リーダのモードをwarn-legacyにセットすると、 デフォルトと同じように振る舞いますが、古い構文を見つけた場合は警告が出力されます。 詳しくはリーダー字句モードを参照してください。

古い構文と新しい構文の両方で動作するコードが必要な場合は、\uエスケープを 使ってください。

Function: char-set? obj

[SRFI-14] objが文字集合であれば真の値を返します。

Function: char-set-contains? char-set char

[SRFI-14] 文字集合char-setが文字charを含んでいれば真の値を返します。

 
(char-set-contains? #[a-z] #\y) ⇒ #t
(char-set-contains? #[a-z] #\3) ⇒ #f

(char-set-contains? #[^ABC] #\A) ⇒ #f
(char-set-contains? #[^ABC] #\D) ⇒ #t

(char-set-contains? #[あ-お] #\う) ⇒ #t
(char-set-contains? #[あ-お] #\ぷ) ⇒ #f
Generic application: char-set char

文字集合オブジェクトは文字に適用されると、 (char-set-contains? char-set char) のように振る舞います。

 
(#[a-z] #\a) ⇒ #t
(#[a-z] #\A) ⇒ #f

(use gauche.collection)
(filter #[a-z] "CharSet") ⇒ (#\h #\a #\r #\e #\t)
Function: char-set char …

[SRFI-14] 文字char … からなる文字集合を作成して返します。

 
(char-set #\a #\b #\c)   ⇒ #[a-c]
(char-set #\あ #\い #\う) ⇒ #[あいう]
Function: char-set-size char-set

[SRFI-14] 文字セットに含まれる文字数を返します。

 
gosh> (char-set-size #[])
0
gosh> (char-set-size #[[:alnum:]])
62
Function: char-set-copy char-set

[SRFI-14] char-setのコピーを作って返します。

Function: char-set-complement char-set
Function: char-set-complement! char-set

[SRFI-14] char-setの補集合を返します。上の手続きは常に新たな集合を作って返しますが、 下の手続きは引数を変更して返す可能性があります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12 文字列

Builtin Class: <string>

文字列のクラスです。Gaucheでは、文字列は文字のシーケンスともバイトのシーケンスとも みなすことができます。

GaucheではSchemeの文字列は、内部的には、変更不可能なオブジェクト(文字列実体)を 用いて表現されている、ということを強調しておきます。 文字列が変更可能であるR7RSの仕様を満たすために、Schemeレベルの文字列は 文字列実体への間接ポインタとなっています。文字列を変更すると、 変更を反映した新しいstring bodyが作成され、Schemeレベルの文字列が 新しい文字列実体を指すように変更されます。

このことから、文字列操作の性能を見積る場合に注意が必要です。

Gaucheはそもそも文字列の変更に関して全く最適化を行っていません。 (string-set! s k c)は、k番目の文字の前後の部分文字列を取って、 c一文字の文字列を間に挟んで継ぎ足すことで新たな文字列を作るのと 全く同じコストがかかります。 文字列を変更不可として扱う方が良いプログラミングスタイルだと考えるからです。 文字列の構築子も参照してください。

R7RSに定義されている文字列操作は非常に限られています。 Gaucheでは追加の組込み手続きのほか、 SRFI-13に定義されている豊富な文字列ライブラリを備えています。 SRFI-13についてはsrfi-13 - 文字列ライブラリを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.1 文字列の表記

Reader Syntax: ""

[R7RS+] リテラル文字列です。ダブルクオートの中では、 以下のエスケープシーケンスが認識されます。

\"

[R7RS] ダブルクオート文字

\\

[R7RS] バックスラッシュ文字

\n

[R7RS] 改行文字 (ASCII 0x0a)

\r

[R7RS] 復帰文字 (ASCII 0x0d)

\f

フォームフィード (ASCII 0x0c)

\t

[R7RS] タブ文字 (ASCII 0x09)

\a

[R7RS] アラーム文字 (ASCII 0x07)

\b

[R7RS] バックスペース文字 (ASCII 0x08)

\0

NUL文字 (ASCII 0x00)

\<whitespace>*<newline><whitespace>*

[R7RS] 無視されます。長い文字列リテラルを読みやすさのために折り返す時に便利です。 このエスケープシーケンスはR6RSで導入されました。

\xN;

[R7RS] 16進数Nで表現されたUnicodeコードポイントの文字。 16進数は何桁でも良い。(下の互換性に関する注参照。)

\uNNNN

4桁の16進数NNNNによって示されるUCS2コードを持つ文字。

\UNNNNNNNN

8桁の16進数NNNNNNNNによって示されるUCS4コードを持つ文字。

下はバックスラッシュ-改行エスケープシーケンスの使用例です。

 
(define *message* "\
  This is a long message \
  in a literal string.")

*message*
  ⇒ "This is a long message in a literal string."

‘message’ の後の空白に注意してください。‘in’の前の空白は読み込みルーチンによって 無視されてしまうので、空白を入れたければ‘message’と続くバックスラッシュの間に 入れる必要があります。もし文字列中に実際に改行文字を入れ、さらにその後の 文字列をインデントさせたい場合は次のようにすると良いでしょう:

 
(define *message/newline* "\
  This is a long message, \
  \n   with a line break.")

互換性に関する注: 以前は、\xNN (2桁固定の16進数、終端のセミコロン無し) を 文字列中の文字と認識していました。 例えば"\x0d\x0a""\r\n"と同じでした。 互換性のため、終端のセミコロンが見当たらない場合は古い構文もサポートされます。 しかし曖昧な場合もあります。"\x0a;"は新しい構文では "\n"と同じですが、、古い構文では"\n;"となります。

リーダのモードをleagcyにセットすると、常に古い構文で認識されます。 リーダのモードをwarn-legacyにセットすると、 デフォルトと同じように振る舞いますが、古い構文を見つけた場合は警告が出力されます。 詳しくはリーダー字句モードを参照してください。

Reader Syntax: #*""

不完全な文字列のリテラル表記です。完全な文字列と同様のエスケープシーケンスが 使えます。

#*’ という構文はCommon Lispでビットベクタの表記に使われています。 不完全な文字列は実際はバイトベクタであることから、類似点を認めてこの構文を 採用しました。(もし将来必要になってビットベクタが実装されたとしても、 この構文と共存できます)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.2 文字列に関する述語

Function: string? obj

[R7RS] objが文字列なら#tを、そうでなければ#fを返します。

Function: string-immutable? obj

objが変更不可な文字列なら#tを、そうでなければ#fを返します。

Function: string-incomplete? obj

objが不完全文字列なら#tを、そうでなければ#fを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.3 文字列の構築子

Function: make-string k :optional char

[R7RS] 長さkの文字列を作成して返します。 charが与えられればそれで内容を満たします。charが与えられなければ 空白文字で満たされます。常に完全な文字列が返されます。

 
(make-string 5 #\x) ⇒ "xxxxx"
(make-string 5 #\ふ) ⇒ "ふふふふふ"

make-stringで必要な長さの文字列をアロケートして、string-set! で順番に埋めて行くアルゴリズムは、Gaucheでは極めて非効率であることに 注意してください。そのようなアルゴリズムは、文字列の内部表現とアロケーションメカニズムに 関して不必要な仮定を置いており、Gaucheはその仮定とは合致しません。 文字列の順次作成に適しているのは文字列ポートです (文字列ポート参照)。それが使えない場合、 文字のリストを作成し、list->stringで変換する方がまだmake-stringstring-set!を使うより良いでしょう。

Function: make-byte-string k :optional byte

大きさkの不完全な文字列を作成して返します。 byteが与えられた場合は、その下位1バイトで文字列の各バイトを初期化します。 byteは正確な整数でなければなりません。

Function: string char …

[R7RS] 文字char … から構成された文字列を返します。

Generic Function: x->string obj

文字列への強制型変換手続きです。 objの文字列表現を返します。 デフォルトのメソッドでは、文字列はそのまま返され、数値はnumber->stringで、 シンボルはsymbol->stringで変換され、その他のオブジェクトはdisplay表現 が使われます。

他のクラスはこのメソッドを定義することにより、独自の変換関数を提供することができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.4 文字列の補間

「文字列の補間(string interpolation)」という用語は、 PerlやPythonなど様々なスクリプト言語で、文字列リテラル内に式を埋め込んでおき 実行時に式を評価した値をそのリテラル内に埋め込む機能を指します。

Schemeはそのような機能を定義していませんが、Gaucheではリーダーマクロを使って 文字列の補間を実装しました。

Reader Syntax: #string-literal

文字列に評価されます。string-literal内に、~expr というシーケンス(ここでexprは有効なScheme式の外部表現)が 現われたら、exprが評価されてその結果がもとの位置に埋め込まれます。 結果の文字列化にはx->stringが使われます(文字列の構築子参照)。

チルダと続く式とは、空白文字等を入れずに隣接していなければなりません。 そうでない場合は置換されません。

すぐ後ろに非空白文字が来る場所にチルダ自身を埋め込みたい場合は ~~とします。

それ以外のstring-literal内の文字シーケンスはそのままコピーされます。

Exprに単独の変数を使う場合で、それに続く文字列と変数名を区切りたい 場合は、‘|’文字を使ったシンボルエスケープ構文が使えます。下の例の最後の 2つを見て下さい。

 
#"This is Gauche, version ~(gauche-version)."
 ⇒ "This is Gauche, version 0.9.5."

#"Date: ~(sys-strftime \"%Y/%m/%d\" (sys-localtime (sys-time)))"
 ⇒ "Date: 2002/02/18"

(let ((a "AAA")
      (b "BBB"))
 #"xxx ~a ~b zzz")
 ⇒ "xxx AAA BBB zzz"

#"123~~456~~789"
 ⇒ "123~456~789"

(let ((n 7)) #"R~|n|RS")
 ⇒ "R7RS"

(let ((x "bar")) #"foo~|x|.")
 ⇒ "foobar"

実は、リーダーはこの構文をマクロ呼び出しへと変換し、それが最終的には string-appendへの呼び出しへと変換されます。

 
#"This is Gauche, version ~(gauche-version)."
 ≡
(string-append "This is Gauche, version "
               (x->string (gauche-version))
               ".")
Reader Syntax: #`string-literal

これは文字列補間の古い書法です。まだ認識されますが、 新たなコードでは使わないでください。

string-literalの中で、(~expr ではなく) ,expr とマークされた部分のexprが評価されます。 コンマのすぐ次に式を開始する文字がこなければ、コンマはその効力を失います。

 
#`"This is Gauche, version ,(gauche-version)"

この構文を採用した理由: スクリプト言語の文字列補間構文には様々なバリエーションがありますが、通常、 その言語の他の構文と関連しているものが多いです (例えば、$で変数参照する言語が、$を文字列中の評価部分に前置する等)。

Gaucheの古い形式の文字列補間構文では、準クオートの構文を流用していました (準クオート(Quasiquote)参照)。準クオートと文字列補間は意味的に似ているからです。 けれども、コンマはそれ自体が文字列中によく出てくる文字なので、少々不格好な仕様でした。

そこで、以下のような理由から、チルダの方がアンクオート文字としてふさわしいと判断しました。

Schemeは他のスクリプト言語より一般的により多くの文字を変数名に使うことが出来ることに注意して下さい。 結果として、変数の値を文字列に挿入する際、ほとんどの場合において変数名を‘|’で区切る 必要があるでしょう。例えば、Perlでは "$year/$month/$day $hour:$minutes:$seconds" と書けたものが、Gaucheでは #`"~|year|/~|month|/~day ~|hour|:~|minutes|:~seconds" と書かねばなりません。 混乱を避けるためには、この構文内では常に直接の変数参照は‘|’で区切るようにしておくのが良いかもしれません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.5 文字列のアクセスと変更

Function: string-length string

[R7RS] 文字列stringの長さ(文字数)を返します。 stringは不完全な文字列であっても構いません。

Function: string-size string

文字列stringの大きさを返します。文字列の大きさは、 stringが占めるメモリ上のバイト数で、これは文字列の内部エンコーディングに 依存します。同じ文字列であっても内部エンコーディングが違えば違う大きさになる場合も あります。

不完全な文字列では、文字列の長さと大きさは常に一致します。

Function: string-ref cstring k :optional fallback

[R7RS+] 完全な文字列cstringk番目の文字を返します。 不完全な文字列を渡すのはエラーです。

kが負数であったりcstringの長さと同じかそれ以上であった場合には エラーが報告されます。但し、引数fallbackが与えられている場合にはエラーを 報告せずfallbackが返されます。これはGaucheの拡張です。

Function: string-byte-ref string k

(多分、不完全な)文字列stringk番目のバイトを返します。 戻り値は、0から255の範囲の整数です。kは0以上、 (string-size string)より小でなければなりません。

Function: string-set! string k char

[R7RS] stringk番目の文字をcharで置き換えます。 kは0以上、(string-length string)より小でなければ なりません。戻り値は未定義です。

stringが不完全文字列の場合、charの下位8ビットの整数値は、 stringk番目のバイトをセットするために使われます。

パフォーマンス上の考慮点について、make-stringの説明を参照して下さい。

Function: string-byte-set! string k byte

stringk番目のバイトを整数byteで置き換えます。 byteは0から255の範囲(255を含む)でなければなりません。 kは0以上、(string-size string)より小である必要があります。 stringが完全文字列の場合、この操作により不完全文字列になります。 戻り値は未定義です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.6 文字列の比較

Function: string=? string1 string2 string3 …

[R7RS] 全ての引数が内容の等しい文字列であれば#tを返します。

Function: string<? string1 string2 string3 …
Function: string<=? string1 string2 string3 …
Function: string>? string1 string2 string3 …
Function: string>=? string1 string2 string3 …

[R7RS] 文字列同士をコードポイントの順序で比較します。全ての引数が順序どおりであれば #tが、そうでなければ#fが返されます。

Function: string-ci=? string1 string2 string3 …
Function: string-ci<? string1 string2 string3 …
Function: string-ci<=? string1 string2 string3 …
Function: string-ci>? string1 string2 string3 …
Function: string-ci>=? string1 string2 string3 …

大文字小文字を無視する文字列比較です。

これらの手続きは、引数に「文字ごとの大文字小文字変換」を適用します。 (変換にはUnicodeの文字単位の大文字小文字マッピングテーブルを使います)。 詳しくはchar-foldcaseの説明を見てください (文字)。 文字ごとの大文字小文字変換は、ドイツ語のエスツェットのような特別な場合を 考慮しません。

 
(string-ci=? "\u00df" "SS") ⇒ #f

R7RSでは、string-ci*手続きは文字列としての大文字小文字変換を要求しています。 Gaucheは、R7RSに準拠した大文字小文字の違いを無視する比較手続きを gauche.unicodeモジュールに用意しています(Full string case conversion参照)。 R7RSでプログラミングする際に(scheme char)をインポートした場合は、 gauche.unicodeモジュールのstring-ci=?などが使われます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.7 文字列を扱うその他の手続き

Function: substring string start end

[R7RS] stringstart番目の文字(これを含む)から、end番目の文 字(これを含まない)までの部分文字列を返します。引数startおよび endは以下を満さなければなりません。 0 <= start < N0 <= end <= Nstart <= end。ただし、Nは与えられた文字列の長 さです。

startがゼロでかつendNの場合には、stringのコ ピーが返ります。

実は後述する拡張されたstring-copysubstringのスーパーセッ トになっています。この手続きの役割は主にR7RSとの互換性のためです。 gauche.sequence - シーケンスフレームワークのジェネリック版subseqも参照してください。

Function: string-append string …

[R7RS] string …を連結した内容を含む文字列を新しくアロケートして返 します。

文字列の反転と追加string-concatenateも参照 してください。

Function: string->list string :optional start end
Function: list->string list

[R7RS] 文字列を文字のリストへ、またはその逆の変換をします。

string->listにはオプショナル引数として開始、終了位置のインデッ クスを渡せます。

list->stringではlistの要素はすべて文字でなければなりません。 そうでなければ、エラーシグナルがあがります。文字列や文字がまざったリス トから文字列を構成したい場合にはtext.tree - 怠惰なテキスト構築にある tree->stringが使えます。

Function: string-copy string :optional start end

[R7RS] stringのコピーを返します。startおよび/あるいはendの 位置インデックスを渡すと元の文字列の部分文字列を取り出せます。 (したがってstring-copyは事実上substringのスーパーセット です)。

start引数のみを与えた場合には、部分文字列はstart番目の文字 (これを含む)からstringの最後までで、それが返ります。 startendの両方を与えたときは、部分文字列はstart番 目の文字(これを含む)から、end番目の文字(これを含まない)までで、 それが返ります。startendが満すべき条件については 前述のsubstringの項を見てください。

註: R7RSの破壊的バージョンstring-copy!srfi-13モジュールで 提供されます (srfi-13 - 文字列ライブラリ参照)。

Function: string-fill! string char :optional start end

[R7RS] stringchar で埋めます。 オプションの startend は、影響を受ける領域を 制限します。

 
(string-fill! "orange" #\X)
  ⇒ "XXXXXX"
(string-fill! "orange" #\X 2 4)
  ⇒ "orXXge"
Function: string-join strs :optional delim grammer

[SRFI-13] リスト strs 中にある文字列を、文字列 delim を’糊’ として連結します。

引数 grammer は、文字列がどのように連結されるかを指定する シンボルで、以下のうちの一つです。

infix

それぞれの文字列の間に delim を使います。このモードが デフォルトです。strs が空文字列かヌル文字列を含むリスト である場合は、曖昧に(適当に)動作します。

 
(string-join '("apple" "mango" "banana") ", ")
  ⇒ "apple, mango, banana"
(string-join '() ":")
  ⇒ ""
(string-join '("") ":")
  ⇒ ""
strict-infix

infix のように動作しますが、strs には空リストは 許されません。したがって、曖昧さはありません。

prefix

delim をそれぞれの文字列の前に補います。

 
(string-join '("usr" "local" "bin") "/" 'prefix)
  ⇒ "/usr/local/bin"
(string-join '() "/" 'prefix)
  ⇒ ""
(string-join '("") "/" 'prefix)
  ⇒ "/"
suffix

delim をそれぞれの文字列の後ろに補います。

 
(string-join '("a" "b" "c") "&" 'suffix)
  ⇒ "a&b&c&"
(string-join '() "&" 'suffix)
  ⇒ ""
(string-join '("") "&" 'suffix)
  ⇒ "&"
Function: string-scan string item :optional return
Function: string-scan-right string item :optional return

string から item (文字列あるいは文字)を探します。 string-scanは最も左にある一致を、 string-scan-rightは最も右にある一致を見つけます。

引数 return は、string 中に item が見つかった 場合にどの値が返されるかを指定します。それは以下のシンボルのうちの 一つでなければなりません。

index

item が見つかった場合はstring 内でのインデックス、 そうでなければ #f を返します。これがデフォルトの振る舞いです。

 
(string-scan "abracadabra" "ada") ⇒ 5
(string-scan "abracadabra" #\c) ⇒ 4
(string-scan "abracadabra" "aba") ⇒ #f
before

item よりも前にある string の部分文字列、あるいは item が見つからなければ #f を返します。

 
(string-scan "abracadabra" "ada" 'before) ⇒ "abrac"
(string-scan "abracadabra" #\c 'before) ⇒ "abra"
after

item より後ろにある string の部分文字列、あるいは item が見つからなければ #f を返します。

 
(string-scan "abracadabra" "ada" 'after) ⇒ "bra"
(string-scan "abracadabra" #\c 'after) ⇒ "adabra"
before*

item の前にある string の部分文字列と後ろにある string の部分文字列を返します。item が見つからない場合は、 (values #f #f) を返します。

 
(string-scan "abracadabra" "ada" 'before*)
  ⇒ "abrac" and "adabra"
(string-scan "abracadabra" #\c 'before*)
  ⇒ "abra" and "cadabra"
after*

string のうち、item の終端までの部分文字列とその残りを 返します。item が見つからなかった場合は、(values #f #f) を返します。

 
(string-scan "abracadabra" "ada" 'after*)
  ⇒ "abracada" and "bra"
(string-scan "abracadabra" #\c 'after*)
  ⇒ "abrac" and "adabra"
both

string のうち、item の前と item の後ろの 部分文字列を返します。item が見つからない場合、 (values #f #f) を返します。

 
(string-scan "abracadabra" "ada" 'both)
  ⇒ "abrac" and "bra"
(string-scan "abracadabra" #\c 'both)
  ⇒ "abra" and "adabra"
Function: string-split string splitter &optional limit

stringsplitter で分割し、文字列のリストを返します。 splitter には、文字、文字セット、文字列、正規表現、手続きが 使えます。

splitter が文字の場合、その文字がデリミタとして使われます。

splitter が文字セットの場合は、その文字セットに含まれる文字の 連続がデリミタとして使われます。

splitter に手続きが与えられた場合、string にある各文字に 対してその手続きが呼ばれ、splitter が真の値を返すような連続した 文字群がデリミタとして使われます。

 
(string-split "/aa/bb//cc" #\/)    ⇒ ("" "aa" "bb" "" "cc")
(string-split "/aa/bb//cc" "/")    ⇒ ("" "aa" "bb" "" "cc")
(string-split "/aa/bb//cc" "//")   ⇒ ("/aa/bb" "cc")
(string-split "/aa/bb//cc" #[/])   ⇒ ("" "aa" "bb" "cc")
(string-split "/aa/bb//cc" #/\/+/) ⇒ ("" "aa" "bb" "cc")
(string-split "/aa/bb//cc" #[\w])  ⇒ ("/" "/" "//" "")
(string-split "/aa/bb//cc" char-alphabetic?) ⇒ ("/" "/" "//" "")

;; some boundary cases
(string-split "abc" #\/) ⇒ ("abc")
(string-split ""    #\/) ⇒ ("")

limit引数が与えられた場合、それは#fか非負整数でなければなりません。 非負整数の場合はsplitterがマッチするデリミタの最大数を指定します。 デリミタがその数だけ見つかったら、残りの文字列は分割されずにそのまま結果に含められます。

 
(string-split "a.b..c" "." 0)   ⇒ ("a.b..c")
(string-split "a.b..c" "." 1)   ⇒ ("a" "b..c")
(string-split "a.b..c" "." 2)   ⇒ ("a" "b" ".c")

string-tokenize (他の文字列操作) も参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.12.8 不完全文字列

Gaucheの内部エンコーディングで正当なマルチバイト文字で構成されていない ようなバイト列を含む文字列は「不完全文字列」となります。

不完全文字列が生成される状況はいくつかあります。たとえば、バイナリデー タを文字列として読み込んだとき、マルチバイト文字の途中で切れた文字列を 読み込んだとき、別の不完全文字列が連結された場合などです。

不完全文字列は例外的な状況であるとみなすべきです。これまではバイト列処 理を使っていましたが、現在はu8vector (gauche.uvector - ユニフォームベクタ参照)を使うこ とができますので、将来のリリースでは削除する計画です。

万が一、不完全文字列に出会ってしまったら以下の手続を使って完全文字列に 変換することができます。

Function: string-incomplete->complete str :optional handling

不完全文字列strの内容を再解釈して、あらたに完全文字列を返す。 handling引数でstr中の不正なバイト列の扱いかたを指定します。

#f

strが不正なバイト列を含んでいる場合、変換を諦めて#fを返し ます。これがデフォルトのふるまいです。

:omit

不正なバイト列を捨てます。常に完全文字列を返します。

1文字

不正なバイト列中の各バイトを与えた文字で置き換えます。常に完全文字列を 返します。

strが完全文字列なら、そのコピーが返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.13 正規表現

GaucheはPOSIXの拡張正規表現にほぼ上位互換で、さらに Perl 5の正規表現から拡張機能を採り入れた正規表現エンジンを持っています。 Gaucheはまた、リテラル正規表現用の構文を備えています。

正規表現用のリテラル構文が用意されており、 また、正規表現オブジェクトは適用可能、つまり文字列に対して 手続きのように振る舞い、自分自身にマッチするかどうかを判定できるようになっています。 この二つの機能によって、文字列のマッチをスキャンするようなイディオムを 簡潔に書けるようになっています。

 
(find #/pattern/ list-of-strings)
  ⇒ match object or #f

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.13.1 正規表現の構文

Reader Syntax: #/regexp-spec/
Reader Syntax: #/regexp-spec/i

リテラルの正規表現オブジェクトを表記します。読まれた際に<regexp>の インスタンスとなります。

末尾に文字iが与えられた場合は、マッチ時に大文字小文字を区別しない 正規表現オブジェクトとなります。

string->regexpに対してこの構文を使う利点は、 正規表現のコンパイルが一度しか行われない点です。この構文は、 内部ループの中でも、正規表現のコンパイルのオーバヘッドを気にせずに 使うことができます。動的に正規表現を作成したい場合のみstring->regexpを 使って下さい。

Gaucheの組み込み正規表現構文はPOSIX拡張正規表現に準じたものに、 Perlの拡張の一部を採り入れたものです。

ここに示す構文は表面的な構文にすぎないことに注意して下さい。 Gaucheの正規表現コンパイラは抽象構文木を扱うようになっており、 将来はSREのような別の構文もサポートされる予定です。

re*

reの0回以上の繰り返しにマッチします。

re+

reの1回以上の繰り返しにマッチします。

re?

reの0回または1回の出現にマッチします。

re{n}
re{n,m}

回数に範囲のある繰り返しです。 re{n}ren回の繰り返しにマッチします。 re{n,m}ren回以上、m回以下の 繰り返しにマッチします。但しn <= mとします。 2番目の形式ではnmのどちらかを省略することが できます。nが省略された場合は0とみなされます。 mが省略された場合は無限大とみなされます。

re*?
re+?
re??
re{n,m}?

上記の繰り返し構造とほぼ同じですが、これらの構文は「non-greedy」または 「lazy」と呼ばれるマッチ戦略を用います。すなわち、まずreがマッチする 最小の回数を試し、それが失敗したら順に繰り返しの回数を増やしてゆきます。 最後の形式ではnmのどちらかは省略できます。 次の例を比べてみてください:

 
(rxmatch-substring (#/<.*>/ "<tag1><tag2><tag3>") 0)
  ⇒ "<tag1><tag2><tag3>"

(rxmatch-substring (#/<.*?>/ "<tag1><tag2><tag3>") 0)
  ⇒ "<tag1>"
(re…)

捕捉クラスタリング。括弧でくくられた正規表現の列がグループとして 扱われ、またそれにマッチした文字列はサブマッチとして保存されます。

(?:re…)

捕捉無しクラスタリング。reはグループとして 扱われますが、サブマッチとして保存されません。

(?<name>re…)

名前つきの捕捉とクラスタリング。(re…)と同様ですが、 マッチした文字列に名前nameがつけられます。マッチした文字列には インデックスの数字と名前のどちらでも参照できます。

同じ名前が複数回正規表現内に出現した場合、どの名前付き捕捉にマッチした 部分文字列が返されるかは不定です。

(?i:re…)
(?-i:re…)

大文字小文字の区別の制御。 (?i:re…)re…が大文字小文字にかかわらず マッチするようにします。 (?-i:re…)はその逆です。

Perlの正規表現では’?’と’:’の間に他のいくつかのフラグを使うことが できますが、Gaucheでは今のところこのフラグのみをサポートしています。

pattern1|pattern2|…

パターンのいずれかにマッチします。

\n

バックリファレンス。nは整数です。 n番目(1から数える)の捕捉カッコに捕捉された文字列と一致する場合に、\nが マッチします。補足カッコがネストしている場合、開きカッコの順番で数えます。 n番目のカッコが繰り返しの中にあり、複数回マッチ している場合は、最後にマッチした文字列との比較が行われます。

\k<name>

名前によるバックリファレンス。 名前nameを持つ捕捉カッコで捕捉された文字列と一致する場合に、 \k<name>がマッチします。参照しているカッコが繰り返しの中にあり、 複数回マッチしている場合は、最後にマッチした文字列との比較が行わ れます。同じ名前nameを持つ捕捉カッコが複数ある場合には、 それらのカッコの最後にマッチした文字列のいずれかと一致する場合、 マッチが成功します。

.

任意の1文字にマッチします。改行文字にもマッチします。

[char-set-spec]

char-set-specで指定される文字セット内の文字にマッチします。 char-set-specについては文字集合を参照して下さい。

\s, \d, \w

それぞれ空白文字(#[[:space:]])、 数字(#[[:digit:]])、 単語を構成する文字(#[[:alpha:][:digit:]_])にマッチします。

文字セット内でも、その外でも使えます。

\S, \D, \W

それぞれ\s\d\wで指定される文字セットの補集合の 文字にマッチします。

^, $

それぞれ、パターンの最初または最後に指定された場合、 文字列の最初か最後にマッチします。

これらの文字は、パターンの最初(^の場合)か最後($の場合)に 現れた時のみ特別な意味を持ち、それ以外の場所ではこれらの文字自身にマッチします。 これらの文字を特殊文字と認識する段階では、肯定および否定の先読み ((?=...), (?!...), (?<=...), (?<!...))と アトミックなクラスタリング((?>...))はあたかもそれが独立した パターンであるかのように扱われます。つまり、^がこれらの構造の先頭に現れた 場合は、これらの構造が全体の正規表現中のどこにあるかには関わらず、文字列先頭への マッチとみなされます。$がこれらの構造の末尾に現れたば場合も同様です。

\b, \B

\bは単語の境界の空文字列にマッチします。 \Bはその逆です。

\;
\"
\#

これらはそれぞれ;"、および#と同じです。 Emacs等、Scheme構文を理解するエディタを混乱させないために使うことができます。

(?=pattern)
(?!pattern)

肯定および否定の先読み。 patternが文字列の現在の位置にマッチする(あるいはマッチ しない)ときにマッチが成功しますが、現在の位置は変更しない ので、後に続く正規表現は現在と同じ位置から適用されます。

例えば、次の表現は、電話番号のうち日本の番号("81"から始まるもの) を除く文字列にマッチします。

 
\+(?!81)\d{9,}
(?<=pattern)
(?<!pattern)

肯定および否定の後読み。 現在の位置の左側にpatternにマッチする文字列がある場合に マッチが成功(あるいは失敗)します。先読みと同様、現在の位置は 変更しません。

内部的にこの表現は、patternを逆転させたうえで、現在の位置 から左に向かってマッチを進めることで実現されています。したがって、 patternには任意のものにマッチする表現を含めることができますが、 マッチの順番や長さが重要な場合(例えば2通りにマッチしうる捕捉の カッコ)などは、左から右に現在位置が進むときとは異なる場所に マッチするかもしれません。

(?>pattern)

アトミックなクラスタリング。patternがいったんマッチすると、 そのマッチは確定します。後続のパターンが失敗した場合でも、 pattern内にバックトラックして他のマッチが試みられることはありません。

re*+
re++
re?+

それぞれ(?>re*)、(?>re+)、(?>re?)と同じです。

(?test-pattern then-pattern)
(?test-pattern then-pattern|else-pattern)

条件つきマッチング。test-patternが成功すれば then-patternへと、そうでなければ else-patternへと(もしあれば)マッチを進めます。

test-patternには以下の形式が書けます。

(integer)

バックリファレンス。integer番目の捕捉クラスタリングのマッチに 成功していた場合に成功となります。

(?=pattern)
(?!pattern)

肯定および否定の先読み。入力の現在位置から、入力を消費することなく patternのマッチを試み、それがそれぞれ成功もしくは失敗した 場合に、このtest-patternを成功とみなします。

(?<=pattern)
(?<!pattern)

肯定および否定の後読み。入力の現在位置から左側に向かって、 patternの逆向きにマッチを試みます、 それがそれぞれ成功もしくは失敗した場合に、 このtest-patternを成功とみなします。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.13.2 正規表現を使う

Regexpオブジェクトとrxmatchオブジェクト

Builtin Class: <regexp>

正規表現オブジェクトのクラスです。string->regexpを使って実行時に 作成できます。また、Gaucheはリテラルの正規表現を表す構文を持っており、 ロード時に作成することもできます。

Gaucheの正規表現エンジンはマルチバイト文字列に対応しています。

Builtin Class: <regmatch>

正規表現マッチオブジェクトのクラスです。正規表現エンジンrxmatchは、 一致した場合にこのオブジェクトを返します。部分一致の情報を含めた 全てのマッチに関する情報がこのオブジェクトに含まれています。

一致した部分文字列やそのインデックスのリストではなく マッチオブジェクトを返すことの利点は効率です。 regmatchオブジェクトはマッチの内部状態を保持しており、 要求された時にはじめて該当する部分文字列やインデックスを計算します。 これは特にマルチバイト文字列に有効です。マルチバイト文字列 へのインデックスアクセスは遅いからです。

Function: string->regexp string :key case-fold

文字列stringを正規表現とみなして、<regexp>のインスタンスを 作成して返します。

キーワード引数case-foldに真の値が与えられた場合、作成される正規表現は 大文字小文字を区別しないものとなります。 (大文字小文字を区別しない正規表現に関しては上の説明を参照して下さい)。

Function: regexp? obj

objが正規表現オブジェクトなら真の値を返します。

Function: regexp->string regexp

正規表現regexpを記述する元になった文字列を返します。 返される文字列は変更不可な文字列です。

Function: regexp-num-groups regexp
Function: regexp-named-groups regexp

正規表現regexp中の、捕捉グループ(サブマッチに使われるグループ)の総数、 及び名前つき捕捉グループのalistをそれぞれ返します。

捕捉グループの総数は、この正規表現でマッチした場合のマッチオブジェクトの rxmatch-num-matchesと同じになります。正規表現全体もひとつのグループだと みなされるので、総数は常に1以上です。

regexp-named-groupsから返されるalistは carに名前(シンボル)、cdrにそのサブマッチ番号を持つペアを 要素とします。alist内でのグループの順番は不定です。

 
(regexp-num-groups #/abc(?<foo>def)(ghi(?<bar>jkl)(mno))/)
  ⇒ 5
(regexp-named-groups #/abc(?<foo>def)(ghi(?<bar>jkl)(mno))/)
  ⇒ ((bar . 3) (foo . 1))

マッチを試みる

Function: rxmatch regexp string

正規表現オブジェクトregexpに一致するものを文字列stringから 探します。一致が見付かった場合は<regmatch>オブジェクトを返し、 見付からなかった場合は#fを返します。

他のScheme処理系ではこれは matchregexp-searchstring-matchなど 様々な名で呼ばれています。

入力から複数のマッチを取り出したり、あるいはストリーム入力 (ポートから読み出すデータなど) にマッチをかけたい場合は、gauche.generatorモジュールのgrxmatch も使えるかもしれません (ジェネレータの操作参照)。

Generic application: regexp string

正規表現オブジェクトは直接文字列に対して適用することもできます。 これは(rxmatch regexp string)と同じ動作をしますが、 表記が短くて済みます。この機能は適用可能なオブジェクト で述べているメカニズムを 使って実装されています。

マッチの結果を取り出す

Function: rxmatch-start match :optional (i 0)
Function: rxmatch-end match :optional (i 0)
Function: rxmatch-substring match :optional (i 0)

rxmatchが返すマッチオブジェクトmatchから情報を取り出します。 iが省略されるか0の場合、これらの手続きはそれぞれ一致した 文字列の開始インデックス、終了インデックス、および一致した部分文字列を 返します。iに正の整数が与えられた場合は、i番目のサブマッチ に関する情報を返します。iにシンボルが与えられた場合は、名前 iを持つサブマッチの情報を返します。同じ名前iを持つ複数の サブマッチがある場合には、成功したサブマッチの情報を返します。 iにそれ以外の値を与えるのはエラーです。

簡便のために、match#fを渡すことも許されています。 その場合、これらの手続きは#fを返します。

これらの手続きはScshでmatch:startmatch:endmatch:substringと呼ばれているものと等価です。

Function: rxmatch-after match :optional (i 0)
Function: rxmatch-before match :optional (i 0)

マッチオブジェクトmatchの前および後の文字列を返します。 正の整数がiに与えられた場合はi番目のサブマッチの前および後の 文字列を返します。シンボルが与えられた場合は、その名前を持つ サブマッチの前後の文字列を返します。

 
(define match (rxmatch #/(\d+)\.(\d+)/ "pi=3.14..."))

(rxmatch-after match) ⇒ "..."
(rxmatch-after match 1) ⇒ ".14..."

(rxmatch-before match) ⇒ "pi="
(rxmatch-before match 2) ⇒ "pi=3."
Function: rxmatch-substrings match :optional start end
Function: rxmatch-positions match :optional start end

複数のサブマッチ (0番目のサブマッチはマッチ全体) を取り出す手続きです。 それぞれ、部分文字列のリストと、開始位置と終了位置のコンスのリストが返されます。

 
(rxmatch-substrings (#/(\d+):(\d+):(\d+)/ "12:34:56"))
  ⇒ ("12:34:56" "12" "34" "56")

(rxmatch-positions (#/(\d+):(\d+):(\d+)/ "12:34:56"))
  ⇒ ((0 . 8) (0 . 2) (3 . 5) (6 . 8))

簡便のために、match#fを渡すことも許されています。 その場合、これらの手続きは()を返します。

省略可能なstartend引数は取り出すサブマッチのインデックスの 範囲を指定します。省略された場合、startは0、 end(rxmatch-num-matches match)の値が使われます。 例えばマッチ全体が不要なら、start1を渡せば良いのです。

 
(rxmatch-substrings (#/(\d+):(\d+):(\d+)/ "12:34:56") 1)
  ⇒ ("12" "34" "56")
Function: rxmatch->string regexp string :optional selector …

文字列に正規表現でマッチをかけ、マッチした文字列を得る、便利な関数です。 マッチしなかった場合は#fが帰ります。

selectorが与えられなかった場合、この手続きは次の式と同じです。

 
(rxmatch-substring (rxmatch regexp string))

selectorに整数が与えられた場合は、 それで指定されるサブマッチの文字列が返されます。

selectorにはシンボルafterbeforeを与えることも出来ます。 その場合は、マッチした文字列の前や後の文字列が返されます。これらのシンボルの後に さらにサブマッチを指定する整数を与えることもできます。

 
gosh> (rxmatch->string #/\d+/ "foo314bar")
"314"
gosh> (rxmatch->string #/(\w+)@([\w.]+)/ "foo@example.com" 2)
"example.com"
gosh> (rxmatch->string #/(\w+)@([\w.]+)/ "foo@example.com" 'before 2)
"foo@"
Generic application: regmatch :optional index
Generic application: regmatch 'before :optional index
Generic application: regmatch 'after :optional index

マッチオブジェクトは直接整数のインデックスもしくはシンボルに対して適用することが できます。整数に適用したときは(rxmatch-substring regmatch index)、 シンボルbeforeのときは(rxmatch-before regmatch)、シンボル afterのときは(rxmatch-after regmatch)、そのほかのシンボルのときは (rxmatch-substring regmatch symbol)と同じ動作をします。

表記が短くて済みます。 この機能は適用可能なオブジェクト で述べているメカニズムを使って実装されています。

 
(define match (#/(\d+)\.(\d+)/ "pi=3.14..."))

  (match)           ⇒ "3.14"
  (match 1)         ⇒ "3"
  (match 2)         ⇒ "14"

  (match 'after)    ⇒ "..."
  (match 'after 1)  ⇒ ".14..."

  (match 'before)   ⇒ "pi="
  (match 'before 2) ⇒ "pi=3."

(define match (#/(?<integer>\d+)\.(?<fraction>\d+)/ "pi=3.14..."))

  (match 1)         ⇒ "3"
  (match 2)         ⇒ "14"

  (match 'integer)  ⇒ "3"
  (match 'fraction) ⇒ "14"

  (match 'after 'integer)   ⇒ ".14..."
  (match 'before 'fraction) ⇒ "pi=3."
Function: rxmatch-num-matches match
Function: rxmatch-named-groups match

それぞれ、matchの持つマッチの数、 および名前つきグループの名前とインデックスのalistを返します。 これらは、matchを返した正規表現オブジェクトに対する regexp-num-groupsregexp-named-groups手続きに対応します。 元の正規表現オブジェクトなしに、matchの中身を調べたい時に便利です。

返されるマッチの数には「マッチ全体」も含まれます。 つまり、<regmatch>オブジェクトに対しては常に正の整数が返ることになります。 値を持たないマッチもカウントされます(下の例を参照)。 rxmatch-named-matchesが返すalistについても、元の正規表現の持つ 名前つきグループの情報がマッチの有無によらず返されます。

簡便のために、match#fを渡すこともできます。 その場合、rxmatch-num-matchesは0を、 rxmatch-named-groups()を返します。

 
(rxmatch-num-matches (rxmatch #/abc/ "abc")) ⇒ 1
(rxmatch-num-matches (rxmatch #/(a(.))|(b(.))/ "ba")) ⇒ 5
(rxmatch-num-matches #f) ⇒ 0

(rxmatch-named-groups
 (rxmatch #/(?<h>\d\d):(?<m>\d\d)(:(?<s>\d\d))?/ "12:34"))
 ⇒ ((s . 4) (m . 2) (h . 1))

便利なユーティリティ

Function: regexp-replace regexp string substitution
Function: regexp-replace-all regexp string substitution

string中でregexpにマッチした部分をsubstitutionで 置き換えます。regexp-replaceは最初にマッチした部分のみを置き換え、 regexp-replace-allは全てのマッチを置き換えます。

substitutionは文字列か手続きです。 文字列の場合、バックスラッシュに続く数値、もしくは \k<name>という形式でサブマッチ文字列を参照できます \0はマッチ文字列全体を参照します。文字列リテラルにバックスラッシュを 埋め込む場合は二つのバックスラッシュが必要であることに注意して下さい。 バックスラッシュそのものをsubstitution中で使いたい場合は 二つのバックスラッシュを重ねます; 文字列リテラルの場合は4つのバックスラッシュが 必要になります。

 
(regexp-replace #/def|DEF/ "abcdefghi" "...")
  ⇒ "abc...ghi"
(regexp-replace #/def|DEF/ "abcdefghi" "|\\0|")
  ⇒ "abc|def|ghi"
(regexp-replace #/def|DEF/ "abcdefghi" "|\\\\0|")
  ⇒ "abc|\\0|ghi"
(regexp-replace #/c(.*)g/ "abcdefghi" "|\\1|")
  ⇒ "ab|def|hi"
(regexp-replace #/c(?<match>.*)g/ "abcdefghi" "|\\k<match>|")
  ⇒ "ab|def|hi"

substitutionが手続きである場合、string中の各マッチについて、 マッチオブジェクトを引数としてその手続きが呼ばれます。その手続きが返す 値をdisplayで表現したものが置換文字列として使われます。

 
(regexp-replace #/c(.*)g/ "abcdefghi"
                (lambda (m)
                  (list->string
                   (reverse
                    (string->list (rxmatch-substring m 1))))))
 ⇒ "abfedhi"

註: regexp-replace-all は文字列でマッチした部分の後ろの部分に ついて再帰的に自分自身を適用します。従って、regexpが 文字列先頭のアサーション (^) を含んでいても、それはstringの 先頭だけにマッチするとは限りません。

註: 文字列中の、正規表現にマッチする部分すべてに対して何か操作をしたいが、 置き換えた文字列が欲しいわけではない、という場合は、 gauche.lazyモジュールのlrxmatchや、 gauche.generatorモジュールのgrxmatchが使えるでしょう。 これらは文字列に対して繰り返し正規表現でのマッチを、必要に応じて適用し、 前者はマッチオブジェクトの遅延シーケンスを、後者はマッチオブジェクトを生成する ジェネレータを返します。

 
(map rxmatch-substring (lrxmatch #/\w+/ "a quick brown fox!?"))
 ⇒ ("a" "quick" "brown" "fox")
Function: regexp-replace* string rx1 sub1 rx2 sub2 …
Function: regexp-replace-all* string rx1 sub1 rx2 sub2 …

まず、regexp-replace あるいは regexp-replace-all を 正規表現 rx1、置換 sub1string に適用し、 その結果にさらに regexp-replace あるいは regexp-replace-all を正規表現 rx2、置換 sub2 で 適用し、以下同様です。これらの関数はひとつの文字列上で複数回置換を行う ときに便利です。

Function: regexp-quote string

string中で、正規表現において特別な意味を持つ文字を全てエスケープした 文字列を返します。

 
(regexp-quote "[2002/10/12] touched foo.h and *.c")
 ⇒ "\\[2002/10/12\\] touched foo\\.h and \\*\\.c"

以下のマクロにおいて、match-exprはマッチオブジェクトか #fを生成する式でなければなりません。通常それは rxmatchを呼ぶ式になりますが、それだけに限られるわけではありません。

Macro: rxmatch-let match-expr (var …) form …

match-exprを評価し、それがマッチオブジェクトを返したら、 マッチした文字列をvar …に束縛し、formを評価します。 最初のvarはマッチした文字列全体に束縛され、 以降の変数はサブマッチ文字列に束縛されます。実際のサブマッチ文字列が 与えられた変数より少なかった場合は、余った変数は#fに束縛されます。

特定のマッチ文字列を受け取る必要が無いときは、その場所の 変数の変わりに#fを置いておくこともできます。

 
(rxmatch-let (rxmatch #/(\d+):(\d+):(\d+)/
                      "Jan  1 23:59:58, 2001")
   (time hh mm ss)
  (list time hh mm ss))
 ⇒ ("23:59:58" "23" "59" "58")

(rxmatch-let (rxmatch #/(\d+):(\d+):(\d+)/
                      "Jan  1 23:59:58, 2001")
   (#f hh mm)
  (list hh mm))
 ⇒ ("23" "59")

このマクロはscshのlet-matchに相当します。

Macro: rxmatch-if match-expr (var …) then-form else-form

match-exprを評価し、それがマッチオブジェクトを返したら マッチした文字列を変数var …に束縛してthen-formを 評価します。マッチオブジェクトが返されなければ束縛は行われず、 else-formが評価されます。変数varをマッチ文字列に 束縛するルールはrxmatch-letと同じです。

 
(rxmatch-if (rxmatch #/(\d+:\d+)/ "Jan 1 11:22:33")
    (time)
  (format #f "time is ~a" time)
  "unknown time")
 ⇒ "time is 11:22"

(rxmatch-if (rxmatch #/(\d+:\d+)/ "Jan 1 11-22-33")
    (time)
  (format #f "time is ~a" time)
  "unknown time")
 ⇒ "unknown time"

このマクロはscshのif-matchに相当します。

Macro: rxmatch-cond clause …

clauseの条件を順に評価してゆき、条件を満たすものが現れたら そのclauseの残りのフォームを評価し、最後のフォームの値を rxmatch-condの値とします。clauseは以下のいずれかの 形式でなければなりません。

(match-expr (var …) form …)

match-exprを評価し、それがマッチオブジェクトを返した場合は マッチ文字列を変数var …に束縛した上で form …を評価します。

(test expr form …)

exprを評価し、それが真の値を返した場合はform …を評価します。

(test expr => proc)

exprを評価し、それが真の値を返した場合は それを唯一の引数として手続きprocを呼びます。

(else form …)

このclauseは、もし与えられたとすれば最後のclauseでなければ なりません。全てのclauseが失敗した場合に、form …が 評価されます。

else clauseが与えられず、かつ全てのclauseが 失敗した場合の戻り値は未定義です。

 
;; 何通りかの日付のフォーマットをパーズする
(define (parse-date str)
  (rxmatch-cond
    ((rxmatch #/^(\d\d?)\/(\d\d?)\/(\d\d\d\d)$/ str)
        (#f mm dd yyyy)
      (map string->number (list yyyy mm dd)))
    ((rxmatch #/^(\d\d\d\d)\/(\d\d?)\/(\d\d?)$/ str)
        (#f yyyy mm dd)
      (map string->number (list yyyy mm dd)))
    ((rxmatch #/^\d+\/\d+\/\d+$/ str)
        (#f)
     (errorf "ambiguous: ~s" str))
    (else (errorf "bogus: ~s" str))))

(parse-date "2001/2/3") ⇒ (2001 2 3)
(parse-date "12/25/1999") ⇒ (1999 12 25)

このマクロはscshのmatch-condに相当します。

Macro: rxmatch-case string-expr clause …

string-exprがまず評価され、続いてclauseが順に検査されます。 clauseは以下のいずれかの形式でなければなりません。

(re (var …) form …)

reはリテラル正規表現オブジェクトでなければなりません (正規表現参照)。string-exprの結果が文字列であり reにマッチした場合は、マッチ文字列が変数var …に 束縛され、formが評価されます。最後のformの値がrxmatch-case の値となります。

string-exprの結果の文字列がreにマッチしないか、 string-exprの結果が文字列以外であった場合は次のclauseへと 処理が進みます。

(test proc form …)

手続きprocstring-exprの結果を引数として呼ばれます。 それが真の値を返した場合はformが順に評価され、最後のformの 値がrxmatch-caseの値として返されます。

proc#fを返した場合は次のclauseへと 処理が進みます。

(test proc => proc2)

手続きprocstring-exprの結果を引数として呼ばれます。 それが真の値を返した場合は、その値を引数としてproc2が呼ばれ、 その返り値がrxmatch-caseの値として返されます。

proc#fを返した場合は次のclauseへと 処理が進みます。

(else form …)

このフォームは、与えられる場合は最後のclauseでなければなりません。 他の全てのclauseが失敗した場合に、formが順に評価され、最後のformの 値がrxmatch-caseの値として返されます。

(else => proc)

このフォームは、与えられる場合は最後のclauseでなければなりません。 他の全てのclauseが失敗した場合に、procが評価されます。 その値は一引数の手続きにならなければなりません。 その手続きが、string-exprの値を引数として呼ばれます。 procの結果がrxmatch-caseの値となります。

else clauseが与えられず、かつ全てのclauseが 失敗した場合の戻り値は未定義です。

上のparse-dateの例はrxmatch-caseを使うとより単純になります。

 
(define (parse-date2 str)
  (rxmatch-case str
    (test (lambda (s) (not (string? s))) #f)
    (#/^(\d\d?)\/(\d\d?)\/(\d\d\d\d)$/ (#f mm dd yyyy)
     (map string->number (list yyyy mm dd)))
    (#/^(\d\d\d\d)\/(\d\d?)\/(\d\d?)$/ (#f yyyy mm dd)
     (map string->number (list yyyy mm dd)))
    (#/^\d+\/\d+\/\d+$/                (#f)
     (errorf "ambiguous: ~s" str))
    (else (errorf "bogus: ~s" str))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.13.3 正規表現の調査と合成

Gaucheはregexpの文字列表記を読むと、その文字列をパーズして 抽象構文木 (AST) を作り、それにいくらかの最適化を施したのち、 正規表現エンジンで実行されるインストラクション列へとコンパイルします。

以下の手続きはこのプロセスをユーザプログラムに見せるものです。 プログラム的に正規表現をいじる場合は、文字列形式よりも ASTの方が扱いやすいでしょう。

Function: regexp-parse string :key case-fold

正規表現の文字列表記であるstringをパーズし、S式で表現された ASTを返します。ASTの仕様は下で述べます。

case-foldキーワード引数に真の値が与えられた場合は、 大文字小文字を区別せずにマッチを行うASTが変えされます (エンジン自体に「大文字小文字を区別しない」モードがあるわけではなく、 パーザの段階で異なるASTが生成されます。)

Function: regexp-optimize ast

正規表現のASTに対して、いくつかの原始的な最適化を施して結果のASTを返します。

現在のところ、最適化はごく簡単なものです。将来はもっと賢くする予定です。

Function: regexp-compile ast

正規表現のASTを受け取り、マッチに使える正規表現オブジェクトを返します。 渡されるASTの一番外側は、0番めの捕捉グループでなければなりません (ast(0 #f x …)という形でなければ ならない、ということです)。regexp-parseは常にこの捕捉グループを 追加します。正規表現にマッチした全体の文字列を捕捉するためです。

註:この関数は渡されたASTが有効なものであるかどうかについて 簡単なチェックを行いますが、有効でないASTを受け入れてしまうかもしれません。 その場合、変えされる正規表現オブジェクトの動作は不定です。 正しい形式のASTを渡すのは呼出側の責任です (この関数が有効でないASTを見つけてエラーにする場合でも、 そのエラーメッセージからどこがおかしいかを判断するのが難しい場合があります。 この手続きをASTが有効であるかどうかのチェッカーとして使うのは避けましょう。)

Function: regexp-ast regexp

正規表現オブジェクトregexpが使っているASTを返します。

Function: regexp-unparse ast :key (on-error :error)

正規表現のastから、その正規表現の文字列表記を再構成して返します。 キーワード引数on-errorは、キーワード:error (デフォルト) もしくは#fです。それが:errorの場合、astが不正 であればエラーが報告され、#fの場合はregexp-unparseから #fが返されます。

以下にASTの構造を示します。この仕様は当初、内部的に使うためだけに設計されたので、 コードで直接いじるのには若干不便です (例えば、部分木を削除したり 追加した場合、捕捉グループの番号を振りなおして一貫性を保つ必要があるかもしれません。) いずれSREのようなより良い表現を採用し、この内部形式との相互変換手続きを用意する 予定です。パッチ歓迎です。

 
<ast> : <clause>   ; special clause
      | <item>     ; matches <item>

<item> : <char>       ; matches char
       | <char-set>   ; matches char set
       | (comp . <char-set>) ; matches complement of char set
       | any          ; matches any char
       | bol | eol    ; beginning/end of line assertion
       | wb | nwb     ; word-boundary/negative word boundary assertion

<clause> : (seq <ast> ...)       ; sequence
       | (seq-uncase <ast> ...)  ; sequence (case insensitive match)
       | (seq-case <ast> ...)    ; sequence (case sensitive match)
       | (alt <ast> ...)         ; alternative
       | (rep <m> <n> <ast> ...) ; repetition at least <m> up to <n> (greedy)
                               ; <n> may be `#f'
       | (rep-min <m> <n> <ast> ...)
                               ; repetition at least <m> up to <n> (lazy)
                               ; <n> may be `#f'
       | (rep-while <m> <n> <ast> ...)
                               ; like rep, but no backtrack
       | (<integer> <symbol> <ast> ...)
                               ; capturing group.  <symbol> may be #f.
       | (cpat <condition> <ast> <ast>)
                               ; conditional expression
       | (backref . <integer>) ; backreference
       | (once <ast> ...)      ; standalone pattern.  no backtrack
       | (assert . <asst>)     ; positive lookahead assertion
       | (nassert . <asst>)    ; negative lookahead assertion

<condition> : <integer>     ; (?(1)yes|no) style conditional expression
       | (assert . <asst>)  ; (?(?=condition)...) or (?(?<=condition)...)
       | (nassert . <asst>) ; (?(?!condition)...) or (?(?<!condition)...)

<asst> : <ast> ...
       | ((lookbehind <ast> ...))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.14 ベクタ

Builtin Class: <vector>

ベクタはSchemeオブジェクトの単純な一次元配列です。 インデックスを用いて定数時間でその要素にアクセスできます。 一度作成されたベクタはその大きさを変えることはできません。

<vector>クラスはまた<sequence>クラスを継承し、 mapfoldなど様々な総称関数を使うことができます。 gauche.collection - コレクションフレームワークgauche.sequence - シーケンスフレームワーク を参照して下さい。

数値しか要素に持たないベクタを使う場合、SRFI-4の 単一型ベクタも使えるかもしれません (srfi-4 - 単一型のベクタ参照)。

R7RSはバイトベクタを定義しています。Gaucheではそれは単に gauche.uvectorモジュールのu8vectorです (R7RSモジュールは別名を定義しています。scheme.base - R7RS基本ライブラリ参照。)

より多くのベクタに対する操作がsrfi-133 - ベクタライブラリで提供されています。

Function: vector? obj

[R7RS] objがベクタなら#tを、そうでなければ#fを返します。

Function: make-vector k :optional fill

[R7RS] 長さkのベクタを作成して返します。 省略可能な引数fillが与えられていれば、ベクタの各要素はその値で 初期化されます。そうでなければベクタの各要素の値は不定です。

Function: vector obj …

[R7RS] 要素がobj …であるようなベクタを作成して返します。

Function: vector-tabulate len proc

長さlenのベクタを作成し、0以上len未満のiについて i番目の要素を(proc i)の値で初期化します。

 
(vector-tabulate 5 (^x (* x x)))
  ⇒ #(0 1 4 9 16)
Function: vector-length vector

[R7RS] ベクタvectorの長さを返します。

gauche.collectionモジュールをロードしていれば、 メソッドsize-ofも同じ目的で使えます。

Function: vector-ref vector k :optional fallback

[R7RS+] ベクタvectork番目の要素を返します。

vector-refkが負の値であったりベクタの長さより 大きかったりした場合はエラーを通知します。但し、省略可能な引数fallback が与えられている場合はその値が返されます。これはGaucheの拡張です。

gauche.sequenceモジュールをロードしていれば、 メソッドrefも同じ目的で使えます。

Function: vector-set! vector k obj

[R7RS] ベクタvectork番目の要素をobjに変更します。 kが負数であったりベクタの長さより大きい場合はエラーとなります。

gauche.sequenceモジュールをロードしていれば、 メソッドrefのsetterメソッドも使えます。

Function: vector->list vector :optional start end
Function: list->vector list :optional start end

[R7RS+][SRFI-133+] ベクタをリストに変換したり、その逆を行う手続きです。

省略可能な引数startendを与えることにより、 値を取り出す範囲を制限することができます。 (R7RSおよびSRFI-133のlist->vectorstartend引数を規定していません。)

 
(vector->list '#(1 2 3 4 5))     ⇒ (1 2 3 4 5)
(list->vector '(1 2 3 4 5))      ⇒ #(1 2 3 4 5)
(vector->list '#(1 2 3 4 5) 2 4) ⇒ (3 4)
(list->vector (circular-list 'a 'b 'c) 1 6)
  ⇒ #(b c a b c)

gauche.collectionモジュールをロードしていれば、 (coerce-to <list> vector)(coerce-to <vector> list) も同じ目的で使えます。

Function: reverse-list->vector list :optional start end

[SRFI-133] 省略可能引数が無い場合、 (list->vector (reverse list))と同じ結果を返しますが、 中間リストを作りません。省略可能引数startおよびendが 与えられた場合は、それはlistの範囲を指定します。

 
(reverse-list->vector '(a b c d e f g) 1 5)
  ⇒ #(e d c b)
Function: vector->string vector :optional start end
Function: string->vector string :optional start end

[R7RS] 文字のベクタを文字列に変換したり、その逆を行う手続きです。 vector->stringに、文字以外の要素を含むベクタを渡した場合はエラーになります。

省略可能な引数startendを与えることにより、 値を取り出す範囲を制限することができます。

 
(vector->string '#(#\a #\b #\c #\d #\e))     ⇒ "abcde"
(string->vector "abcde")                     ⇒ #(#\a #\b #\c #\d #\e)
(vector->string '#(#\a #\b #\c #\d #\e) 2 4) ⇒ ("cd")

gauche.collectionモジュールをロードしていれば、 (coerce-to <string> vector)(coerce-to <vector> string) も同じ目的で使えます。

Function: vector-fill! vector fill :optional start end

[R7RS][SRFI-133] ベクタvectorの全ての要素をfillに変更します。

省略可能な引数startendが与えられた場合、 start番目の要素からend-1番目の要素までのみに fillを格納します。startendの既定値は それぞれ0とvectorの大きさです。

Function: vector-copy vector :optional start end fill

[R7RS][SRFI-133] ベクタvectorをコピーします。引数startendを与えることで コピーされる範囲を制限することができます。 startendで指定される範囲が元のvectorの範囲を越えた 場合は、その部分がfillで埋められます。

 
(vector-copy '#(1 2 3 4 5))     ⇒ #(1 2 3 4 5)
(vector-copy '#(1 2 3 4 5) 2 4) ⇒ #(3 4)
(vector-copy '#(1 2 3 4 5) 3 7 #f) ⇒ #(4 5 #f #f)
Function: vector-copy! target tstart source :optional sstart send

[R7RS][SRFI-133] ベクタsourceの内容をベクタtargetのインデックスtstart以降に コピーします。targetは変更可能でなければなりません。 省略可能なsstartsend引数はコピー元ベクタの範囲を限定します。

 
(rlet1 v (vector 'a 'b 'c 'd 'e)
  (vector-copy! v 2 '#(1 2)))
  ⇒ #(a b 1 2 e)
(rlet1 v (vector 'a 'b 'c 'd 'e)
  (vector-copy! v 2 '#(1 2 3 4) 1 3))
  ⇒ #(a b 2 3 e)

コピーされるデータが、コピー先の領域(tstartから末尾まで)より 大きい場合はエラーが報告されます。

targetsourceに同じベクタを渡しても構いません。 コピー元とコピー先の領域に重なりがある場合でもこの手続きは使えます。

Function: vector-append vec …

[R7RS][SRFI-133] ベクタvecを順に継ぎ足した内容を持つベクタを新たに作成して返します。

 
(vector-append '#(1 2 3) '#(a b)) ⇒ #(1 2 3 a b)
(vector-append) ⇒ #()
Function: vector-map proc vec1 vec2 …

[R7RS][SRFI-133] ベクタvec1 vec2 …の各i番目の要素にprocを 適用したものをi番目の要素とする新たなベクタを作って返します。 結果のベクタの長さは、引数のベクタのうち最も短いものと同じになります。

 
(vector-map + '#(1 2 3) '#(4 5 6 7))
 ⇒ #(5 7 9)

procが呼ばれる順番は定義されておらず、将来のバージョンで変わるかもしれないので、 procでは順序に影響されるような副作用を起こしてはいけません。

註: gauche.collectionをuseして (map-to <vector> proc vec1 vec2 … としても同じ機能は得られます。

Function: vector-map-with-index proc vec1 vec2 …

vector-mapと似ていますが、procの第1引数に 現在のインデックスが渡されます。

 
(vector-map-with-index list '#(a b c d e) '#(A B C))
 ⇒ #((0 a A) (1 b B) (2 c C))

これがSRFI-43でvector-mapと呼ばれている手続きです。 srfi-43 - ベクタライブラリ(旧式)参照。

註: gauche.collectionをuseして (map-to-with-index <vector> proc vec1 vec2 … としても同じ機能は得られます。

Function: vector-map! proc vec1 vec2 …

[SRFI-133] 各インデックスiについて、ベクタvec1 vec2 …の i番目の要素を引数としてprocを呼び出した結果、vec1i番目の要素にセットします。ベクタの長さが異なる場合は最も短いベクタ の範囲が計算されます。

 
(rlet1 v (vector 1 2 3)
  (vector-map! ($ + 1 $) v))
  ⇒ #(2 3 4)

(rlet1 v (vector 1 2 3 4)
  (vector-map! + v '#(10 20)))
  ⇒ #(11 22 3 4)
Function: vector-map-with-index! proc vec1 vec2 …

vector-map!と似ていますが、procの第1引数に 現在のインデックスが渡されます。SRFI-43でvector-map! と定義されているものと同じ動作です (srfi-43 - ベクタライブラリ(旧式)参照)。

 
(rlet1 v (vector 'a 'b 'c)
  (vector-map-with-index! list v))
  ⇒ #((0 a) (1 b) (2 c))
Function: vector-for-each proc vec1 vec2 …

[R7RS][SRFI-133] 0から渡されたベクタのうち一番短いものの最大のインデックスまでのiについて、 vec1 vec2 …のそれぞれi番目の要素を引数に procを呼び出します。

 
(vector-for-each print '#(a b c))
 ⇒ prints a, b and c.
Function: vector-for-each-with-index proc vec1 vec2 …

vector-for-eachと似ていますが、procの第1引数に 現在のインデックスが渡されます。

この手続きはSRFI-43のvector-for-eachと等価です。 srfi-43 - ベクタライブラリ(旧式)参照。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.15 ハッシュテーブル

Gaucheの組み込みハッシュテーブルAPIはGauche特有のものです。 ポータブルなプログラムを書く場合は、今のところsrfi-69が使えます (srfi-69 - 基本的なハッシュテーブル参照)。

R7RS-largeの一部になる、新たなハッシュテーブルのSRFIが出たら、 Gaucheもそれに合わせる予定です。

Builtin Class: <hash-table>

ハッシュテーブルのクラスです。<collection><dictionary>を 継承します。

Function: make-hash-table :optional comparator

ハッシュテーブルを作成します。省略可能なcomparator引数には、 比較器 (基本的な比較器参照)もしくは、シンボル eq?eqv?equal?string=?のいずれかを 指定します。シンボルの場合はそれぞれ eq-comparatoreqv-comparatorequal-comparatorstring-comparatorが使われます。

比較器はハッシュ関数を持っていなければなりません。組み込みのハッシュ関数については ハッシュを参照してください。比較器を組み合わせて作られた比較器については、 元の比較器がハッシュ関数を持っていれば、通常は適切なハッシュ関数が設定されます。

comparatorが省略された場合はeq-comparatorが使われます。

Function: hash-table? obj

objがハッシュテーブルであれば#tを返します。

Function: hash-table-comparator ht

ハッシュテーブルhtで使われている比較器を返します。

Function: hash-table-type ht

これは古いAPIで、hash-table-comparatorによって置き換えられます。

ハッシュテーブルhtのタイプに応じて、シンボル eq?eqv?equal?string=?generalのいずれかを返します。

Function: hash-table-num-entries ht

ハッシュテーブルht中の要素の個数を返します。

Function: hash-table comparator key&value …

与えられたキーと値の列からハッシュテーブルを構築して 返します。comparator引数の意味はmake-hash-tableと同じです。 各key&valueはペアでなければならず、そのcarがキー、cdrが値として使われます。

 
(hash-table 'eq? '(a . 1) '(b . 2))
  ≡
  (rlet1 h (make-hash-table 'eq?)
     (hash-table-put! h 'a 1)
     (hash-table-put! h 'b 2))
Function: hash-table-copy ht

ハッシュテーブルhtのコピーを作って返します。

Function: hash-table-get ht key :optional default

キーkeyをハッシュテーブルhtから探します。見つかればキーに対応する 値を返します。キーが見つからなかった場合、defaultが与えられていればそれを 返し、そうでなければエラーを報告します。

Function: hash-table-put! ht key value

キーkeyと対応する値valueをハッシュテーブルhtに挿入します。

Method: ref (ht <hash-table>) key :optional default
Method: (setter ref) (ht <hash-table>) key value

hash-table-gethash-table-put!のメソッド版です。

Function: hash-table-exists? ht key

ハッシュテーブルhtにキーkeyを持つエントリがあれば#tを返します。

Function: hash-table-delete! ht key

ハッシュテーブルhtからキーkeyを持つエントリを削除します。 keyを持つエントリが実際に存在して削除された場合は#tを、 エントリが存在しなかった場合は#fを返します。 この手続きはSTkでhash-table-remove!と呼ばれているものです (STkのは戻り値が定義されていませんが)。GaucheではSRFI-1, SRFI-13やその他の ライブラリとの一貫性のために ‘delete’ を採用しました。

Function: hash-table-clear! ht

ハッシュテーブルhtの全てのエントリを削除します。

Function: hash-table-push! ht key value

ハッシュテーブルht中の、キーkeyに対応する値にvalueをコンスし、 それをkeyに対する新たな値とします。もしkeyに対応する値がまだ無ければ、 新たなエントリが作成され、(list value)がその値となります。

この手続きは次のコードと同じ動作をしますが、キーの探索が一度しか行われないためより高速です。

 
(hash-table-put! ht key
    (cons value (hash-table-get ht key '())))
Function: hash-table-pop! ht key :optional default

ハッシュテーブルht中のキーkeyに対応する値が存在し、かつペアで あった場合に、そのエントリーを元の値のcdrで置き換え、元の値のcarを返します。 keyに対応する値が存在しないかペアではなかった場合、テーブルは変更されず、 defaultが与えられていればそれが返され、与えられていなければエラーが報告されます。

値が置き換えれる場合でもキーの探索は一度しか行われないため効率が良いです。

Function: hash-table-update! ht key proc :optional default

hash-table-push!等のより一般的なバージョンです。 ハッシュテーブルの探索が一度しか行われないことを除いては、 基本的に次のように動作します。

 
(let ((tmp (proc (hash-table-get ht key default))))
  (hash-table-put! ht key tmp)
  tmp)

例えば、ハッシュテーブルを使ってオブジェクトの個数を数えているとしましょう。 次の1行で、オブジェクトitemが既に出現したかどうかを気にせずに その個数をインクリメントできます。

 
(hash-table-update! ht item (cut + 1 <>) 0))
Function: hash-table-for-each ht proc
Function: hash-table-map ht proc

ハッシュテーブルht内の全てのエントリについて、各エントリのキーと値を 2つの引数として手続きprocを呼びます。

Function: hash-table-fold ht kons knil

ハッシュテーブルht内の全てのエントリについてkonsを呼びます。 konsには3つの引数が渡されます。 各エントリのキーと値、および一つ前のkonsの返り値です。 最初のkonsの呼び出しの時には、第3引数にknilが渡されます。 最後のkonsの返り値がhash-table-foldの返り値となります。

Function: hash-table-keys ht
Function: hash-table-values ht

それぞれ、ハッシュテーブルht内の全てのキーまたは値をリストにして返します。

Function: alist->hash-table alist :optional comparator

alistに含まれるそれぞれの要素をエントリとして持つハッシュテーブルを 作成して返します。その時、要素のcarがキーとして、要素のcdrが値として 使われます。comparator引数の意味はmake-hash-tableと同じです。 comparatorのデフォルト値はeq-comparatorです。

Function: hash-table->alist hash-table
 
  (hash-table-map h cons)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.16 ツリーマップ

Builtin Class: <tree-map>

ツリーマップクラス。ツリーマップはキーオブジェクトから値オブジェクトへ の写像をあらわすデータ構造です。ツリーマップでは平衡木を使うということ 以外はハッシュテーブルと同じです。ツリーマップでは挿入と検索の手間は O(log n)です。

ハッシュテーブルとはちがい、キーの順序は保存されます。したがってキーの 順序どおりにトラバースするのは簡単で、キーの最小値/最大値を見つけたり、 指定したキーにもっとも近いキーを探すのも簡単です。

<tree-map>クラスは<sequence>および <ordered-dictionary>を継承しています。

Function: make-tree-map :optional comparator
Function: make-tree-map key=? key<?

<tree-map>オブジェクトを作成して返します。 キーは比較器comparatorを使って比較されます。comparatorの デフォルト値はdefault-comparatorです。キーには全順序関係が必要なので、 比較器は比較手続きを持っていなければなりません。 詳しくは基本的な比較器を参照してください。

互換性のため、make-tree-mapcomparator引数に 単なる手続きを取ることもできます。渡される手続きは2引数を取り、 最初の引数が2番目の引数より小さいか、等しいか、大きいかに応じて -101をそれぞれ返します。つまり この手続きは比較器の比較手続きそのものです。

make-tree-mapの2番目の呼び出し形式も互換性のためのものです。 それぞれ2引数の2つの手続きを取り、 最初の手続きkey=?は、2つのキーが等しい場合にのみ真を返し、 2番目の手続きkey<?は、最初のキーが2番目のキーより前にある(小さい)場合にのみ 真を返すようにします。

Function: tree-map-comparator tree-map

tree-mapで使われている比較器を返します。

Function: tree-map-copy tree-map

tree-mapのコピーを作り、それを返します。返された木に対す る破壊的操作は、元の木に影響を与えません。

Function: tree-map-empty? tree-map

tree-mapが要素を持たないなら#tを、そうでなければ#fを 返します。

Function: tree-map-num-entries tree-map

tree-map内の要素の個数を返します。

Function: tree-map-exists? tree-map key

tree-mapにキーkeyを持つエントリがあれば#tを、 そうでなければ#fを返します。

Function: tree-map-get tree-map key :optional fallback

キーkeytree-mapから探します。見つかればkeyに対応する値を返 します。キーが見つからなかった場合、fallbackが与えられていればそれ を返し、そうでなければエラーを報告します。

Function: tree-map-put! tree-map key value

キーkeyと対応する値valuetree-mapに挿入します。もし、keyと、 key=?における意味で同じキーがすでに存在する場合、キーに対応する値 は新たな値に置き換えられます。

Function: tree-map-delete! tree-map key

tree-mapからキーkeyを持つエントリを削除します。keyを持つエン トリが実際に存在して削除された場合は#tを、エントリが存在しなかっ た場合は#fを返します。

Function: tree-map-clear! tree-map

tree-map内の全てのエントリを削除します。

Function: tree-map-update! tree-map key proc :optional fallback

tree-map-push!等のより一般的なバージョンです。木の探索が一度 しか行われないことを除いては、基本的に次のように動作します。

 
(let ((tmp (proc (tree-map-get tree-map key fallback))))
  (tree-map-put! tree-map key tmp)
  tmp)
Function: tree-map-push! tree-map key value

tree-map中の、キーkeyに対応する値にvalueをコンスし、 それをkey に対する新たな値とします。もしkeyに対応する値がまだ無ければ、新た なエントリが作成され、(list value)がその値となります。

Function: tree-map-pop! tree-map key :optional fallback

tree-map中のキーkeyに対応する値が存在し、かつペアであった場合 に、そのエントリの値を元の値のcdrで置き換え、元の値のcarを返します。 keyに対応する値が存在しないかペアではなかった場合、tree-mapは 変更されず、fallbackが与えられていればそれが返され、与えられていな ければエラーが報告されます。

Function: tree-map-min tree-map
Function: tree-map-max tree-map

それぞれ、tree-mapに含まれる最小および最大のキーを探索し、その キーと値のペアを返します。tree-mapが空だった場合は#fが返されます。

Function: tree-map-pop-min! tree-map
Function: tree-map-pop-max! tree-map

それぞれ、tree-mapに含まれる最小および最大のキーを探索し、そ のエントリをtree-mapから削除したうえで、そのキーと値のペアを 返します。tree-mapが空だった場合は#fが返されます。

Function: tree-map-fold tree-map proc seed
Function: tree-map-fold-right tree-map proc seed

tree-mapの各要素に対し、(key, value, seed) -> seed の型を持つ procを適用してゆきます。 tree-map-foldtree-map-fold-rightの違いは foldfold-right違いと同じ、すなわち 結合の方向にあります。

 
tree-map-fold:
  (proc Kn Vn (proc Kn-1 Vn-1 ... (proc K0 V0 seed)))

tree-map-fold-right
  (proc K0 V0 (proc K1 V1 ... (proc Kn Vn seed)))

例:

 
(define tree (alist->tree-map '((3 . a) (7 . b) (5 . c)) = <))

(tree-map-fold tree list* '())
   ⇒ (7 b 5 c 3 a)
(tree-map-fold-right tree list* '())
   ⇒ (3 a 5 c 7 b)
Function: tree-map-map tree-map proc

2引数を取る手続きprocを、tree-mapの各キーおよび値のペアに 対して呼び出し、結果をリストにして返します。 結果のリストは、キーの昇順に並んでいます。つまり、最小のキーとその値で呼び出した procの結果が最初の要素に、最大のキーとその値で呼び出したprocの 結果が最後の要素になります。 (mapと同様、実際にprocが呼ばれる順序は保証されていません。 したがってprocは副作用の無いものにすべきです)。

Function: tree-map-for-each tree-map proc

2引数を取る手続きprocを、tree-mapの各キーおよび値のペアに対して、 キーの昇順に呼び出します。procは副作用のためだけに呼ばれ、結果は捨てられます。

Function: tree-map-floor tree-map probe :optional fallback-key fallback-value
Function: tree-map-ceiling tree-map probe :optional fallback-key fallback-value
Function: tree-map-predecessor tree-map probe :optional fallback-key fallback-value
Function: tree-map-successor tree-map probe :optional fallback-key fallback-value

これらの手続きは、probeに最も近いキーを持つエントリをtree-mapから 探します。そのようなエントリが見つかれば、該当するキーと値が2つの戻り値となります。 見つからない場合は、fallback-keyfallback-valueが 2つの戻り値として返されます。それぞれの省略時の値は#fです。

それぞれの手続きは異なった「最も近い」の基準を持っています。 tree-map-floorprobe以下で最大のキーを、 tree-map-ceilingprobe以上で最小のキーを、 tree-map-precedessorprobe未満で最大のキーを、 tree-map-successorprobeを越える最小のキーを探します。

Function: tree-map-floor-key tree-map probe optional fallback-key
Function: tree-map-ceiling-key tree-map probe optional fallback-key
Function: tree-map-predecessor-key tree-map probe optional fallback-key
Function: tree-map-successor-key tree-map probe optional fallback-key

tree-map-floor等と同様ですが、見つかったエントリのキーのみを返します。 該当エントリが見つからない場合はfallback-keyの値(デフォルトは#f)が 返されます。

Function: tree-map-floor-value tree-map probe optional fallback-value
Function: tree-map-ceiling-value tree-map probe optional fallback-value
Function: tree-map-predecessor-value tree-map probe optional fallback-value
Function: tree-map-successor-value tree-map probe optional fallback-value

tree-map-floor等と同様ですが、見つかったエントリの値のみを返します。 該当エントリが見つからない場合はfallback-valueの値(デフォルトは#f)が 返されます。

Function: tree-map-keys tree-map
Function: tree-map-values tree-map

それぞれ、tree-map内の全てのキーまたは値をリストにして返しま す。返されるリストの要素はキーの昇順に並んでいます。

Function: tree-map->alist tree-map

tree-map含まれる要素を連想リストにして返します。返される連 想リストのキーは昇順に並んでいます。

Function: alist->tree-map alist :optional comparator
Function: alist->tree-map alist key=? key<?

comparatorまたはkey=?, key<? によって新たなtreemapを作成し、 連想リストalistに含まれる要素を追加した上で返します。 alistの各ペアのcarがキーに、cdrが値に使われます。 comparator, key=?, key<?引数の意味は make-tree-mapと同じです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.17 Weak ポインタ

Weak ポインタとは、それが参照しているオブジェクトがガベージコレクトされることを 許すようなポインタです。 Gaucheはweak ベクタオブジェクトによってweak ポインタの機能を提供します。 Weak ベクタは通常のベクタに似ていますが、要素のオブジェクトがweak ベクタ以外から 参照されていない場合、オブジェクトはガベージコレクトされ、weak ベクタの該当するエントリは #fで置き換えられます。

 
gosh> (define v (make-weak-vector 1))
v
gosh> (weak-vector-ref v 0)
#f
gosh> (weak-vector-set! v 0 (cons 1 1))
#<undef>
gosh> (weak-vector-ref v 0)
(1 . 1)
gosh> (gc)
#<undef>
gosh> (gc)
#<undef>
gosh> (weak-vector-ref v 0)
#f
Builtin Class: <weak-vector>

Weak ベクタのクラスです。<sequence><collection>を継承しているので、 gauche.collection (gauche.collection - コレクションフレームワーク参照) と gauche.sequence (gauche.sequence - シーケンスフレームワーク参照) も使えます。

 
(coerce-to <weak-vector> '(1 2 3 4))
  ⇒ a weak vector with four elements
Function: make-weak-vector size

大きさsizeのweak ベクタを作成して返します。

Function: weak-vector-length wvec

weak ベクタ wvecの大きさを返します。

Function: weak-vector-ref wvec k &optional fallback

weak ベクタ wveck番目の要素を返します。

weak-vector-refkが負の値であったりベクタの長さより 大きかったりした場合はエラーを通知します。但し、省略可能な引数fallback が与えられている場合はその値が返されます。

該当する要素が既にガベージコレクトされていた場合、fallbackが 与えられていればそれが、そうでなければ#fが返されます。

gauche.sequenceモジュールをロードしていれば、 メソッドrefも同じ目的で使えます。

Function: weak-vector-set! wvec k obj

weak ベクタ wveck番目の要素をobjに変更します。 kが負数であったりベクタの長さより大きい場合はエラーとなります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18 手続きと継続

Schemeでは、プログラムを組み立てる最も基本となるブロックが手続きです (手続きを作るも参照)。 手続きは、特定の計算を表現します。引数を取ることも出来ます。 そして、実引数に適用されると、その計算を実行します。 Schemeはまた、現在の計算の継続を取り出して手続きにラップして返す手段を 提供しています(継続参照)。

Gaucheは手続きの適用の概念を拡張し、どんなオブジェクトでもそれが手続きであるかのように 適用できるようにしました。例えば("abc" 2)が有効な適用であるように Gaucheをセットアップすることができます。 詳しくは適用可能なオブジェクトを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.1 Procedure class and applicability

Builtin Class: <procedure>

手続きのクラスです。lambdaで作られた手続きや 組み込みのプリミティブ手続きはこのクラスのインスタンスです。 Gaucheではどんな型のオブジェクトも適用可能にできるので、あるオブジェクトが <procedure>クラスのインスタンスであるかとうかということは あまり気にする必要はありません。Gaucheの中身をいじってみる時以外は。

Function: procedure? obj

[R7RS] obj生得的に適用可能なオブジェクトであれば#tを、 そうでなければ#fを返します。生得的にというのは、 Gaucheが最初から手続きとして呼び出せるオブジェクトとして備えているもの、程度の意味です。 <procedure>クラスのインスタンスや、 ジェネリックファンクションやメソッドがそうです。 (ジェネリックファンクションとメソッドに関しては ジェネリックファンクションとメソッドを参照してください)。

Gaucheでは、どんなオブジェクトも適用可能にすることができます (適用可能なオブジェクト参照)。従って、procedure?#fを 返したからといってそのオブジェクトを手続きのように呼び出せないとは限りません。 オブジェクトが適用可能かどうかをより正確に調べるには、 下に説明するapplicable?を使ってください。

Function: apply proc arg1 … args

[R7RS] (arg1 … . args)を引数として手続きprocを呼びます。 最後の引数argsは正規のリストでなければなりません。 procが返す 値をそのまま返します。

 
(apply list 'a 'b '(c d e)) ⇒ (a b c d e)

(apply + 1 2 '(3 4 5))      ⇒ 15
Function: applicable? obj class …

それぞれの型がclass …であるような引数リストを伴って objを呼び出すことができるかどうかを調べます。 例えば(applicable? foo <string> <integer>)#tを 返したなら、foo(foo "x" -2)のように呼び出せるということです。 (これは、エラーが出ないことを保証するものではありません。fooは もしかする非負整数しか受け取れないかもしれませんが、そのことは applicable?の結果からはわかりません。でも、applicable?#tを 返したなら、fooを呼び出した時に“foo is not applicable”と Gaucheに文句を言われることはありません。)

この手続きは適用可能オブジェクトも考慮に入れます。 従って、例えば(applicable? #/a/ <string>)#tを返します。 正規表現は文字列に適用可能だからです (正規表現参照)。

ジェネリックファンクションに対しては、 渡されたclass引数それぞれが、対応する特定化子と一致するかサブクラスになっている ようなメソッドが最低ひとつあれば、applicable?#tを返します。

 
(define-method foo ((x <sequence>) (y <integer>)) #f)

(applicable? foo <sequence> <integer>) ⇒ #t
(applicable? foo <string> <integer>) ⇒ #t
(applicable? foo <hash-table> <integer>) ⇒ #f
(applicable? foo <string> <real>) ⇒ #f

2番目の例では、<string><sequence>のサブクラスなので #tが返ります。一方、3番目の例では<hash-table><sequence>のサブクラスではないので#fとなります。 4番めの例が#fなのは、<real><integer>のサブクラスではないからです。

伝統的なSchemeの手続き (lambdaで作られるようなもの) は 引数の数のみで呼び出し可能かどうかが判断されます。オブジェクトが 引数の型にかかわらず、特定の個数の引数を取るかどうかを判定するには、 class引数に<top>を渡します。(<top>は 全てのクラスのスーパークラスです。)

 
(applicable? cons <top> <top>) ⇒ #t

逆に、何らかの特定の型の引数を取るかどうかを調べたい場合は、 <bottom>を渡してください。(<bottom>は全てのクラスのサブクラスです。)

 
(define-method foo ((x <sequence>) (y <integer>)) #f)

(applicable? foo <top> <top>) ⇒ #f
(applicable? foo <bottom> <bottom>) ⇒ #t

<top>, <bottom>クラスおよびGaucheでの型の扱いについては 型とクラスを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.2 万能アクセサ

Function: ~ obj key keys …
Function: (setter ~) obj key keys …

手続き~は、様々な集合型のオブジェクトの部分にアクセスするのに使えます。

 
;; Access to an element of a sequence by index
(~ '(a b c) 0)       ⇒ a
(~ '#(a b c) 2)      ⇒ c
(~ "abc" 1)          ⇒ #\b
(~ '#u8(10 20 30) 1) ⇒ 20

;; Access to an element of a collection by key
(~ (hash-table 'eq? '(a . 1) '(b . 2)) 'a)
  ⇒ 1

;; Access to a slot of an object by slot name
(~ (sys-localtime (sys-time)) 'hour)
  ⇒ 20

アクセスはチェインすることができます。

 
(~ '#((a b c) (d e f) (g h i)) 1 2) ⇒ f

(~ (hash-table 'eq? '(a . "abc") '(d . "def")) 'a 2)
  ⇒ #\c

~は左結合します。つまり、

 
(~ x k j) ≡ (~ (~ x k) j)

等。

一般化されたset!~に使えば、アクセスされる要素を置き換えることができます。

 
(define z (vector 'a 'b 'c))
(set! (~ z 1) 'Z)

z ⇒ #(a Z c)

(define z (vector (list (vector 'a 'b 'c)
                        (vector 'd 'e 'f)
                        (vector 'g 'h 'i))
                  (list (vector 'a 'b 'c)
                        (vector 'd 'e 'f)
                        (vector 'g 'h 'i))))

z ⇒ #((#(a b c) #(d e f) #(g h i))
     (#(a b c) #(d e f) #(g h i)))

(set! (~ z 1 2 0) 'Z)
z ⇒  #((#(a b c) #(d e f) #(g h i))
     (#(a b c) #(d e f) #(Z h i)))

~は内部的にジェネリックファンクションrefを使って実現されています。 ジェネリックファンクションについて詳しくはオブジェクトシステムを参照してください。

Generic function: ref object key :optional args …
Generic function: (setter ref) object key value

多くの集合型はこのジェネリックファンクションを特殊化し、 統一されたアクセス方法と変更方法を提供しています。 refのオプショナル引数argsの意味はメソッド毎に異なりますが、 最初のオプショナル引数は、objectkeyに対する値が無い場合の フォールバック値として使われるのが普通です。

正確な動作の定義は、refメソッドを提供しているクラスごとに説明されています。

~の動作は次のコードで理解できるでしょう。

 
(define ~
  (getter-with-setter
   (case-lambda
     [(obj selector) (ref obj selector)]
     [(obj selector . more) (apply ~ (ref obj selector) more)])
   (case-lambda
     [(obj selector val) ((setter ref) obj selector val)]
     [(obj selector selector2 . rest)
      (apply (setter ~) (ref obj selector) selector2 rest)])))

(Gaucheは最適化のためにいくつかの型で短絡経路を使うこともあるので、 実際の実装とは異なります)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.3 コンビネータ

Gaucheには、combinatory programmingに使えるいくつかの基本手続きがあります。

Function: pa$ proc arg …

部分適用。手続きを返します。その手続きが引数m …を伴って 呼ばれた場合、それは(proc arg … m …)と等価になります。

 
(define add3 (pa$ + 3))
(add3 4) ⇒ 7

(map (pa$ * 2) '(1 2 3)) ⇒ (2 4 6)

SRFI-26で定義されているマクロcutcuteも似たような抽象化の 方法を提供しますが、pa$より多少柔軟性が高く、その分やや冗長です。 手続きを作るを参照して下さい。

Function: apply$ proc
Function: map$ proc
Function: for-each$ proc

apply, mapfor-eachの部分適用版です。

 
(define map2* (map$ (pa$ * 2)))
(map2* '(1 2 3)) ⇒ (2 4 6)
Function: count$ pred
Function: fold$ kons :optional knil
Function: fold-right$ kons :optional knil
Function: reduce$ f :optional ridentity
Function: reduce-right$ f :optional ridentity
Function: filter$ pred
Function: remove$ pred
Function: partition$ pred
Function: member$ item
Function: find$ pred
Function: find-tail$ pred
Function: any$ pred
Function: every$ pred
Function: delete$ pred
Function: assoc$ item

SRFI-1(srfi-1 - リストライブラリ参照)の手続に対応する部分適用版手続。

Function: .$ f …
Function: compose f …

複数の手続きを結合します。引数は全て手続きでなければなりません。 2つの引数が渡された時、(.$ f g)は次の式と等価です。

 
(lambda args (call-with-values (lambda () (apply g args)) f))

2つ以上の引数が渡された場合は、次のように結合されます。

 
(.$ f g h ...) ≡ (.$ (.$ f g) h ...)

いくつか例を示します。

 
(define not-zero? (.$ not zero?))
(not-zero? 3) ⇒ #t
(not-zero? 0) ⇒ #f

(define dot-product (.$ (apply$ +) (map$ *)))
(dot-product '(1 2 3) '(4 5 6)) ⇒ 32

境界のケース:ひとつだけ引数が渡された場合は、その引数がそのまま返されます。 引数が全く渡されなかった場合は手続きvaluesが返されます。

註: .$という名前は、文献やいくつかの他のプログラミング言語で .が関数合成によく使われること、そしてGaucheではコンビネータの末尾に $をつける習慣があることから来ています。ただ、これはR7RSの 範囲内では有効な識別子でないので、ポータビリティを考えるプログラムは 別名のcomposeを使った方が良いでしょう。そうすればsrfi-0などを 使って容易に移植が可能です。

Function: complement pred

述語predの意味を逆にした手続きを返します。すなわち、predが真を 返すような引数にたいして偽を返す、またその逆も同様であるような手続きです。

 
(map (complement even?) '(1 2 3)) ⇒ '(#t #f #t)
(map (complement =) '(1 2 3) '(1 1 3)) ⇒ '(#f #t #f)
((complement (lambda () #f))) ⇒ #t
Function: any-pred pred …

与えられた引数をそれぞれ述語predに適用する手続きを返します。 いずれかのpred#fでない値を返す場合、その値を返します。 全てのpred#fを返す場合、#fを返します。

 
(define string-or-symbol? (any-pred string? symbol?))
(string-or-symbol? "abc") ⇒ #t
(string-or-symbol? 'abc)  ⇒ #t
(string-or-symbol? 3)     ⇒ #f

(define <> (any-pred < >))
(<> 3 4) ⇒ #t
(<> 3 3) ⇒ #f

((any-pred (cut memq <> '(a b c))
           (cut memq <> '(1 2 3)))
 'b)  ⇒ '(b c)
Function: every-pred pred …

与えられた引数をそれぞれ述語predに適用する手続きを返します。 全てのpred#fでない値を返す場合、戻り値は最後の predの戻り値になります。いずれかのpred#fを 返す場合、every-predはそれ以降のpredを呼び出さずに #fを返します。

 
((every-pred odd? positive?) 3)  ⇒ #t
((every-pred odd? positive?) 4)  ⇒ #f
((every-pred odd? positive?) -3) ⇒ #f

(define safe-length (every-pred list? length))
(safe-length '(a b c))  ⇒ 3
(safe-length "aaa")     ⇒ #f

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.4 省略可能引数のパージング

Gaucheは省略可能引数やキーワード引数を拡張lambda構文で サポートしています (手続きを作る参照)。 けれども、Gauche拡張に頼らずに、以下のマクロを使って独自にこれらの引数を パーズすることもできます。

 
(define (foo a b :optional (c #f) (d 'none))
  body ...)

;; は次の式とだいたい同じ:

(define (foo a b . args)
  (let-optionals* args ((c #f) (d 'none))
    body ...))

明示的に拡張引数をパーズする方法は、ポータブルなコードを書く時に 役に立つでしょう。以下のマクロを実装するのは、lambdaの構文を 拡張するより簡単だからです。

また、共通する拡張引数の処理ルーチンを括り出す場合にもこれらのマクロは有用です。

Macro: let-optionals* restargs (var-spec …) body …
Macro: let-optionals* restargs (var-spec … . restvar) body …

与えられた値のリストrestargsを、var-specにしたがって 変数に束縛し、bodyを評価します。

var-specはシンボルか、そのcarがシンボルである2要素のリストの いずれかです。シンボルは束縛された変数名です。 restargsにある値は、順番にシンボルに束縛されます。 restargsvar-specに示される数の値がない場合は、 残りのsymbolは以下に従ってデフォルト値が束縛されます。 var-specが単なるシンボルなら、デフォルト値は未定義です。 var-specがリストなら、デフォルト値はリストの2番目の要素を 評価した結果です。後者の場合、2番目の要素は十分な引数がない場合にのみ 評価されます。 束縛はvar-specの順番にしたがって行われるので、2番目の要素は 以前のvar-specのバインディングを参照するかも知れません。

2番目のフォームでは、restvarはシンボルでなければならず、 var-specに束縛された後、restargsに残っている値のリストに 束縛されます。

restargvar-specよりも多い値を持っていてもエラーでは ありません。最初のフォームでは、余分な値は単に無視されます。

 
(define (proc x . args)
  (let-optionals* args ((a 'a)
                        (b 'b)
                        (c 'c))
    (list x a b c)))

(proc 0)         ⇒ (0 a b c)
(proc 0 1)       ⇒ (0 1 b c)
(proc 0 1 2)     ⇒ (0 1 2 c)
(proc 0 1 2 3)   ⇒ (0 1 2 3)

(define (proc2 . args)
  (let-optionals* args ((a 'a) . b)
    (list a b)))

(proc2)          ⇒ (a ())
(proc2 0)        ⇒ (0 ())
(proc2 0 1)      ⇒ (0 (1))
(proc2 0 1 2)    ⇒ (0 (1 2))

(define (proc3 . args)
  (let-optionals* args ((a 0)
                        (b (+ a 1))
                        (c (+ b 1)))
    (list a b c)))

(proc3)          ⇒ (0 1 2)
(proc3 8)        ⇒ (8 9 10)
(proc3 8 2)      ⇒ (8 2 3)
(proc3 8 2 -1)   ⇒ (8 2 -1)
Macro: get-optional restargs default

これはlet-optionals*の短いバージョンで、オプショナル引数が 1つしかないときに使います。オプショナル引数のリストとしてrestargsが 与えらると、このマクロはオプショナル引数が与えられていればその値を返し、 そうでなければdefaultの結果を返します。defaultrestargsが 空リストでなければ評価されません。

 
(define (proc x . maybe-opt)
  (let ((option (get-optional maybe-opt #f)))
    (list x option)))

(proc 0)         ⇒ (0 #f)
(proc 0 1)       ⇒ (0 1)
Macro: let-keywords restarg (var-spec …) body …
Macro: let-keywords restarg (var-spec … . restvar) body …

このマクロはキーワード引数のためのものです。var-specは 以下のフォームのうちのいずれかです。

(symbol expr)

restargsymbolと同じ名前を持つキーワードを含んでいる場合、 symbolを対応する値に束縛します。そのようなキーワードがrestargに ない場合は、symbolexprの結果に束縛します。

(symbol keyword expr)

restargがキーワードkeywordを含む場合、 symbolを対応する値に束縛します。そのようなキーワードがrestargに ない場合、symbolexprの結果に束縛します。

デフォルト値exprは、restargにキーワードが与えられてなかった 場合にのみ評価されます。

1番目のフォームでは、var-specにないキーワード引数がrestargに 現れるとエラーとなります。 他のキーワード引数を許したい場合は次の2番目のフォームを使ってください。

2番目のフォームでは、restvarはシンボルか#fでなければなりません。 シンボルのときは、var-specに束縛されなかったrestargsのキーワード リストがrestvarに束縛されます。#fのときは、それらのrestargs のキーワードは単に無視されます。

 
(define (proc x . options)
  (let-keywords options ((a 'a)
                         (b :beta 'b)
                         (c 'c)
                         . rest)
    (list x a b c rest)))

(proc 0)         ⇒ (0 a b c ())
(proc 0 :a 1)    ⇒ (0 1 b c ())
(proc 0 :beta 1) ⇒ (0 a 1 c ())
(proc 0 :beta 1 :c 3 :unknown 4) ⇒ (0 a 1 3 (:unknown 4))
Macro: let-keywords* restarg (var-spec …) body …
Macro: let-keywords* restarg (var-spec … . restvar) body …

このマクロはlet-keywordsとほぼ同じですが、束縛がvar-specでの 順番に行われるところが異なります。exprは以前のvar-specにより 束縛された変数を参照できます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.5 手続きのアリティ

手続きのアリティを問い合わせるインターフェースです。 APIは、MzScheme (PLT Scheme)を参考にしました。

Function: arity proc

手続きprocを与え、整数、arity-at-leastオブジェクト、 整数とarity-at-leastオブジェクトからなるリストのいずれかを 返します。

整数の戻り値は、procが正確にその数の引数を取ることを表します。 arity-at-leastは、procが最低でも 引数(arity-at-least-value arity-at-least)を取ることを 表します。リストは、異なるアリティを持つ複数の手続きがあることを 表します。

Gaucheではいつでも、既存の手続きやジェネリック関数にメソッドを追加 できるので、arityが返す値はその手続きの現在の状態を示すに 過ぎません。その手続きやジェネリック関数に新しいメソッドが追加 されると、それも変更されます。

 
(arity cons) ⇒ 2
(arity list) ⇒ #<arity-at-least 0>
(arity make) ⇒ (#<arity-at-least 1>)
Function: arity-at-least? obj

objがarity-at-leastオブジェクトなら、真を返します。

Function: arity-at-least-value arity-at-least

arity-at-leastオブジェクトが表す必須引数の数を返します。

Function: procedure-arity-includes? proc k

手続きprocが引数kを取れる場合、#tを返します。 そうでなければ#fを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.6 適用可能なオブジェクト

Gaucheでは、特別な組み込みの機構によって任意のオブジェクトを 「適用可能」にすることができます。

Generic Function: object-apply object arg

手続きでもジェネリックファンクションでもないオブジェクトが何らかの引数に 適用されたとき、そのオブジェクトと引数がジェネリックファンクションobject-apply に渡されます。

この機能は、具体的な例を挙げた方が説明し易いでしょう。

例えば、次のような式を評価しようとしたとします。

 
("abcde" 2)

オペレータは文字列に評価されますから、手続きでもジェネリックファンクションでも ありません。そこで、Gaucheはこの式を、あたかも次のような式が与えられた かのように解釈します。

 
(object-apply "abcde" 2)

デフォルトでは、<string><integer>を引数とする object-applyのメソッドは定義されていないので、 この式はエラーになります。しかし、次のようなメソッドを定義すると:

 
(define-method object-apply ((s <string>) (i <integer>))
  (string-ref s i))

最初の式はまるで文字列が整数に適用されたかのように動作します。

 
("abcde" 2) ⇒ #\c

このメカニズムは手続きが許されるほとんどの箇所で使うことができます。

 
(apply "abcde" '(1))   ⇒ (#\b)
(map "abcde" '(3 2 1)) ⇒ (#\d #\c #\b)

Gauche組み込みオブジェクトのうち、<regexp>オブジェクトと <regmatch>オブジェクトに対してはobject-applyメソッドが定義されて います。正規表現を参照して下さい。

Generic Function: (setter object-apply) object argvalue

適用可能オブジェクトを適用するフォームがset!フォームの第一ポジションに 現れた場合、そのフォームは下に示すように展開され、このメソッドが呼ばれます。

 
(set! (object arg …) value)
 ⇒ ((setter object-apply) object argvalue)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.7 継続

Function: call-with-current-continuation proc
Function: call/cc proc

[R7RS] 現在の継続を手続き (継続手続き) にパッケージ化して、それを引数として procを呼び出します。procが戻ったら、その返り値がcall/ccの 値となります。作成された継続手続きがどこかで0個または複数個の引数を伴って呼ばれたら、 あたかもcall/ccから戻ったかのように実行が継続されます。その場合、 call/ccは、継続手続きに与えられた引数を複数の値として返します。

ファーストクラスの継続はSchemeの最も特徴的な機能のひとつですが、それを 十分に説明するにはこの本の余白は狭すぎます。適切なドキュメントを参照してください。

Schemeの継続とC言語の実行環境との間に、ちょっとわかりずらい干渉が生じることがあります。 次のシナリオを考えます。

  1. アプリケーションのCランタイムがSchemeで書かれた手続きをコールバックします。 例えば、GUIフレームワークがSchemeで書かれた描画ルーチンを呼ぶ、というようなケースを考えてください。
  2. そのSchemeルーチンで継続が捕捉される。
  3. Schemeルーチンが終了してCランタイムに制御を戻す。
  4. 2で捕捉した継続が起動される。

継続を起動すること自体には問題は無いのですが、 制御がSchemeからCへと再び戻ろうとすると (つまり、step 3が再び実行されようとすると) 次のようなエラーが投げられます。

 
*** ERROR: attempt to return from a ghost continuation.

これは、Cの世界では関数が1回より多く戻ってくることを想定していないからです。 最初にSchemeのコールバックが呼ばれた時のCのスタックフレームは、 継続が再び呼ばれた時には状態が変わっているか、捨てられてしまっているでしょう。

継続を、根から上へ向かって成長する制御フレームの連鎖のようにイメージした場合、 Cの世界へ戻った時点でその連鎖が断ち切られる、と考えることができます。 そのような根無しの継続も実行することはできますが、既に失った根に戻る前に 別の箇所へと制御を移さねばなりません。他の箇所で捕まえた継続を呼んだり、 例外を投げるといったことが考えられます。

部分継続(限定継続)を使うのも手です。 gauche.partcont - 部分継続を参照してください。

Macro: let/cc var body …

このマクロは次のように展開されます : (call/cc (lambda (var) body …)). APIはPLT Schemeから取りました。

Function: dynamic-wind before body after

[R7RS] これは動的環境を管理するための基本手続きです。 動的環境とは、ある式を実行している間、維持される状態のセットのことです。 例えば「現在の出力ポート」というのは、with-output-to-port手続きの 実行中だけ切り替えることができます。 動的環境は動的にネストできます。これは、プログラムのソースから静的に ネストが決定できる字句環境とは異なります。

beforebodyおよびafter は引数を取らない手続きです。 dynamic-windはまずbeforeを呼び出し、続いてbodyを呼び出し、 続いてafterを呼び出します。そしてbodyが返した値を返します。

before手続きがbodyを実行するための動的環境を 設定し、after手続きが動的環境を元に戻す、ということが意図されています。

もしdynamic-windのダイナミックスコープの外で捕捉された継続が bodyの中で呼ばれることにより制御がbodyから飛び出した場合、 (bodyの中でエラーが起こった場合などが考えられます)、 afterが呼ばれます。

もし、bodyの中で捕捉された継続がdynamic-windのダイナミックスコープの 外で呼ばれることにより制御がbodyの中へ飛び込んだ場合、 beforeが呼ばれます。

 
(letrec ((paths '())
         (c #f)
         (add (lambda (s) (push! paths s))))
  (dynamic-wind
   (lambda () (add 'connect))
   (lambda ()
     (add (call/cc (lambda (c0) (set! c c0) 'talk1))))
   (lambda () (add 'disconnect)))
  (if (< (length paths) 4)
      (c 'talk2)
      (reverse paths)))
 ⇒ (connect talk1 disconnect connect talk2 disconnect)

註:エラーによりbodyが中断された時に必ずafterが呼ばれることから、 dynamic-windを例えばJavaのtry-catch構文のように考えて リソースの後処理などに使いたくなるかもしれません。 しかしdynamic-windはそのためのものではありません。 一旦離れた制御がbodyに再び戻ってくる可能性があるので、 dynamic-windが管理するのはむしろコンテキストスイッチに近い状況です。

リソースの後処理には、guardunwind-protectなどの例外処理 が使えます(例外の処理参照)。 それらはdynamic-windを使って構築されています。

基本的な指針として、afterは常にbeforeによって効果を戻せる 処理だけを行うべきです。例えば、(エラーを直接処理するのではなく) エラーハンドラスタックを操作する、といった具合に。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.8 多値

Function: values obj …

[R7RS] obj … を多値として返します。 呼び出し側は、組み込み構文の receive (変数束縛参照)か、 下に説明するR7RSの手続きcall-with-valuesを使って多値を受け取ることが できます。 srfi-11 - Let-valuesも参照してください。

 
(values 1 2) ⇒ 1 and 2
Function: call-with-values producer consumer

[R7RS] 手続きproducerを引数無しで呼びます。そして、それが返した値 を引数としてconsumerを呼びます。consumerが返す値を 返します。

 
(call-with-values (lambda () (values 1 2)) cons)
  ⇒ (1 . 2)
Macro: values-ref mv-expr k

mv-exprが返す多値のk-番目の値を返します。概念としては、 以下のコードと同じです。

 
(call-with-values (lambda () mv-expr) (lambda r (list-ref r k)))

このマクロは k がゼロであるような典型的な場合にはより単純な形へと 展開されます。

Common Lisp の nth-value に似ていますが、引数の順が逆になっています。 Scheme の他の*-ref 手続きと合わせるためです。

Macro: values->list mv-expr

mv-exprを評価し、結果の値をリストにして返します。Common Lispで multiple-value-listと呼ばれているものです。

 
(values->list (div-and-mod 10 3)) ⇒ (3 1)

(values->list 1) ⇒ (1)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.18.9 生成された値の畳み込み

一回呼ばれる度にひとつの値を生成するような手続きは、 一連の値を生成するジェネレータとして使われることがあります。 値の終端を表すマークとしては、慣例としてEOFが使われます。 例えばread-charはそうした、一連の文字を生成し、終端としてEOFを返す手続きです。

こうした抽象化は手軽であるため、Gaucheは、他のジェネレータを含むさまざまなソースから ジェネレータを構築するユーティリティをひとそろい提供しています。

生成された値は最終的に消費されなければなりません。そのための手続きもいくつか 提供されています。こうした手続きは、readのような入力手続きと組み合わせるのに 有用です。このため、別のモジュールに分割するのではなく、組み込み手続きになっています。

Function: generator-fold proc seed gen gen2 …

ジェネレータである手続き gen gen2 … が生成する値に対して fold のように働きます(foldの詳細はリストをたどる手続き参照)。

引数としてジェネレータがひとつ与えられると、その gen が生成する各値 v に 対して、proc(proc v r) のように呼び出されます。 rは現在の積算結果であり、その初期値は seed です。そして、proc が 返す値が次回の proc 呼び出し時の積算値として渡されることになります。 genがEOFを返すと、その時点の積算値が generator-fold から値として 返されます。

2つ以上のジェネレータが渡された場合は、proc(proc v1 v2r) のように呼び出されます。 v1, v2 … はそれぞれ gen, gen2, … が生成 した値であり、rは現在の積算値です。ジェネレータのどれかひとつ以上がEOFを返すと、 イテレーションは終了します。

 
(with-input-from-string "a b c d e"
  (cut generator-fold cons 'z read))
  ⇒ (e d c b a . z)
Function: generator-fold-right proc seed gen gen2 …

ジェネレータである手続きgen gen2 …が生成する値に対して fold-rightのように働きます(fold-rightの詳細はリストをたどる手続き参照)。

この手続きは完全性のために提供されていますが、ジェネレータとの相性はあまり良くありません。 値を右結合で計算していくためには、ジェネレータから(少なくともどれかひとつのジェネレータがEOFを返すまで) 全ての値を読み取らなければなりません。そうして初めて proc の呼び出しが始まることになります。

 
(proc v0_0 v1_0 ... (proc v0_1 v1_1 ... (proc v0_n v1_n ... seed) ...))

vn_mは、n番目のジェネレータがm回目の呼び出しで返した値です。

 
(with-input-from-string "a b c d e"
  (cut generator-fold-right cons 'z read))
  ⇒ (a b c d e . z)

ごらんのように、全ての中間値を保持することで、ジェネレータの利点が ある意味帳消しになってしまうのです。

Function: generator-for-each proc gen gen2 …

ジェネレータ版のfor-eachです。gen, gen2 … が生成する 値に対して、ジェネレータのどれかがEOFを返すまで proc を繰り返し適用していきます。 procが返す値は無視されます。

これは、生成される値を副作用で消費するのに便利です。

Function: generator-map proc gen gen2 …

ジェネレータ版のmapです。gen, gen2 … が生成する 値に対して、ジェネレータのどれかがEOFを返すまで proc を繰り返し適用していきます。 procが返す値をひとつのリストに束ねて返します。

 
(with-input-from-string "a b c d e"
  (cut generator-map symbol->string read))
  ⇒ ("a" "b" "c" "d" "e")

generator->listgmap(ジェネレータの操作参照)の組み合わせと 同じ挙動を実現します。この手続きは後方互換性のために提供されています。

 
(generator->list (gmap proc gen gen2 …))
Function: generator-find pred gen

ジェネレータgenから返された中から、述語predを満たす最初の要素を返します。

以下の例は、ファイル‘foo.txt’の中から正規表現#/XYZ/にマッチする最初の行を返します。

 
(with-input-from-file "foo.txt"
  (cut generator-find #/XYZ/ read-line))

註: grepコマンドのように、正規表現にマッチする全ての行を取り出したいなら、 gfiltergenerator->listが使えます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.19 遅延評価

Gaucheには、ふたつの組み込みの遅延評価メカニズムがあります。

ひとつめはScheme標準で定められている、明示的なメカニズムです。遅延評価したい式を delay構文でマークし、値が必要になったところでforceにより 評価を強制します。Gaucheはさらに、srfi-45で導入された、 末尾再帰アルゴリズムでメモリを効率的に利用するためのlazyというプリミティブも サポートしています。

もうひとつは遅延シーケンスです。こちらは評価をforceで明示する必要が ありません。Schemeプログラムからは、遅延シーケンスは通常のリストと全く 同じに見えます。carcdrを使ったり、mapを始めとする 様々なリスト手続きをそのまま適用することができます。けれども内部的には、 遅延シーケンスの要素は必要になるまで計算されません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.19.1 Delayとforceとlazy

Schemeは伝統的に、delayforceを使った 明示的な遅延評価メカニズムを提供してきました。しかし、R5RSの後で、 それが末尾再帰的なアルゴリズムとの相性がよくないことがわかりました。 末尾再帰的なアルゴリズムの本体が反復的に表現できるにもかかわらず、 メモリを際限なく要求してしまうのです。SRFI-45によって、 新たなプリミティブ構文lazyを使えばその問題が回避できることが示されました。 詳しい議論はSRFI-45のドキュメントを見てください。 ここではこれらのプリミティブの使い方を説明します。

Special Form: delay expression
Special Form: lazy expression

[R7RS][SRFI-45] これらの形式はexpressionの評価を遅延するプロミスを生成し ます。Expression はこのプロミスがforceにわたったときに評 価されます。

expression自身がプロミスを返す式ならlazyを、 そうでなければ、delayを使います。 型で考えるとわかりやすいでしょう。

 
lazy  : Promise a -> Promise a
delay : a -> Promise a

Schemeでは静的な型付けをしないので、この使い分けを強制することができません。 文脈にしたがってプログラマが適切に選択する必要があります。 一般的にはlazyは遅延アルゴリズムを表現している関数本体全体を囲む場合 にのみ出現します。

註: R7RSではlazydelay-forceと呼ばれています。概念的に (delay (force expr))という操作と考えられるからです (forceの型はPromise a -> aであると考えられます)。

lazyの実用的な使用例についてはutil.stream (util.stream - ストリームライブラリ)の実装をチェックするといいでしょう。

Function: force promise

[R7RS] もし、promiseがプロミスでなければ、それをそのまま返します。

そうではない場合で、もしpromiseの値がまだ計算されていない場合には、 forcepromiseが内包している式を評価し、その結果を返します。

いったん、promiseの値が計算されると、その値はメモ化され、あとで 再びforceされても、再計算がおこなわれることはありません。

Function: promise? obj

[R7RS] objがプロミスオブジェクトである場合に #tを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.19.2 遅延シーケンス

イントロダクション

遅延シーケンスはリストのようなデータ構造ですが、要素は必要になるまで 計算されません。内部的には、これはcdrの評価が遅延される特別な種類の ペアを使って実現されています。しかし、Schemeのレベルで 「遅延ペア」のような特別なデータ型が見えることは決してありません。 遅延ペアにアクセスしようとした途端、Gaucheは自動的に 遅延されていた計算をforceして、遅延ペアは通常のペアに変化してしまうからです。

これはつまり、遅延シーケンスをcarcdrmapといった 通常のリスト処理手続きにそのまま渡せるということです。

次の例を見てください。generator->lseqは、 「呼ばれる度に次の値を返す」という手続きを取り、返される値からなる遅延シーケンス にして返す手続きです。

 
(with-input-from-file "file"
  (^[] (let loop ([cs (generator->lseq read-char)] [i 0])
         (match cs
           [() #f]
           [(#\c (or #\a #\d) #\r . _) i]
           [(c . cs) (loop cs (+ i 1))]))))

このコードは、ファイル‘file’中に最初に出現する “car”または”cdr”という文字列の場所を返します。 文字は必要に応じて読まれ、目的の文字列が見つかれば残りは読まれません。 これを遅延無しでやろうとすると、一旦全てのファイルを読み込んでリストに 変換するか、あるいは便利なmatchマクロを使うのを諦めて 一文字つづ読み込んで処理する原始的な状態機械を書くしかないでしょう。

暗黙のforceの他にも、Gaucheの遅延シーケンスは Schemeの典型的な遅延ストリーム実装に比べて次のような違いがあります。

  1. 遅延シーケンスを、繰り返しによる怠惰なアルゴリズムから構築する際に、 遅延ペアのcdr側のみが遅延評価の対象となります。 ペアのcar側は直ちに評価されます。 一方、util.streamstream-consでは、 carcdrのどちらも遅延評価の対象となり、 必要となるまで評価されません (util.stream - ストリームライブラリ参照)。
  2. Gaucheの遅延シーケンスは、常にひとつ余分に評価します。 遅延ペアを手にした段階で、その中身にアクセスするかしないかにかかわらず、 既にそのcarは評価済みであるということです。 ひとつ余分に計算してしまうことのコストは、通常はあまり問題にならないでしょう (全てを余分に計算するよりは良いわけですから)。けれども、自分自身を参照 する遅延データ構造、つまり、遅延シーケンスの次の要素を計算するために そのシーケンスの前の方の要素を参照する必要がある場合は注意が必要です。 遅延評価言語で正しい自己参照遅延データの生成コードが、 そのままではGaucheで停止しなくなる場合があります。後で例を示します。 また、評価に副作用がある場合にもこの差異が観測される場合があります。 例えば、ポートから一文字づつ読む遅延シーケンスは、 見かけよりも一文字余分に読むことになるでしょう。

プリミティブ

Function: generator->lseq generator
Function: generator->lseq item … generator

ジェネレータ手続きgeneratorから生成される値の列を、遅延シーケンスとして返します。 ジェネレータ手続きは引数を取らない手続きで、呼ばれる度に次の値を返すようなものです。 EOFが返されたら、シーケンスの終了とみなされます (EOF自体はシーケンスには含まれません)。 例えばread-charはそのままgeneratorに渡せます。 ジェネレータ手続きを作ったり加工したりする便利なユーティリティが gauche.generatorモジュールで提供されています (gauche.generator - ジェネレータ参照)。

二番目の形式では、item …が遅延シーケンスの先頭に 配置されます。遅延ペアと通常のペアは区別できないので、これは (cons* item … (generator->lseq generator))とも書けますが、 少々冗長になるでしょう。

内部的には、Gaucheの遅延シーケンスはジェネレータを使うように最適化 されています。遅延シーケンスを作る最も効率の良い方法はこの手続きを使うことです。

Macro: lcons car cdr

carcdrからなる遅延ペアを作って返します。 carlconsを呼び出した時点で評価されますが、 cdrの評価は遅延されます。

遅延ペアと通常のペアを区別する方法はありません。ペアのcarcdrに アクセスしたり、それどころかpair?によって型を確かめただけでも、 遅延ペアのcdr側はforceされて、遅延ペアは通常のペアへと変化します。

consと違って、遅延ペアのcdrはリスト (遅延でも通常でも) を返す 関数でなければなりません。遅延シーケンスは全てforceされたら、 常に空リストで終端されているリストになる、と言っても良いでしょう。つまり、 ドット対を最後に持つような「不完全なリスト」に対応する「不完全な遅延シーケンス」 というものはありません。(Schemeは静的型ではないので、実際に評価するまで cdrが完全なリストを生成することを保証することができません。 現在の実装では、cdrがリストでない値を生成した場合、 それを単に無視して空リストが返されたかのように扱います)。

 
(define z (lcons (begin (print 1) 'a) (begin (print 2) '())))
 ⇒ ; car部はすぐに評価されるので、'1'が表示される

(cdr z) ⇒ () ;; そして'2'が表示される

;; これも'2'を表示する。遅延ペアのcarにアクセスすると、その時点で
;; cdr部の評価もforceされるので。
(car (lcons 'a (begin (print 2) '()))) ⇒ a

;; これも同じ。pair?と聞くだけで遅延ペアはforceされる
(pair? (lcons 'a (begin (print 2) '()))) ⇒ #t

;; 念のため。次の例では'2'は出力されない。二番目の遅延ペアはアクセス
;; されていないので、そのcdrも評価されない。
(pair? (lcons 'a (lcons 'b (begin (print 2) '())))) ⇒ #t

Gaucheの「一つ先の要素まで計算」が問題となる例も見ておきましょう。 次の例は、自己参照する遅延シーケンスを使った、とても美しい無限フィボナッチ数列の 定義です (lmapは遅延バージョンのmapで、gauche.lazyモジュールで 提供されています)。

 
(use gauche.lazy)  ;; for lmap
(define *fibs* (lcons* 0 1 (lmap + *fibs* (cdr *fibs*)))) ;; BUGGY

残念ながら、Gaucheではこれはうまく動きません。

 
(car *fibs*)
 ⇒ 0
(cadr *fibs*)
 ⇒ *** ERROR: Attempt to recursively force a lazy pair.

*fibs*の二番目の要素(cadr)にアクセスするということは、 *fibs*の二番目のペアのcarを取るということです。*fibs*の 二番目のペアは1(lmap ...)の遅延ペアになっています。 carを取ろうとした時点でこの遅延ペアはforceされ、そのcdrが計算されます。 lmapが最初に返さなければならないのは、*fibs*の一番目と二番目の 要素の和です。しかし*fibs*の二番目の要素は、今まさにアクセスしようと している値です!

この問題は、ある要素を計算するために直前の要素を参照しなければ回避できます。 フィボナッチ数はF(n) = F(n-1) + F(n-2) = 2*F(n-2) + F(n-3)と変形できるので、 遅延フィボナッチシーケンスはこう定義できます。

 
(define *fibs*
  (lcons* 0 1 1 (lmap (^[a b] (+ a (* b 2))) *fibs* (cdr *fibs*))))

これでok!

 
(take *fibs* 20)
  ⇒ (0 1 1 2 3 5 8 13 21 34 55 89 144 233
     377 610 987 1597 2584 4181)

多くの遅延アルゴリズムは、完全な遅延評価をするconsを基礎にしています。 そういったアルゴリズムをlconsを使ってGaucheに移植する際には、 Gaucheのこのちょっとした「熱心さ」に気をつけてください。

lconsは、実行の度にcdr部の評価を遅延するためのクロージャを 作るということにも注意してください。lconsを使った遅延アルゴリズムでは 要素ひとつにつきクロージャをひとつ作るオーバヘッドがかかります。 性能が重要な部分では、可能な限りgenerator->lseqを使いましょう。

ユーティリティ

Macro: lcons* x … tail
Macro: llist* x … tail

遅延バージョンのcons*です (リストの作成参照)。 lcons*llist*は全く同じです。 cons*/list*との対称性から両方の名前が定義されています。

tail引数は(遅延もしくは通常の)リストを生成する式でなければなりません。 tail引数の評価は遅延されます。x …引数はすぐに 評価されます。次の関係が成り立ちます。

 
(lcons* a)           ≡ a
(lcons* a b)         ≡ (lcons a b)
(lcons* a b ... y z) ≡ (cons* a b … (lcons y z))
Function: lrange start :optional end step

startからstepづつ増加し、endを越える直前までの遅延数列を 返します。stepのデフォルトは1、endのデフォルトは無限大です。 endを省略すると無限数列になるので、REPLで安易に (lrange 0)など評価しないようにしましょう。

startstepの少なくとも一方が不正確数なら、 不正確数列が返されます。

 
(take (lrange -1) 3) ⇒ (-1 0 1)

(lrange 0.0 5 0.5)
  ⇒ (0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5)

(lrange 1/4 1 1/8)
  ⇒ (1/4 3/8 1/2 5/8 3/4 7/8)
Function: liota :optional (count +inf.0) (start 0) (step 1)

遅延バージョンのiotaです (リストの作成参照)。 start(デフォルト0)からstep(デフォルト1)づつ増加する、 count個(デフォルト無限大)の遅延数列を返します。

iotaと同様、startstepの両方が正確数の時に限り、 結果は正確数のリストとなり、そうでなければ非正確数のリストとなります。

Function: port->char-lseq :optional port
Function: port->byte-lseq :optional port
Function: port->string-lseq :optional port
Function: port->sexp-lseq :optional port

これらの手続きは以下の式とそれぞれ同等です。このパターンは良く現れるので、 簡便のために用意しました。

 
(generator->lseq (cut read-char port))
(generator->lseq (cut read-byte port))
(generator->lseq (cut read-line port))
(generator->lseq (cut read port))

portが省略された場合はcurrent-input-portが使われます。

遅延シーケンスが入力をいくらかバッファする可能性があるので、 一度lseqを作った後では、portから直接読み出しをしないようにしてください。

遅延シーケンスはポートがEOFを返した時点で終端となりますが、 ポート自体はクローズされないことに注意してください。ポートの管理は、 遅延シーケンスを使う部分全体を囲むような大きな動的エクステントで 行う必要があります。

入力ポートを色々なリストに変換するには、他に次のような手続きがあります (入力ユーティリティ手続き参照)。lseq版がポートを 必要に応じて読むのに対し、 これらの手続きはポートをEOFに達するまで一気に読み込み、 全てのデータをリストにしてから返します。

 
(port->list read-char port)
(port->list read-byte port)
(port->string-list port)
(port->sexp-list port)

これらの手続きは、ポートからリストを作りだします。反対の手続きとして、 open-input-char-listopen-input-byte-listがあります (gauche.vport - 仮想ポート参照)。

遅延シーケンスを作るユーティリティ関数は他にもたくさん提供されています。 gauche.lazy - 遅延シーケンスユーティリティを参照してください。

素数の無限列を計算してみましょう。(註: アプリケーションで素数が必要な場合は、わざわざ書かなくても math.primeが使えます。math.prime - 素数参照。)

まず、既にある程度の計算済みの素数列が*primes*にあるとします。 すると、与えられたn以上の素数をひとつ見つける手続きが次のとおり書けます (nは奇数とします。)

 
(define (next-prime n)
  (let loop ([ps *primes*])
    (let1 p (car ps)
      (cond [(> (* p p) n) n]
            [(zero? (modulo n p)) (next-prime (+ n 2))]
            [else (loop (cdr ps))]))))

この手続きは素数列をループし、(sqrt n)以下の素数で nを割ろうとします。一つも割りきれる素数がなければ、nが素数です。 (実際の条件は、(> p (sqrt n))より効率の良い(> (* p p) n)を 使っています)。 nを割り切る素数があった場合は、 next-primeを再帰的に呼んで(+ n 2)を試します。

next-primeを使うと、次々に素数を生成してゆくジェネレータを書くことができます。 次の手続きはlastより大きい素数を次々に生成するジェネレータです。

 
(define (gen-primes-above last)
  (^[] (set! last (next-prime (+ last 2))) last))

generator->lseqを使えば、 gen-primes-aboveを遅延シーケンスに変換することができ、 それを*prime*の値とすることができます。最初の方の素数を計算するために、 あらかじめ計算済みの素数をいくつか用意しておくのがポイントです。

 
(define *primes* (generator->lseq 2 3 5 (gen-primes-above 5)))

*primes*を直接REPLで評価しないように。無限リストなので、 REPLの表示が終わらなくなります。 かわりに、例えばこんなふうにして最初の20個の素数を見たり:

 
(take *primes* 20)
 ⇒ (2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71)

10000番目の素数は何かを見たり:

 
(~ *primes* 10000)
 ⇒ 104743

あるいは1000000以下の素数はいくつあるかを調べたりできます:

 
(any (^[p i] (and (>= p 1000000) i)) *primes* (lrange 0))
 ⇒ 78498

註:遅延評価な関数型言語に慣れたプログラマには、この例は奇妙に見えるかもしれません。 わざわざ副作用のあるジェネレータを経由しないでも、 次に示すとおり、素数列は純粋に関数的な方法で定義できます。

 
(use gauche.lazy)

(define (prime? n)
  (not (any (^p (zero? (mod n p)))
            (ltake-while (^k (<= (* k k) n)) *primes*))))

(define (primes-from k)
  (if (prime? k)
    (lcons k (primes-from (+ k 2)))
    (primes-from (+ k 2))))

(define *primes* (llist* 2 3 5 (primes-from 7)))

(gauche.lazyモジュールには、take-whileの 遅延バージョンltake-whileが定義されています。 anyについては、遅延バージョンは必要ありません。anyはもともと 述語が真を返したら直ちに評価をやめて残りは見ないからです)。

primes-fromでのlconsを使った余再帰は、 関数型プログラミングでの典型的なイディオムです。もちろん、 Gaucheでもこのようなコードを書くことに何の問題もありません。 ただし、Gaucheではジェネレータを使った方がずっと効率が良くなります (筆者のマシンでは、最初の5000個の素数を計算するのに、 ジェネレータ版は余再帰版より17倍速いです)。

だからといって何が何でも余再帰を避けるべきということにはなりません。 アルゴリズムが余再帰で自然にかけるならそうして構わないでしょう。 ただその場合でも、遅延評価な関数型言語とのセマンティクスの違いを いつも気をつけるようにしてください。単に遅延評価アルゴリズムのコードを そのまま移植しても動くとは限りません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.20 例外

Gaucheの例外システムは次の3つのコンポーネントから構成されています。 (1)例外状況が発生したことを通知する方法、 (2)例外状況をどのように処理するかを指定する方法、 (3)例外状況を知らせたコードとそれを処理するコードがやりとりするための 標準オブジェクト(コンディション)。

普通これらの3つのコンポーネントは一緒に使われます。 そこで、最初に例を用いて典型的な使い方について説明し、 そのあとでそれぞれの機能について詳しく解説します。

用語について: いくつかの言語では例外(exception)というと、 例外的状況に遭遇したコードとそのハンドラがやりとりをするために用られる オブジェクトのことを指します。Gaucheではそのようなオブジェクトのことを言うときには、 SRFI-35にならってコンディション(condition)を使います。 例外というのは、状況であり、コンディションはそれを記述する 実行時のオブジェクトです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.20.1 例外処理の概要

特定のエラーを捕まえる

最もよくある例外処理のひとつは、組み込みあるいは ライブラリの手続きから発生した特定のエラーを捕捉するというものです。 guardマクロがこのような目的の場合に使えます。 コードは以下のような感じになるでしょう。

 
(guard (exc [(condition-has-type? exc <read-error>)
             (format #t "read error!")
             'read-error]
            [else 'other-error])
  (read-from-string "(abc"))

guard節の cadr 部は (variable clause …) という 形式です。この例では、変数は excで、2つの節があります。 それぞれの clausecond と似た形式になります。

guard の cddr 部は本体で、式のリストです。この例では、式は (read-from-string "(abc") のひとつだけです。

guard はその本体部を実行するところから始めます。 read-from-string は構文エラーに出くわすと、<read-error>型の エラーを発生させます。guardフォームがこのエラーを捕捉し、 そのコンディションオブジェクトを変数excに束縛し、excの後の 節を、condと同じようにチェックします。この場合、投げられた コンディションは <read-error>なので、最初の節のテストを満し、 その節の残りの部分が実行されます。すなわち、"read error!" が 印字され、シンボル read-error が返ります。

他の言語を使い慣れていれば、同じパターンであることがわかると思います。 guard フォームの cddr 部は、C++やJavaの try 節、あるいは、 Common Lisp の handler-case の cadr 部に似ています。 また、guard フォームの cdadr 部は、catch 節あるいは、 handler-case の cddr 部に似ています。

テスト式においては、投げられたコンディションのタイプをチェックする のが普通です。condition-has-type? という関数が SRFI-35 で定義されていますが、これはちょっと冗長です。Gauche の コンディションクラスは述語のようにも使えるようになっており、上の 式は以下のように書くこともができます。

 
(guard (exc [(<read-error> exc)
             (format #t "read error!")
             'read-error]
            [else 'other-error])
  (read-from-string "(abc")))

注意事項: 一般的には、投げられたコンディションが特定の タイプであるかをチェックするのにis-a?は使えません。 コンディションが合成されたものである可能性があるからです。 合成されたコンディションについての 詳細は コンディション を参照してください。

もし、clause のどのテストも満されず、かつ else 節があたえられて いなければ、その例外は guard から「抜け」ます。すなわち、guard の外側のレベルあるいはトップレベルで処理されることになります。たとえば、 以下の guard フォームでは、<read-error><system-error> としか処理できず、もし、本体が他のタイプのコンディション を投げてきたら、その外側のレベルで処理しなければなりません。

 
(guard (exc [(<read-error> exc) (handle-read-error)]
            [(<system-error> exc) (handle-system-error)])
  body …)

guardおよび他の低レベルの例外処理の構成についての詳細は 例外の処理 を参照してください。

自分のコードから例外を通知する

例外を通知する一般的な方法は raise手続きを使うことです。

 
(raise condition)

conditionにはどんなオブジェクトでも渡すことができます。 それをどのように解釈するかはひとえに例外ハンドラにかかってます。 もし、コンディションとして整数があがってくるというのが判っていれば、 guard で以下のように捕捉することができます。

 
(guard (exc [(integer? exc) 'raised])
  (raise 3))

とはいうものの、<condition> あるいはそのサブクラスのインスタンスを 使うのが好ましいというのが通例です。condition マクロはコンディション オブジェクトを作成するのに使えます。以下の例は、いくつかのスロット値をもち それらを発生させるコンディションの作りかたを示したものです。

 
;; create and raise an error condition
(raise (condition
        (<error> (message "An error occurred."))))

;; create and raise a system error condition
(raise (condition
        (<system-error> (message "A system error occurred.")
                        (errno EINTR))))

condition マクロおよび、どのようなコンディションクラスが用意 されているかの詳細については コンディション を参照してください。

最も一般的なコンディションのタイプはエラーコンディションなので、 error および errorf という便利な手続きが 用意されています。これらはメッセージ付きのエラーコンディションを 生成し、それを発生させます。

 
;; `error' concatenates the arguments into a message.
(unless (integer? obj)
  (error "Integer expected, but got:" obj))

;; `errorf' uses format to create a message.
(unless (equal? x y)
  (errorf "~s and ~s don't match" x y))

いくつかの言語での例外を投げる機構、たとえば、 C++やJavaの throw はその継続を破棄します。これとは違い Schemeの raise はその呼び出し元へ戻ることができます。もし、 raise で元へもどらないで欲しいのなら、簡便な方法としては、 常にエラーコンディションの一つをわたるようにするというのがあります。 そうすると Gauche では raise は戻らないことを保証します。 raiseの詳細については、例外の通知を参照してください。

註: R7RSでは少し違ったセマンティクスを採用しています。 raiseは継続不可能な例外を投げるものとして(もし例外ハンドラから 制御が戻ってきたら、別のエラーを報告します)、 別に継続可能な例外を投げるraise-continuableという手続きを 設けました。R7RS環境にいるときは、本項のraiseではなく R7RS互換のraiseが見えるようになっています。

独自のコンディションを定義する

独自のコンディションクラスを定義することが可能で、そうすることで、 アプリケーション固有の情報を例外が発生した点からハンドラへ渡すことが できます。

Gauche のフレームワーク(SRFI-35)に適合させるためには、新しく定義する コンディションクラスは組み込みの <condition> クラスあるいは その子孫を継承し、また、メタクラス <condition-meta> のインスタンスであることが望まれます。

可搬性を増すと同時に上の慣例を確実にするための方法のひとつは、 define-condition-typeマクロを使うことです。これは、 SRFI-35で定義されています。

 
(define-condition-type <myapp-error> <error>
  myapp-error?
  (debug-info myapp-error-debug-info)
  (reason myapp-error-reason))

これは、(Gauche内のクラス)<myapp-err>を定義するもので、 このクラスにはmyapp-error?という述語とアクセサのあるスロット があります。こうすれば、以下のようなコードで新しいコンディション 型が使えます。

 
(guard (exc
         [(myapp-error? exc)
          (let ([debug-info (myapp-error-debug-info exc)]
                [reason (myapp-error-reason exc)])
            ... handle myapp-error ...)])
  ...
  ...
  (if (something-went-wrong)
    (raise (condition
             (<myapp-error> (debug-info "during processing xxx")
                            (reason "something went wrong")))))
  ...
  ...
  )

SRFIとの互換性が重要でないなら、Gaucheの拡張されたerror手続きを 使うと<error>のサブタイプであるコンディションを投げるコードを より簡潔に書くことができます。

 
  (if (something-went-wrong)
    (error <myapp-error>
           :debug-info "during processing xxx"
           :reason "something went wrong"))

Gauche のオブジェクトシステムでコンディション型がどのように実装されているかは define-condition-typeマクロの解説を参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.20.2 例外の通知

エラーの通知

最も良くある例外ケースはエラーです。単純なエラーを通知するために、 ふたつの簡単な関数が用意されています。 複合コンディションを通知する必要がある場合は 下で説明するraise手続きを使って下さい。

Function: error string arg …
Function: error condition-type keyword-arg … string arg …

[R7RS+][SRFI-23+] エラーを通知します。最初の形式は、 stringarg …からなるメッセージを持つ <error>コンディションを作成それをraiseします。 この形式はR7RS及びSRFI-23のerrorと互換です。

 
gosh> (define (check-integer x)
        (unless (integer? x)
           (error "Integer required, but got:" x)))
check-integer
gosh> (check-integer "a")
*** ERROR: Integer required, but got: "a"
Stack Trace:
_______________________________________

2番目の形式は<error>以外のエラーコンディションを通知したいときに 使います。condition-typeはコンディションタイプ でなければなりません (コンディションタイプについてはコンディションを 参照して下さい)。その後に、キーワードと値のリストを与えることで コンディションのスロットを初期化することができます。また、その後に メッセージを構成する文字列と他のオブジェクトのリストを与えることができます。

 
(define-condition-type <my-error> <error> #f
  (reason)
  (priority))

...
  (unless (memq operation *supported-operations*)
    (error <my-error>
           :reason 'not-supported :priority 'urgent
           "Operation not supported:" operation))
...
Function: errorf fmt-string arg …
Function: errorf condition-type keyword-arg … fmt-string arg …

errorに似ていますが、エラーメッセージはformatによりフォーマット されます。すなわち、最初のフォームは以下と等価です。

 
(define (errorf fmt . args)
  (error (apply format #f fmt args)))

2番目の形式は<error>以外のエラーコンディションを通知するのに 使えます。condition-type, keyword-argの意味については errorと同じです。

一般的な条件の通知

Function: raise condition

[SRFI-18][R7RS] これは、例外事態を通知する基本となるメカニズムです。

この手続きは現在の例外ハンドラを呼び出します。引数conditionは 例外の性質を表現するのに使われ、例外ハンドラに渡されます。 Gaucheの組み込み手続きやライブラリ手続きは常に、<condition>クラス もしくはそのサブクラスのインスタンスをconditionとして用いますが、 ユーザは任意のオブジェクトをraiseに渡すこともできます。 渡されたconditionの解釈は例外ハンドラに任されます。

注意事項: いくつかの主流の言語では、例外を「投げる」と制御はそこに 戻りません。Gaucheではraiseから戻って来れるように セットアップすることが可能です。詳細は例外の処理を 参照してください。

raiseから戻って来ることが無いようにしたい場合は、 <serious-condition>やそのサブクラスのインスタンスをcondition としてraiseに渡すのが確実です。 組み込みコンディションのクラス階層についてはコンディションを参照して下さい。

R7RSではやや異なるセマンティクスを採用しています。R7RSのraiseは 決して戻りません—もしハンドラが戻ってきた場合は、別の例外が投げられます。 R7RSはraise-continuableという別の手続きで、ハンドラから 戻っても良いことを明示します。ポータブルなプログラムでは、 raiseには常に<serious-condition>かそのサブクラスを 渡すようにするのが良いでしょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.20.3 例外の処理

上位レベルの例外処理機構

Macro: guard (var clause …) body …

[R7RS][SRFI-34] これはGaucheでのエラー処理の高水準フォームです。

var はシンボルで clausecond 節と同じ形式です。 つまり、各節は以下の形式のどれかひとつです。

  1. (test expr …)
  2. (test => proc)

最後のclause(else expr …)という形式も許されます。

このフォームは通常の場合には body … を評価し最後の body の式の値を返します。本体の式を評価している最中に例外が 発生した場合、発生した例外を変数 var に束縛し、その後 各節の test 式を評価します。もし、test 式のひとつが 真値を返したとき、その節が上述の最初の形式であれば、対応する expr が評価されます。あるいは、節が二番目の形式であれば、 手続きproctestの結果が渡されます。

指定された節の test および expr が評価されるとき、 guardを呼び出した時点での例外ハンドラが設定されます。つまり、 clause 内部で再び例外が発生した場合、その例外は、外側の 例外ハンドラまたは guard フォームで処理されます。

もし、どの test も真値を返さず、最後の clauseelse 節であれば、それに結びつけられた expr が評価されます。 もし、どの test も真値を返さず、else 節がなければ、再び 例外が発生し、外側の例外ハンドラで処理されます。

例外が clause のどれかで処理された時には、guard は それを処理した節の最後の expr の値を返します。

clauseguardと同じ動的環境で評価されます。すなわち、 body中のdynamic-windclauseの評価の前に 巻戻されます。これは低レベル関数のwith-error-handlerwith-exception-handlerとは異なることに注意してください。 これら低レベル関数では例外ハンドラが呼ばれてから動的環境が巻戻されます。

 
(let ([z '()])
  (guard (e [else (push! z 'caught)])
    (dynamic-wind (lambda () (push! z 'pre))
                  (lambda () (error "foo"))
                  (lambda () (push! z 'post))))
  (reverse z))
 ⇒ (pre post caught)

(guard (e [else (print 'OUTER) #f])
  (with-output-to-string
    (lambda ()
      (print 'INNER)
      (error "foo"))))
 ⇒ OUTERを文字列ポートではなくguard実行時の
      current-output-portに出力。
Macro: unwind-protect expr cleanup …

exprを実行してからcleanup …を実行し、exprの結果を返します。 expr内で例外が挙がった場合、その例外がunwind-protectフォー ムを抜ける前に、cleanup …が実行されます。たとえば、以下のコードで はなにも問題が起きなければ、start-motordrill-a-holestop-motorが、この順で呼ばれます。何か不具合が start-motorあるいはdrill-a-holeで起った場合でも、例外が unwind-protectを抜ける前にstop-motorが呼ばれます。

 
(unwind-protect
  (begin (start-motor)
         (drill-a-hole))
  (stop-motor))

cleanupフォームはunwind-protectと同じ動的環境で評価されま す。例外がcleanup内で投げられた場合その例外は、 unwind-protectフォームの外側で処理されることになります。

このフォームはdynamic-windと似ていますが、この2つは動作するレイ ヤが違いますので混同しないようにしてください。 dynamic-windは最下位レイヤのもので、現在の例外ハンドラ、現在の入出力 ポート、パラメータなどを管理するのに用います。 dynamic-windbeforeおよびafterのサンクは 対応する制御フローの遷移が起きたときに必ず呼ばれます。 一方、unwind-protectはGaucheの例外システムの面倒しか見ません。 unwind-protectcleanupexprが正常終了するか Gaucheの例外を投げたときにのみ呼びだされます。上述の例で、unwind-protectの外側で 補足された継続が呼ばれ、制御がdrill-a-holeを抜けると、 cleanupは呼ばれません。制御が再びdrill-a-holeに戻る可能性 があるからです。たとえば、ユーザレベルのスレッドシステムが call/ccで実装されているような場合にこのようなことが起こる可能性 があります。

このフォームの名前はCommon Lispから取りました。同様のマクロに try-finallyというような別の名前を使っているSchemeの処理系もあります。

Function: with-error-handler handler thunk

handlerをアクティブなエラーハンドラにし、thunkを実行します。 thunkが正常に戻ったら、その結果が返されます。 thunkの実行中にエラーが通知されたら、エラーを表す例外オブジェクトを 1引数とするhandlerが、with-error-handlerの継続とともに呼ばれます。 すなわち、with-error-handlerは、handlerが返す値を返します。

handlerがエラーを通知したら、それはwith-error-handlerが 呼ばれたときにインストールされていたハンドラにより処理されます。

handlerが実行される場合の動的な環境は、エラーが起きたときのそれと 同じです。thunk内でdynamic-windが使われていたら、 そのafterメソッドはhandlerが戻った後、かつwith-error-handlerが 戻る前に呼ばれます。

註: この手続きを直接使うことはもはや推奨されませんguardの方が より安全でポータブルだからです。互換性を保つためまだしばらくは この手続きを残しますが、この手続きを使っているコードをguardによって 書き直すことを推奨します。「エラー時に後始末をする」というよくある次のような処理は:

 
(with-error-handler (lambda (e) (cleanup) (raise e))
  (lambda () body …))

次のように書き直すことができます。

 
(guard (e [else (cleanup) (raise e)])
  body …)

処理されなかった例外のふるまい

プログラムで定義した例外ハンドラを設定していないところで例外が発生した 場合以下のようなことが起ります。

  1. If an unhandled exception occurs within a thread other than the primordial one, it terminates the thread, and the thrown condition is wrapped by <uncaught-exception> condition and stored in the thread object. If other thread calls thread-join! to retrieve result, the the <uncaught-exception> is thrown in that thread. Note that no messages are displayed when the original uncaught exception is thrown. See section スレッドプログラミングTips, for the details. もしメインスレッド以外のスレッドで捕捉されない例外が起きた場合、そのスレッドは終了し、 投げられた例外は<uncaught-exception>でラップされて スレッドオブジェクトに保存されます。他のスレッドがthread-join!で スレッドの結果を取り出そうとした時に、その<uncaught-exception>が投げられます。 元の捕捉されない例外が起きた時点では何もメッセージなどが表示されないことに注意してください。 詳しくはスレッドプログラミングTipsを参照。
  2. そうでなく、プログラムが対話的に走っている場合(REPL)、投げられた例外の情報とスタッ クトレースが表示され、プログラムはトップレベルのプロンプトに戻ります。
  3. そうでなく、プログラムが対話的に走っているのではない場合は、投げられた例外の情報と スタックトレースが表示された後、プログラムは終了コード EX_SOFTWARE (70)で終了します。

上の2と3でのエラーメッセージとスタックトレースは、report-error手続きによって 出力されています。自分のエラーハンドラ内で同じ情報を出力したければ これを使うことができます。

Function: report-error exn :optional sink

投げられたコンディションオブジェクトexnの型とメッセージを表示し、 それからスタックトレースを出力します。REPLでエラーが報告される時の 表示を出しているのがこの手続きです。

raiseは任意のオブジェクトをコンディションとして投げることが出来るので、 exn<condition>オブジェクトの インスタンスである必要はなく、どんな型でも許されます。 report-errorは適切なメッセージを選んで表示します。

出力の行き先は省略可能引数sinkで指定できます。出力ポートを渡せば そこに出力されます。また、formatと同様に、#tを渡すことで 現在の出力ポートに、#fを渡すことで一時的な文字列ポートに出力できます。 #fを渡した場合、一時的な文字列ポートに出力された文字列が 返り値となります。その他の場合は未定義値が返されます。 sinkが省略されるか、上記以外の値であった場合は 現在のエラーポートが使われます。

註: 0.9.5の時点で、この手続きはexnが投げられたコンテキストでの スタックトレースではなく、report-error自身が呼ばれたコンテキストでの スタックトレースを表示します。report-errorをエラーハンドラから 直接呼んでいる限りにおいてはあまり違いは出ませんが、一般的に望ましいのは 前者なので、将来的には<condition>オブジェクトにスタックトレース情報を つける予定があります。

下位レベルの例外処理機構

このレイヤはSRFI-18互換のシンプルな例外メカニズムを提供します。 with-error-handlerのような高次元の構造の振る舞いを、 with-exception-handlerを使って上書きすることができます。

これは諸刃の剣であることに注意して下さい。あなたは独自の例外処理 セマンティクスを構築する自由がありますが、Gaucheシステムは何か 間違いがあっても救ってくれません。システムの高次元のセマンティクスを カスタマイズしたいか、他のSRFI-18準拠のコードを移植している場合にのみ、 これらのプリミティブを使って下さい。

Function: current-exception-handler

[SRFI-18] 現在の例外ハンドラを返します。

Function: with-exception-handler handler thunk

[R7RS][SRFI-18] handlerは1引数を取る手続きです。この手続きは、handlerを 現在の例外ハンドラにセットし、thunkを呼び出します。

一般的に、エラーのような継続不可能な例外をこの低レベルなメカニズムで 扱いたい場合は、明示的にそのハンドラから制御を移さなければなりません (前述のwith-error-handlerの説明を参照して下さい)。 raiseは、ハンドラが継続不可能な例外を返したことを検知し、 デフォルトのエラーハンドラメカニズムを使ってエラーを報告しますが、 それは単に安全ネットにしか過ぎません。

handlerraiseの動的な環境と同じ環境で呼ばれることにも 注意して下さい。したがって、handler内部で例外を発生させると、 再度handlerによって捕捉されます。その例外処理を“外側”の例外 ハンドラへ伝播させるのはプログラマの責任です。

これらの手続きの振る舞いは、次の概念的なSchemeコードによって 説明されるでしょう。

 
;; 低レベルな例外メカニズムの概念的な実装
;; %xhは例外ハンドラのリスト

(define (current-exception-handler) (car %xh))

(define (raise exn)
  (receive r ((car %xh) exn)
    (when (uncontinuable-exception? exn)
      (set! %xh (cdr %xh))
      (raise (make-error "returned from uncontinuable exception")))
    (apply values r)))

(define (with-exception-handler handler thunk)
  (let ((prev %xh))
    (dynamic-wind
      (lambda () (set! %xh (cons handler %xh)))
      thunk
      (lambda () (set! %xh prev)))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.20.4 コンディション

組み込みコンディションクラス

現在のところGaucheには以下の組み込みコンディションクラスの階層があります。 これは、おおよそのところ SRFI-35 および SRFI-36 のコンディションの階層を 反映したものですが、Gauche風のクラス名になっています。対応する SRFI の コンディションタイプがあるものについては、SRFI でのクラス名も使えます。

 
  <condition>
    +- <compound-condition>
    +- <serious-condition>
    |    +- <serious-compound-condition> ; also inherits <compound-condition>
    +- <message-condition>
         +- <error>                      ; also inherits <serious-condition>
              +- <system-error>
              +- <unhandled-signal-error>
              +- <read-error>
              +- <io-error>
                   +- <port-error>
                        +- <io-read-error>
                        +- <io-write-error>
                        +- <io-closed-error>
                        +- <io-unit-error>

いくつかのコンディションが同時に発生することがあることに注意してください。 たとえば、ファイルの読み込がデバイスの欠陥により失敗した場合は、 <system-error> および <io-read-error> の両方からなる エラーとなるでしょう。 このような場合、合成したコンディション (compound condition) が発生します。 したがって、たとえば、<io-read-error> が投げられたかどうかをチェック するのに、単に (is-a? obj <io-read-error>) を使えばよいというわけ にはいきません。 後述の「コンディション API」の節を参照してください。

Metaclass: <condition-meta>

すべてのコンディションクラスはこのクラスのインスタンスです。 このクラスは object-apply を定義していますので、 コンディションクラスは述語として使うことができます。たとえば、

 
(<error> obj) ≡ (condition-has-type? obj <error>)
Class: <condition>
Condition Type: &condition

[SRFI-35] コンディションの階層のルートクラスです。

Class: <compound-condition>

合成コンディションを表現します。合成コンディションは make-compound-condition を用いて1つ以上のコンディションから 生成することができます。このクラスを直接使ってはいけません。

合成コンディションはcondition-has-type? に対して、 元のコンディションのどれかが、与えられたタイプなら、#t を返します。

Class: <serious-condition>
Condition Type: &serious

[SRFI-35] このクラスのコンディションは無視して先を続けることはできない深刻な 状況のためにあります。特に、このタイプのコンディションを raise したら、それは元には絶対に戻らないと考えて問題ありません。

Class: <serious-compound-condition>

深刻なコンディションを含む合成コンディションを表現するための 内部クラスです。<compound-condition> および <serious-condition> の両方を継承しています。 make-compound-condition は深刻なコンディションを含む複数の コンディションを渡されると、このクラスを使います。このクラスを 直接使ってはいけません。

Class: <message-condition>
Condition Type: &message

[SRFI-35] このクラスはメッセージ付のコンディションを表現し、スロットを一つ もっています。

Instance Variable of <message-condition>: message

メッセージ

Class: <error>
Condition Type: &error

[SRFI-35] エラーを表します。<serious-condition> および <message-condition> を継承しています。したがって、 message スロットを持っています。

注意事項: SRFI-35 の&errorコンディションは &serious のみを継承し、&message は継承していません。したがって、 このエラーコンディションにメッセージを付与するためには、 合成コンディションを使わなければなりません。Gauche は 主として過去のバージョンとの互換性を確保するために、 ここで多重継承を用いています。 可搬性のあるコードを書くには、以下のようにメッセージコンディション 付きのエラーコンディションを使うべきです。

 
(condition
  (&message (message "Error message"))
  (&error))
Class: <system-error>

<error> のサブクラス。 システムコールがエラーを返したとき、この型の例外が投げられます。 message スロットには通常エラーの(strerror(3)のような)説明が 含まれています。それ以外に、このクラスにはもうひとつ次のような インスタンススロットがあります。

Instance Variable of <system-error>: errno

システムのエラー番号の整数値を持ちます。

エラー番号はシステムによって異なる可能性があります。Gaucheは典型的な Unixのエラー番号に対して定数を定義している(例: EACCES, EBADF)ので、 それを使うと良いでしょう。定義されている定数に関しては システムへの問い合わせsys-strerrorの説明を参照してください。

このクラスには対応する SRFI のコンディションタイプがありませんが、 OSの生のエラーコードを取得するのに重要です。いくつかのケースで このタイプのコンディションは他の、たとえば <io-read-error> のようなコンディションと合成されます。

Class: <unhandled-signal-error>

<error>のサブクラス。多くのシグナルのデフォルトハンドラは このコンディションを投げます。詳しくはシグナルの処理を 参照してください。

Instance Variable of <unhandled-signal-error>: signal

受け取ったシグナル番号を示す整数値。典型的なシグナル番号については 定数が定義されています。シグナルとシグナルセットを参照のこと。

Class: <read-error>
Condition Type: &read-error

[SRFI-36] <error> のサブクラス。リーダがS式を読み込み中に、字句エラー または構文エラーを検出したとき、この型のコンディションが投げられます。

Instance Variable of <read-error>: port

リーダがS式を読みこんでいたポート。 (註: SRFI-36 の &read-error はこのスロットを定義していません。 ポータブルなプログラムを書く場合はこのスロットを使わないで下さい)。

Instance Variable of <read-error>: line

リーダがこのエラーを発生させたときの入力行カウント(1がベース)。 リーダが行カウントを保持しないポートから読み込むときには、-1 となる。

Instance Variable of <read-error>: column
Instance Variable of <read-error>: position
Instance Variable of <read-error>: span

これらのスロットは、SRFI-36 の &read-error で定義されています。 今のところ、これらのスロットは常に #f を保持するようになっています。

Class: <io-error>
Condition Type: &io-error

[SRFI-36] I/O エラーのベースとなるクラス。<error> を継承しています。

Class: <port-error>
Condition Type: &io-port-error

[SRFI-36] ポート関連の I/O エラー。<io-error> を継承しています。

Instance Variable of <port-error>: port

エラーを起したポートを保持。

Class: <io-read-error>
Condition Type: &io-read-error

[SRFI-36] ポートから読み込み中の I/O エラー。<port-error> を継承しています。

Class: <io-write-error>
Condition Type: &io-write-error

[SRFI-36] ポートへの書き出し中の I/O エラー。<port-error> を継承しています。

Class: <io-closed-error>
Condition Type: &io-closed-error

[SRFI-36] クローズされたポートで読み込み/書き出しをしようしたときの I/O エラー。 <port-error> を継承しています。

Class: <io-unit-error>

対象となるポートでサポートされていない単位での読み/書き要求 (たとえば、キャラクタ専用ポートでのバイナリ I/O 要求)の際の I/O エラー。 <port-error> を継承しています。

コンディションAPI

Macro: define-condition-type name supertype predicate field-spec …

[SRFI-35+] 新しいコンディションタイプを定義します。Gaucheでは、コンディションタイプは クラスであり、そのメタクラスは <condition-meta> です。

name が新しいタイプの名前になり、この名前の変数が作成された コンディションタイプに束縛されます。supertype はこのコンディション タイプのスーパータイプ(直接のスーパークラス)の名前です。コンディション タイプは <condition> を継承するか、その子孫を継承しなければ なりません。(この形式では、多重継承を指定することはできません。 一般的にいって、コンディションタイプの階層の中では多重継承は避ける べきです。そのかわりに、合成コンディションを使えます。合成コンディションは 多重継承を使いません。)

変数 predicate はこのコンディションタイプ用の述語手続きに 束縛されます。

field-spec(field-name accessor-name) の形式で このコンディションは、フィールド名は field-name で決まります。 変数 accessor-name はそのフィールドにアクセスする手続きに 束縛されます。Gauche では、それぞれのフィールドは生成された クラスのスロットとなります。

Gaucheはsrfi-35を拡張して、predicateaccessor-nameを 定義する必要が無い場合はその位置に#fを指定できるようにしています。 accessor-nameが必要無い場合はそれを省略することもできます。

define-condition-typeがクラス定義に展開される際に、 各スロットは:init-keywordスロットオプションにスロット名と 同名のキーワードを取るように定義されます。

Function: condition-type? obj

[SRFI-35] objがコンディションタイプである場合で、その場合にかぎり、 #t を返します。Gauche では (is-a? obj <condition-meta>) と同じです。

Function: make-condition-type name parent field-names

[SRFI-35] 新しいコンディションタイプを生成する手続き版です。

Function: make-condition type field-name value …

[SRFI-35] コンディションタイプ type のコンディションを生成し、 field-name および value のペアで指定されたように フィールドを初期化します。

Function: condition? obj

[SRFI-35] obj がコンディションである場合で、その場合にかぎり、 #t を返します。Gauche では (is-a? obj <condition>) と 同じです。

Function: condition-has-type? obj type

[SRFI-35] obj がコンディションタイプ type に属している場合で、その 場合にかぎり、#t を返します。合成コンディションがあるので、 これは、is-a? と同じではありません

Function: condition-ref condition field-name

[SRFI-35] condition のフィールド field-name の値を検索します。 condition が合成コンディションであれば、元のコンディションの フィールドにアクセスできます。もし、複数の元のコンディションが、 field-name を持つ場合には、最初に make-compound-condition に渡されたものが優先されます。

コンディションのフィールドにアクセスするには、slot-ref および ref の両方あるいはどちらかを使えます。合成コンディションでは、 slot-missing メソッドが定義されますので、slot-ref は あたかも、合成コンディションが元になったコンディションの全てのスロットを もつかのように振舞います。しかしながら、condition-ref を 使う方が可搬性が増します。

Function: make-compound-condition condition0 condition1 …

[SRFI-35] condition0 condition1 … のすべてを持つ合成コンディション を返します。返されたコンディションのフィールドは、与えられたコンディション のすべてのフィールドの和集合になります。同じ名前のフィールドを持つ コンディションがある場合には最初に与えられものが優先されます。 返されたコンディションは元になったコンディションのすべてのタイプの コンディションタイプをもつことになります。 (これは多重継承ではありません。上の <compound-condition> を参照)

Function: extract-condition condition condition-type

[SRFI-35] condition はコンディションで、condition-typeタイプで なければなりません。この手続きは condition-type のコンディション を返し、condition からとりだされた値のフィールドを持ちます。

Macro: condition type-field-binding …

[SRFI-35] コンディションを生成するのに便利なマクロ。 合成されたコンディションも生成できます。 Type-field-binding は、 (condition-type (field-name value-expr) …) という形式になります。

 
(condition
  (type0 (field00 value00) ...)
  (type1 (field10 value10) ...)
  ...)
 ≡
(make-compound-condition
  (make-condition type0 'field00 value00 ...)
  (make-condition type1 'field10 value10 ...)
  ...)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.21 eval と repl

Function: eval expr env

[R7RS] exprを環境env内で評価します。 Gaucheにおいて、envは単なる<module>オブジェクトです。

R5RS and R7RS provides a portable way to obtain environment specifiers. R5RS way is described below. R7RS way is described in scheme.eval - R7RS eval.

Function: null-environment version
Function: scheme-report-environment version
Function: interaction-environment

[R5RS] evalの第2引数として使われる環境識別子を返します。 現時点では、環境識別子は単にモジュールです。 (null-environment 5)は、R5RSで規定されている単なる構文的な 束縛を含むnullモジュールを返します。 (scheme-report-environment 5)は、R5RSで規定されている 構文的な束縛と手続きの束縛を含むschemeモジュールを返します。 (interaction-environment)は、全てのGaucheのビルトインと ユーザ定義の全てを含んだuserモジュールを返します。 将来、Gaucheがファーストクラスの環境オブジェクトを採用する可能性が あるので、環境識別子が単なるモジュールであることを当てにしないで 下さい。

引数versionに5以外の値を渡すとエラーが通知されます。

Function: read-eval-print-loop :optional reader evaluator printer prompter

goshのデフォルトのread-eval-printループをアプリケーションに エクスポートします。それぞれの引数は、Gaucheのデフォルトの手続きを使うことを 示す#fであっても良いですし、以下の条件を満たす手続きであっても構いません。

reader

引数を取らない手続きです。式を読み込んでそれを返すことを期待されます。

evaluator

式と環境識別子を2引数として取る手続きです。式を評価して、0個以上の値を 返すことを期待されます。

printer

0個以上の引数を取る手続きです。それらの値を出力することを期待されます。 この手続きの戻り値は無視されます。

prompter

引数を取らない手続きです。プロンプトを出力することを期待されます。 この手続きの戻り値は無視されます。

これらの手続きが与えられると、read-eval-print-loopは以下のように 動作します。

  1. prompterを呼んでプロンプトを出力します。
  2. readerを呼んで式を読み込みます。EOFが返されたら、ループを終了し read-eval-print-loopから戻ります。
  3. evaluatorを呼んで式を評価します。
  4. printerを呼んで結果を出力し、その後、1から繰り返します。

これらの手続きのうちの1つからエラーが通知されると、そのエラーは デフォルトのエスケープハンドラにより捕捉・報告され、その後、 ループが1から再開します。

これらの手続きの内部で継続を捕捉し、後で再起動することは 許されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22 入出力


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.1 ポート

Builtin Class: <port>

Portは、Schemeにおいて抽象化された入出力のインタフェースを提供します。 Gaucheはportにいくつかの拡張を行い、いろいろなアプリケーションに対応できるようにしました。

テキストとバイナリI/O

R7RSはテキストポートとバイナリポートを定義しています。 Gaucheでは、ほとんどのポートはテキストI/OとバイナリI/Oを混ぜて使えます。 概念的には、異なる型のオブジェクトのソース/シンクは別物と考える方が綺麗ですし、 ひとつのポートでテキストI/OとバイナリI/Oを混在して使うことは滅多にありません。

しかし現実には、ポートを型の無いデータの集まりにつながったものとして、 データをどう解釈するかは後から決めたい、という場合があります。 例えば標準入出力です。Unix的な環境では、あらかじめオープンされている標準入出力 に対してテキストI/Oを行うかバイナリI/Oを行うかはプログラムに任されています。 R7RSでは、current-input-port等の初期値はテキストポートであると 規定しています。Gaucheではどちらにも使うことができます。

変換

ポートはまた、データストリームを変換するのにも使えます。例えばgauche.charconv モジュールでは、文字コード間の変換を行うポートを提供しています (詳しくはgauche.charconv - 文字コード変換を参照)。

追加機能

また、特殊な機能を実現するポートもあります。 コーディング認識ポート(コーディング認識ポート参照)は ファイル中の特殊なコメントを認識して、そのファイルがどの文字エンコーディング で書かれているかを検出します。 仮想ポート(gauche.vport - 仮想ポート参照)はSchemeでふるまいをプログラムできる ポートを提供します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.2 ポートとスレッド

GaucheがスレッドサポートをONにしてコンパイルされている場合、 組み込みのポート操作関数はポートをロックして、 複数のスレッドからの同一のポートへのアクセスがポートの内部状態を壊さないように しています。 (SRFI-18によって要求されている動作です)。 ここで「組み込みのポート操作関数」はGaucheにより提供される、 ポートを引数に取り何らかのI/O動作や問い合わせを行う手続きで、 read/writeread-char/write-charport->string等を含みます。 但し、call-with-*with-*系関数は、 与えられた手続きを呼ぶ際にはポートをロックしません。 その手続きが別のスレッドにポートを渡すかもしれず、Gaucheにはそれを知ることが できないからです。

従って、マルチスレッド環境でポートへのアクセス競合により ポートの内部状態を壊してしまうんじゃないか、などとあまり神経質に なる必要はありません。但し、このロック機構はあくまで予想外の アクセス競合によってポートがおかしな状態になってしまうことを 防ぐための安全ネットであって、一般的な排他制御機構として使われる ことは想定していないのに注意して下さい。このロックの実装は、 ポートへのアクセス競合は例外的な場合のみであると仮定し、 通常のアクセスにおけるオーバヘッドを避けるために、スピンロックを 使用します。もし、意図的にポートアクセスが競合するようなコードを書く場合は、 明示的に排他制御をしてください。

Function: with-port-locking port thunk

portをロックし、thunkを実行します。 ロックはthunkのダイナミックエクステントの期間有効です。

portがロックされている期間での組み込みのポートアクセス関数の 呼び出しは排他制御をバイパスするため、性能向上が見込まれます。

ロックの有効期間はthunkのダイナミックエクステントなので、 thunk内からwith-port-lockingの外で捕捉された 継続を呼んだ場合、ロックは解放されます。その後、thunk内で 捕捉された継続が呼ばれた場合、再びロックが獲得されます。

with-port-lockingはネスト可能です。ロックは最も外側の with-port-lockingの期間中有効となります。

この手続きはポート組込みのロック機構を利用します。つまり、ポートアクセスが 競合した場合はbusy waitになるということです。この手続きはあくまで 頻繁なロックによるオーバヘッドを回避するためのものです。 もし本当に競合が予測される場合は明示的に排他制御を行ってください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.3 ポート共通の操作

Function: port? obj
Function: input-port? obj
Function: output-port? obj

[R7RS] obj がそれぞれポート、入力ポート、出力ポートなら真を返します。 port?はR5RSの"Standard Procedures"の項には 載っていませんが、"Disjointness of Types"の項に挙げられています。

Function: port-closed? port

objがポートであり、既に閉じられていた場合に真を返します。 一度閉じたポートは再び開くことはできません。

Parameter: current-input-port
Parameter: current-output-port
Parameter: current-error-port

[R7RS] 現在の入力ポート、出力ポート、エラー出力ポートをそれぞれ返します。

R7RSではこれらのポートの初期値はテキストポートであると規定されています。 Gaucheでは、これらのポートの初期値はテキスト入出力もバイナリ入出力も扱えます。

現在のポートの値はparameterizeを使って一時的に変更できます (gauche.parameter - パラメータ参照)。もっとも、典型的な場合には より簡単なwith-output-to-stringwith-input-from-fileなどの 手続きが使えるでしょう。

 
(use gauche.parameter)
(let1 os (open-output-string)
  (parameterize ((current-output-port os))
    (display "foo"))
  (get-output-string os))
 ⇒ "foo"
Parameter: standard-input-port
Parameter: standard-output-port
Parameter: standard-error-port

プログラム開始時点の標準入出力ポートを返します。これらの値が current-input-portcurrent-output-portcurrent-error-portのデフォルトとなります。

これらの手続きの値もparameterizeで変えることができますが、 (1) current-*-portはプログラム実行前に初期化されるので、 プログラム中でstandard-*-portを変更しても影響は及ばない、 (2) これらの手続きの値の変更はScheme世界だけのことで、低レベルライブラリが 参照しているシステムのstdioファイルディスクリプタは変更されない、 という点に注意してください。

Function: with-input-from-port port thunk
Function: with-output-to-port port thunk
Function: with-error-to-port port thunk

それぞれ入力、出力、エラーポートをportにセットした状態でthunkを呼び出します。 portthunk実行後にも閉じられないことに注意してください。

Function: with-ports iport oport eport thunk

上の3つの動作を同時に行う手続きです。 入力、出力、エラーの各ポートをそれぞれiport, oport, eportに セットしてthunkを呼び出します。変更する必要がないポートの引数には #fを渡すことができます。

portthunk実行後にも閉じられないことに注意してください (最近のScheme標準は良く似た名前の手続きcall-with-portを追加しましたが、 生憎そちらはポートを閉じる仕様になっています。call-with-portについては 下で説明します。)

Function: close-port port
Function: close-input-port port
Function: close-output-port port

[R7RS] ポートを閉じます。close-portは入力ポートも出力ポートも閉じられますが、 close-input-portclose-output-portはそれぞれ対応するポートのみに 使え、異なる種類のポートが渡されたらエラーを通知します。

理屈の上では、close-portだけあれば十分です。3つ手続きがあるのは歴史的理由です。 R5RSにはclose-input-port and close-output-portだけがありました。 R6RSとR7RSは3つ全てをサポートしています。

Function: call-with-port port proc

[R7RS] procを、portを引数として呼び出します。procからリターンするか、 エラーが投げられた場合はportがクロースざれます。 procが返す値がそのままcall-with-portの返り値となります。

Function: port-type port

portのタイプを、シンボルfilestringprocの いずれかで返します。

Function: port-name port

portの名前を返します。ポートがファイルに関連付けられている場合は、ポートの名前は ファイル名です。そうでない場合、ポートを説明する文字列が返されます。

Function: port-buffering port
Function: (setter port-buffering) port buffering-mode

ファイルポート((port-type port)fileを返すもの) に対して、そのバッファリングモードを読みだし、もしくは変更します。 入力ポートではバッファリングモードは :full:modest:noneのいずれかです。 出力ポートでは :full:line:noneのいずれかです。 バッファリングモードの詳細な説明は、ファイルポート を参照してください。

port-bufferingがファイルポート以外のポートに対して呼ばれた場合は #fを返します。port-bufferingのsetterが ファイルポート以外のポートに対して呼ばれた場合はエラーとなります。

Function: port-current-line port

portの現在の行番号を返します。行番号は、ファイルに関連付けられたポートで かつシーケンシャルなキャラクタI/Oを行っている場合のみ有効です。それ以外の場合は -1を返します。

Function: port-file-number port

portがファイルに関連付けられている場合、そのファイルディスクリプタ番号を 返します。それ以外の場合は#fを返します。

Function: port-seek port offset :optional whence

portがランダムアクセス可能なポートの場合、 この手続きはportのread/writeポインタをoffsetwhenceの値によって 設定し、新たなread/writeポインタの値(データの先頭からのバイトオフセット)を 返します。portがランダムアクセス可能でない場合は#fが返されます。 現在のバージョンでは、ファイルポートおよび入力文字列ポートがランダムアクセス可能です。 出力文字列ポートは現在のポインタの値を問い合わせる動作だけが可能です。

ポートのポインタはバイト数で表現され、文字数とは異なることに注意して下さい。

portが出力ファイルポートの場合は、データの終端を超えた位置までseek することが可能です。その場合の動作はPOSIXのlseek(2)に準じます。 入力ファイルポートや入力文字列ポートではデータの終端以降にseekすることはできません。

whence引数は、offsetの基準を指定する小さな整数です。 以下の定数が定義されています。

SEEK_SET

offsetはデータ先頭からのバイト数を指定します。 whenceが省略された場合のデフォルトの動作です。

SEEK_CUR

offsetは現在のread/writeポインタからの相対バイト数を指定します。 offsetが0であれば、ポインタを動かさずに現在のポート位置を知ることができます。

SEEK_END

offsetはデータの終端からの相対バイト数を指定します。

Function: port-tell port

portの現在のread/writeポインタの値をバイト数で返します。 portがランダムアクセス可能でない場合は#fが返されます。 これは以下の呼び出しと等価です。

 
(port-seek port 0 SEEK_CUR)

名前に関するメモ: port-seekは他の処理系で seekfile-positioninput-port-position/ output-port-position等と呼ばれています。 port-telltellftellset-file-position!等と 呼ばれています。いくつかの処理系はport-positionという手続きを 持っていますが、port-seekとは別の機能を実現しています。 file-positionはCommonLisp由来の名前ですが、 fileポート以外のものも扱うため採用しませんでした。 また、seektellはPOSIXの名前由来であり、 Gaucheの名前付け規則を使ってsys-seeksys-tellとしても よさそうですが、portの操作はシステムコールレベルよりも抽象度が高いため これも採用しませんでした。結局、新しい名前を採用することにしました。

Function: copy-port src dst :key (unit 0) (size #f)

srcからEOFまでデータを読みだし、dstへ書き出します。

キーワード引数unitは0以上の整数か、シンボルbyteもしくはchar でなければなりません。これはデータをコピーする単位を指定します。 整数ならば、その大きさ(0の場合はシステム規定の大きさ)のバッファが確保され、 ブロックI/Oを使って転送が行われます。通常のファイルをコピーする場合などはこれが 速いでしょう。もしunitがシンボルbyteであれば、バイト毎 に読みだし/書き込みが行われます。unitがシンボルcharであれば、 キャラクタ毎に読みだし/書き込みが行われます。

キーワード引数sizeに非負の整数が与えられた場合、それはコピーされるデータの 最大量を指定します。unitがシンボルcharの場合はsizeは コピーされる文字数を、そうでない場合はバイト数を指定します。

unitがシンボルcharの場合はコピーされた文字数を返し、 そうでない場合はコピーされたバイト数を返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.4 ファイルポート

Function: open-input-file filename :key if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: open-output-file filename :key if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R7RS+] ファイルfilenameを入力または出力用にオープンし、 入力ポートまたは出力ポートを作成して返します。

キーワード引数により、動作を細かく指定できます。

:if-exists

このキーワード引数はopen-output-fileのみに指定でき、 filenameが既に存在した場合の動作を指定します。次の値のいずれかを与えることができます。

:supersede

既存のファイルが長さ0に縮められます。これが既定の動作です。

:append

既存のファイルにこれから書き出す内容が追加されます。

:overwrite

既存のファイルにこれから書き出す内容が上書きされます。 書き出されるデータが既存のファイルのデータよりも短い場合、 残りの部分はそのまま残されます。

:error

エラーが報告されます。

#f

何もせず、#fを返します。

:if-does-not-exist

このキーワード引数はfilenameが存在しない場合の動作を指定します。

:error

エラーを報告します。これがopen-input-fileの既定の動作です。

:create

ファイルが作成されます。これがopen-output-fileの既定の動作です。 ファイルの存在のチェックと作成はアトミックに行われます。 このオプションに加え、if-existsオプションに:error#fを 指定することで、排他的にファイルを作成することができます。 open-input-fileに対してはこの値を指定することはできません。

#f

何もせず、#fを返します。

:buffering

この引数はバッファリングモードを指定します。以下の値が設定できます。 ポートのバッファリングモードは手続きport-buffering (ポート共通の操作参照)によって 読みだし/変更可能です。

:full

出来る限りデータをバッファリングします。これがデフォルトのモードです。

:none

バッファリングを行いません。出力ポートにデータが書き出されるか、 入力ポートからデータが読み込まれる度に、下位にあるシステムコールが呼ばれます。 プロセスの標準エラーポートはこのモードでオープンされています。

:line

このモードは出力ポートにのみ有効です。書き出されたデータはバッファに 貯められますが、改行文字が書かれたらフラッシュされます。 このモードは対話的な出力ポートなどに便利です。 プロセスの標準出力ポートはこのモードでオープンされています。 (これは、Cのstdioライブラリの「ラインバッファリング」とちょっと違うことに 注意してください。stdioでは同じファイルディスクリプタから入力が行われる時も バッファはフラッシュされますが、Gaucheではそうはなりません)。

:modest

このモードは入力ポートにのみ有効です。ほとんど:fullバッファリングモードと 同じですが、read-uvectorはポートに要求されたデータより少ないデータしか 無かった場合、要求された量がたまるまで待つのではなく、今あるデータだけを 返します(:fullの場合はread-uvectorはすべてのデータが到着するまで 待ちます)。このモードはポートがパイプやネットワークに接続されている場合に 便利です。

:element-type

この引数はファイルのタイプを指定します。

:character

ファイルはキャラクタモード(テキストモード)でオープンされます。

:binary

ファイルはバイナリモードでオープンされます。

現在のバージョンでは、この引数は無視され、全てのファイルはバイナリモードで オープンされます。いずれにせよUnixプラットフォームでは違いはありません。

:encoding

この引数はファイルの文字エンコーディングを指定します。引数は文字列かシンボルで、 文字エンコーディングスキーム(CES)の名前を渡します。

open-input-fileでは、ここにワイルドカードCES (例: *jp) を 渡して、入力ファイルのエンコーディングを推測させることもできます (文字エンコーディングの自動判定参照)。 また、ここに#tを渡すと、入力ポートはCoding aware portでラップされます (コーディング認識ポート参照)。入力ファイルがエンコーディング指定の マジックコメントを持っていることが期待できる場合に便利です。

この引数が与えられた場合、Gaucheは自動的にgauche.charconvモジュールを ロードし、ポートの入出力時に文字コード変換を行います。 CESについて詳しくはサポートされる文字エンコーディングを参照してください。

:conversion-buffer-size

この引数は、文字エンコーディング変換に使うバッファサイズを指定するために encoding引数と共に使うことができます。渡された値はそのまま 文字コード変換ポートのコンストラクタのbuffer-size引数に渡されます (変換ポート参照)。

この引数を指定する必要は滅多にありませんが、入力ファイルの文字エンコーディングを 推測しなければならない場合、大きめのバッファサイズの方が精度が上がります。 推測ルーチンがより多くのデータを見て文字エンコーディングを決定できるからです。

if-existsif-does-not-existフラグの組合せにより、 色々な動作を実現できます。

 
(open-output-file "foo" :if-exists :error)
 ⇒ ;"foo"を排他的にオープンするかエラーを報告する

(open-output-file "foo" :if-exists #f)
 ⇒ ;"foo"を排他的にオープンするか#fを返す

(open-output-file "foo" :if-exists :append
                        :if-does-not-exist :error)
 ⇒ ;"foo"が既に存在する場合に限り、それを追加モードでオープン

ファイルをオープンせずにその存在をチェックするには、 sys-accessfile-exists?を使って下さい (ファイルの状態参照)。

移植性に関する註:Schemeシステムによっては、filenameのところに シェルコマンドを指定して、サブプロセスの標準入出力と通信できるようにするものが あります。他のスクリプティング言語(例:Perl)にも同様の機能があります。 Gaucheでは、open-input-fileopen-output-fileは あくまでファイル (OSがファイルとして扱うもの) のみに対して使えます。 サブプロセスと通信するためには、「プロセスポート」という機能が提供されています。 Process portsを参照して下さい。

Function: call-with-input-file string proc :key if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: call-with-output-file string proc :key if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R7RS+] stringで示されるファイルを入力または出力用にオープンし、 作成されたポートを引数として手続きprocを呼び出します。 procが正常終了するか、proc内で捕捉されないエラーが起きた場合に ファイルはクローズされます。

キーワード引数は open-input-file及びopen-output-fileのものと同じ意味を持ちます。 if-existsif-does-not-exist#fを指定した場合、 ファイルがオープンされなかった場合はprocにポートではなく#fが渡される ことに注意して下さい。

procが返す値を返します。

Function: with-input-from-file string thunk :key if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: with-output-to-file string thunk :key if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R7RS] stringで示されるファイルを入力または出力用にオープンし、オープンされた ポートを現在の入力または出力ポートに設定して、thunkを呼び出します。 thunkが戻るか、thunk内で捕捉されないエラーが生じた際にファイルは閉じられます。

thunkが返す値を返します。

キーワード引数は open-input-file及びopen-output-fileのものと同じ意味を持ちます。 但しif-existsif-does-not-exist#fが指定され、 ファイルがオープンできなかった場合は、thunkは呼ばれずに 直ちに#fが返されます。

ポートを閉じるセマンティクスについて: R7RSはcall-with-port等の説明において、次のように述べています。 「procが戻って来なかった場合、今後ポートが読み書きに一切使われないことが 証明できない限りは、ポートは自動的には閉じられない」。

Gaucheの実装は若干この条件には反しています。捕捉されないエラーがprocから 発せられたというだけでは、そのポートが今後一切使われないかどうかはわかりません。 しかし実際には、そのようなエラーが発せられた後でポートに対して意味のある操作をするのは 非現実的です。ポートがどのような状態にあるかわからないわけですから。 現実的なプログラムでは、ポートに対して意味のある操作をしつづけたいのなら、 procの中で明示的にエラーをハンドルすべきでしょう。

call-with-input-fileの外で捕捉された継続をproc内で呼んだ場合には ポートは閉じられないことに注意して下さい。後でprocへと制御が戻ってくるかも しれないからです (コルーチン等)。また、 低レベルの例外メカニズム(例外の処理 参照)を利用した場合、エラー時にポートを閉じるのはプログラマの責任になります。

Function: open-input-fd-port fd :key buffering name owner?
Function: open-output-fd-port fd :key buffering name owner?

与えられたファイルディスクリプタにアクセスする入力または出力ポートを 作成して返します。bufferingopen-input-file の項で 説明されたポートのバッファリングモードを指定します。デフォルトは:fullです。 nameport-nameによって返されるポートの名前を指定します。 owner? は、このポートを閉じた時にfdもクローズすべきかどうかを 指定するブーリアン値です。

Function: port-fd-dup! toport fromport

システムのdup2(2)のインタフェースです。 アトミックにtoportのファイルディスクリプタをクローズし、fromportの ファイルディスクリプタを複製したものをtoportに設定します。 toportfromportはいずれもファイルポートでなければなりません。

ファイルディスクリプタが「複製」されると、ふたつのディスクリプタ番号が異なっていても それらはシステムのオープンファイルテーブルの同じエントリを指します。 例えば、現在の(システムレベルでの)ファイル上の読み書き位置は共有されます。 port-fd-dup!の後で、port-seekfromportに 対して呼び出せば、その変更はtoportの読み書き位置にも影響を与えるでしょうし、 その逆もまたあります。ただし、共有されるのはシステムレベルの情報のみで、 toportfromportがバッファリングされている場合、バッファの内容は 共有されません。

この手続きは、主にファイルディスクリプタを明示的に制御する必要のあるプログラム のために用意されています。例えばデーモンプロセスがその入出力を‘/dev/null’などの 無難なデバイスに切り替えたり、シェルプロセスが子プロセスをexecする前に そのファイルディスクリプタをセットアップしたりするような場合です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.5 文字列ポート

文字列ポートは、メモリ上のデータと関連付けられたポートです。

Function: open-input-string string

[R7RS][SRFI-6] stringを内容とする入力文字列ポートを作って返します。 文字列に逐次的にアクセスする場合、インデックスをインクリメントしながら string-refを呼び出すより効率の良い方法です。

 
(define p (open-input-string "文字 列"))
(read p) ⇒ 文字
(read-char p) ⇒ #\space
(read-char p) ⇒ #\列
(read-char p) ⇒ #<eof>
(read-char p) ⇒ #<eof>
Function: get-remaining-input-string port

portは入力文字列ポートでなければなりません。 入力ポートに残っている文字列を返します。 portの内部ポインタは動かされないので、portに対するreadは 影響を受けません。portが既にEOFに達していた場合は、空文字列が返されます。

 
(define p (open-input-string "abc\ndef"))
(read-line p)                  ⇒ "abc"
(get-remaining-input-string p) ⇒ "def"
(read-char p)                  ⇒ #\d
(read-line p)                  ⇒ "ef"
(get-remaining-input-string p) ⇒ ""
Function: open-output-string

[R7RS][SRFI-6] 出力文字列ポートを作成して返します。このポートに書き出された文字列は 内部のバッファにたくわえられ、get-output-string で取り出すことが できます。 これは、順番に文字列を構成する方法として、あらかじめ文字列をアロケートして string-set!で埋めて行くよりもずっと効率の良い方法です。

Function: get-output-string port

[R7RS][SRFI-6] 出力文字列ポートportを取り、それまでそのポートに蓄積された 文字列を返します。バイトデータがそのポートに書き出されていた場合、 この手続きはまず内部バッファをスキャンし、結果が完全な文字列で表現できるかどうかを 調べます。もし表現できなければ、不完全な文字列が返されます。

これはportの操作には影響をあたえません。get-ouptut-stringを 呼んだ後でも、portに内容を蓄積しつづけることができます。

Function: call-with-input-string string proc
Function: call-with-output-string proc
Function: with-input-from-string string thunk
Function: with-output-to-string thunk

これらのユーティリティ関数は次に定義されるような動作をします。 インタフェースはファイルポートを扱う類似の関数と揃えてあります。

 
(define (call-with-output-string proc)
  (let ((out (open-output-string)))
    (proc out)
    (get-output-string out)))

(define (call-with-input-string str proc)
  (let ((in (open-input-string str)))
    (proc in)))

(define (with-output-to-string thunk)
  (let ((out (open-output-string)))
    (with-output-to-port out thunk)
    (get-output-string out)))

(define (with-input-from-string str thunk)
  (with-input-from-port (open-input-string str) thunk))
Function: call-with-string-io str proc
Function: with-string-io str thunk
 
(define (call-with-string-io str proc)
  (let ((out (open-output-string))
        (in  (open-input-string str)))
    (proc in out)
    (get-output-string out)))

(define (with-string-io str thunk)
  (with-output-to-string
    (lambda ()
      (with-input-from-string str
        thunk))))
Function: write-to-string obj :optional writer
Function: read-from-string string :optional start end

文字列ポートを使う定型句をユーティリティ関数にしました。

 
(write-to-string obj writer)
  ≡
  (with-output-to-string (lambda () (writer obj)))

(read-from-string string)
  ≡
  (with-input-from-string string read)

writerの既定値はwriteです。start, endは 省略されればそれぞれ0と文字列の長さになります。

移植性への註:Common Lispに同名の関数があります。必須引数の動作は同じですが、 省略可能な引数は異なります。 STkにはread-from-stringがありますが、省略可能な引数は取りません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.6 コーディング認識ポート

コーディング認識ポートは特殊な手続的入力ポートで、loadが プログラムソースコードを読む際に使われています。このポートは ;; -*- coding: utf-8 -*-のような、プログラムソースの 文字エンコーディングを指定する特殊なコメントを認識し、適切な 文字エンコーディング変換を行います。 特殊なコメントでソースの文字エンコーディングを指定することについては、 マルチバイトスクリプトを参照して下さい。

Function: open-coding-aware-port iport

入力ポートを引数としてとりコーディング認識入力ポートを返します。 基本的には iport からの入力データをリーダにわたしているだけです。 しかし、iport からの入力データの最初の2行以内に、特別な呪文コメント が現れた場合、コーディング認識ポートは、その後に読み込まれるデータについて 必要な文字エンコーディング変換を行います。

引数として渡されたポート、iport は生成されたコーディング認識 ポートによって所有されます。つまり、コーディング認識ポートがクローズ されると、iport もクローズされます。iport から読み込まれた 内容はコーディング認識ポート内でバッファリングさます、したがって、 別のコードで iport から読み出しを行うべきではありません。

デフォルトでは、Gauche の load はプログラムソースを読むのに コーディング認識ポートを使います。したがって、文字エンコーディングを 示す特別な呪文コメントは、Gauche のソースプログラムでは有効になります (Schemeファイルのロード参照)。ただし、この機構自身は load とは 独立しており、このポートを別の目的で利用できます。特にコーディングの 呪文コメントがある Scheme のソースプログラムを処理する関数を書くときに 便利です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.7 入力

入力に関する手続きで、省略可能な引数iportは入力ポートでなければなりません。 省略された場合が現在の入力ポートが使われます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.7.1 データの読み込み

Function: read :optional iport

[R7RS] iportからS式をひとつ読み込んで返します。 GaucheはR7RSに定義されている構文要素に加え、字句構造に 定義されている拡張構文要素を認識します。

iportが既にEOFに達していた場合は、EOFオブジェクトが返されます。

この手続きはS式を構成する最後の文字までを読み、その後の文字はポートに 残します。これは、S式に続く空白文字も読み込むCommonLispのreadの 振る舞いとは異なります。

Function: read-with-shared-structure :optional iport
Function: read/ss :optional iport

[SRFI-38] これらの手続きは、SRFI-38 で定義されていて、共有構造を表す記法 (#n=, #n#)を認識できます。Gauche の組み込み read は この SRFI-38 の記法を認識します。それゆえ、これらの手続きは、read と同じで、SRFI-38 との互換性のために用意されています。

Function: read-char :optional iport

[R7RS] iportから1文字読み込んで返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。 iportにあるバイトストリームが正しい文字を構成しない場合、 ふるまいは未定義です。(将来はポート側に、不正な文字に対する対応を決める オプションを設ける予定です)。

Function: peek-char :optional iport

[R7RS] iportから1文字読み込んで返します。文字はそのままiportに留まります。 iportが既にEOFに達していた場合はeofオブジェクトを返します。 iportにあるバイトストリームが正しい文字を構成しない場合、 ふるまいは未定義です。(将来はポート側に、不正な文字に対する対応を決める オプションを設ける予定です)。

Function: read-byte :optional iport

入力ポートiportから1バイト読み込み、0から255までの整数値として返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

R7RSではread-u8と呼ばれています。

Function: peek-byte :optional iport

入力ポートiportの先頭の1バイトを見て、それを0から255までの整数値として返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

R7RSではpeek-u8と呼ばれています。

Function: read-line :optional iport allow-byte-string?

[R7RS+] 入力ポートから、行末もしくはEOFまで読み込んで文字列として返します。 よく使われる行末 (LF only, CRLF, and CR only) を認識します。 戻り値にはこれらの行末文字は含まれません。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

iportから、内部文字エンコーディングでは文字を構成し得ないバイトシーケンスが 読まれた場合、デフォルトではread-lineはエラーを通知します。 しかし、省略可能な引数allow-byte-string?に真の値が与えられた場合は、 read-lineはエラーを通知せず、かわりにバイト文字列 (不完全な文字列) を 返します。この動作は、特に文字エンコーディングが不明なソースから読み込む際に 便利です。例えばXMLドキュメントを読み込む際、最初の行のcharsetパラメータを チェックしてから適切な文字エンコーディング変換ポートを使うといった用途などです。 この動作はGauche独自の拡張です。

Function: read-string nchars :optional iport

[R7RS] nchars文字を読み込み、もしくはそれ以前にEOFに達したら読めただけの文字を 使って文字列を作って返します。もし入力が既にEOFに達していた場合は EOFを返します。

Function: read-block nbytes :optional iport

この手続きは非推奨になりました。read-uvectorを使ってください (ユニフォームベクタのブロック入出力参照)。

nbytesバイトのデータをiportから読み込み、 不完全な文字列として返します。iportに十分なデータが無い場合、 返される文字列はnbytesより短いかもしれません。 nbytesが0の場合は、常に空文字列が返されます。

iportが既にEOFに達していた場合はEOFオブジェクトが返されます。

iportがファイルポートだった場合、read-blockは ポートのバッファリングモードによってふるまいが異なります (バッファリングモードの詳細についてはファイルポートを参照して下さい)。

データブロックをポートに書き出すには、データが文字列で表現されている 場合は単純にdisplayが使えます。データがuniform vectorで表現されている 場合はgauche.uvectorモジュールのwrite-uvectorが 使えます (ユニフォームベクタのブロック入出力参照)。

Function: eof-object

[R7RS] EOFオブジェクトを返します。

Function: eof-object? obj

[R7RS] objがEOFオブジェクトなら#tを返します。

Function: char-ready? :optional port

[R7RS] portから文字が読み出せる状態ならば#tを返します。

今のところ、この手続きはportから少なくとも1バイト読み出せる状態なら#t を返します。そのバイトがマルチバイト文字を構成する場合、char-ready?を返した ポートから文字全てを読み込もうとすると、ブロックする可能性があります。 (通常の使用状況ではそのようなことは起きないでしょうが、理論的には起こり得ます。 慎重を期したい場合はread-uvectorでバイトシーケンスとして読み込んだ後、 入力文字列ポート等を使って文字毎に読むようにして下さい。)

Function: byte-ready? :optional port

portから1バイトをすぐに読み込める状態なら#tを返します。

R7RSではこの手続きはu8-ready?と呼ばれています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.7.2 リーダー字句モード

Parameter: reader-lexical-mode

現在のリーダ字句モードを表すパラメータです。 このパラメータの値を変えることにより、旧来のGaucheとR7RSで非互換となる コーナーケースの字句構文についての解釈を変更できます。

一般的には、このパラメータを直接変える必要のある場合は少ないでしょう。 字句構文は読み込み時に問題となりますが、パラメータの変更は実行時に起こります。 いつ、何が起きるかを正確に把握しているのでない限り、意図したとおりの効果は 得られないかもしれません。

hash-bang指示子 #!gauche-legacy および #!r7rs は 間接的にこのパラメータに影響を与えます。#!gauche-legacyは リーダモードをlegacyに、#!r7rsstrict-r7に 変更します。

コマンドライン引数の-fwarn-legacyはデフォルトのリーダモードを warn-legacyにセットします。

load中にこのパラメータを変更した場合、その効果はloadの期間中に 留まります。そのloadが終了した時点で、 load開始時のリーダモードが復元されます。

このパラメータは値として次のシンボルのうちいずれかを取ります。

permissive

これがデフォルトのモードです。二つの構文の、ほどほどの妥協点を見つけようとします。

文字列リテラル中の16進数エスケープシーケンスは、まず R7RSの字句構文で解釈されます。もしそれがR7RSの16進数エスケープシーケンスの 構文に沿っていなかった場合は、旧来のGaucheの構文で解釈されます。 例えば、"\x30;a" は、セミコロンがR7RSのエスケープシーケンスの終端と みなされ、"0a" と読まれます。一方で "\x30a""0a" と 読まれます。終端のセミコロンが無く、有効なR7RSの構文でないため、 旧来のGaucheのエスケープシーケンスとして2桁固定で解釈されるからです。 このモードでは、新たなコードでR7RSエスケープシーケンスを利用できるうえ、 ほとんどの既存のGaucheコードはそのまま走ります。 しかし、もし既存のGaucheコードで、たまたま2桁の16進数エスケープの直後に セミコロンがあった場合、それはR7RSとして読まれ、非互換性が生じます。

コロンで始まる識別子は従来通りキーワードとして読まれます。厳密にR7RSと同じ 振る舞いにする、つまりシンボルとして読ませるには、縦棒でエスケープしてください。 (例: |:foo|)。この非互換性は、将来のGaucheでキーワードがシンボルの サブタイプとなった時に解消されます。

strict-r7

厳密にR7RS互換なモード。 リーダが、hash-bang指示子#!r7rsに出会うと、ファイルの残りの部分は このモードで読まれます。

このモードでは、Gaucheの拡張字句構文はエラーとなります。 コロンで始まる識別子はシンボルとして読まれます。

R7RSとの最大限の互換性を得るにはこのモードを使ってください。

legacy

リーダは以前の(0.9.3.3及びそれ以前の)Gaucheと同じように動作します。 リーダがhash-bang指示子#!gauche-legacyに出会うと、ファイルの残りの部分は このモードで読まれます。

このモードが必要になるのは、2桁の16進数エスケープの直後にセミコロンが来ているリテラルを、 文字+セミコロンと読みたい場合だけです。例えば "\x30;a""0a"でなく"0;a"と読みたい、等。 こういうケースはコードではほとんど無いと思いますが、 データを文字列リテラルとしてダンプしてある場合、それも不完全な文字列として ダンプしてある場合に、こういった並びが出現しているかもしれません。

warn-legacy

リーダはpermissiveモードと同様に動作しますが、 旧来の16進数エスケープに出会うと警告を発します。 gosh-fwarn-legacyコマンドライン引数が与えられた場合は これがデフォルトのモードになります。

コードの中に非互換なエスケープシーケンスが無いかどうか調べるのに役立ちます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.7.3 読み込み時コンストラクタ

SRFI-10で定義されている読み込み時コンストラクタは、ユーザ定義の構造の 外部表現を作るための簡単な方法を提供します。

Reader Syntax: #,(tag arg …)

[SRFI-10] Gaucheはtag (シンボル)をコンストラクタ手続きに関連付ける グローバルなテーブルを管理しています。

リーダーがこの構文に出会ったとき、arg …を読み込み、 tagに関連付けられた読み込みコンストラクタを探し、 arg …を引数としてそのコンストラクタを呼び出し、 その構文を読み込んだ結果としてそのコンストラクタが返した値を挿入します。

この構文はリーダー内部で処理されることに注意して下さい。評価器は argを見ず、リーダーが返したオブジェクトしか見ません。

Function: define-reader-ctor tag procedure

[SRFI-10] 読み込みコンストラクタproceduretagに関連付けます。

例:

 
(define-reader-ctor 'pi (lambda () (* (atan 1) 4)))

#,(pi) ⇒ 3.141592653589793

'(#,(pi)) ⇒ (3.141592653589793)

(define-reader-ctor 'hash
  (lambda (type . pairs)
    (let ((tab (make-hash-table type)))
      (for-each (lambda (pair)
                  (hash-table-put! tab (car pair) (cdr pair)))
                pairs)
      tab)))

(define table
 #,(hash eq? (foo . bar) (duh . dah) (bum . bom)))

table ⇒ #<hash-table eq? 0x80f9398>
(hash-table-get table 'duh) ⇒ dah

write-objectメソッド(出力参照)と組み合わせて、 読み戻ることが可能なフォームで書かれたユーザ定義のクラスを 作ることが簡単になります。

 
(define-class <point> ()
  ((x :init-value 0 :init-keyword :x)
   (y :init-value 0 :init-keyword :y)))

(define-method write-object ((p <point>) out)
  (format out "#,(<point> ~s ~s)" (ref p 'x) (ref p 'y)))

(define-reader-ctor '<point>
  (lambda (x y) (make <point> :x x :y y)))

注意: define-reader-ctorの効果の範囲はSRFI-10には 規定されておらず、SRFI-10をサポートする実装においても互換性の問題を 起こすことがあるかもしれません。 (実際に、define-reader-ctorの存在そのものが実装の選択に 任されています。)

Gaucheでは、現時点においては、define-reader-ctorはそのフォームが コンパイルされ評価された時点で効力を持ちます。 Gaucheはトップレベルのフォームを順番にコンパイル・評価するので、 define-reader-ctorで指定されたtagは、その指定の直後から 使えます。 しかし、define-reader-ctorの呼び出しとtagの使用が beginフォームで囲まれている場合は、beginフォーム全体は 評価される前に一度にコンパイルされるため、うまく動作しません。

他の実装では、define-reader-ctorの呼び出しが効力を持つようにする 前にファイル全体を読み込むことを要求するかも知れません。 その場合は、define-reader-ctorと定義されたtagの使用を 同じファイルに置く事は実質的に不可能です。 可能ならば、define-reader-ctorの呼び出しと、tagの使用は 異なるファイルに分離されることが望まれます。

現在のdefine-reader-ctorに関するもう1つの問題は、それが Gaucheシステムのグローバルテーブルを変更してしまうことで、それゆえに モジュール性が良くありません。 複数人によって書かれたコードは同じタグを使っているかも知れず、 期待されない結果を引き起こすかも知れません。 作者にはまだ明確なアイデアがありませんが、将来のバージョンでは、 Gaucheにはtagのスコープをカプセル化する方法が導入されるかも しれません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.7.4 入力ユーティリティ手続き

Function: port->string port
Function: port->list reader port
Function: port->string-list port
Function: port->sexp-list port

便利な入力手続きです。APIはScshとSTkから取りました。

port->stringportをEOFまで読み込み、 読んだものを文字列として返します。

port->listは手続きreaderportに繰り返し適用し、 結果をリストに蓄積します。readerがEOFを返したら 蓄積されたリストを返します。 port自体はクローズされないことに注意してください。

port->string-listport->listread-lineで特定化したもので、 port->sexp-listport->listreadで特定化したものです。

もし入力にGaucheの内部エンコーディングにおいて有効な文字をつくらない バイト列が含まれていた場合、port->stringport->string-list の返り値には不完全な文字列が含まれている可能性があります。 入力をバイナリデータとして扱いたい場合は、 gauche.uvectorport->uvectorを使って下さい (ユニフォームベクタのブロック入出力参照)。

Function: port-fold fn knil reader
Function: port-fold-right fn knil reader
Function: port-for-each fn reader
Function: port-map fn reader

readerによって読まれる入力に対する便利な繰り返し手続きです。

実際にはこれらの手続きはポートからの入力以外にも使えるため、それぞれ generator-fold, generator-fold-right, generator-for-each, generator-mapによって置き換えられました。 詳しくは生成された値の畳み込みを参照してください。

これらの手続きは後方互換性のためだけに提供されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.8 出力


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.8.1 出力ルーチンの階層

Gaucheにはたくさんの出力手続きがあり、慣れないうちはどれを使えばよいか迷ってしまう かもしれません。次の表は様々な出力手続きを分類するものです。

オブジェクトの出力

Schemeオブジェクトを出力する手続きです。 もっと低レベルの手続きもありますが、一般にはこの層が出力ルーチンの基本であると みなされています。Schemeのオブジェクト単位で操作をするからです。 この手続き群はさらに次の二つのグループに分かれます。

高レベル整形出力

出力を指定の桁数に揃えたり、どちらかに寄せたり、といった書式を指定できる手続きが formatです。Cのprintfのようなものです。

低レベル、特定の型用の出力

「生」のデータを扱う手続きです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.8.2 出力制御

Lisp構造の出力形式は、 <write-controls>オブジェクトによって制御することができます。 Lispオブジェクトを出力するルーチン(write, display等)や、 高レベル出力手続き(format等)は出力制御オブジェクトを取ることができます。

以下の例で、出力制御の様子がわかると思います。

 
(write '(1 10 100 1000)
       (make-write-controls :print-base 16 :print-radix #t))
 prints (#x1 #xa #x64 #x3e8)

(write (iota 100)
       (make-write-controls :print-length 5))
 prints (0 1 2 3 4 ...)

make-write-controls手続きは出力制御オブジェクトを作って返します。 出力制御オブジェクトは以下のスロットを持ちます (これらのスロット名はCommon Lispの対応するプリント制御変数から取られました)。

print-length

このスロットが非負整数の場合、それはリストとベクタ(ユニフォームベクタを含む)の 要素が表示される最大数を指定します。もしリストやベクタがその最大数より 多くの要素を持っている場合、残りの要素は表示されず、代わりに ...が表示されます。このスロットが#f(デフォルト)であれば、 すべての要素が表示されます。

print-level

このスロットが非負整数の場合、それは構造体(リストとベクタ)が表示される 最大の深さを指定します。もし構造体がより深いノードを持っている場合、 そのノードの代わりに#が表示されます。 このスロットが#f(デフォルト)であれば、すべての要素が表示されます。

print-base

このスロットは2以上36以下の整数でなければならず、 正確な整数を印字する基数を指定します。デフォルトは10です。

print-radix

このスロットは真偽値を持ちます。真であれば、 正確な整数の前に常に基数プレフィクスが表示されます。 デフォルトは#fです。

出力制御オブジェクトは変更不可です。もし既に存在する制御オブジェクトを 少し変えたものが欲しければ、write-controls-copyが使えます。

Function: make-write-controls :key print-length print-level print-base print-radix

出力制御オブジェクトを返します。

Function: write-controls-copy controls :key print-length print-level print-base print-radix

出力制御オブジェクトcontrolsのコピーを返します。もしキーワード引数が 与えられれば、その要素が置き換えられます。

高レベル出力手続きはwrite-objectメソッドを経由して再帰的に 呼ばれる可能性があります。その場合、大元の出力手続き呼び出しに渡された 出力制御オブジェクトが、同じポートに対して出力する再帰的に呼ばれる 出力手続きへと自動的に引き継がれます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.8.3 オブジェクトの出力

以下の手続きで、省略可能な引数portは出力ポートでなければなりません。 省略された場合が現在の出力ポートが使われます。

いくつかの手続きはport/controls引数を取ります。これは、出力ポート もしくは<write-controls>オブジェクトでなければなりません。例えば writeは二つまでそういった引数を取ります。すなわち、writeは 以下のいずれの形式でも呼び出すことができます: (write obj)(write obj port)(write obj contorls)(write obj port controls)(write obj controls port)。 省略された場合、ポートは現在の出力ポート、出力制御についてはデフォルトの出力制御が 使われます。

Function: write obj :optional port/controls1 port/controls2
Function: write-shared obj :optional port/controls1 port/controls2
Function: write-simple obj :optional port/controls1 port/controls2

[R7RS+] write族の手続きは、Schemeオブジェクトの外部表現を出力します。 外部表現は一般に、read手続きによって読み戻せば、 元のオブジェクトと同等のオブジェクトとなります。 この3つの手続きの違いは、共有構造および循環構造の扱いにあります。

writeは循環構造に対して安全です。データに循環がある場合は データラベル記法(#n=#n#)を用いてそれを表現します。 循環の無い、単に共有されているだけの構造についてはデータラベル記法を使いません (二番目の例参照)。

 
(let1 x (list 1)
  (set-cdr! x x)   ; create a cycle
  (write x))
 ⇒ shows #0=(1 . #0#)

(let1 x (list 1)
  (write (list x x)))
 ⇒ shows ((1) (1))

write-sharedも循環構造に対して安全で、さらに共有構造も データラベル記法で表示します。グラフ構造のトポロジーを維持する必要がある 場合はこの手続きを使ってください。

 
(let1 x (list 1)
  (write (list x x)))
 ⇒ shows (#0=(1) #0#)

最後に、write-simpleはオブジェクトを、共有構造や循環構造を考慮せずに 再帰的に表示します。これは出力前に共有構造の検出パスを走らせる必要がないので 高速です。しかし、渡されたデータに循環があった場合は出力が止まらなくなります。

これらの手続きが表示中にユーザ定義クラスのオブジェクトに 出会った場合は、ジェネリックファンクションwrite-objectを呼び出します。

歴史的経緯: writeは以前のScheme標準にもありましたが、 循環構造があった場合の振る舞いはR7RSになるまで規定されていませんでした。 実際、Gauche 0.9.4より前のwriteは、循環構造を渡されると停止しません。 SRFI-38はデータラベル記法および、それを表示するための write-with-shared-structurewrite/ss手続きを導入し、 Gaucheもそれをサポートしていました。 R7RSでこの問題は明確にされ、Gaucheも0.9.4からそれに沿っています。

Function: write-with-shared-structure obj :optional port
Function: write/ss obj :optional port
Function: write* obj :optional port

[SRFI-38] これらは上記のwrite-sharedの別名です。

Gaucheは、STklosから取ったwrite*という名前を長く使ってきましたが、 srfi-38によってwrite-with-shared-structurewrite/ssが 定義されました。これらの名前は互換性のためだけに残してあります。 新たなコードにはwrite-sharedを使ってください。

Function: display obj :optional port/controls1 port/controls2

[R7RS] オブジェクトobjの人に読みやすい表現を、出力ポートに書き出します。

objが循環構造を含んでいる場合は、データラベル記法を使って出力します。

displayが表示中にユーザ定義クラスのオブジェクトに 出会った場合は、ジェネリックファンクションwrite-objectを呼び出します。

 
(display "\"Mahalo\", he said.")
 ⇒ shows "Mahalo", he said.

(let ((x (list "imua")))
  (set-cdr! x x)
  (display x))
 ⇒ shows #0=(imua . #0#)
Function: print expr …

expr … をdisplayを使って現在の出力ポートに表示し、 最後に改行を書き出します。

Method: write-object (obj <object>) port

このメソッドをつかって、オブジェクトをどのように印字するかをカスタマイズ できます。

Function: newline :optional port

[R7RS] portに改行文字を書き出します。(write-char #\newline port)(display "\n" port)と書いても同じことですが、歴史的な理由から 残されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.8.4 フォーマット出力

Function: format dest controls string arg …
Function: format controls dest string arg …
Function: format dest string arg …
Function: format controls string arg …
Function: format string arg …

[SRFI-28+] string の指示に従い、arg …をフォーマットします。 この手続きはCommonLispのformatのサブセットに、Gauche独自の拡張を 加えたものです。また、これはSRFI-28 "Basic format strings" のスーパーセットに なっています (SRFI-28)。

dest引数は出力先を指定します。それが出力ポートであれば、フォーマットされた 結果はそのポートに書き出されます。port#tであれば、結果は 現在の出力ポートに書き出されます。port#fであれば、結果は 文字列としてformatから返されます。 destは省略することもできます。その場合は、dest#f を指定したのと同じ動作をします(SRFI-28のformat)。

controls引数は<write-controls>オブジェクトで、 ~s~aの出力に影響を与えます (出力制御参照)。これはGauche独自の拡張です。

(formatの関数シグネチャはちょっと変わっていますが、 これは便利さを優先したためです。portcontrolsはどちらも省略可能で、 またどの順序でも指定できます。)

stringはフォーマット指示子を含んだ文字列です。 フォーマット指示子はチルダ‘~’から始まり、特定の文字で終了する文字の並びで、 それぞれのフォーマット指示子が対応するargを取りフォーマットします。 string内のフォーマット指示子以外の文字列はそのまま出力されます。

 
(format #f "the answer is ~s" 42)
  ⇒ "the answer is 42"

フォーマット指示子は一つ以上のコンマで区切られたパラメータを取ることもできます。 パラメータは整数か文字です。文字の場合、クオート文字に続けてその文字を置きます。 パラメータが省略された場合は既定値が使われます。パラメータの意味はフォーマット指示子毎に 異なります。

さらに、フォーマット指示子は2種類のフラグ、‘@’ と ‘:’ を 取ることができます。これらの組合せでフォーマットの動作が変わります。フラグは (もしあれば)パラメータの後、指示子の文字の直前に置かれなければなりません。

パラメータの位置に文字 ‘v’ か ‘V’ を置くこともできます。 その場合、パラメータの値が引数リストから取られます。対応する引数は整数か 文字、または#fでなければなりません。#fの場合はそのパラメータが 省略されたのと同じになります。

いくつかの例です。

~10,2s

パラメータ10と2を伴う、フォーマット指示子~s

~12,,,'*A

第1パラメータに数値12、第4パラメータに文字‘*’を取るフォーマット指示子~a。 第2と第3のパラメータは省略されています。

~10@d

フォーマット指示子~d。パラメータ10と‘@’フラグがついています。

~v,vx

フォーマット指示子~x。第1パラメータと第2パラメータは引数リストから取られます。

以下にサポートされているフォーマット指示子を示します。フォーマット指示子の文字自体は 大文字であっても小文字であっても構いません。特に断りのない限り両者は同じ動作をします。

~mincol,colinc,minpad,padchar,maxcolA

ASCII出力。対応する引数がdisplayを使ってフォーマットされます。 整数がmincolに与えられた場合、それは出力される最小の文字数を指定します。 引数のフォーマット結果がmincolより短ければ、空白が右に追加されます(つまり、 左詰めになります)。

colincminpad、そしてpadcharは更に細かいパディング方法を 指定します。padcharに文字が与えられた場合、それが空白文字の代わりにパディング文字と して使われます。minpadに0以上の整数が与えられた場合、少なくともその数だけの パディング文字が追加されます。colincが指定された場合、 追加されるパディング文字の数がcolincの倍数に調整されます。

アトマーク ‘@’ フラグが与えられた場合、結果は右詰めになります。

maxcolパラメータは与えられていれば書かれる文字数の上限を指定します。 フォーマット後の文字列の長さがmaxcolを超えた場合、maxcol文字だけが 書かれます。コロン ‘:’ フラグが同時に与えられていれば、 maxcol - 4 文字が書かれた後、文字列“ ...”が書かれます。

 
(format #f "|~a|" "oops")
  ⇒ "|oops|"
(format #f "|~10a|" "oops")
  ⇒ "|oops      |"
(format #f "|~10@a|" "oops")
  ⇒ "|      oops|"
(format #f "|~10,,,'*@a|" "oops")
  ⇒ "|******oops|"
(format #f "|~10,,,'☆a|" "oops")
  ⇒ "|oops☆☆☆☆☆☆|"

(format #f "|~,,,,10a|" '(abc def ghi jkl))
  ⇒ "|(abc def gh|"
(format #f "|~,,,,10:a|" '(abc def ghi jkl))
  ⇒ "|(abc de ...|"
~mincol,colinc,minpad,padchar,maxcolS

S式出力。対応する引数がwriteを使ってフォーマットされます。 パラメータの意味は~A指示子と同じです。

 
(format #f "|~s|" "oops")
  ⇒ "|\"oops\"|"
(format #f "|~10s|" "oops")
  ⇒ "|\"oops\"    |"
(format #f "|~10@s|" "oops")
  ⇒ "|    \"oops\"|"
(format #f "|~10,,,'*@s|" "oops")
  ⇒ "|****\"oops\"|"
(format #f "|~10,,,'★s|" "oops")
  ⇒ "|\"oops\"★★★★|"
~mincol,padchar,commachar,intervalD

10進出力。対応する引数が10進数表記でフォーマットされます。もし引数が数値でなければ、 全てのパラメータは(‘v’パラメータの処理後に)無視され、 引数は~Aでフォーマットされます。

もしmincolに整数が与えられたら、それが最小の文字数を指定します。 結果の文字数がそれより少なければ、文字padcharが左に追加されます(右詰めになります)。 padcharが省略された場合は空白文字が使われます。

 
(format #f "|~d|" 12345)
  ⇒ "|12345|"
(format #f "|~10d|" 12345)
  ⇒ "|     12345|"
(format #f "|~10,'0d|" 12345)
  ⇒ "|0000012345|"

アトマーク ‘@’ フラグが与えられた場合、正の引数に対して ‘+’ が 先頭につけられます。

コロンフラグ ‘:’ が与えられた場合、結果の文字はinterval文字毎に まとめられ、間に文字commacharが挿入されます。デフォルトでは3文字毎にコンマが 挿入されます。

 
(format #f "|~:d|" 12345)
  ⇒ "|12,345|"
(format #f "|~,,'_,4:d|" -12345678)
  ⇒ "|-1234_5678|"
~mincol,padchar,commachar,intervalB

2進出力。対応する引数が2進数の整数としてフォーマットされます。 パラメータの意味は~Dと同じです。

~mincol,padchar,commachar,intervalO

8進出力。対応する引数が8進数の整数としてフォーマットされます。 パラメータの意味は~Dと同じです。

~mincol,padchar,commachar,intervalX
~mincol,padchar,commachar,intervalx

16進出力。対応する引数が16進数の整数としてフォーマットされます。 フォーマット指示文字に ‘X’ が与えられた場合は ‘ABCDEF’ が桁文字として 使われ、 ‘x’ が与えられた場合は ‘abcdef’ が桁文字として使われます。 パラメータの意味は~Dと同じです。

 
(format #f "~8,'0x" 259847592)
  ⇒ "0f7cf5a8"
(format #f "~8,'0X" 259847592)
  ⇒ "0F7CF5A8"
~count*

引数のカウンタをcountだけ後方にずらします。つまり、count個の引数が 無視されることになります。countのデフォルト値は1です。 コロンフラグが与えられた場合は引数カウンタを前方に動かします。 例えば~:*は次のディレクティブが直前に使った引数を再び使うようにします。 アトマークフラグが与えられた場合は、countが引数の絶対位置を示します。 0が最初の引数です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.22.8.5 低レベル出力

Function: write-char char :optional port

[R7RS] 文字charをポートに出力します。

Function: write-byte byte :optional port

出力ポートに1バイトのデータbyteを書き出します。 byteは0から255の間の正確な整数でなければなりません。

この手続きはR7RSではwrite-u8と呼ばれています。

Function: flush :optional port
Function: flush-all-ports

それぞれ、port、および全てのポートにバッファされているデータを 全て書き出します。

手続き"flush"はScheme実装によって様々な名前で呼ばれています: force-output (Scsh, SCM)、 flush-output (Gambit)、flush-output-port (Bigloo) 等。 flushの名前はSTkとSTklosから取りました。 R7RSではflush-output-portを採用しています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.23 プログラムのロード


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.23.1 Schemeファイルのロード

Function: load file :key paths (error-if-not-found #t) environment ignore-coding

[R7RS+] fileをロードします。すなわち、fileに書かれたScheme式を順次読み込んで 評価します。fileの拡張子 (“.scm”) は省略できます。

fileが “/”, “./” または “../” で始まっていない場合は、 システムファイルサーチパス (変数*load-path* に格納されています) の中から該当ファイルが探されます。あるいは、キーワード引数pathsにディレクトリ名の リストを渡すことによりサーチパスを指定することもできます。

ロードに成功した場合は#tが返されます。 指定ファイルがみつからない場合はエラーとなりますが、もしキーワード引数 error-if-not-found#fが与えられていれば単にloadから #fが返されます。

デフォルトでは、loadはコーディング認識ポート(コーディング認識ポート参照)を 使ってソースファイルを読み込むので、ファイル先頭の"coding:" コメントによる 文字エンコーディング指定が有効になります。 (codingコメントについてはマルチバイトスクリプトを参照)。 但し、キーワード引数ignore-codingに真の値が渡された場合、 loadはcoding-aware portを作成せず、直接ファイルポートから ソースを読み込みます。

キーワード引数environmentにモジュールが渡された場合は、 あたかもそのモジュールがファイルの先頭でselectされたかのように loadされます。

カレントモジュールは保存されます。つまり、file中でselect-moduleによって カレントモジュールを変更しても、loadが終わったらloadを読んだ時点の モジュールに戻ります。

GaucheのloadはR5RSのloadの上位互換ですが、 R7RSのloadは省略可能引数が異なります。scheme.load - R7RS load参照。

ライブラリファイルをロードする場合は、‘use’ (モジュールの定義と選択参照) を 使うか、下に説明する‘require’ を使う方が良いでしょう。 loadrequireの違いについてはコンパイルを参照してください。

Variable: *load-path*

loadrequireがファイルを探すディレクトリのリストを保持しています。

もしサーチパスにディレクトリを追加したい場合は、この変数を直接変更せずに、下に説明する add-load-pathを用いて下さい。

Special Form: add-load-path path flag …

パスpathをライブラリロードパスのリストに加えます。 pathが相対パスの場合、:relativeフラグが与えられて いなければ、プロセスの現在のワーキングディレクトリからの相対と解釈されます。

以下の値がflag引数として認識されます。

:after

pathを現在のロードパスリストの末尾に加えます。 デフォルトでは、pathはロードパスリストの先頭に加えられます。

#t

:afterと同じです。互換性のために認識されます。

:relative

pathを、(カレントワーキングディレクトリではなく) 現在ロード中のファイルのあるディレクトリからの相対パスとして 解釈します。現在ロード中のファイルが不明な場合 (REPLから 評価されたり、ソケットから読まれたりしている場合) は、このフラグは無視されます。

ロードパスを変更したい場合、*load-path*を直接替えずにこのフォームを 使って下さい。このフォームはコンパイル時に解釈されるのに対し、*load-path*を 書き換えるコードは実行時に解釈されます。“use” や “require” は コンパイル時のロードパスを使うので、*load-path*への変更は反映されないかもしれません。

更に、add-load-pathpathの下にアーキテクチャ依存のディレクトリが ないかどうかを探し、あればそれを内部の共有ライブラリサーチパスに追加します。 例えばあなたが自分のSchemeモジュールを/home/yours/libに入れていて、 それが共有ライブラリを必要としていたとします。手続きgauche-architecture (環境の問い合わせ参照)が返す値をARCHとして、 共有ライブラリを/home/yours/lib/ARCH/に置いておくと、 共有ライブラリはそこからロードされます。この方法を取ると、複数のプラットフォーム用に 別々にコンパイルされた共有ライブラリを管理することができます。

Function: load-from-port port

入力ポートportから、EOFを読むまで繰り返しScheme式を読み込み評価します。

portにコーディング認識ポートを渡さない限り、"coding:"コメント による文字コード変換は行われないことに注意して下さい。

Function: current-load-port
Function: current-load-path
Function: current-load-history
Function: current-load-next

これらの手続きによって、現在のロードのコンテクストを知ることができます。 ロードされているファイルの中でこれらの手続きを呼ぶと、次のような値が返されます。

current-load-port

現在のフォームがロードされている入力ポート。

current-load-path

現在のフォームがロードされているファイル。 ロードのソースがファイルでない場合、この値は#f

current-load-history

入力ポートと行番号のペアのリストで、ロードのネスティングを示したもの。 例えばあなたが‘foo.scm’をロードし、そのファイルの7行目で ‘bar.scm’がロードされ、そのファイルの18行目で‘baz.scm’が ロードされたとします。current-load-historyを‘baz.scm’中で 呼ぶと、それは次のような値を返します。

 
((#<port "foo.scm"> . 7) (#<port "bar.scm"> . 18))
current-load-next

現在のファイルがロードされた時点での、ファイルサーチパスの残りを返します。 例えば *load-path*("." "../lib" "/home/gauche/lib" "/share/gauche/lib")で、 あなたが‘foo.scm’をロードしたところ、それが‘../lib/’中に見つかったと しましょう。このとき、‘foo.scm’中でcurrent-load-nextを呼べば

 
("/home/gauche/lib" "/share/gauche/lib")

が返されます。

loadされていない状態で呼ばれた時は、これらの手続きはそれぞれ#f#f()()を返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.23.2 ダイナミックライブラリのロード

Function: dynamic-load file :key init-function

ダイナミックローダブルライブラリ(共有ライブラリ)fileをロードしてリンクします。 fileにはサフィックス(“.so” 等) を含めないで下さい。システムによって サフィックスは異なるため、dynamic-loadがそれを追加します。

キーワード引数init-functionは共有ライブラリ中の初期化関数の名前を 指定します。デフォルトでは、サフィックスを除くファイル名が “foo” の場合、 初期化関数名は “Scm_Init_foo” となります。

通常、共有ライブラリはSchemeモジュール中でロードされるので、モジュールユーザが 直接この手続きを呼ぶ必要はほとんどないでしょう。

一度ロードされた共有ライブラリをアンロードすることはできません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.23.3 requireとprovide

requireprovideは、Lispでライブラリファイルを一度だけ 読み込むことを保証するための伝統的な方法です。 あるfeatureを最初にrequireすると、その機能を提供する ライブラリファイルがロードされ、その機能が提供されたということが記憶されます。 2回め以降のそのfeatureのリクエストではファイルはロードされません。

Gaucheではuse構文 (モジュールの使用参照) が requireのメカニズムをボンネットの下に隠してくれるので、 これらの式を直接目にする必要はほとんどありません。 もしあなたがちょっと変わった方式でライブラリを構成したくて、したがって Gaucheの標準的なメカニズムをバイパスしたい場合に限り、使うようにしてください。

Special Form: require feature

featureがまだロードされていなければロードします。featureは文字列で なければなりません。それがそのまま(サフィックスを除く)ファイル名としてロードパスから 探されます。requireの解釈はコンパイル時に行われます。

SLIBモジュールをロードすると、requireが拡張されます。詳しくはslib - SLIBインタフェースを 参照して下さい。

もしロードされたファイルがprovideフォームを含んでいなかった場合、 あたかもファイルの最後に(provide feature)があったかのように、 featureが自動的にprovideされます。これを autoprovide機能と呼んでいます。

requireは、ファイルをロードする前に現在のモジュールを gauche.require-baseという変更不可なモジュールにセットします。 requireされるファイルは通常、最初にdefine-module/select-moduledefine-libraryフォームを持つので、このgauche.require-baseモジュール を目にすることはほとんど無いでしょう。 ただ、もしロードされたファイルがモジュールを指定すること無くトップレベルの 変数を定義したり他のモジュールをimport(use)しようとした場合、 次のようなエラーとなります。

 
*** ERROR: Attempted to create a binding (a) in a sealed
module: #<module gauche.require-base>

理由: requireされているファイルがどのタイミングで読まれるかを 正確にコントロールするのは難しいです(他のモジュールが既にrequireしているかも しれないので)。もし呼び出し側の現在のモジュールをそのまま使った場合、二つの 問題が生じ得ます。(1)呼び出し側の現在のモジュールから、 define-moduledefine-libraryが見えているとは限りません。 (2)モジュールを指定しないトップレベル定義が呼び出し側の現在のモジュールに 定義を追加することは保証されません(既に別のモジュールへと読みこまれているかもしれません)。 モジュールを指定しないトップレベル定義やimportを持つファイルをrequireすることは、 単に悪いアイディアです。したがってそういう事例はエラーとすることにしました。

Function: provide feature

featureをシステムのprovideされたフィーチャーリストに加えます。 以降、featurerequireされてもファイルはふたたびロードされません。

requireが要求された機能を自動的にprovideされたかのように 扱うAutoprovide機能があるために、provideを明示的に呼ぶ必要は ほとんど無いでしょう。provideを使いたいシチュエーションとしては 次のようなものが考えられます。

Function: provided? feature

featureが既にprovideされていれば#tを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.23.4 Autoload

Macro: autoload file/module item …

item … がオートロードされるように設定します。すなわち、 次にitemが参照された時、それが実際に評価される前にfile/moduleがロード されるようにします。これによって、必要とされる時までfile/moduleのロードを遅らせる ことができます。

引数file/moduleには、文字列かシンボルを指定できます。文字列の場合は そのファイルがロードされます。シンボルの場合、その名のモジュールが(useと 同じルールで)ロードされ、itemfile/moduleから オートロードを呼び出したモジュールへとインポートされます。 (モジュールのuseの詳細についてはモジュールの定義と選択を参照して下さい)。

itemは変数名(シンボル)か、(:macro symbol)というフォーム でなければなりません。変数名の場合は、その変数が評価されようとした時に 指定のファイル/モジュールがロードされます。後者のフォームの場合は、 (symbol arg …) というフォームがコンパイルされようとした 時に指定のファイル/モジュールがロードされます。後者はマクロのオートロードになります。

symbolfile/moduleの中で定義されなければなりません。 そうでなければロード時にエラーが報告されます。

手続きのオートロードの例を示します。

 
(autoload "foo" foo0 foo1)
(autoload "bar" bar0 bar1)

(define (foobar x)
  (if (list? x)
      (map bar0 x)
      (foo0)))

(foobar '(1 2)) ; この時点で "bar" がロードされる

(foobar #f)     ; この時点で "foo" がロードされる

マクロのオートロードを設定した場合、指定のファイルまたはモジュールは、 そのマクロを使っているフォームが実行されるされないにかかわらず、 コンパイラがそのフォームを見た時点でロードされることに注意してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.23.5 ライブラリの操作

ある特定のライブラリおよび/あるいはモジュールがシステムにインストールされ ているかどうかをチェックするための手続きがいくつかあります。

以下の説明の中では、patternはシンボルまたは文字列です。シンボル である場合にはモジュール名(たとえば、foo.bar)を指定します。 文字列である場合にはライブラリの部分パス(たとえば、"foo/bar")を 指定します。これはライブラリサーチパス以下で検索されます。

Function: library-fold pattern proc seed :key paths strict? allow-duplicates?

ライブラリ/モジュールファイルに対する基本イテレータ。この手続きは pattern にマッチする Scheme のプログラムファイルを検索します。 検索は paths (デフォルトは標準のファイルロードパス、 *load-path*)にリストされたディレクトリ以下でおこなわれます。 マッチしたファイル毎に、proc が、そのマッチしたモジュール名あるいは ライブラリ名、そのプログラムファイルのフルパス、状態値、の3つの引数を ともなって呼びだされます。seedは、初期状態値として使われ、 proc が返す値は次の proc の呼出し時に状態値として使われます。 最後の proc から返された値は library-fold の値として返ります。

pattern がシンボルでかつ、キーワード引数 strict?#t (デフォルト値)であれば、この手続きは あたえられたモジュール名のパターンにマッチするようにみえるファイル名に たいして library-has-module? を適用して、 本当にそのモジュールを実装しているファイルを見付けます。 大量のモジュールにマッチさせようとすると、時間がかる可能性があります。 #fstrict? に渡すことで、余分なチェックを回避できます。 pattern が文字列だった場合、照合はファイル名に対してのみ行われ、 strict?は無視されます。

デフォルトでは、path 中の pattern にマッチする同じ名前をもつ 2つ以上のファイルがあるばあい、path に最初に出現したものだけが 採用されます。そのライブラリに対して、require あるいは use を用いたをつかった場合に得られるのはこのファイルです。 すべてのマッチしたファイルについて反復したければ、allow-duplicates? キーワード引数に #t を渡します。

 
(library-fold 'srfi-1 acons '())
 ⇒ ((srfi-1 . "../lib/srfi-1.scm"))

(library-fold "srfi-1" acons '())
 ⇒ (("srfi-1" . "../lib/srfi-1.scm"))

;; acons が呼ばれるのとは逆順のリストが
;; 返ることに注意してください
(library-fold 'srfi-1 acons '() :allow-duplicates? #t)
 ⇒ ((srfi-1 . "/usr/share/gauche/0.7.1/lib/srfi-1.scm")
   (srfi-1 . "../lib/srfi-1.scm"))

;; 以下の場合は、モジュール名ではマッチしませんが、
;; ファイル名ではマッチします
(library-fold 'srfi-19.* acons '())
 ⇒ ()

(library-fold "srfi-19/*" acons '())
 ⇒ (("srfi-19/read-tai" . "../lib/srfi-19/read-tai.scm")
   ("srfi-19/format" . "../lib/srfi-19/format.scm"))

;; 利用可能な dbm の実装を見付けます
(library-fold 'dbm.* acons '())
 ⇒ ((dbm.cdb . "/usr/share/gauche/0.7.4.2/lib/dbm/cdb.scm")
   (dbm.gdbm . "../lib/dbm/gdbm.scm")
   (dbm.ndbm . "../lib/dbm/ndbm.scm")
   (dbm.odbm . "../lib/dbm/odbm.scm"))
Function: library-map pattern proc :key paths allow-duplicates? strict?
Function: library-for-each pattern proc :key paths allow-duplicates? strict?

マッチしたライブラリ/モジュール上のイテレータの map版および for-each版。照合操作とキーワード引数の詳細については上述の library-foldを参照してください。

procは、マッチしたモジュール/ファイル名と、そのファイルの フルパスの 2 つの引数をうけとります。library-for-each は 結果を捨てます。

 
(library-map 'srfi-4 list :allow-duplicates? #t)
 ⇒ ((srfi-4 "../lib/srfi-4.scm")
            (srfi-4 "/usr/share/gauche/0.7.1/lib/srfi-4.scm"))

(library-map 'dbm.* (lambda (m p) m))
 ⇒ (dbm.odbm dbm.ndbm dbm.gdbm dbm.cdb)
Function: library-exists? mod/path :key paths force-search? strict?

mod/path で指定されたライブラリあるいはモジュールを検索し、 もしあれば、真値を返します。キーワード引数 paths および strict?library-fold のそれと同じ意味です。

上述のイテレータ手続きとはちがい、この手続きは呼び出しの過程で 最初に既にロードされているライブラリおよびモジュールをチェックします。 もしそのときに mod/path を見つけたら、真値を返し、ファイル システムを見にいくことはありません。キーワード引数 force-search?#t を渡せば、すでにロードされいるライブラリおよびモジュール のチェックはスキップされます。

Function: library-has-module? path module

path で指定したファイルが存在し、かつ、module で名指しされた モジュールが実装されている場合でその場合に限り、#t を返します。 path は実際のファイル名でなければなりません。

 
(library-has-module? "./test/foo/bar.scm" 'foo.bar)
 ⇒ #t ;; if ./test/foo/bar.scm implements module foo.bar.

この手続きは典型的なソースコードの配置を仮定して、与えられたファイルが そのモジュールを実装しているかどうかを決定します。すなわち、 まずそのコードのフォームを読み、与えられたモジュールを定義している define-module フォームかどうかを見ます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.24 ソートとマージ

ソートとマージのインタフェースはSRFI-95に準拠し、さらに次の点で拡張されています。

Function: sort seq :optional cmp keyfn
Function: sort! seq :optional cmp keyfn

[SRFI-95+] シーケンスseqの要素を昇順にソートし、 ソートされたシーケンスを返します。 sort!は、オリジナルのシーケンスを破壊的に再利用します。

seqには<sequence>クラスのインスタンスを渡すことができます。 同じ型のシーケンスが返されます。sortの場合、 seqと同じ型のシーケンスを新たに作る必要があるので、 seqの型はビルダーインタフェースを実装していなければなりません (ビルダーインタフェースについては基礎的なイテレータ構築メソッドを参照)。 sort!の場合、seqは変更可能でなければなりません。

ソート順はcmpで指定されます。これは手続きか比較器でなければなりません。 手続きの場合は、seqのふたつの要素を 引数に取り、最初の要素が厳密に2番目の要素より先行する場合に #tを返します。 比較器の場合は、比較手続きを持つものでなければなりません。 省略された場合はdefault-comparatorが使われます。

省略可能な手続き引数keyfnが与えられた場合、 要素はまずkeyfnに渡され、その結果が比較に使われます。 keyfnは各要素に対してたかだか1回しか呼ばれないことが保証されます。

 
(sort '(("Chopin" "Frederic")
        ("Liszt" "Franz")
        ("Alkan" "Charles-Valentin"))
      string<?
      car)
  ⇒ (("Alkan" "Charles-Valentin")
      ("Chopin" "Frederic")
      ("Liszt" "Franz"))

現在の実装では、cmpが省略された場合は クィックソートとヒープソートを使い、 cmpが与えられた場合はマージソートを使っています。 すなわち、少なくともcmpを指定すれば、ソートは安定であることが 保証されます (ただし、安定であるためには cmpは等しい引数が与えられた時に必ず#fを返さなければなりません)。 SRFI-95は安定性を要求しますが、同時にcmpが与えられることも要求するので、 これらの手続きはSRFI-95の上位互換です。

なお、オブジェクトをひとつづつ集合に追加しつつ、常にソートされた 状態に保ちたい場合は、treemapの使用を考えても良いでしょう (ツリーマップ参照)。 また、最大または最小から数要素だけを必要とする場合は、 全ての要素をソートするかわりにヒープが使えます (data.heap - ヒープ参照)。

Function: sorted? seq :optional cmp keyfn

[SRFI-95+] seqの要素がソートされている時に限り#tを返します。 seqにはどんなシーケンスを渡すこともできます。 省略可能引数cmpkeyfnの意味はsortと同じです。

SRFI-95ではcmpは必須になっています。

Function: merge a b :optional cmp keyfn
Function: merge! a b :optional cmp keyfn

[SRFI-95+] 引数abはリストで、比較関数または比較器cmpによって 既にソートされているものとします。これらの手続きは二つのソート済みリストを マージして、ソートされた一つのリストにします。merge!は破壊的バージョンで、 abのセルを再利用します。戻り値はabに対してeq?と なります。

SRFI-95ではcmpは必須になっています。

以下の手続きは後方互換性のために残されていますが、その機能は 拡張されたsortおよびsort!によってカバーされています。

Function: stable-sort seq :optional cmp keyfn
Function: stable-sort! seq :optional cmp keyfn

安定ソートアルゴリズムを使って、シーケンス seqをソートします。 cmpfnkeyfn引数はsortおよびsort!と同じです。

実のところ、現在ではsortsort!cmpが与えられれば 安定ソートアルゴリズムを使うので、これらの手続きは どうしてもcmpを省略しつつ安定ソートしたい、という場合でなければ 使う必要はありません。

Function: sort-by seq keyfn :optional cmp
Function: sort-by! seq keyfn :optional cmp
Function: stable-sort-by seq keyfn :optional cmp
Function: stable-sort-by! seq keyfn :optional cmp

比較のためのキーを取り出す関数を取る、ソート手続きの別バージョンです。 sort等の手続きが省略可能なkeyfnを取るようになったので、 現在ではこれらの関数は冗長です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25 システムインタフェース

GaucheはPOSIX.1の多くの関数と、さまざまなUnixで共通して使われているシステム関数へのインタフェースを 組込み手続きとして提供しています。

多くのScheme実装はいろいろな形で何らかのシステムインタフェースを提供 しています。名前だけが違うもの(例:delete-fileremove-fileunlink) もあれば、新しいSchemeオブジェクトを導入して 抽象化を行っているものもあります。Gaucheでは、既存のAPIのどれかを そのまま真似るのではなく、2つのレイヤを提供することにしました。 この章で述べられる低レベルレイヤは、OSのインタフェースに似せたものを実装します。 その上に、既存のシステムと互換性のある高レベルのインタフェースが実装されます。

sys-nameという名前を持つ手続きは、多くの場合、 システムコールnameに対応しています。 可能な限り、インタフェースが似るようにしました。

なお、Gaucheはシステムコールがシグナルによって割り込まれた場合、 システムコールをリスタートします。詳しくはシグナルを参照して下さい。

Cによるシステムプログラミングに慣れている方は、 CとSchemeの関数の対応も参考にして下さい。 Cの標準ライブラリとGaucheの手続きとの対応が示してあります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.1 プログラムの終了

Gaucheは(mainから返る以外に)自分自身を終了させる方法をいくつか 提供しています。exit手続きが、正しいクリーンアップを行って 優雅に終了する方法です。一方、正しいクリーンアップが不可能な非常事態には sys-exitsys-abortを使います。

Function: exit :optional (code 0) (fmtstr #f) args …

[R7RS+] 現在のプロセスを終了し、codeを終了コードにします。 codeはゼロもしくは正の正確な整数でなければなりません。 fmtstr文字列が与えられた場合、それは残りの引数argsと ともにformatに渡され、standard error portにメッセージとして 印字されます (current-error-portではありません。 ポート共通の操作参照。また、formatについては 出力参照。)

実のところ、プロセス終了の手続きはもう少し複雑です。 正確な段階を次に説明します。

  1. パラメータexit-handlerの値がチェックされます。 もしそれが#fでなければ、それは3引数の手続きとみなされ、 codefmtstr、そして残りの引数のリストを引数として 呼び出されます。実はメッセージを標準エラー出力に出しているのは デフォルトのexit-handlerです。 終了ハンドラの中でエラーが起きた場合、そのエラーは捕捉され捨てられます。 エラー以外の例外は捕捉されません。
  2. アクティブなdynamic-windafterサンクが順に呼び出されます。 afterサンク中で発生した例外は捕捉され捨てられます。
  3. C APIのScm_AddCleanupHandlerで登録されたクリーンアップハンドラが 呼ばれます。このハンドラは通常、Gaucheを内蔵しているアプリケーション特有の クリーンアップをするために使われます。Schemeの世界からはあまり気にする必要は ないでしょう。
  4. まだ閉じられていない出力ポートのバッファが掃き出されます。
  5. exit(3)を呼び、プロセスはcodeを終了コードとして終了します。

exit-handlerのメカニズムはアプリケーションが終了処理をフックすることを 可能にします。ただし、単純なクリーンアップ処理のためのものではありません (クリーンアップ処理には dynamic-wind, guardあるいはunwind-protectを使う方が 適切です。) exit-handlerはもっと「アプリケーションの終了」という特定の事態に 対して何か処理を行いたい場合に使います。 例えばGUIアプリケーションはメッセージを標準エラー出力ではなく ダイアログとして表示する、というようなことです。

従って、ライブラリコードはexit-handlerに触るべきではありません。 アプリケーションの終了時に何をすべきかを知っているのはアプリケーション自身なのですから。

別の使いどころとしては、サードパーティ製のコードで中でexitが 呼ばれる可能性のあるものを安全に呼び出したいという場合があります。 そのコードを呼び出している間だけ、exit-handlerを エラーではない例外を発生させる手続きへと差し替えます。 エラーではない例外はexitでは捕捉されないので、実質的に 上で説明したような終了処理のステップは中断されることになります (但し、ダイナミックハンドラのafterサンクは通常の例外発生時と 同じように処理されます)。 アプリケーションコードの方でその例外を捕捉してやればよいのです。 parameterizeを使えばexit-handlerを動的に、 かつスレッドセーフに差し替えることが容易になります (gauche.parameter - パラメータ参照)。

 
(guard (e [(eq? e 'exit-called) (handle-exit-as-desired)])
  (parameterize ((exit-handler (lambda (c f a) (raise 'exit-called))))
    (call-third-party-library)))

一般的に、他のスレッドが走っている時にexitを呼ぶことは推奨されません。 exit手続きは該当スレッド上でアクティブなダイナミックハンドラしか巻き 戻さないので、他のスレッドはいきなり終了させられてしまいます。 しかしexitを呼ばざるを得ない場合には、 exit-handlerを使って他のスレッドにアプリケーションが終了しつつあることを 伝えられるかもしれません (但しGauche自身は走っているスレッドのリストといった 情報は持っていないので、アプリケーションごとに固有のロジックを実装する必要が あります。)

設計に関する覚書: 終了を一種の例外とすることで、 終了処理を例外処理と統合している言語もあります。 良いアイディアに思えたので我々もそのような実装を試してみましたが、 Gaucheではうまくいきませんでした。大きな理由のひとつは、 dynamic-windの巻き戻し中にafterで例外が 発生すると、その例外がもともとの「終了」例外を隠してしまうことでした。

Function: exit-handler :optional new-handler

引数なしで呼ばれた場合、現在のexit handlerの値を返します。 引数つきで呼ばれた場合は、new-handlerを現在のexit handlerの 値とし、以前の値を返します。new-handlerは3つの引数をとる 手続きか、#fでなければなりません。

exit handlerの値はスレッドごとに固有で、デフォルト値はスレッドが作られた時点の 親スレッドのexit handlerの値から引き継がれます。 exit-handlerはあたかもパラメータのようにparameterizeマクロで 使うことができます (gauche.parameter - パラメータ参照)。

Function: sys-exit code

[POSIX] 現在のプロセスを終了し、codeを終了コードにします。 codeはゼロもしくは正の正確な整数でなければなりません。 この手続きは_exit(2)を直接コールします。 クリーンアップは一切行われません。 フラッシュされてないファイルバッファの内容は捨てられます。

Function: sys-abort

[POSIX] POSIXのabort()を呼びます。通常、現在のプロセスは終了され、コアダンプされます。 クリーンアップは一切行われません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.2 コマンドライン引数

Schemeスクリプトに渡されたコマンドライン引数を受け取るには、 main関数の引数を使うのが推奨される方法です (see section Schemeスクリプトを書く)。 ただ、簡便のために、どこからでもコマンドライン引数にアクセスできる インターフェースもいくつか用意してあります。

Schemeコードが実行される時、常にコマンドライン引数が存在するわけではない ことに注意してください。例えばアプリケーションに埋め込まれたSchemeコード片にとっては、 コマンドライン引数は意味を持ちません。なるべくmain関数の引数を使うべき、 というのはそのためです。main関数の引数は明示的だからです。 mainが呼ばれたなら、呼び出す側は意識してコマンドライン引数を渡している わけですから。

この注意を念頭に置いておいてください。 以下にコマンドライン引数にアクセスする方法を示します。

Parameter: command-line

[R7RS+] 引数なしで呼ばれた場合は、コマンドライン引数のリストを文字列のリストとして返します。 最初の要素はプログラム名です。

Gaucheが埋め込み言語として使われた場合、このパラメータの値はアプリケーションの 設定に依存します。アプリケーションが何もしていなければ、 このパラメータの値は空リストとなります。ライブラリ中でこのパラメータの値を 使う場合は、その可能性を念頭に置いてください。

一つの引数で呼び出すこともできます。その場合、引数は文字列のリストでなければなりません。 渡された引数が、新たなパラメータの値となります。 parameterizeを使ってcommand-lineの値を 動的に変えることができます (gauche.parameter - パラメータ参照)。 R7RSは、ゼロ引数のcommand-lineのみ定義しています。

Variable: *program-name*
Variable: *argv*

これらの変数はそれぞれ、プログラム名および、コマンドライン引数のリストに束縛されます。 goshを使ってGaucheスクリプトを実行しているなら、*program-name*は (goshに与えられたとおりの) スクリプト名になっています。goshが インタラクティブREPLとして起動された場合は*program-name*gosh自身です。

これらの変数はuserモジュールにあります。

これは主として互換性のためだけに残されています。STkと互換な名前ですが、 他のScheme実装へはポータブルではありません。上のcommand-lineパラメータの 方がお勧めです。

Gaucheが埋め込み言語として使われている場合、これらの変数を設定するかどうかは ホストアプリケーションに任されます。一般には、これらの変数が必ず存在している とは限りません。これもまた、他の方法を使うべき理由です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.3 環境の問い合わせ

Function: sys-getenv name

[POSIX] 環境変数nameの値を文字列で返します。もしnameが定義されていなければ、 #fが返ります。

ポータブルなコードでは、SRFI-98およびR7RSのget-environment-variableを 使うのが良いでしょう(srfi-98 - 環境変数へのアクセス参照)。

註: 多くのシステムでは、環境が変更されている最中のgetenvのスレッド安全性を 保証していませんが、Gaucheは内部的に環境へのアクセスと変更のAPIを排他制御しているので、 Gaucheの手続きを使っている限りにおいてはスレッド安全です。

Function: sys-environ

現在の環境を文字列のリストとして返します。それぞれの文字列は NAME=VALUEというフォーマットになっています。ここでNAMEは 環境変数名、VALUEはその値です。NAMEが文字#\=を含 むことはありません。この手続きは現在のプロセスの環境変数をすべて取得し たい場合に便利です。特定の環境変数の値が欲しい場合には sys-getenvを使ってください。

Function: sys-environ->alist :optional envlist

sys-environの便利版。環境リスト(sys-environが返すような 形式のもの)をenvlistとして与えると、この手続きは各環境変数を名前 と値に分けて連想リストにして返します。

envlistを省略すると、この手続きはsys-environを呼んで現在 の環境変数を取得します。

ポータブルなコードでは、SRFI-98およびR7RSの get-environment-variables (srfi-98 - 環境変数へのアクセス参照), を使うのが良いでしょう。

 
(sys-environ->alist '("A=B" "C=D=E"))
  => (("A" . "B") ("C" . "D=E"))
Function: sys-setenv name value &optional overwrite
Function: sys-putenv name=value

sys-setenvは環境変数nameとその値valueを プロセスの実行環境に挿入します。 ただし、省略可能引数overwrite#f(デフォルト値)の場合、 nameが既に存在したら実行環境は変更されません。 overwriteが真の値であれば、環境変数は上書きされます。

sys-putenvでは、環境変数を名前と値を#\=でつないだ NAME=VALUEという形式で指定します。 同名の環境変数が既に存在する場合は常に上書きされます。

これらのAPIはPOSIXのsetenv(3)putenv(3)のインタフェースを 反映しています。しかし、putenv(3)と違って sys-putenvに渡した文字列はコピーされるので、後でその文字列を 破壊的変更してもプロセスの実行環境に影響はありません。

これらの手続きは機能識別子gauche.sys.setenvがある場合のみ使えます。 下の例のようにcond-expandで使えるかどうかチェックしてください (機能条件式参照)。

 
(cond-expand
 [gauche.sys.setenv
   ... use sys-setenv or sys-putenv ... ]
 [else
   ... fallback code ...])

これらの手続きは、他の方法で環境変数を変更しない限り、スレッドセーフです。

Function: sys-unsetenv name
Function: sys-clearenv

sys-unsetenvは環境変数nameを、また sys-clearenvは全ての環境変数をプロセスの実行環境から取り除きます。 sys-clearenvは、子プロセスを走らせる際に予期せぬ環境変数を 注入されないようにするのに便利です。

これらの手続きは機能識別子gauche.sys.unsetenvがある場合のみ使えます。 下の例のようにcond-expandで使えるかどうかチェックしてください (機能条件式参照)。

 
(cond-expand
 [gauche.sys.unsetenv
   ... use sys-unsetenv or sys-clearenv ... ]
 [else
   ... fallback code ...])

SRFI-98 (srfi-98 - 環境変数へのアクセス参照) にも、 環境変数を読み出すための、上記の手続きのサブセットにあたるものが 定義されています。ポータブルなコードにしたければそちらも見てみてください。

Function: gauche-version
Function: gauche-architecture
Function: gauche-library-directory
Function: gauche-architecture-directory
Function: gauche-site-library-directory
Function: gauche-site-architecture-directory

これらの手続きはGaucheインタプリタの情報を文字列で返します。

Function: sys-available-processors

現在Gaucheが走っているプラットフォームで使えるプロセッサの個数を返します。 戻り値は常に正の正確な整数です。Gaucheがこの情報を入手できなかった場合は 1が返ります。

但し、もし環境変数GAUCHE_AVAILABLE_PROCESSORSが定義されていて、 その値が正の整数と解釈できるものだった場合、ハードウェア/OSがどう言おうと 環境変数の値が返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.4 ファイルシステム

ファイルシステムに関するシステムコール群です。 ここで述べる手続きの上に、より高レベルなAPIがモジュールfile.utilとして 実装されています。file.util - ファイルシステムユーティリティを参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.4.1 ディレクトリ

高レベルAPIに関してはディレクトリユーティリティも参照して下さい。

Function: sys-readdir path

pathは存在するディレクトリを示すパス名でなければなりません。 この手続きはディレクトリの全エントリを文字列のリストとして返します。 リストはソートされません。pathが存在しなかったり、ディレクトリでなかった場合は エラーとなります。

Function: glob pattern :key separator folder
Function: sys-glob pattern :key separator folder

伝統的なUnixのglob(3)の機能を提供します。この手続きはpatternとマッ チするパス名のリストを返します。

この機能は、かつてはプラットフォームが提供するglob関数をラップする もので、sys-globという名前でした。しかし、プラットフォーム間の非互 換性を解消し機能の追加を容易にするために、Gauche 0.8.12以降、システムコー ルの上にSchemeで再実装されています。そのため、globと改名してあ ります。古い方の名前sys-globは互換性のために残してありますが、 新たにプログラムを書くときはglobを使ってください。

引数patternは単一のグロブパターンもしくは、グ ロブパターンのリストです。リストが渡されたときには、 最低1つのパターンにマッチするパス名がすべて返されます。 Unixのユーザならglobの動作は馴染み深いものでしょう。

 
gosh> (glob "*.scm")
("test.scm" "ext.scm")
gosh> (glob "src/*.[ch]")
("src/ext.c" "src/ext.h")
gosh> (glob '("*.scm" "src/*.c"))
("src/ext.c" "test.scm" "ext.scm")

シェルのグロブとはちがって、マッチするパス名がないときは、()が返ります。

グロブはファイルシステムに限らず階層的なデータ構造を検索するのに非常に便利なツールです。 したがって、glob関数はファイルシステムからは切り離して実装されています。 キーワード引数を使うことで、どのようなツリー状のデータ構造からでもグロブできます。 デフォルト値がファイルシステム用に設定されているにすぎません。

引数separatorは文字の集合を与えます。これを使ってpatternを コンポーネントに分割します。デフォルトは#[/]です。実際のパス名 をマッチさせるのには使われません。

folderはデータ構造をトラバースするための手続きです。この手続きは 引数を5つとります。

 
(folder proc seed parent regexp non-leaf?)

procは引数を2つ取る手続きです。folderは、parent内で コンポーネントとなる名前がregexpとマッチする各ノードと、 foldと同様に順に渡されるシード値とで、procを呼びます。 folderprocが最後に返した値を返します。たとえば、 consprocとして、()seedとして渡されれば、 folder手続きが返す値はregexpとマッチするノードのリストになります。

ノードの表現がどうなるかはfolderの実装によります。 パス名であったり、オブジェクトであったりするかもしれません。 glob手続きはノードの表現については感知しません。 glob手続きがやることはノードを次に呼ぶ folderparent引数として渡して、 その結果のノードリストを返すだけです。

parent引数は基本的にはノードで、folderはその子ノードを 対象にマッチするものを探索します。 例外は最初にfolderが呼ばれる時です。その時点ではglobは ノードについて何も知らないので、 絶対パスでマッチをかける場合は#tを、 相対パスでマッチをかける場合は#fを最初のparentの値として渡します。

regexp引数は子ノードをフィルターするのに使い、ディレクトリ名を含 まない、子のコンポーネントである名前に対して照合されるものです。特別な 場合として、シンボルdirにすることができます。この場合は、folder はnodeそのものを返し、nodeディレクトリと見なされ ます。すなわち、nodeがパス名を表わす場合には、folderはディレクト リ区切り子が最後に付いたパス名を返します。 特別な場合として、nodeがブール値でかつ regexpdirである場合、folderはルートノードをあらわすノー ドあるいはカレントノードを返すことになっています。すなわち、node はパス名を表す場合、folderは"/"または"./"を返します。

non-leaf引数はブール値フラッグです。真なら、フィルタは結果からリー フノードをのぞきます(すなわち、ディレクトリだけが含まれるようになり ます)。

ここからはグロブパターンマッチングの細かい仕様をみていきます。

各グロブパターンはパス名様式文字列にマッチする文字列です。

パス名様式文字列は1つ以上のセパレータで区切られた コンポーネントから構成される文字列です。 デフォルトのセパレータは#[/]です。キーワード引数separator を使うとこれを変更できます。コンポーネントにはセパレータを含められませ ん。また空文字列にすることもできません。連続したセパレータは単一のセパ レータとみなされます。パス名様式の文字列は状況に応じてセパレータから始 めたり、セパレータで終端したりできます(両方をやるのも可)。

グロブパターンもコンポーネントとセパレータ文字から構成されます。コンポー ネントにおいては、以下の文字、構文は特別な意味を持ちます。

*

これがコンポーネントの先頭に表われた場合、0個以上のピリオド(.) を除く文字にマッチします。入力文字列のコンポーネントがピリオドではじま るような文字列の場合にはマッチしません。

先頭以外の場所では、0個以上の文字の列にマッチします。

**

コンポーネントが**だけの場合、これは*にマッチする0個以上の コンポーネントにマッチします。例えばsrc/**/*.hは 次のようなパターン全てにマッチします。

 
src/*.h
src/*/*.h
src/*/*/*.h
src/*/*/*/*.h
...
?

コンポーネントの先頭にあらわれた場合、ピリオド(.)以外の一文字に マッチします。先頭以外の場所では任意の一文字にマッチします。

[chars]

文字の集合を指定します。その集合に含まれる文字のどれかにマッチします。 charsの構文はGaucheの文字集合を表す構文と同じです (文字集合参照)。伝統的なグロブとの互換性のために、! 文字をつかって、補集合で文字集合を表わすこともできるようになっています。 すなわち、[!abc][^abc]と同じです。

Function: glob-fold pattern proc seed :key separator folder

これはグロブ関数の低レベル版です。実際globは以下のように書いたの と同じです。

 
(define (glob patterns . opts)
  (apply glob-fold patterns cons '() opts))

patternseparatorおよびfolderの意味を前述のものと同 じです。

patternにパス名ごとにglob-foldprocをパス名とシー ド値で呼びます。最初のシード値はseedで、procが返す値は次の シード値になります。最後のprocの呼び出し結果はglob-foldの 結果となります。マッチするパス名が存在しないときは、procは呼ばれ ずに、seedが返ります。

Function: make-glob-fs-fold :key root-path current-path

これはglob-foldglobfolderキーワード引数に 渡せる形の手続きを作るユーティリティ手続きです。 引数を渡さなければ、glob-foldglobがデフォルトで使うのと 同じ手続きが返ります。

キーワード引数root-pathおよびcurrent-pathはそれぞれ、 glob-foldが探索を開始するディレクトリを指定します。

 
gosh> (glob "/tmp/*.scm")
("/tmp/x.scm" "/tmp/y.scm")
gosh> (glob "/*.scm"
            :folder (make-glob-fs-fold :root-path "/tmp"))
("/tmp/x.scm" "/tmp/y.scm")
gosh> (glob "*.scm"
            :folder (make-glob-fs-fold :current-path "/tmp"))
("/tmp/x.scm" "/tmp/y.scm")

パスがディレクトリかどうかチェックする方法は、ファイルの状態を参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.4.2 ディレクトリ操作

Function: sys-remove filename

[POSIX] filenameがファイルであればそれを消去します。 システムによってはfilenameが空のディレクトリであっても動作しますが、 移植性を気にする場合はその動作に依存しない方が良いでしょう。

Function: sys-rename old new

[POSIX] ファイルoldnewにリネームします。新しい名前は 古い名前と異なるディレクトリにあってもかまいませんが、両者は同じデバイス上に なければなりません。

Function: sys-tmpnam

[POSIX] ユニークなファイル名を作成して返します。この関数はPOSIXに含まれていますが、 セキュリティ上の問題が指摘されており、使わない方が良いとされています。 可能なら下にあげるsys-mkstempを使って下さい。

Function: sys-mkstemp template

ユニークな名前を持つファイルを作成してオープンし、オープンされたポートとファイル名の 二つの値を返します。ファイルは排他的に作成されるため、レースコンディションは起こりません。 templateはファイル名のプレフィックスに使われます。Unixのmkstempと違って、 パディングキャラクタをつける必要はありません。ファイルは書き込み用としてオープンされ、 パーミッションは600にセットされます。

Function: sys-mkdtemp template

ユニークな名前を持つディレクトリを作成し、その名前を返します。 templateはディレクトリ名のプレフィックスに使われます。 Unixのmkdtempと違って、パディングキャラクタをつける必要はありません。 ディレクトリのパーミッションは700にセットされます。

Function: sys-link existing new

[POSIX] 既存のファイルexistingに対し、newという名のハードリンクを作成します。

Function: sys-unlink pathname

[POSIX] pathnameで示されるファイルを消去します。 pathnameはディレクトリであってはなりません。 もし消去に成功したら#tが、pathnameが存在しなければ#fが返されます。 他の場合はエラーが通知されます。

file.utilモジュールに似た手続きdelete-file/remove-fileが ありますが、そちらはファイルが存在しない場合にエラーを投げます (ファイル操作参照)。

R7RSはdelete-fileを定義しています。ポータブルなプログラムでは そちらを使うのが良いでしょう。

Function: sys-symlink existing new

existingを指すnewという名のシンボリックを作成します。 シンボリックリンクをサポートしないシステムでは、この手続きは定義されません。

Function: sys-readlink path

pathで示されるファイルがシンボリックリンクならば、それが指すパスを返します。 もしpathが存在しなかったり、シンボリックリンクでなければ、エラーが通知されます。 シンボリックリンクをサポートしないシステムでは、この手続きは定義されません。

Function: sys-mkdir pathname mode

[POSIX] ディレクトリpathnameをモードmodeで作成します。 (modeはさらにumaskでマスクされることに注意して下さい;下のsys-umask 参照)。 pathnameの親ディレクトリは存在して、プロセスが書き込めるようになっていなければ なりません。中間のディレクトリも一度に作成するには、 file.utilmake-directory*が使えます (ディレクトリユーティリティ)。

Function: sys-rmdir pathname

[POSIX] ディレクトリpathnameを消去します。ディレクトリは空でなければなりません。 ディレクトリの内容も一緒に消去するには、file.utilremove-directory* が使えます(ディレクトリユーティリティ)。

Function: sys-umask :optional mode

[POSIX] umaskをmodeにセットします。変更される前のumaskを返します。 modeが省略されるか#fが渡された場合、 現在のumaskを変更せずにその値を返します。 umaskについてはman umaskを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.4.3 パス名

高レベルのAPIに関してはパスネームユーティリティも参照して下さい。

Function: sys-normalize-pathname pathname :key absolute expand canonicalize

与えられたキーワード引数によってpathnameを以下のように変換します。 キーワード引数は同時に複数指定できます。

absolute

このキーワード引数に真の値が与えられて、pathnameが絶対パスでない場合、 pathnameの前にプロセスのワーキングディレクトリを足して絶対パスにします。

expand

このキーワード引数に真の値が与えられて、pathnameが‘~’ で始まっていた 場合、以下のように展開されます。

  • pathnameが“~”のみであるか、または“~/”で始まっている 場合、文字“~”が現在のプロセスのユーザのホームディレクトリに置き換えられます。
  • 上記以外の場合、‘~’以降、‘/’かpathnameの終端までの文字列 がユーザ名とみなされ、そのユーザのホームディレクトリに置換されます。もし該当するユーザが いなければエラーとなります。
canonicalize

パス名から “.” や “..” を除き、単純化します。 この操作は実際のファイルシステムを参照せずに行われます。元のパス名がディレクトリへの シンボリックリンクを含んでいた場合、単純化されたパス名は正しくないかもしれません。

Function: sys-basename pathname
Function: sys-dirname pathname

sys-basenameは与えられたパスのベース名、すなわち最後のコンポーネントを返します。 sys-dirnameは与えられたパスのディレクトリ名、すなわち最後のコンポーネント以外の コンポーネントを返します。pathnameの末尾が‘/’である場合、その文字は 無視されます。

 
(sys-basename "foo/bar/bar.z") ⇒ "bar.z"
(sys-basename "coo.scm") ⇒ "coo.scm"
(sys-basename "x/y/") ⇒ "y"
(sys-dirname "foo/bar/bar.z") ⇒ "foo/bar"
(sys-dirname "coo.scm") ⇒ "."
(sys-dirname "x/y/") ⇒ "x"

この手続きはpathnameが存在するかどうかはチェックしません。

特殊なケース:

 
(sys-basename "") ⇒ ""
(sys-dirname "") ⇒ "."

(sys-basename "/") ⇒ ""
(sys-dirname "/") ⇒ "/"

註:このふるまいはPerlのbasenameおよびdirnameと同様です。 システムによっては、コマンドのbasename"/"に対して"/"を、 "."に対して"."を返すものがあります。

Function: sys-realpath pathname

sys-realpathは“.”, “..”およびシンボリックリン クを含まないpathnameの絶対パスを返します。pathname自体が存在しなかったり、 存在しないパスを指すシンボリックリンクが含まれていたり、関連するパスへのアクセス権限が 不足している場合はエラーが通知されます。

注:POSIXのrealpath(3)は安全でない場合があるので、 Gaucheは内部でCランタイムのrealpathを呼ばずに、 独自にsys-realpathの機能を実装しています。

Function: sys-tmpdir

一時ファイルを置くためのデフォルトのディレクトリ名を返します。

Unix系システムでは、環境変数TMPDIRTMPがこの順でチェックされ、 フォールバックとして/tmpが返されます。

Windowsネイティブの環境では、GetTempPath Windows APIを呼びます。 これは環境変数TMPTEMPUSERPROFILEを順に調べ、 いずれも定義されていなければWindowsシステムディレクトリを返します。

いずれのプラットフォームでも、返されたパス名は存在しないかもしれず、 また書き込み可能ではないかもしれないことに注意してください。

一般的に、ユーザプログラムやライブラリはtemporary-directory (ディレクトリユーティリティ参照) の方を利用するのが良いでしょう。 sys-tmpdirはプラットフォームが推奨する生の値を知りたい場合に 限り使ってください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.4.4 ファイルの状態

高レベルのAPIに関してはファイル属性ユーティリティも参照して下さい。

Function: file-exists? path

[R7RS] pathが存在していれば#tを返します。

Function: file-is-regular? path
Function: file-is-directory? path

それぞれ、pathが存在するか、存在してそれがレギュラーファイルであるか、 存在してそれがディレクトリであれば#tを返します。

Builtin Class: <sys-stat>

ファイルシステム内のエントリの属性を表す、struct statのラッパー オブジェクトです。以下に示す読みだし専用のスロットを持ちます。

Instance Variable of <sys-stat>: type

ファイルのタイプを示すシンボルです。

regular通常のファイル
directoryディレクトリ
characterキャラクタデバイス
blockブロックデバイス
fifoFIFO
symlinkシンボリックリンク
socketソケット

以上のどれにも当てはまらない場合は#fが返されます。

注:いくつかのオペレーティングシステムではsocketファイルタイプを fifoと区別せず、どちらに対してもfifoを返します。 ポータブルなプログラムを書くときは注意して下さい。

Instance Variable of <sys-stat>: perm

パーミッションビットマスク。"mode"スロットの下位9ビットと同じですが、 便利なので独立したスロットとして提供されます。

Instance Variable of <sys-stat>: mode
Instance Variable of <sys-stat>: ino
Instance Variable of <sys-stat>: dev
Instance Variable of <sys-stat>: rdev
Instance Variable of <sys-stat>: nlink
Instance Variable of <sys-stat>: uid
Instance Variable of <sys-stat>: gid
Instance Variable of <sys-stat>: size

struct statの該当するフィールドの値。正確な整数です。

Instance Variable of <sys-stat>: atime
Instance Variable of <sys-stat>: mtime
Instance Variable of <sys-stat>: ctime

struct statの該当するフィールドの値を、Unix Epochからの秒数で。

Function: sys-stat path
Function: sys-fstat port-or-fd

[POSIX] 与えられたパス名path、またはポートかファイルディスクリプタ port-or-fdで示されるファイルの情報を<sys-stat>オブジェクトで 返します。

sys-statは、pathがシンボリックリンクであった場合は リンクの指す先のファイルに関する情報を返します。

sys-fstatは、port-or-fdがファイルに関係ないポートであった 場合は#fを返します。

Function: sys-lstat path

sys-statと同じですが、pathがシンボリックリンクであった 場合はリンクそのものの情報を返します。

 
gosh> (describe (sys-stat "gauche.h"))
#<<sys-stat> 0x815af70> is an instance of class <sys-stat>
slots:
  type      : regular
  perm      : 420
  mode      : 33188
  ino       : 845140
  dev       : 774
  rdev      : 0
  nlink     : 1
  uid       : 400
  gid       : 100
  size      : 79549
  atime     : 1020155914
  mtime     : 1020152005
  ctime     : 1020152005
Function: sys-stat->mode stat
Function: sys-stat->ino stat
Function: sys-stat->dev stat
Function: sys-stat->rdev stat
Function: sys-stat->nlink stat
Function: sys-stat->size stat
Function: sys-stat->uid stat
Function: sys-stat->gid stat
Function: sys-stat->atime stat
Function: sys-stat->mtime stat
Function: sys-stat->ctime stat
Function: sys-stat->file-type stat

Deprecated. <sys-stat>オブジェクトの情報にアクセスするにはslot-refを 使って下さい。

Function: sys-access pathname amode

[POSIX] pathnameへのアクセスが modeに示されるモードで許可されているかどうかを示す真偽値を返します。 この手続きは、suid/sgidプログラムで使われるとエラーとなります(下記註参照)。 modeは以下に示す定数のコンビネーション(logical or)です。

R_OK

pathnameをカレントユーザが読み出し可能かどうか

W_OK

pathnameへカレントユーザが書き込み可能かどうか

X_OK

pathnameをカレントユーザが実行可能かどうか(pathnameが ディレクトリの場合はサーチ可能かどうか)

F_OK

pathnameのパーミッションフラグにかかわらず、pathnameが 存在するかどうか (但しpathnameがあるディレクトリの読みだし許可は必要)。

註: access(2)は、suid/sgidプログラム中で 実ユーザの権限を確かめるために使われた場合、セキュリティホールと なります。

Function: sys-chmod path mode
Function: sys-fchmod port-or-fd mode

名前pathを持つ、もしくはport-or-fdで指定される ファイルのパーミッションビットをmodeに変更します。 modeは小さな正の正確な整数で、POSIXスタイルのパーミッションビットマスク でなければなりません。

Function: sys-chown path owner-id group-id

ファイルpathのオーナーとグループをowner-idgruop-id で示されるものに変更します。owner-idgruop-idは正確な整数で なければなりません。どちらかに-1が渡された場合は、対応する情報は変更されません。

Function: sys-utime path :optional atime mtime

ファイルのアクセスタイムと変更タイムをatimemtimeが示す 値にセットします。atimemtimeが省略された場合もしくは #fの場合は現在の時刻が使われます。 ファイル操作touch-fileも参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.4.5 他のファイル操作

Function: sys-chdir dir

[POSIX] chdir(2)へのインタフェースです。 current-directory(ディレクトリユーティリティ)も参照して下さい。

Function: sys-pipe :key (buffering :line)

[POSIX] パイプを作り、ポートを2つ返します。 最初に返されるポートは入力ポートで、2番目に返されるポートは出力ポートです。 出力ポートへ書き出したデータは、入力ポートから読み込めます。

buffering:full:line:noneのいずれかで、 パイプ上に開かれたポートのバッファリングモードを指定します。 バッファリングモードの詳細については、ファイルポートを参照して下さい。 通常のケースでは、デフォルトのモードで間に合うでしょう。

 
(receive (in out) (sys-pipe)
  (display "abc\n" out)
  (flush out)
  (read-line in)) ⇒ "abc"

注意: 戻り値はバージョン0.3.15から変更されています。それまでは、 sys-pipeは2つのポートのリストを返します。

Function: sys-mkfifo path mode

[POSIX] 名前がpathでモードがmodeのFIFO(名前付きパイプ)を 作ります。modeはファイルのモードを表す正の正確整数でなければ なりません。

Function: sys-isatty port-or-fd

[POSIX] port-or-fdはポートか整数のファイルディスクリプタです。 ポートがコンソールに接続されていれば#tを、そうでなければ#fを 返します。

Function: sys-ttyname port-or-fd

[POSIX] port-or-fdはポートか整数のファイルディスクリプタです。 ポートに接続された端末の名前か、ポートが端末に接続されていなければ #fを返します。

Function: sys-truncate path length
Function: sys-ftruncate port-or-fd length

[POSIX] pathあるいはport-or-fdによって指定される通常ファイルの長さを lengthにします。 ファイルがlengthより長かった場合、余分なデータは捨てられます。 ファイルがlengthより短かった場合、残りの部分にはゼロが詰められます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.5 Unixのグループとユーザ

Unix groups

Builtin Class: <sys-group>

Unixのグループの情報です。以下のスロットを持ちます。

Instance Variable of <sys-group>: name

グループ名。

Instance Variable of <sys-group>: gid

グループID

Instance Variable of <sys-group>: passwd

グループパスワード。

Instance Variable of <sys-group>: mem

このグループに属するユーザ名のリスト。

Function: sys-getgrgid gid
Function: sys-getgrnam name

[POSIX] グループIDgidもしくはグループ名nameで示されるグループの情報を <sys-group>で返します。該当するグループが存在しない場合は #fが返されます。

Function: sys-gid->group-name gid
Function: sys-group-name->gid name

グループIDとグループ名を相互変換する便利な手続きです。

Unix users

Builtin Class: <sys-passwd>

Unixのユーザの情報です。以下のスロットを持ちます。

Instance Variable of <sys-passwd>: name

ユーザ名。

Instance Variable of <sys-passwd>: uid

ユーザID

Instance Variable of <sys-passwd>: gid

ユーザのプライマリグループID。

Instance Variable of <sys-passwd>: passwd

ユーザの(暗号化された)パスワード。システムがシャドウパスワードファイルを 使っている場合は、 "x" のような無意味な文字列が入っています。

Instance Variable of <sys-passwd>: gecos

Gecosフィールド。

Instance Variable of <sys-passwd>: dir

ユーザのホームディレクトリ。

Instance Variable of <sys-passwd>: shell

ユーザのログインシェル。

Instance Variable of <sys-passwd>: class

ユーザのクラス。(特定のシステムでのみ有効)。

Function: sys-getpwuid uid
Function: sys-getpwnam name

[POSIX] ユーザIDuidもしくはユーザ名nameで示されるユーザの情報を <sys-passwd>で返します。該当するユーザが存在しない場合は #fが返されます。

Function: sys-uid->user-name uid
Function: sys-user-name->uid name

ユーザIDとユーザ名を相互変換する便利な手続きです。

Password encryption

Function: sys-crypt key salt

これは、crypt(3)へのインターフェースです。keysaltは 文字列でなければならず、暗号化された文字列が返されます。 crypt(3)が利用できないシステムでこの関数を呼ぶとエラーが通知されます。

このルーチンは、システムのパスワードデータベースを使ってパスワードチェックを しなければならない時以外に使うべきではありません。独自のパスワードデータベースを 新たに作る場合は、crypt.bcryptモジュール(crypt.bcrypt - パスワードハッシュ)を 使ってください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.6 ロケール

Function: sys-setlocale category locale

[POSIX] カテゴリーcategoryのロケールをlocaleにセットします。 categoryは整数でなければなりません;以下の変数がcategoryの ために定義されています。localeはロケールを表す文字列です。 成功した場合は新しいロケール名を、ロケールが変更できなかった場合は#fを 返します。

Variable: LC_ALL
Variable: LC_COLLATE
Variable: LC_CTYPE
Variable: LC_MONETARY
Variable: LC_NUMERIC
Variable: LC_TIME

sys-setlocalecategoryに渡せる数値を定義しています。

Function: sys-localeconv

[POSIX] 現在のロケールで数値をフォーマットする際に必要な様々な情報をassoc listに して返します。

例を示します。あなたのシステム設定によっては異なる結果になるかもしれません。

 
(sys-localeconv)
 ⇒
   ((decimal_point . ".") (thousands_sep . "")
    (grouping . "") (int_curr_symbol . "")
    (currency_symbol . "") (mon_decimal_point . "")
    (mon_thousands_sep . "") (mon_grouping . "")
    (positive_sign . "") (negative_sign . "")
    (int_frac_digits . 127) (frac_digits . 127)
    (p_cs_precedes . #t) (p_sep_by_space . #t)
    (n_cs_precedes . #t) (n_sep_by_space . #t)
    (p_sign_posn . 127) (n_sign_posn . 127))

(sys-setlocale LC_ALL "fr_FR")
 ⇒ "fr_FR"

(sys-localeconv)
 ⇒
  ((decimal_point . ",") (thousands_sep . "")
   (grouping . "") (int_curr_symbol . "FRF ")
   (currency_symbol . "F") (mon_decimal_point . ",")
   (mon_thousands_sep . " ") (mon_grouping . "\x03\x03")
   (positive_sign . "") (negative_sign . "-")
   (int_frac_digits . 2) (frac_digits . 2)
   (p_cs_precedes . #f) (p_sep_by_space . #t)
   (n_cs_precedes . #f) (n_sep_by_space . #t)
   (p_sign_posn . 1) (n_sign_posn . 1))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.7 シグナル

Gaucheでは、OSのシグナルを自分自身や他のプロセスに送ったり、 送られたシグナルを処理することができます。

マルチスレッド環境では、全てのスレッドがシグナルハンドラを共有し、 各スレッドが独自のシグナルマスクを持ちます。 詳しくはシグナルとスレッドを参照して下さい。

システムコールがシグナルによって割り込まれ、 プログラマがシグナルハンドラをセットしており、そのハンドラが別コンテキストに 制御を移さずに戻った場合、そのシステムコールはハンドラからの復帰後に リスタートされます。

Windowsネイティブ環境では、sys-killの限定的なサポートを 除いてシグナルは動作しません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.7.1 シグナルとシグナルセット

シグナルはオペレーティングシステムで定義された小さな整数値で表現されます。 システムのシグナル番号に束縛された変数が定義されています。 システムのシグナル番号はアーキテクチャによって異なるので、 なるべく変数を利用するようにして下さい。

Variable: SIGABRT
Variable: SIGALRM
Variable: SIGCHLD
Variable: SIGCONT
Variable: SIGFPE
Variable: SIGHUP
Variable: SIGILL
Variable: SIGINT
Variable: SIGKILL
Variable: SIGPIPE
Variable: SIGQUIT
Variable: SIGSEGV
Variable: SIGSTOP
Variable: SIGTERM
Variable: SIGTSTP
Variable: SIGTTIN
Variable: SIGTTOU
Variable: SIGUSR1
Variable: SIGUSR2

これらの変数はPOSIXで定義された対応するシグナルの番号に束縛されています。

Variable: SIGTRAP
Variable: SIGIOT
Variable: SIGBUS
Variable: SIGSTKFLT
Variable: SIGURG
Variable: SIGXCPU
Variable: SIGXFSZ
Variable: SIGVTALRM
Variable: SIGPROF
Variable: SIGWINCH
Variable: SIGPOLL
Variable: SIGIO
Variable: SIGPWR

これらの変数はシステム依存のシグナル番号に束縛されています。 全てのシステムで全てのシグナルがサポートされているわけではありません。

それぞれのシグナル番号の他に、<sys-sigset>オブジェクトを使って シグナルの集合を扱うことができます。シグナルの集合はシグナルマスクを操作したり、 ひとつのシグナルハンドラを多数のシグナルに同時に設定したりする際に使えます。

Class: <sys-sigset>

シグナルの集合を表します。空のシグナルの集合は次の式で作成できます:

 
(make <sys-sigset>) ⇒ #<sys-sigset []>
Function: sys-sigset signal …

signal …をメンバーとする<sys-sigset>の インスタンスを作成して返します。 各signalにはシグナル番号、他の<sys-sigset>オブジェクト、あるいは #tを渡すことができます。#tを渡した場合は全てのシグナルが 対象となります。

 
(sys-sigset SIGHUP SIGINT) ⇒ #<sys-sigset [HUP|INT]>
Function: sys-sigset-add! sigset signal …
Function: sys-sigset-delete! sigset signal …

sigset<sys-sigset>オブジェクトでなければなりません。 これらの手続きはsigsetに指定されたシグナルを追加、 もしくはsigsetから指定されたシグナルを削除します。 変更されたsigsetが返されます。

signalにはシグナル番号、他の<sys-sigset>オブジェクト、あるいは #tを渡すことができます。#tを渡した場合は全てのシグナルが 対象となります。

Function: sys-sigset-fill! sigset
Function: sys-sigset-empty! sigset

システムで定義された全てのシグナルをsigsetにセット、 もしくはsigsetを空にします。

Function: sys-signal-name signal

シグナル番号の名前を返します。(シグナル番号はシステムに依存します)。

 
(sys-signal-name 2) ⇒ "SIGINT"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.7.2 シグナルの送出

シグナルを送るには、sys-killを使うことができます。 これはシステムのkill(2)のように動作します。

Function: sys-kill pid sig

[POSIX] シグナルsigを指定されたプロセス(群)に送ります。 sigは正確な正整数でなければなりません。pidは正確な整数でなければ ならず、次のルールで対象となるプロセスを指定します。

Windowsネイティブ環境では、sys-killpidに正整数か プロセスハンドル(<win:handle>のインスタンス)を取ります。 sigがサポートするのはSIGKILLSIGINTSIGABRT のみです。SIGKILLに対してはTerminateProcessを使って 対象プロセスを終了させます。SIGINTSIGABRTに対しては 対象プロセスにそれぞれCTRL_C_EVENTCTRL_BREAK_EVENTを 送ります。

POSIXのraise()に対応するScheme関数はありませんが、 (sys-kill (sys-getpid) sig) で同じことができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.7.3 シグナルの処理

Schemeでシグナルを処理する手続きを登録できます。 (マルチスレッド環境では、シグナルハンドラの設定は全てのスレッドで共有されます。 シグナルとスレッドを参照して下さい)。

シグナルがSchemeプロセスに送られると、VMはそれを記録し、 VMの状態が一貫している「安全なポイント」に達した時に処理します。 シグナルがVMに記録され、しかしまだ処理されていない状態を、 シグナルが保留されていると呼ぶことにします。

(このメカニズムのため、SIGILLのようなシグナルはSchemeレベルでは 処理できません。そのシグナルを記録した後でプロセスが意味のある処理を続行できない からです)。

VMがシグナルを処理する前に同じシグナルが到着した場合、後に到着した方の シグナルは無効になります。(これは伝統的なUnixのシグナル処理と同様です)。 言い替えれば、各VM loop毎に、シグナルハンドラは各シグナルについて たかだか1回しか呼ばれません。

同じシグナルがたくさん保留された状態になった場合、 Gaucheは異常事態が起きたとみなし (例えばCルーチンで無限ループに 入った等)、プロセスをabortします。デフォルトではこの限界は かなり低い値(3)に設定されています。これは、インタラクティブスクリプトが 反応しなくなった場合にCtrl-Cを3回打てば強制終了できる、という場合を 想定しているためです。この限界を調べるカウンタはシグナル毎にあるので、 例えばSIGHUPがひとつ、SIGINTがふたつ保留になった、 といった場合はabortしません。この限界は下で述べるset-signal-pending-limit で変更することができます。

goshインタプリタを使っている場合、デフォルトでのシグナルの処理は 次のように設定されています。

SIGABRT, SIGILL, SIGKILL, SIGCONT, SIGSTOP, SIGSEGV, SIGBUS

Schemeでは処理できません。goshではこれらのシグナルを受けると システムのデフォルトの動作をします。

SIGCHLD, SIGTSTP, SIGTTIN, SIGTTOU, SIGWINCH

goshは初期状態ではこれらのシグナルのハンドラを設定せず、 システムのデフォルトの振るまいに任せます。Schemeプログラムは必要ならば これらのシグナルのハンドラを設定できます。

SIGHUP, SIGQUIT, SIGTERM

goshはこれらのシグナルに対して、終了コード0でアプリケーションを 終了するシグナルハンドラをセットします。

SIGPIPE

Goshは何もしないシグナルハンドラを設定します。 つまり、事実上このシグナルはデフォルトで無視されます。

この設計の意図は次のとおりです。Gaucheはシグナルの処理を安全な ポイントまで遅延するため、SIGPIPEはそれを発生させた システムコールがEPIPEを返した後で処理されることになります。 この事実によって、SIGPIPEを処理する必要性は大きく低下します。 書き出した先のパイプが壊れていた場合にはEPIPE<system-error> をハンドルすれば良いからです。

UnixのデフォルトのSIGPIPE処理は、プロセスを終了させます。 これは伝統的な、パイプでつないでゆくコマンドラインツールにとっては 便利な振る舞いです。下流のコマンドが失敗した場合、上流のコマンドは SIGPIPEを受け取るので、つながれたすべてのコマンドが 静かに終了するからです。 けれども、ソケットなど他の種類の出力に対しては、このシグナルは しばしば邪魔になります。

Gaucheは、この「パイプが詰まったら終了する」という慣習を、 ポートによってサポートします。ポートを「SIGPIPEを感知する」モード にセットすることができます。そのようなポートへの書き込みが EPIPEを発生させる状況になったら、プロセスが終了します。 デフォルトでは、標準出力および標準エラー出力のポートが このモードになっています。

SIGPWR, SIGXCPU, SIGUSR1, SIGUSR2

Linuxプラットフォームでスレッドを使用している場合は、 これらのシグナルはシステムで使用されるため、Schemeからは使用できません。 他のシステムではこれらのシグナルは下記の「他のシグナル」と同じ動作と なります。

他のシグナル

goshはデフォルトのシグナルハンドラを設定します。 デフォルトのシグナルハンドラは<unhandled-signal-error>コンディションを 通知します(コンディション参照)。 Schemeプログラムはシグナル毎に独自のハンドラを設定することが可能です。

goshでなく、他のアプリケーションに埋め込まれたGaucheを使っている場合、 Schemeレベルでのシグナルの使用をアプリケーションが制限している場合があります。

Schemeからシグナルハンドラを設定するには以下の手続きを使って下さい。

Function: set-signal-handler! signals handler :optional sigmask

signalsはシグナル番号か<sys-sigset>オブジェクト、 handler#t#f#<undef>、 一つの引数を取る手続きのいずれかでなければなりません。 handlerが手続きの場合、プロセスが指定されたシグナル(のうちのいずれか)を 受けた時に、そのシグナル番号を引数としてhandlerが呼ばれます。

デフォルトでは、handlerは、signalsに含まれる シグナルが(その時点で有効なシグナルマスクに加えて)ブロックされた状態で 実行されます。オプショナルなsigmask引数に <sys-sigset>オブジェクトを渡すことで、 ブロックすべきシグナルを明示することもできます。 ただ、シグナルマスクはスレッド毎であることに注意して下さい。 もし複数のスレッドがあるシグナルをブロックしていない場合、例えsigmask を指定していたとしても、ひとつのスレッドでhandlerを実行中に 別のスレッドで並行してhandlerが呼ばれる可能性はあります。 各スレッドのシグナルマスクを適切に設定することでそのようなケースを 避けるようにして下さい。

handlerの中でできる操作にはほとんど制限がありません。 handlerからエラーを投げたり、他の場所で補捉された継続を呼ぶことも できます。但し、handler内で補捉した継続はhandlerから 戻った時点で無効になります。

handler#tの場合、指定されたシグナルにはオペレーティングシステムの デフォルトの振るまいが設定されます。 handler#fの場合、指定されたシグナルは無視されます。

handler#<undef> (未定義値参照) であった場合は、 Gaucheはその時点のOSレベルのシグナルハンドラを変更しません。 この引数はset-signal-handler!に対してはあまり 意味を持ちません; 何もせずに返るだけだからです。 しかし、get-signal-handler#<undef>が 返った場合、Gaucheがそのシグナルハンドラを一度もいじっていないことを 示します。 (一度でもGaucheがシグナルハンドラをインストールした場合、 get-signal-handler#<undef>を再び 返すことはありません。)

マルチスレッドプログラムでは、 シグナルハンドラの設定はスレッド間で共有されることに注意して下さい。 ハンドラはシグナルを受けたスレッドで実行されます。 詳しくはシグナルとスレッドを参照して下さい。

Function: get-signal-handler signum
Function: get-signal-handler-mask signum

シグナルsignumに設定されたハンドラもしくはシグナルマスクを それぞれ返します。get-signal-handlerが返す値の意味については set-signal-handler!を参照してください。

Function: get-signal-handlers

現在の全てのシグナルハンドラの設定を連想リストにして返します。 返されるリストの各要素のcarには<sys-sigset>オブジェクトが、 cdrにはそれらのシグナルに対応するハンドラ(手続きもしくはブール値)がセットされて います。

Function: get-signal-pending-limit
Function: set-signal-pending-limit limit

それぞれ、同一のシグナルをいくつまで保留することを許すかの現在の限界値を 読み出し、また設定します。 同一のシグナルが限界値を越える回数、処理されないまま保留された場合、 Gaucheはプロセスを強制終了します。本節の冒頭の説明を参照してください。 limitは非負の正確な整数でなければなりません。 現在の実装では、limitに設定できる最大値は255です。 また、limitに0を設定すると、無制限にシグナルを保留できるようになります。

Macro: with-signal-handlers (handler-clause …) thunk

thunkの実行中だけシグナルハンドラを一時的に設定する便利なマクロです (このマクロは便利ですが下に述べるような多少危険な性質もあるので、 注意して使って下さい)。

handler-clauseは以下のいずれかの形式です

(signals expr …)

signalsは、評価された時に単独のシグナル番号、シグナル番号のリスト、 あるいは<sys-sigset>オブジェクトを生成する式でなければなりません。 signalsに含まれるシグナルを受け取った時に、expr …を 評価するようなハンドラを設定します。

(signals => handler)

signalsは上と同じです。signalsに含まれるシグナルに 対して、処理handlerを設定します。

handler#t#f、一つの引数を取る手続きのいずれか でなければなりません。 handlerが手続きの場合、プロセスが指定されたシグナル(のうちのいずれか)を 受けた時に、そのシグナル番号を引数としてhandlerが呼ばれます。 handler#tの場合、指定されたシグナルにはオペレーティングシステムの デフォルトの振るまいが設定されます。 handler#fの場合、指定されたシグナルは無視されます。

thunkから制御が抜けた時に、with-signal-handlersが呼ばれた時点での シグナルハンドラが再設定されます。

注意: このフォームで一つ以上のシグナルハンドラを設定する場合、 それらは順にシステムに設定されます。全てのハンドラの設定が終る前に シグナルが届いた場合、シグナルハンドラの設定や再設定が不完全なままになる かもしれません。また、シグナルハンドラはグローバルな設定であり、 「スレッドローカル」なハンドラを設定することはできませんが、 with-signal-handlersの形式はそれを誤解させるかもしれません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.7.4 シグナルのマスクと待機

Schemeプログラムで、送出がブロックされるシグナルの集合であるシグナルマスク をセットできます。プロセスで完全にブロックされるシグナルが送出されると、 そのシグナルは“保留”となります。保留されたシグナルは、指定されたシグナルを ブロックしないようにシグナルマスクが変更されると送出されるかもしれません。 (しかし、保留されたシグナルがキューに入れられるかどうかはオペレーティング システムに依存します。)

マルチスレッドの環境では、スレッド毎に独自のシグナルマスクを持ちます。

Function: sys-sigmask how mask

現在のスレッドのシグナルマスクを変更し、以前のシグナルマスクを返します。 maskには新しいマスクを指定する<sys-sigset>オブジェクトか、 あるいはマスクを変更せず現在のマスクを得るだけなら#fを渡します。

mask<sys-sigset>オブジェクトを渡した場合、 引数howは以下の整数定数のうちの1つでなければなりません。

SIG_SETMASK

maskをそのスレッドのシグナルマスクとしてセットします。

SIG_BLOCK

そのスレッドのシグナルマスクにmaskにあるシグナルを追加します。

SIG_UNBLOCK

そのスレッドのシグナルマスクからmaskにあるシグナルを削除します。

Function: sys-sigsuspend mask

アトミックに、スレッドのシグナルマスクをmaskにセットし、 呼び出しているスレッドを一時停止します。ブロックされておらずシグナル ハンドラがインストールされているシグナルが送出されると、関連 付けられたハンドラが呼ばれ、sys-sigsuspendは戻ります。

Function: sys-sigwait mask

[POSIX] mask<sys-sigset>オブジェクトでなければなりません。 アトミックに、保留されたシグナルからmaskにあるシグナルの1つを クリアし、クリアしたシグナルの番号を返します。maskにある シグナルが1つも保留されていなければ、sys-sigwaitはシグナルが 届くまでブロックします。

sys-sigwaitを呼ぶ前に、maskにある全てのシグナルを、 全スレッドからブロックしておく必要があります。シグナルをブロックして いないスレッドがある場合、sys-sigwaitの動作は未定義です。

註:sys-sigwaitはシステムのsigwait関数を呼び出しますが、 この関数は待つべきシグナルにシグナルハンドラが設定されていた場合の 振る舞いが未定義となっています。困ったことになるのを避けるために、 sys-sigwaitはまずmaskに含まれるシグナルに ハンドラが設定されていたらそれをSIG_DFLにリセットしてから sigwaitを呼び出し、それが戻った後でハンドラを元に戻します。 sys-sigwaitが待っている間に他のスレッドでシグナルハンドラを 変更してはいけません。そうした場合の動作は不定です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.7.5 シグナルとスレッド

シグナルのセマンティクスはマルチスレッド環境では少々複雑に見えます。 しかし、いくつかのルールを覚えてしまえば、とても理解しやすいものでも あります。さらにGaucheでは、プログラマの簡単に使えるようにデフォルトの 振る舞いをセットアップしています。

細かいことは知りたくないという場合は、 次の1つのことだけを覚えておいて下さい。 デフォルトでは、シグナルはメインスレッドで処理されます。 しかし、メインスレッドがmutexや条件変数で一時停止している場合は、 シグナルは全く処理されないので注意が必要です。

詳細に興味がある場合は、ここにルールがあります。

これらのルールにはいくつかの暗黙の了解があります。

特定のシグナルをブロックしないスレッドが1つ以上ある場合、 どのスレッドがそのシグナルを受け取るかを知る術はありません。 そのような状況は、GaucheにおいてはCプログラムよりもさらに不便です。 なぜなら、受信側のスレッドがmutexや条件変数で待機している場合、 シグナル処理は無期限に遅延されうるからです。 したがって、それぞれのシグナルについて、それを受け取ることのできるスレッドが 常にただの1つしかないようにすることを推奨します。

Gaucheでは、make-thread(スレッド手続き参照) で作られた全てのスレッドは、デフォルトで全てのシグナル(予約済みを除く) をブロックします。これは、全てのシグナルがメインスレッドへ 送られるということです。

もう1つの戦略は、シグナル処理のみを行うスレッドを作る方法です。

もしシステムがsys-sigwaitをサポートしていれば、ハンドル したい全てのシグナルを全てのスレッドでブロックしておき、シグナル 処理専用のスレッドでsys-sigwaitを呼び出してシグナルの 受信を待つことができます。sys-sigwaitの返り値がシグナル番号 なので、その値でディスパッチしてください。この方法ではシグナル ハンドラが呼び出されることはありません。

sys-sigwaitを使わないのであれば、メインスレッドでシグナルを ブロックして、シグナル処理専用スレッドを作り、そのスレッドで全ての シグナルを受け付けることができます。そのようなスレッドはsys-pause で単にループしていれば良いでしょう。sys-pauseがシグナルで中断 されると、シグナルハンドラがシグナル処理専用スレッドで呼び出されます。

 
(thread-start!
  (make-thread
    (lambda ()
      (sys-sigmask SIG_SETMASK (make <sys-sigset>)) ;;empty mask
      (let loop () (sys-pause) (loop)))))

複雑なアプリケーションでは、正確にスレッド毎のシグナル処理を 制御したいかもしれません。それは、いつでも、指定されたスレッドのみが 望むシグナルをブロックしないようにすれば、可能です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.8 システムへの問い合わせ

Function: sys-uname

[POSIX] 次の5要素のリストを返します。 (sysname nodename release version machine)

Function: sys-gethostname

ホスト名を返します。システムでgethostname()が使えない場合、 sys-unameが返すリストの2番目の要素が使われます。

Function: sys-getdomainname

ドメイン名を返します。システムでgetdomainname()が使えない場合、 "localdomain"が返されます。

Function: sys-getcwd

[POSIX] 現在の作業ディレクトリを文字列で返します。 システムから現在の作業ディレクトリが得られない場合は、エラーが通知されます。 sys-chdir(他のファイル操作参照)、current-directory (ディレクトリユーティリティ参照)も参照して下さい。

Function: sys-getgid
Function: sys-getegid

[POSIX] 現在のプロセスの実グループIDと実効グループIDをそれぞれ整数で返します。 返されたグループIDからグループ名その他の情報を得るには sys-gid->group-namesys-getgrgidを使って下さい (Unixのグループとユーザ参照)。

Function: sys-setgid gid

[POSIX] 現在のプロセスの実効グループIDをセットします。

Function: sys-getuid
Function: sys-geteuid

[POSIX] 現在のプロセスの実ユーザIDと実効ユーザIDをそれぞれ整数で返します。 返されたユーザIDからユーザ名その他の情報を得るには sys-uid->user-namesys-getpwuidを使って下さい (Unixのグループとユーザ参照)。

Function: sys-setuid uid

[POSIX] 現在のプロセスの実効ユーザIDをセットします。

Function: sys-getgroups

[POSIX] 補助的なグループのIDの整数のリストを返します。

Function: sys-setgroups gids

現在のプロセスのグループIDのリストを、 引数gidsで与えたグループIDのリストに置き換えます。 呼び出すプロセスは適切な特権を持っていなければなりません。

この手続きは、機能識別子gauche.sys.setgroupsがあるときのみ 使えます。ポータブルなコードではcond-expandを用いてください。

 
(cond-expand
  [gauche.sys.setgroups (sys-setgroups '(0 1))]
  [else])
Function: sys-getlogin

[POSIX] 現在のプロセスの制御端末にログインしているユーザの名前を文字列で 返します。システムがその情報を決定できない場合、#fが返されます。

Function: sys-getpgrp

[POSIX] 現在のプロセスのプロセスグループIDを返します。

Function: sys-getpgid pid

pidで指定されたプロセスのプロセスグループIDを返します。 pidが0の場合、現在のプロセスが使われます。

getpgid()はPOSIXではないことに注意して下さい。 システムにgetpgid()がない場合、pidが0ならば sys-getpgidがまだ動作しますが(それは単にsys-getpgrpを呼びます) 、pidが0でない場合はエラーが通知されます。

Function: sys-setpgid pid pgid

[POSIX] プロセスpidのプロセスグループIDをpgidにセットします。 pidが0ならば、現在のプロセスのプロセスIDが使われます。 pgidが0ならば、pidで指定されたプロセスのプロセスIDが 使われます。 (したがって、sys-getpgid(0, 0)は、現在のプロセスのプロセス グループIDを現在のプロセスIDにセットします。)

Function: sys-setsid

[POSIX] 呼んでいるプロセスがプロセスグループリーダでなければ、 新しいセッションを作ります。

Function: sys-getpid
Function: sys-getppid

[POSIX] 現在のプロセスIDと親プロセスのIDをそれぞれ返します。

Function: sys-times

[POSIX]

Function: sys-ctermid

[POSIX] プロセスの制御端末の名前を返します。 これは単に``/dev/tty''かもしれません。sys-ttynameも参照して下さい。

Function: sys-getrlimit resource
Function: sys-setrlimit resource current :optional maximum

[POSIX] プロセスのリソースリミットを取得あるいは設定します。 resourceはリソースの種類を指定する整数値です。 以下にリストする定数が定義されています。 (bsdlinuxとマークされている値は、POSIXでは 定義されていないけれどBSDもしくはLinuxで定義されていることを示します。 他のプラットフォームでそれらが定義されているかどうかは、 該当システムのgetrlimitのマニュアルを参照してください。)

 
RLIMIT_AS                      RLIMIT_CORE
RLIMIT_CPU                     RLIMIT_DATA
RLIMIT_FSIZE                   RLIMIT_LOCKS
RLIMIT_MEMLOCK (bsd/linux)     RLIMIT_MSGQUEUE (linux)
RLIMIT_NICE (linux)            RLIMIT_NOFILE
RLIMIT_NPROC (bsd/linux)       RLIMIT_RSS (bsd/linux)
RLIMIT_RTPRIO (linux)          RLIMIT_SIGPENDING (linux)
RLIMIT_SBSIZE                  RLIMIT_STACK
RLIMIT_OFILE
Function: sys-strerror errno

errorはシステムエラー番号を表現する非負正確整数でなければなりま せん。この関数はエラーを説明する文字列を返します。

errnoを表現するのに、以下の定義済み定数が使えます。各定数はシス テムエラーを表現する非負正確整数に束縛されています。実際の値はシステム ごとに違い、またシステムによっては定義されていない定数があるということ に注意してください。

 
E2BIG             EHOSTDOWN         ENETDOWN          ENXIO
EACCES            EHOSTUNREACH      ENETRESET         EOPNOTSUPP
EADDRINUSE        EIDRM             ENETUNREACH       EOVERFLOW
EADDRNOTAVAIL     EILSEQ            ENFILE            EPERM
EADV              EINPROGRESS       ENOANO            EPFNOSUPPORT
EAFNOSUPPORT      EINTR             ENOBUFS           EPIPE
EAGAIN            EINVAL            ENOCSI            EPROTO
EALREADY          EIO               ENODATA           EPROTONOSUPPORT
EBADE             EISCONN           ENODEV            EPROTOTYPE
EBADF             EISDIR            ENOENT            ERANGE
EBADFD            EISNAM            ENOEXEC           EREMCHG
EBADMSG           EKEYEXPIRED       ENOKEY            EREMOTE
EBADR             EKEYREJECTED      ENOLCK            EREMOTEIO
EBADRQC           EKEYREVOKED       ENOLINK           ERESTART
EBADSLT           EL2HLT            ENOMEDIUM         EROFS
EBFONT            EL2NSYNC          ENOMEM            ESHUTDOWN
EBUSY             EL3HLT            ENOMSG            ESOCKTNOSUPPORT
ECANCELED         EL3RST            ENONET            ESPIPE
ECHILD            ELIBACC           ENOPKG            ESRCH
ECHRNG            ELIBBAD           ENOPROTOOPT       ESRMNT
ECOMM             ELIBEXEC          ENOSPC            ESTALE
ECONNABORTED      ELIBMAX           ENOSR             ESTRPIPE
ECONNREFUSED      ELIBSCN           ENOSTR            ETIME
ECONNRESET        ELNRNG            ENOSYS            ETIMEDOUT
EDEADLK           ELOOP             ENOTBLK           ETOOMANYREFS
EDEADLOCK         EMEDIUMTYPE       ENOTCONN          ETXTBSY
EDESTADDRREQ      EMFILE            ENOTDIR           EUCLEAN
EDOM              EMLINK            ENOTEMPTY         EUNATCH
EDOTDOT           EMSGSIZE          ENOTNAM           EUSERS
EDQUOT            EMULTIHOP         ENOTSOCK          EWOULDBLOCK
EEXIST            ENAMETOOLONG      ENOTTY            EXDEV
EFAULT            ENAVAIL           ENOTUNIQ          EXFULL
EFBIG
Function: sys-errno->symbol k
Function: sys-symbol->errno symbol

これらの手続きは、整数のエラー番号と、Unixでのエラー名を表すシンボル(例:EINTR)とを 相互に変換します。

与えられたエラー番号もしくはエラー名が実行中のプラットフォームで有効でない場合は #fが返されます。使えるかもしれないエラー名については上のsys-strerror の項を参照してください。

有効なエラー名およびその整数値はプラットフォーム毎に異なります。 これらの手続きは、システムエラーを扱うメタなコードをポータブルに書く時に役立つでしょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.9 時間

Gaucheでは時間は2種類の表現を持ちます。ひとつはPOSIX APIとコンパチブルな 表現で、もう一つはSRFI-18、SRFI-19、SRFI-21とコンパチブルな表現です。 多くの手続きはどちらの表現も理解しますが、そうでない場合は 適用可能な表現を’POSIX time’または’SRFI time’と表記します。

POSIX timeは実数で、Unix Epoch(Jan 1, 1970, 0:00:00GMT)からの秒数で 表現されます。POSIXのtime(2)に対応するsys-time手続きは この表現を返します。

SRFI互換の時間は<time>クラスのインスタンスとして表現され、 秒、およびナノ秒のスロットを持ちます。 また、時間の種別(UTC、TAI、期間、プロセス時間、他)も保持しています。 Current-timeはこの表現を返します。

POSIX time

Function: sys-time

[POSIX] 現在の時間を POSIX 時間(エポック(00:00:00 UTC, January 1, 1970) からの秒数)で返します。マシンのアーキテクチャによっては、不正確数で あるかもしれません。

Function: sys-gettimeofday

2つの値を返します。1970/1/1 0:00:00 UTC を基準として、 1つ目の値は秒数、2つ目の値は端数をマイクロ秒で表したものです。 システムが gettimeofday 呼び出しをサポートしていない場合、 この手続きは time() を呼び出し、その場合はマイクロ秒の 部分はいつも 0 です。

Builtin Class: <sys-tm>

カレンダーである、struct tm を表します。以下のスロットを持ちます。

Instance Variable of <sys-tm>: sec

秒。0-61。

Instance Variable of <sys-tm>: min

分。0-59。

Instance Variable of <sys-tm>: hour

時。0-23。

Instance Variable of <sys-tm>: mday

月の日。1 から数える。1-31。

Instance Variable of <sys-tm>: mon

月。0 から数える。0-11。

Instance Variable of <sys-tm>: year

1900年からの年数。例えば、102 なら 2002年。

Instance Variable of <sys-tm>: wday

曜日。日曜は 0 .. 土曜は 6。

Instance Variable of <sys-tm>: yday

1年の中での日数。1月1日は 0 .. 12月31日は 364 か 365。

Instance Variable of <sys-tm>: isdst

夏時間が有効であるかどうかを指定するフラグ。有効なら正の数、無効なら 0、 分からなければ負の数。

Function: sys-gmtime time
Function: sys-localtime time

[POSIX] time<sys-tm> オブジェクトに変換します。 標準時での表現かローカルタイムゾーンでの表現かで使い分けます。 Time は POSIX 時間か SRFI 時間です。

Function: sys-ctime time

[POSIX] time を POSIX の ctime() を使って文字列表現に変換します。 Time は POSIX 時間か SRFI 時間です。

Function: sys-difftime time1 time0

[POSIX] 2つの時間の差を、秒の実数で返します。Time0time1 は POSIX 時間か SRFI 時間です。

Function: sys-asctime tm

[POSIX] <sys-tm> オブジェクトである tm を文字列表現に変換します。

Function: sys-strftime format tm

[POSIX] <sys-tm> オブジェクトである tm を、フォーマット文字列 format に従って文字列表現に変換します。

Function: sys-mktime tm

[POSIX] ローカルタイムとして表現された <sys-tm> オブジェクトである tm を POSIX 時間(エポックからの秒数)に変換します。

Function: sys-tm->alist tm

(Deprecated function)

SRFI time

Builtin Class: <time>

ある時刻を表す<time>オブジェクトです。

Instance Variable of <time>: type

timeの型を表します。デフォルトはtime-utcで、Unixエポックからの 秒数を表します。SRFI-19(srfi-19 - 時間のデータ型と手続き参照) ではさらに型が追加されています。

Instance Variable of <time>: second

timeの秒の部分です。

Instance Variable of <time>: nanosecond

timeのナノ秒の部分です。

Function: current-time

[SRFI-18][SRFI-21] 現在の時間をtime-utcで表す<time>オブジェクトを返します。 srfi-19 - 時間のデータ型と手続きでは、current-timeが再定義され、 timeの型を指定するオプショナル引数を受け付けるようにされています。

Function: time? obj

[SRFI-18][SRFI-19][SRFI-21] objがtimeオブジェクトなら#tを返します。

Function: time->seconds time
Function: seconds->time seconds

[SRFI-18][SRFI-21] timeオブジェクトと秒数(POSIX時間)を変換します。 time->secondsの引数timeは、<time>オブジェクトで なければなりません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.10 プロセス管理

以下の手続きは、システムコールに対する直接的なインタフェースを提供します。 gauche.process - 高レベルプロセスインタフェースも合わせて参照して下さい。 より高レベルの便利な手続きが定義されています。

Fork and exec

Function: sys-system command

[POSIX] サブプロセスでcommandを実行します。commandは通常、 shへ渡されるので、シェルのメタキャラクタは解釈されます。

この関数は、system()が返した整数値を返します。 POSIXはsystem()が何を返すかを定義していないので、 戻り値をポータブルな方法で解釈することができません。

Windowsネイティブな環境では、引数はcmd.exeに渡されます。

Function: sys-fork

[POSIX] 現在のプロセスをフォークします。子プロセスにいるならば0が返り、 親プロセスにいるならば子プロセスのプロセスIDが返ります。 全ての開かれているファイルディスクリプタは、親プロセスと子プロセスで 共有されます。詳細は、システムのfork(2)を参照して下さい。

子プロセスがsys-execを呼ぶ代わりにSchemeコードを実行して 終了する場合は、exitではなくsys-exitを呼ぶ必要があります。 通常のexitの呼び出しはファイルバッファのフラッシュを試み、 OSによっては親のファイルバッファを混乱させるでしょう。

sys-forkは複数のスレッドが走っている時には安全ではないことに 注意してください。fork(2)システムコールはプロセスのメモリイメージを 複製しますが、それには他のスレッドでロックされているmutexも含まれます。 もしsys-forkの瞬間に他のスレッドが何かをロックしていたとすれば、 子プロセスでもその資源はロックされたままになりますが、 その時子プロセスはもはやその資源をアンロックするスレッドを持っていません。 (これは内部で使っているmutexにも起こり得るため、Schemeレベルでmutexを 全く使っていないとしても防ぐことはできません)。

やりたいことが、マルチスレッドアプリケーションで別プログラムをspawnする ことであるなら、下に説明するsys-fork-and-execを使ってください。 どうしても別プロセスでSchemeコードを走らせたいのなら、よくある方法は、 アプリケーションの起動後すぐにマネージャプロセスとなる子プロセスをfork しておき、新たなプロセスが欲しくなったらそのマネージャプロセスに forkを依頼するという方法です。

Windowsネイティブな環境ではこの手続きは使えません。

Function: sys-exec command args :key directory iomap sigmask

[POSIX+] commandを引数のリストであるargsを伴って実行します。 現在のプロセスイメージはcommandに置き換えられるので、 この関数は戻りません。

argsの全ての要素は文字列でなければなりません。 argsの最初の要素は、argv[0]、すなわちプログラム名として 使われます。

キーワード引数directoryは文字列によるディレクトリ名 か#fでなければなりません。 文字列だった場合、sys-execはプログラムを実行する前に カレントワーキングディレクトリを指定ディレクトリに移動します。

キーワード引数のiomapは、与えられた場合は、開かれている ファイルディスクリプタがどのように扱われるかを指定します。 この引数は以下のフォーマットでなければなりません。

 
((to-fd . from-port-or-fd) …)

to-fdは整数でなければならず、from-port-or-fdは整数のファイル ディスクリプタかポートでなければなりません。 リストのそれぞれの要素は、現在のプロセスのfrom-port-or-fdの ファイルディスクリプタを、実行されているプロセスのファイルディスクリプタ to-fdにマップさせます。

iomapが与えられると、iomapリストで指定されていないいかなるファイル ディスクリプタもexec()の前に閉じられます。さもなければ、現在のプロセスの 全てのファイルディスクリプタは開かれたままになります。

 
(sys-exec "ls" '("ls" "-l")) ⇒ ;; ls is executed.

(let ((out (open-output-file "ls.out")))
  (sys-exec "ls" '("ls" "-l") :iomap `((2 . 1) (1 . ,out)))
   ⇒
  ;; ls is executed, with its stderr redirected
  ;; to the current process's stdout, and its
  ;; stdout redirected to the file "ls.out".

sigmaskキーワード引数には、<sys-sigset>のインスタンスか #fを渡すことができます (シグナルマスクについてはシグナルを 参照して下さい)。<sys-sigset>のインスタンスを渡した場合、 この関数を呼び出したスレッドのシグナルマスクが、exec(2)を呼ぶ 直前にそれによって置き換えられます。 これは、全てのシグナルをブロックしているスレッドから外部プログラムを走らせる ような場合に便利です (シグナルとスレッドで述べるように、 新たなスレッドは全てのシグナルをデフォルトでブロックします)。 このような場合にsigmaskを設定しないと、execされたプロセスは スレッドのシグナルマスクを継承し、ほとんどのシグナルを受け付けないプロセスと なってしまうからです。通常、それはあまり便利ではありません。

sys-execがエラーに遭遇すると、たいていの場合エラー条件を投げます。 しかし一旦ファイルディスクリプタの順番が変えられると、合理的な方法でエラーを処理する ことは実行不可能です(標準エラーがまだ有効かどうかさえ知る術がありません)。 したがって、その場合はGaucheはエラー時には単に終了するだけです。

Windowsネイティブ環境では、標準入力、標準出力、標準エラー出力に 関するリダイレクションのみが処理されます。Windowsはプロセス間通信としての シグナルをサポートしないので、シグナルマスクは無視されます。

Function: sys-fork-and-exec command args :key directory iomap sigmask detached

sys-execと同じですが、ファイルディスクリプタとシグナルマスクを変更して execvp(2)を実行する直前に、fork(2)を実行します。 子プロセスのプロセスidを返します。引数の意味はsys-execと同じです。

他のスレッドが走っている環境下で別プログラムをspawnしたい場合は、 sys-forksys-execを別々に使うのではなく、 この手続きを使ってください。 この手続き中では、fork(2)execvp(2)の間で メモリアロケーションもロックの獲得も行われないため、 マルチスレッド環境で実行しても安全になっています。

Windowsネイティブ環境では、この手続きはプロセスIDではなく 作られたプロセスに対するWindowsのハンドルオブジェクト (<win:handle>) を返します。Windowsプロセスハンドル 特有の手続きについては後に述べます。

また、sys-exec同様、Windowsネイティブ環境では、標準入力、標準出力、標準エラー出力に 関するリダイレクションのみが処理されます。

detachedキーワード引数に真の値が与えられた場合、 実行されるプロセスは現在のプロセスグループからは切り離されます。 つまり、現在のプロセスグループに送られるシグナルを、新しいプロセスは受け取りません。 この動作はデーモンプロセスを作る際の標準的な手順のひとつです。

Unixでは、実行されるプロセスがsetsid(2)を呼んで 独自のセッションを作るだけでなく、余分なfork(2) を実行することで親をinitプロセス (pid=1) に切り替えます。 (つまり、実行されるプロセスは実は呼び出すプロセスの孫にあたります。 親子関係はなくなりますが。返されるpidは実際に走っているこのプロセスのもので、 すぐに終了する中間プロセスのものではありません。)

Windowsネイティブ環境では、このフラグがあると プロセス作成時にCREATE_NEW_PROCESS_GROUPフラグが使われます。

Wait

Function: sys-wait

[POSIX] システムのwait(2)を呼びます。プロセスは、子プロセスの1つが 終了するまでその実行を一時停止します。2つの正確整数、1つ目は子プロセスのID、 2つ目はステータスコードを返します。ステータスコードは以下の関数によって 解釈できます。

Function: sys-waitpid pid :key nohang untraced

[POSIX] これは、waitの拡張バージョンである、waitpid(3)への インターフェースです。

pidは、待つべき子プロセスを指定する正確な整数です。正の整数ならば、 特定の子プロセスを待ちます。0ならば、このプロセスグループのいずれかの メンバを待ちます。-1ならば、いずれかの子プロセスを待ちます。 -1よりも小さければ、プロセスグループIDがpidの絶対値と等しいいずれかの 子プロセスを待ちます。

待つべき子プロセスが存在しない場合や、pidに具体的なプロセスIDが 与えられたがそれが現在のプロセスの子プロセスでない場合は エラー(<system-error>, ECHILD)となります。

キーワード引数nohangに真値が指定されていなければ、 呼んでいるプロセスは、それらの子プロセスのうちの1つが終了するまで一時停止します。

キーワード引数untracedに真値が指定されていれば、 停止した子プロセスのステータスも返ります。

戻り値は2つの正確整数で、1つ目は子プロセスのID、2つ目はステータスコードです。 nohangが真で子プロセスのステータスが利用できない場合は、1つ目の値は0です。

Windowsネイティブ環境では、この関数はpidにWindowsプロセスハンドル (<win:handle>)オブジェクトを受け取ることもできます。 その場合はそのハンドルの指すプロセスの終了を待ちます。 また、-1を渡していずれかの子プロセスの終了を待つこともできますが、 特定のプロセスグループの中の子プロセスを待つことはできません。

Function: sys-wait-exited? status
Function: sys-wait-exit-status status

[POSIX] 引数はsys-waitsys-waitpidの2番目の値として返される 終了ステータスです。子プロセスが正常終了したら、sys-wait-exited?#tを返します。sys-wait-exit-statusは、子プロセスが exit(2)に渡した終了コードか、main()の戻り値を返します。

Function: sys-wait-signaled? status
Function: sys-wait-termsig status

[POSIX] 引数は、sys-waitsys-waitpidの2つ目の戻り値である終了ステータスです。 子プロセスが捕捉されないシグナルで終了した場合は、sys-wait-signaled?#tを返します。 sys-wait-termsigは、子プロセスを終了したシグナルの番号を返します。

Function: sys-wait-stopped? status
Function: sys-wait-stopsig status

[POSIX] 引数は、sys-waitpidの2つ目の戻り値である終了ステータスです。 子プロセスが停止されれば、sys-wait-stopped?#tを 返します。このステータスは、sys-waitpiduntraced引数に 真値を持っている場合にのみ捕捉されます。sys-wait-stopsigは 子プロセスを停止させたシグナルの番号を返します。

Windowsネイティブ環境では、終了コードはUnixのように構造化されておらず、 プロセスが自発的に終了したか強制的に終了させられたかを判断する一般的な方法は ありません。Gauche自身は他のプロセスをsys-killで 終了させる場合に終了コード#xff09を送り、上のsys-wait-* 手続きもそれに合わせてあります。従ってGaucheによって終了させられたプロセスでは sys-wait-signaled?でそのことを検出できる可能性が高いでしょう。 (Windows上のシグナルのサポートについてはシグナルを参照してください。) Windowsネイティブ環境では、sys-wait-stopped?が真を返すことは (今のところ)ありません。

Windows特有のユーティリティ

以下の手続きはWindowsのプロセスハンドルにアクセスするものです。 Windowsネイティブ環境でのみ提供されます。

Function: sys-win-process? obj

[Windows] objがWindowsプロセスハンドルの場合に#tを、それ以外では#fを 返します。

Function: sys-win-process-pid handle

[Windows] Windowsプロセスハンドルhandleが示すプロセスの、整数のpid値を 返します。handleが有効なWindowsプロセスハンドルでない場合はエラーが 通知されっす。

プロセスハンドルからプロセスIDを得るAPIはWindows XP SP1から提供されるように なりました。それ以前のWindowsでこの手続きを呼ぶと-1が返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.11 I/Oの多重化

select(2)へのインターフェース関数です。 これらのプリミティブの上に構築された高次元のインターフェースが 提供されています。gauche.selector - 簡単なディスパッチャを 参照して下さい。

Builtin Class: <sys-fdset>

ファイルディスクリプタの集合であるfd_setを表します。 makeメソッドによって、空のファイルディスクリプタの集合を作れます。

 
(make <sys-fdset>)
Function: sys-fdset elt …

elt …で指定されたファイルディスクリプタを持つ 新たな<sys-fdset>のインスタンスを作成して返します。 各eltは、ファイルディスクリプタを指定する整数、ポート、 もしくは<sys-fdset>のインスタンスでなければなりません。 最後の場合は、与えられたfdsetに含まれるディスクリプタが 新たなfdsetにコピーされます。

Function: sys-fdset-ref fdset port-or-fd
Function: sys-fdset-set! fdset port-or-fd flag

fdsetの特定のファイルディスクリプタビットを取得・セットできます。 port-or-fdは関連付けられたファイルディスクリプタを持っていないポートで、 sys-fdset-ref#fを返し、sys-fdset-set!fdsetを 変更しません。flagは真偽値でなければなりません。

下記のように、sys-fdset-refのジェネリックなセッタが使えます。

 
(set! (sys-fdset-ref fdset port-or-fd) flag)
  ≡ (sys-fdset-set! fdset port-or-fd flag)
Function: sys-fdset-copy! dest-fdset src-fdset

src-fdsetの内容をdest-fdsetにコピーします。 dest-fdsetを返します。

Function: sys-fdset-clear! fdset

fdsetの内容をクリアし、それ自身を返します。

Function: sys-fdset->list fdset
Function: list->sys-fdset fds

fdsetを整数のファイルディスクリプタのリストに変換し、またその逆を行います。 実際は、list->sys-fdset(lambda (fds) (apply sys-fdset fds)) のように動作するため、 整数のファイルディスクリプタ以外にポートや他のfdsetを与えることもできます。

Function: sys-fdset-max-fd fdset

fdsetにある最大のファイルディスクリプタ番号を返します。

Function: sys-select readfds writefds exceptfds :optional timeout
Function: sys-select! readfds writefds exceptfds :optional timeout

ステータスを変更するためにファイルディスクリプタの集合を待ちます。 readfdswritefdsexceptfdsは、注目するファイル ディスクリプタの集合を表す<fdset>オブジェクトです。 readfdsにあるファイルディスクリプタは、文字を読み込む準備が 出来たかどうかを検査するために監視されています。 writefdsにあるファイルディスクリプタはそこへ書き込むことができる ようになったか監視されています。exceptfdsにあるファイルディスクリプタは 例外のために監視されています。条件を監視する必要がない場合は、これらの引数の 1つ以上に#fを渡すことができます。

timeoutsys-selectが条件の変更を待つ最大の時間を指定します。 それは、マイクロ秒を表す実数か、1つ目が秒数で2つ目がマイクロ秒である 2つの整数からなるリストです。#fを渡すと、sys-selectは 永久に待ちます。

sys-selectは4つの値を返します。1つ目の値は、ステータスの変更を 検知したディスクリプタの数です。timeoutが経過したら0になるでしょう。 2、3、4番目の値は、それぞれ読み込み、書き出し、例外のステータスを変更 するディスクリプタの集合を含む<fdset>オブジェクトです。 readfdswritefdsexceptfdsのうちの1つ以上に#fを 渡すと、対応する戻り値は#fになります。

sys-select!の仲間は、それが渡された引数<fdset>を変更することを 除けば、sys-selectと同じように動作します。 sys-selectは、新しい<fdset>オブジェクトを作り、その 引数を変更しません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.12 ガベージコレクション

ガベージコレクタは必要になった時に自動的に走るので、普段気にする必要はないでしょう。 けれどももし気にする必要が出てきた時は、いくつか使える手続きがあります。

Function: gc

フルGCを起動します。GCの影響をなるべく排除したいコードがある場合、 その直前にこの手続きを呼ぶことで影響を減らせるかもしれません。

Function: gc-stat

GCに関する統計情報を返します。返り値はリストのリストで、 内側のリストはキーワードと対応する数値からなります。 現在、返されるキーワードは :total-heap-size:free-bytes:bytes-since-gc:total-bytesです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.25.13 その他のシステムコール

Function: sys-pause

[POSIX] シグナルキャッチ関数の実行かプロセスの終了を指示するシグナルを 受け取るまでプロセスを一時停止します。この関数は、シグナルキャッチ関数が 戻ったときにのみ戻ります。戻り値は未定義です。

Schemeレベルでは、pause()を呼ぶだけでは上記のセマンティクスを 満足しません。内部的にこの手続きは現在のシグナルマスクを伴って、 sigsuspend()を呼びます。

Function: sys-alarm seconds

[POSIX] SIGALRMシグナルがseconds秒後に送出されるように準備します。 以前のアラームクロックのセッティングはキャンセルされます。 secondsに0を渡すと、新しいアラームをスケジューリングしません。 以前にスケジューリングされていたアラームが送出されるまでの残り時間を 秒数で返します(アラームがアクティブでない場合は0を返します)。

Function: sys-sleep seconds :optional (no-retry #f)

[POSIX] 指定された秒数が経過するまで、呼び出したスレッドを一時停止します。

libcのsleep(3)は、シグナルを受けると指定時間経過前であっても 戻ってきます。その場合sys-sleepはデフォルトで、(Schemeのシグナルハンドラが あればそれを呼び出した後)残り時間を引数にして再びsleep(3)を呼び出します。 従ってsys-sleepを呼び出したスレッドは、少なくとも指定した時間、 眠れることが保証されます。

もしsys-sleepがシグナルを受けた場合にすぐ戻ってきてほしければ、 省略可能引数no-retryに真の値を渡してください。

デフォルトでsleep(3)を再呼び出しする理由は、GaucheのGCが スレッド間同期にシグナルを使う場合があるからです。 もし一つのスレッドがsys-sleepで待っている間にもうひとつのスレッドが GCをトリガすると、sys-sleepが時間経過前に戻ってくる可能性があります。 他のスレッドがたくさんアロケートする場合、sys-sleepはすぐに 戻ってきてしまい、信頼できなくなるかもしれません。

指定時間が満了した場合(no-retryが偽であれば常にそうです)、 sys-sleepは0を返し、 シグナルによって一時停止が解除された場合は、満了までの秒数を返します。

POSIX実装間でポータブルにするためには、secondsを65536より小さく保って 下さい。

システムによってはsleep(3)の実装にalarm(2)を用いている可能性があるので、 sys-sleepsys-alarmを混ぜて使うことはおすすめしません。

Function: sys-nanosleep nanoseconds :optional (no-retry #f)

[POSIX] 指定されたナノ秒が経過するまで、呼び出したスレッドを一時停止します。 引数nanoseconds<time>オブジェクト(時間参照)か 実数です。

システムのnanosleep(2)は、シグナルを受けると指定時間経過前であっても 戻ってきますが、sys-nanosleepはデフォルトで、(Schemeのシグナルハンドラが あればそれを呼び出した後)残り時間を引数にして再びnanosleep(2)を呼び出します。 従ってsys-nanosleepを呼び出したスレッドは、少なくとも指定した時間、 眠れることが保証されます。

もしsys-nanosleepがシグナルを受けた場合にすぐ戻ってきてほしければ、 省略可能引数no-retryに真の値を渡してください。

デフォルトでnanosleep(2)を再呼び出しする理由は、GaucheのGCが スレッド間同期にシグナルを使う場合があるからです。 もし一つのスレッドがsys-nanosleepで待っている間にもうひとつのスレッドが GCをトリガすると、sys-nanosleepが時間経過前に戻ってくる可能性があります。 他のスレッドがたくさんアロケートする場合、sys-nanosleepはすぐに 戻ってきてしまい、信頼できなくなるかもしれません。

nanosecondsが経過した場合の返り値は#fです。 no-retryが偽であれば常にそうなります。 sys-nanosleepがシグナルによって割り込まれた場合は、 残りの時間を表す<time>オブジェクトを返します。

 
;wait for 0.5 sec
(sys-nanosleep 500000000)

;wait for 1.3 sec
(sys-nanosleep (make <time> :second 1 :nanosecond 300000000))

Windowsネイティブ環境では、この関数はSleepによってエミュレートされます。 引数はミリ秒の単位に切り上げられます。またシグナルによって中断されることはありません。

Function: sys-random
Function: sys-srandom seed

擬似的な乱数ジェネレータです。 sys-randomは0からrand_max(それ自体を含む)までの間で乱数を 返します。これはrandom(3)への直接的なインターフェースです。 システムにrandom(3)がない場合、lrand48(3)が使われます。

sys-srandomは乱数ジェネレータの種をセットします。 システムによって、srandom(3)srand48(3)を使います。

これらの関数の意図は、乱数ジェネレータ(RNG; random number generator)の 品質や速度を気にしないアプリケーションに、簡便なRNGを提供することです。 本格的な統計分析には、math.mt-randomモジュールのMersenne Twister RNGを 使って下さい(math.mt-random - Mersenne Twister乱数発生器参照)。

Variable: RAND_MAX

sys-randomが返す正の整数に束縛されます。

Function: sys-get-osfhandle port-or-fd

[Windows] この手続きはWindowsネイティブ版だけにあります。 与えられたポートまたは整数のファイルディスクリプタに対応する Windowsファイルハンドルを返します。引数がファイルハンドルに 対応していなかった場合はエラーが投げられます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.26 開発補助API

Gauche には開発者がプログラムを解析するための基本的な組込みAPIがあります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.26.1 デバッグ補助

Macro: debug-print expr

このマクロはexprをそのフォームのまま印字してから、その式を評価し、 評価の結果を印字し、その値を返します。

特殊リーダー構文 #?=expr(debug-print expr) に展開されます。 詳細については デバッグ を参照してください。

Parameter: debug-print-width

このパラメータはdebug-printが印字する情報の最大幅を指定するもの です。このパラメータが保持する値よりも大きいカラム数の情報は丸められま す。

すべての情報を表示したければ、このパラメータに#fを設定してくだ さい。

Macro: debug-funcall (PROC ARG ...)

This macro prints the value of arguments right before calling PROC and the result(s) of the call afterwards.

The special reader syntax #?,expr is expanded into (debug-funcall expr). See デバッグ, for the details.

Function: debug-source-info obj

objに付加されているソースコード情報を検索します。ソースコード情 報とはソースコードファイル名と行番号のリストです。objにソースコー ド情報が付加されていなければ、#fが返されます。

Function: source-code closure

closureのソースコードが取り出せればそれを返します。そうでなければ #fを返します。

現在、Schemeソースから直接読み込まれた場合のみ、ソースコードが取り出せます。 プリコンパイルされたSchemeコードではソースコードは保存されません。 将来は変更されるかもしれません。

Function: source-location closure

closureが定義された場所(ファイル名と行番号)がわかればそれをリストにして 返します。わからなければ#fが返ります。

 
gosh> (use rfc.http)
gosh> (source-location http-get)
("/usr/share/gauche-0.9/0.9.5/lib/rfc/http.scm" 443)
Function: disasm closure

コンパイル済のclosureの本体部分をディスアセンブルして印字します。 これはコンパイラのバグをなんとかしようという場合か、限界まで チューンしようとしないかぎりは役には立たないでしょう。

このディスアセンブラの出力結果を読む場合には、コンパイルされたコードに はデッドコードが含まれている可能性があることを心してください。デッドコー ドはジャンプ最適化によって生成されるのですが、コンパイラはこれをわざわ ざ除去することはしていません。

Function: debug-label obj

この手続きは、objについてほぼユニークな文字列を返します。 「ほぼ」ユニークというのは、 次のGCが起きるまでは、オブジェクトが同じ文字列を返したらeq?であり、 またeq?でないオブジェクト同士は異なる文字列を返すということです。

この文字列は、他に区別の手段がないような無名オブジェクトを印字する時に 使われることを想定しています。GCをまたいだ唯一性は保証されないので、 この手続きが返す文字列をオブジェクトを同定するキーとして使ってはいけません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

6.26.2 プロファイラAPI

ここに挙げている関数は、Gauche の組込みプロファイラを制御するためのも のです。プロファイラの説明については プロファイラを使う を参照してく ださい。

注意:現時点ではプロファイラはマルチスレッドプログラムでは正しく動作する 保証はありません。setitimerとスレッドの相互作用が プラットフォーム依存だからです。

Function: profiler-start

標本化プロファイラを始動します。プロファイラが既に始動しいる場合 には何もしません。

Function: profiler-stop

標本化プロファイラを停止し、標本データを内部データ構 造に保存します。既に標本データが保存されていた場合には、新 しく収集された標本データがその後に追加されます。もしプロファイラが動いてい なかった場合には何もしません。

Function: profiler-reset

もしプロファイラが動いていればそれを停止し、保存されていた標本データ を破棄します。

Function: profiler-show :key sort-by max-rows

格納されている標本データを表示します。

キーワード引数 sort-by は指定する場合は、timecount または time-per-call のどれかひとつです。 これで結果の整列順を指定します。デフォルトでは timeです。

キーワード引数 max-rows では結果を表示する最大行数を指定します。 この値が #f であればすべてのデータが表示されます。

Function: with-profiler thunk

手軽にプロファイリングするための手続きです。 プロファイラをonにしてthunkを呼び出し、結果をcurrent output port に出力します。thunkの戻り値が式の戻り値となります。 結果表示後、プロファイラはリセットされます。

この手続きをネストすることはできません。最も内側のwith-profilerが 結果をリセットしてしまうので、外側のwith-profilerに全ての情報が渡らないからです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7. オブジェクトシステム

Gaucheのオブジェクトシステムのデザインは、TinyCLOSから 派生したSTklosから大きな影響を受けています。 多重継承、マルチメソッド、メタオブジェクトプトロコルを サポートしています。

型システムは、オブジェクトシステムに統合されています。 つまり、文字列は<string>クラスのインスタンスである、などです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.1 オブジェクトシステムの紹介

この節では Gauche のオブジェクトシステムの基本的構造について手短かに 説明します。Gauche のオブジェクトシステムは CLOS (Common-Lisp Object System) に強く影響されています。CLOS やそれにかかわりのある TinyCLOS、STklos、 あるいは Guile のオブジェクトシステムの経験がある方は、この節を飛ばして、 次の節にいってもよいでしょう。

CLOS 風のオブジェクトシステムでは 3 つの概念が中心的な役割をはたします。 クラスジェネリック関数、それにメソッドです。

クラスはオブジェクトの構造を指定します。また、データ型も定義します (厳密にいうと、データ型とはちがうものですが、難しいところは今は おいておきましょう)。

たとえば、2 次元空間の点は x 座標と y 座標で表現できます。点のクラスは define-class マクロを使って定義できます。もっとも手短かには、 次のように定義できます。

 
(define-class <2d-point> () (x y))

(この節の例の定義コードは Gauche ソース配布物のなかの examples/oointro.scm にあります。)

シンボル <2d-point> はクラスの名前です。グローバル変数 <2d-point>はクラスオブジェクトに束縛されています。クラス名を <> で囲むのは慣習にすぎません。define-class には任意のシンボルを渡すことができます。

define-classの第二引数は、直接のスーパークラスのリストです。 これでこのクラスの継承を指定します。これについては後で説明します。

define-classの第三引数はスロットのリストです。 スロットはなにかを格納しておく場所で、通常は各オブジェクトに対して、値を格納できます。 これは、他のオジェクト指向言語では、フィールドとかインスタンス変数などと 呼ばれているものに似たものです。しかし、スロットは単なる各オブジェクト毎の 格納スペース以上の使い方ができます。

さて、2 次元の点のクラスを定義しましたので、点のインスタンスを生成できます。 クラスをジェネリック関数 make に渡してインスタンスを生成します。 (ジェネリック関数がどんなものであるかは気にしないで下さい。今はある特殊な タイプの関数とだけ考えておいてください。)

 
(define a-point (make <2d-point>))

a-point  ⇒ #<<2d-point> 0x8117570>

対話モードで gosh を使っているなら、ジェネリック関数 describe を 使って、インスタンスの内部を点検できます。 便利なので、省略形としてdという名前も定義されています。 (詳細については、gauche.interactive - インタラクティブセッション参照)

 
gosh> (d a-point)
#<<2d-point> 0x8117570> is an instance of class <2d-point>
slots:
  x         : #<unbound>
  y         : #<unbound>

スロットの値にアクセスあるいは値を変更するためには、それぞれ、 slot-ref および slot-set! が使えます。これらの名前は STklos からとりました。

 
(slot-ref a-point 'x)  ;; a-point のスロット x にアクセス
  ⇒ error, since slot 'x doesn't have a value yet

(slot-set! a-point 'x 10.0)  ;; a-point のスロット x を 10.0 に設定

(slot-ref a-point 'x)
  ⇒ 10.0

Gauche では、より短かい名前 ref も使えます。これを使えば srfi-17 の一般化された set! の構文が使えます。

 
(ref a-point 'x) ⇒ 10.0

(set! (ref a-point 'y) 20.0)

(ref a-point 'y) ⇒ 20.0

スロットの値が設定されていることを見てみましょう。

 
gosh> (d a-point)
#<<2d-point> 0x8117570> is an instance of class <2d-point>
slots:
  x         : 10.0
  y         : 20.0

実際には、スロットにデフォルト値を設定できたり、インスタンス生成時に スロットに値を設定できると便利なことが多いです。このような情報は、 スロットオプションで指定できます。<2d-point>の定義を 次のように変更してみましょう。

 
(define-class <2d-point> ()
  ((x :init-value 0.0 :init-keyword :x :accessor x-of)
   (y :init-value 0.0 :init-keyword :y :accessor y-of)))

各スロットの指定は、前の例では一つのシンボルでしたが、こんどはリスト であることに注意してください。 こんどは、リストの car 部分がスロット名の指定で、cdr 部分にいろいろな情報が はいります。:init-value のうしろの値はそのスロットのデフォルト値を 定義しています。:init-keywordのうしろのキーワードは、生成時にスロットを 初期化するために make に渡すことのできるキーワード引数を定義しています。 キーワード :accessor のうしろの名前は、スロットへのアクセス/変更の ために使えるジェネリック関数に束縛されます。これは slot-ref/slot-set! のかわりに使います。

ちょっとした対話セッションをみてみましょう。新しい <2d-point> クラスの インスタンスをつくります。スロットがデフォルト値で初期化されているのがわかり ますね。

 
gosh> (define a-point (make <2d-point>))
a-point
gosh> (d a-point)
#<<2d-point> 0x8148680> is an instance of class <2d-point>
slots:
  x         : 0.0
  y         : 0.0

こんどは、キーワード引数で初期値を与えて、別のインスタンスを作ります。

 
gosh> (define b-point (make <2d-point> :x 50.0 :y -10.0))
b-point
gosh> (d b-point)
#<<2d-point> 0x8155b80> is an instance of class <2d-point>
slots:
  x         : 50.0
  y         : -10.0

アクセサは slot-ref/slot-set! より短かくなって便利に なりましたね。

 
gosh> (x-of a-point)
0.0
gosh> (x-of b-point)
50.0
gosh> (set! (y-of a-point) 3.33)
#<undef>
gosh> (y-of a-point)
3.33

利用可能な全スロットオプションのリストはクラスの定義にあります。 ちょっと見ると、これらのスロットオプションの宣言は冗長に見えるかもしれません。 システム側で初期化キーワードやアクセス名を自動的に定義する静的な仕組を 用意することもできたかもしれません。 しかしながら、CLOS 風のシステムでは柔軟性の方をより重視します。 メタオブジェクトプロトコルと呼ばれる機構をもちいて、これらの スロットオプションがどのように解釈されるかをカスタマイズすることができます。 また、自分用のスロットオプションを追加することもできます。 詳しくは メタオブジェクトプロトコル を見てください。

また、似たような流儀で <2d-vector> クラスを定義できます。

 
(define-class <2d-vector> ()
  ((x :init-value 0.0 :init-keyword :x :accessor x-of)
   (y :init-value 0.0 :init-keyword :y :accessor y-of)))

そうです。同じアクセサ名を x-of のように使うことができます。 これは事実上、多重定義されます。

主流のオブジェクト指向言語に慣れた方は、そろそろ、メソッドはどこにあるのだと 思い始めているかもしれません。いよいよメソッドの登場です。以下のフォームは、3 つの引数 ptdydy をとり、 しかもpt<2d-point> のインスタンスであるような メソッド move-by! を定義するものです。

 
(define-method move-by! ((pt <2d-point>) dx dy)
  (inc! (x-of pt) dx)
  (inc! (y-of pt) dy))

define-method マクロの第二引数はメソッド特定化子リストを 指定しています。これは、第一引数が <2d-point> のインスタンスで なければならないことを示し、第二、第三引数は任意の型でよいことを 示しています。メソッド呼び出しの構文は通常関数の呼び出しと同じです。

 
gosh> (move-by! b-point 1.4 2.5)
#<undef>
gosh> (d b-point)
#<<2d-point> 0x8155b80> is an instance of class <2d-point>
slots:
  x         : 51.4
  y         : -7.5

別の特定化子によりこのメソッドを多重定義することが可能です。以下のように ベクタを使って点を動かすこともできます。

 
(define-method move-by! ((pt <2d-point>) (delta <2d-vector>))
  (move-by! pt (x-of delta) (y-of delta)))

特定化はユーザ定義のクラスにだけ限定されているわけではありません。 Gauche の組み込み型を使ってメソッドを特定化することもできます。

 
(define-method move-by! ((pt <2d-point>) (c <complex>))
  (move-by! pt (real-part c) (imag-part c)))

以下はセッション例です。

 
gosh> (define d-vector (make <2d-vector> :x -9.0 :y 7.25))
d-vector
gosh> (move-by! b-point d-vector)
#<undef>
gosh> (d b-point)
#<<2d-point> 0x8155b80> is an instance of class <2d-point>
slots:
  x         : 42.4
  y         : -0.25
gosh> (move-by! b-point 3+2i)
#<undef>
gosh> (d b-point)
#<<2d-point> 0x8155b80> is an instance of class <2d-point>
slots:
  x         : 45.4
  y         : -2.25

メソッドがその主レシーバ <2d-point> によってのみディスパッチされる のではなく、その他の引数によってもディスパッチされているのがわかると思います。 実は、第一引数は残りの引数に比べて何ら特別ではありません。 CLOS 風のシステムではメソッドは特定のクラスに属するものではないのです。

ではメソッドとはいったいなんでしょう。move-by! を点検してみると それが <generic> のインスタンスであり、ジェネリック関数であると わかります。(describe は可読性のために methods スロットの 値の印字を途中で切っていることに注意してください。)

 
gosh> move-by!
#<generic move-by! (3)>
gosh> (d move-by!)
#<generic move-by! (3)> is an instance of class <generic>
slots:
  name      : move-by!
  methods   : (#<method (move-by! <2d-point> <complex>)> #<method (move-
gosh> (ref move-by! 'methods)
(#<method (move-by! <2d-point> <complex>)>
 #<method (move-by! <2d-point> <2d-vector>)>
 #<method (move-by! <2d-point> <top> <top>)>)

ジェネリック関数は特別なタイプの関数だと言いましたが、Gauche ではこれは 適用可能オブジェクトとして認識されます。それは、適用されるとその引数に 対応して適切なメソッドを選択して、その選択されたメソッドを呼び出すという動作を 行います。

実際 define-method マクロがやっていることは、(1)もし与えられた 名前のジェネリック関数がまだ存在していなければ、これを生成し、(2)与えられた 特定化子と本体とでメソッドオブジェクトを生成し、(3)ジェネリック関数に そのメソッドオブジェクトを追加する、ということです。

アクセサも同様にジェネリック関数で、define-class マクロが暗黙のうちに 生成します。

 
gosh> (d x-of)
#<generic x-of (2)> is an instance of class <generic>
slots:
  name      : x-of
  methods   : (#<method (x-of <2d-vector>)> #<method (x-of <2d-point>)>)

主流の動的オジェクト指向言語では、クラスには多くの役割があります。 クラスは構造と型を定義し、スロットとメソッドの名前空間を生成し、 メソッドのディスパッチに責任をもちます。Gaucheでは、名前空間は モジュールによって管理し、メソッドのディスパッチはジェネリック関数で あつかいます。

オブジェクトの印字表現は、デフォルトでは、あまりユーザにやさしくありません。 Gauche の write および display 関数は、どのように印字して よいかわからないインスタンスにであうと、ジェネリック関数 write-object を呼びます。定義したクラスのインスタンスをどのように印字するかを カスタマイズするために、そのクラスに特定したメソッドを定義することができます。

 
(define-method write-object ((pt <2d-point>) port)
  (format port "[[~a, ~a]]" (x-of pt) (y-of pt)))

(define-method write-object ((vec <2d-vector>) port)
  (format port "<<~a, ~a>>" (x-of vec) (y-of vec)))

で、どうなったかというと、

 
gosh> a-point
[[0.0, 3.33]]
gosh> d-vector
<<-9.0, 7.25>>

srfi-10 のフォーマットを満すように印字表現をカスタマイズし、読み込み時 構築子を定義すれば、自分の定義したクラスのインスタンスを、組み込み オブジェクトと同様に書き出し、読み戻しできます。詳しくは 読み込み時コンストラクタ を見てください。

いくつかの組み込み関数はユーザ定義オブジェクトに対して同様の方法で 機能拡張できます。たとえば、object-equal? を特定化すれば、 equal? を使ってインスタンスの比較ができます。

 
(define-method object-equal? ((a <2d-point>) (b <2d-point>))
  (and (equal? (x-of a) (x-of b))
       (equal? (y-of a) (y-of b))))

(equal? (make <2d-point> :x 1 :y 2) (make <2d-point> :x 1 :y 2))
  ⇒ #t

(equal? (make <2d-point> :x 1 :y 2) (make <2d-point> :x 2 :y 1))
  ⇒ #f

(equal? (make <2d-point> :x 1 :y 2) 'a)
  ⇒ #f

(equal? (list (make <2d-point> :x 1 :y 2)
              (make <2d-point> :x 3 :y 4))
        (list (make <2d-point> :x 1 :y 2)
              (make <2d-point> :x 3 :y 4)))
  ⇒ #t

もっとおもしろい例を見てみましょう。描画可能な <shape> というクラス を考えます。 基本クラスとして、色や線の太さといった共通属性をスロットに持たせます。

 
(define-class <shape> ()
  ((color     :init-value '(0 0 0) :init-keyword :color)
   (thickness :init-value 2 init-keyword :thickness)))

インスタンスを生成する際、make はジェネリック関数 initialize を呼びます。この関数は init-keyword や init-value を処理するようなスロットの 面倒を見ます。この initialize メソッドを特定化することによって、初期化 の振舞いをカスタマイズすることができます。initialize メソッドは二つの 引数とともに呼ばれます。ひとつは新しく生成されたインスタンス、もうひとつは make にわたされた引数のリストです。

initialize メソッドを <shape> クラス用に定義し、生成された shape が自動的にグローバルなリストに登録されるようにしましょう。 システムのもつ initialize の振舞いを完全に置き換えたくはないという ことに注意してください。init-keyword は相変わらず扱う必要があるからです。

 
(define *shapes* '())  ;; グローバルな shape のリスト

(define-method initialize ((self <shape>) initargs)
  (next-method)  ;; このシステムがスロットの初期化処理をするようにする
  (push! *shapes* self)) ;; 自分自身をグローバルなリストに登録する

仕掛けは、特別なメソッド next-method にあります。これは メソッド本体の中でしか使えません。同じジェネリック関数の 特定化の度合がより低いメソッドを呼びます。これは、典型的には スーパークラスの同じメソッドを呼ぶということを意味します。 ほとんどのオジェクト指向言語には、スーパークラスのメソッドを呼ぶという 概念があります。多重引数ディスパッチと多重継承のために next-method は少し複雑にはなっていますが、基本的なアイディアは同じです。

さて、では <shape> のスーパークラスはなんでしょう。実は、 すべての Scheme で定義されたクラスは <object> というクラスを 継承します。スロットのめんどうを見ているのは、<object> の 初期化メソッドなのです。自分で定義した initialize メソッド中で next-method を呼びだしたあとでは、すべてのスロットが 正しくイニシャライズされたとみなせます。というわけで、自分で定義した initialize のなかで通常、最初にすべきことは next-method を呼ぶことです。

上のコードを点検してみましょう。(make <shape> args …) を呼ぶと、 システムは <shape> のインスタンスのためのメモリを確保し、 initialize ジェネリック関数を、そのインスタンスと args … で呼びます。これは、いま自分で定義した initialize にディスパッチ されます。その中で next-method よび、それがこんどは <object> クラスの initialize メソッドをよびます。これでこのインスタンスは init-value と init-keyword で初期化されます。next-method からもどった あと、新しい <shape> インスタンスをグローバルの shape リストに *shapes* に登録します。

この <shape> クラスは shape の抽象的概念を表現しているにすぎません。 では、いくつかの具体的な描画可能な shape を <shape>サブクラス化によって定義しましょう。

 
(define-class <point-shape> (<shape>)
  ((point  :init-form (make <2d-point>) :init-keyword :point)))

(define-class <polyline-shape> (<shape>)
  ((points :init-value '() :init-keyword :points)
   (closed :init-value #f  :init-keyword :closed)))

define-class の第二引数に注目してください。これは <point-shape> および <polyline-shape><shape> クラスのスロットを継承していることを示しています。そして <shape> クラスが受け入れるものはすべて、それらのサブクラスでも受け入れらること も示しています。

<point-shape> には point というスロットがひとつ追加 されています。このスロットはこの節の最初で定義した、<2d-point> の インスタンスを持ちます。<polyline-shape> クラスは点のリスト、 フラグを格納します。フラグは多角形のラインが終点と始点つないでいるか どうかを指定します。

継承はとりあつかいに注意が必要な強力な機構です。うっかりするとすぐに 追跡不能なコードができあがります。(Paul Graham は『百年の言語』という 記事のなかで、「オブジェクト指向プログラミングはスパゲッティコードを 書くための持続的な方法を提供してくれる。」と言っているように。) 経験則からいうとサブタイプが必要なときにサブクラスを作るのがよいようです。 スロットの継承は付随するなにかではありますが、サブクラス化のための 主要な理由にしてはいけません。<point-shape> クラスでやったように サブストラクチャを「インクルード」することは常にできるのです。

<point-shape> クラスに新しいスロットオプションがあらわれました。 :init-form というスロットオプションは、init-keyword が make に与えられなかったときのそのスロットのデフォルト値を指定します。 しかし、クラス定義時に評価される :init-value の値とはちがって、 この :init-form をともなう値はシステムが実際のその値を必要とした ときに評価されます。したがって、<point-shape> のインスタンス では、<point-shape> インスタンスが :point キーワード引数を わたされずに生成されたときにのみ、デフォルトの <2d-point> インスタンスが生成されます。

shape は別のデバイスに別の方法で描画され得ます。いまのところは、 PostScript 出力だけを考慮しましょう。draw メソッドに多相性を持たせる ために、PostScript 出力デバイス <ps-device> を定義します。

 
(define-class <ps-device> () ())

こうすると、<shape> および <ps-device> の両方に特定化した draw メソッドを書くことができます。

 
(define-method draw ((self <shape>) (device <ps-device>))
  (format #t "gsave\n")
  (draw-path self device)
  (apply format #t "~a ~a ~a setrgbcolor\n" (ref self 'color))
  (format #t "~a setlinewidth\n" (ref self 'thickness))
  (format #t "stroke\n")
  (format #t "grestore\n"))

このコードでは device 引数はメソッド本体内では使われていません。 メソッドディスパッチのためのみに使われます。いずれ別の出力デバイス を必要になったら、そのデバイスに特定化した draw メソッドを 追加することができます。

上の draw メソッドは共通の仕事をしますが、実際の描画は それぞれのサブクラス用に特定化された方法で行わなければなりません。

 
(define-method draw-path ((self <point-shape>) (device <ps-device>))
  (apply format #t "newpath ~a ~a 1 0 360 arc closepath\n"
         (point->list (ref self 'point))))

(define-method draw-path ((self <polyline-shape>) (device <ps-device>))
  (let ((pts (ref self 'points)))
    (when (>= (length pts) 2)
      (format #t "newpath\n")
      (apply format #t "~a ~a moveto\n" (point->list (car pts)))
      (for-each (lambda (pt)
                  (apply format #t "~a ~a lineto\n" (point->list pt)))
                (cdr pts))
      (when (ref self 'closed)
        (apply format #t "~a ~a lineto\n" (point->list (car pts))))
      (format #t "closepath\n"))))

;; utility method
(define-method point->list ((pt <2d-point>))
  (list (x-of pt) (y-of pt)))

最後にもう少しだけ、ハックしましょう。draw メソッドを shape のリストに対しても動作するようにします。こうすれば、 同一ページ内で複数の shape をひつまとめで描画できます。

 
(define-method draw ((shapes <list>) (device <ps-device>))
  (format #t "%%\n")
  (for-each (cut draw <> device) shapes)
  (format #t "showpage\n"))

これで簡単な図を書けるようになります。

 
(use srfi-1)      ;; for iota
(use math.const)  ;; for constant pi

(define (shape-sample)

  ;; creates 5 corner points of pentagon
  (define (make-corners scale)
    (map (lambda (i)
           (let ((pt (make <2d-point>)))
             (move-by! pt (make-polar scale (* i 2/5 pi)))
             (move-by! pt 200 200)
             pt))
         (iota 5)))

  (set! *shapes* '())  ;; clear the shape list
  (let* ((corners (make-corners 100)))
    ;; a pentagon in green
    (make <polyline-shape>
      :color '(0 1 0) :closed #t
      :points corners)
    ;; a star-shape in red
    (make <polyline-shape>
      :color '(1 0 0) :closed #t
      :points (list (list-ref corners 0)
                    (list-ref corners 2)
                    (list-ref corners 4)
                    (list-ref corners 1)
                    (list-ref corners 3)))
    ;; put dots in each corner of the star
    (for-each (cut make <point-shape> :point <>)
              (make-corners 90))
    ;; draw the shapes
    (draw *shapes* (make <ps-device>)))
  )

関数 shape-sample は現在の出力ポートに簡単な PostScript の描画コード を書き出します。これを以下のような式でファイルに出力し、GhostScriptなどの PostScriptビューワで結果を見てください。

 
(with-output-to-file "oointro.ps" shape-sample)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2 クラス

この節では、Gauche におけるクラスについて詳しく説明します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2.1 クラスの定義

クラスを定義するには、define-class マクロを使います。

Macro: define-class name supers (slot-spec …) option …

引数によって指定されたクラスオブジェクトを作成し、それを name に グローバルに束縛します。このマクロはトップレベルでのみ使うことができます。

Supers はそのクラスが継承する直接のスーパークラスのリストです。 多重継承も使えます。継承の詳細については継承 を参照して下さい。

Slot-spec は「スロット」の仕様で、他の言語ではよく 「フィールド」や「インスタンス変数」と呼ばれるものです (slot-spec を使って「クラス変数」を指定することもできます)。 slot-spec の最も単純なフォームはシンボルそのもので、その名前が スロットであるものです。あるいは、最初の要素がシンボルで残りの要素が キーワードと値が交互に来るリストを渡すこともできます。

このリストフォームは、スロットの名前を定義するだけでなく、そのスロットの 振る舞いも定義します。スロットの定義については以下で説明します。

最後に、option … は、クラスオブジェクトがどのように 作られるかを指定する、キーワードと値が交互に来るリストです。

このマクロでは1つのキーワード引数、:metaclass により、 メタクラス(他のクラスをインスタンス化するクラス)を指定できます。 他のオプションはクラスオブジェクトを作成するために、make メソッドに渡されます。メタクラスの使用方法については、 クラスのインスタンシエーションを参照。

スロットの指定はリストで、以下のようなフォームであるべきです。

 
(slot-name :option1 value1 :option2 value2 …)

各キーワード(option1 など) は slot option を与えます。 デフォルトでは、以下のスロットオプションが認識されます。 メタクラスを定義することで、デフォルト以外のスロットオプションを 追加できます。

:allocation

このスロットのアロケーションタイプを指定します。これは、このスロットが どのように値を格納するかを指定します。以下のようなキーワード値が 標準クラスによって認識されます。プログラマは、自分用のメタクラスを定義し、 これら以外のアロケーションタイプを認識するように、このクラスを拡張すること ができます。

:instance

スロットは各インスタンス毎にアロケートされます。したがって、おのおのの インスタンスは別々の値をもてます。これは、いわゆる「インスタンス 変数」の振舞いを実現します。:allocation スロットオプションが 省略された場合、これがデフォルトとなります。

:class

スロットはクラスオブジェクト自身にアロケートされます。したがって、おのおのの インスタンスはこのスロットの同じ値を共有します。これは、いわゆる 「クラス変数」の振舞いを実現します。このスロットの値は、すべてのサブクラス でも共有されます。(ただし、サブクラスの定義がこのスロットをシャドウする 場合には、そのかぎりではありません。)

:each-subclass

class アロケーションと似ていますが、スロットはクラス毎にアロケート されます。すなわち、このスロットは、このクラスのすべてのインスタンスで 共有されますが、サブクラスのインスタンスには共有されません。

:virtual

このタイプのスロット用には格納領域はアロケートされません。このスロット にアクセスすると以下で説明する :slot-ref および :slot-set! オプションで与えられた手続きが呼ばれます。いいかえれば、手続きスロットを 作成できるということです。スロットアロケーションが virtual と指定されて いる場合、少なくとも :slot-ref オプションが同時に 指定されていなければなりません。さもなければ、define-class は エラーを発生させます。

:builtin

このアロケーションタイプは組み込みクラスの中だけに現れます。 Scheme 定義のクラスでこのタイプを指定することはできません。

:init-keyword

このスロットオプションに与えられたキーワード値は、インスタンスが生成 される際に make メソッドに初期値をわたすために使えます。

:init-value

生成時にキーワード引数で初期化されてないスロットの場合、これによって スロットの初期値を与えます。その値は define-class が評価される ときに、評価されます。

:init-form

init-value と似ていますが、与えられた値は thunk で包まれていて、 その値が必要とされた時に毎回評価されます。init-valueinit-form との両方が与えられた時には init-form が無視されます。 実際には、:init-form exprdefine-class マクロの :init-thunk (lambda () expr) に変換されます。

:initform

init-form と同義です。STk との互換性のためにあります。新しくコードを 書く場合には使うべきではありません。

:init-thunk

thunk を与えます。もし、当該スロットが生成時にキーワード引数によって 初期化されていなければ、その thunk を評価して当該スロットの初期値とします。 :init-formvalue を与えることと、:init-thunk(lambda () value) を与えることは同じことです。

:getter

シンボルをとり、getter メソッドを生成し、同じ名前のジェネリック関数に 束縛します。getter メソッドは当該クラスのインスタンスを引数とし、当該 スロットの値を返します。

:setter

シンボルをとり、setter メソッドを生成し、同じ名前のジェネリック関数に 束縛します。setter メソッドは当該クラスのインスタンスと値をひとつ引数 として、そのインスタンスの当該スロットの値をその値にセットします。

:accessor

シンボルをとり、2 つのメソッド(getter メソッドと setter メソッド)を 生成します。getter メソッドは与えられた名前のジェネリック関数に 束縛され、setter メソッドは、与えられた名前のジェネリック 関数のsetterとして追加されます(setter については 代入 を参照して下さい)。

:slot-ref

評価すると引数(インスタンス)を一つとる手続きとなる値を指定します。 このスロットオプションは当該スロットのアロケーションが virtual である場合、必ず指定されていなければなりません。プログラムが slot-refやgetter メソッドを使って 当該スロットの値を得ようとすると、 このオプションに指定された手続きが呼ばれ、その結果が 当該スロットの値として返されます。手続きは undef 値を返し (undefinedの戻り値)、スロットが値をもっていないことを示す ことができます。もしスロットのアロケーションが virtual で なければ、このスロットオプションは無視されます。

:slot-set!

評価すると二つの引数(インスタンスと値)をとる手続きとなる値を指定します。 プログラムがslot-set! あるいは setter メソッドを使って 当該スロットに値をセットしようとするときに、 指定した手続きがインスタンスとセットすべき値を引数として 呼ばれます。スロットアロケーションが virtualでなければ、このスロットオプションは無視されます。 このスロットオプションの無いvirtualスロットはリードオンリースロットとなります。

:slot-bound?

評価すると引数(インスタンス)を一つとる手続きとなる値を指定します。 このスロットオプションは当該スロットのアロケーションが virtual である場合しか意味を持ちません。 プログラムが当該スロットが値を持っているかどうかを決定しようとしたときに、 この手続きを呼ばれます。手続きは、スロットが値をもつなら、真の値を、 そうでなければ#f を返します。仮想スロットに対して、このスロット オプションが省略されると、システムは代りに slot-ref に与えられ た手続きを呼び、それが、#<undef> を返すかどうか見ます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2.2 継承

継承にはふたつの役割があります。第一に、スロットを追加することで 既存のクラスを拡張できます。第二には、既存のクラス 関連するメソッドを特定化して、元々のメソッドよりもすこし 特定化した仕事をやらせるようにできます。

いくつかの用語を定義しておきましょう。クラス <T> がクラス <S>を継承しているとき、<T><S>サブクラスといい、<S><T>スーパークラスといいます。この関係は推移的です。すなわち、 <T> のサブクラスは、やはり <S> のサブクラスであり、 <S> のスーパークラスは、やはり <T> のスーパークラスです。 特に、<T><S> を直接継承している場合、すなわち、 <S><T> を定義する際のスーパークラスリストに 現われている場合には <S><T>直接スーパークラスといい、<T><S>直接サブクラスといいます。

クラスを定義したとき、そのクラスとそのスーパークラスは、サブクラスから スーパークラスという順序になり、クラスのリストがその順で生成されます。 このリストのことをクラス順位リスト、あるいは CPL といいます。 すべてのクラスはそれぞれ自身の CPL を持っています。 クラスの CPL は常に自分自身からはじまり、<top> で終ります。

手続き class-precedence-list を用いてクラスの CPL を問い合わせ ることができます。

 
gosh> (class-precedence-list <boolean>)
(#<class <boolean>> #<class <top>>)
gosh> (class-precedence-list <string>)
(#<class <string>> #<class <sequence>> #<class <collection>> #<class <top>>)

見るとわかるように、すべてのクラスは <top> という名前のクラスを 継承しています。組み込みクラスには、いくつかの抽象クラスを CPL 中で 自分自身と <top> の間に連ねているものもあります。上の例では <string> クラスは <sequence><collection> を 継承しています。これは、文字列がシーケンスとしても、コレクションとして も振舞うことができるということです。

 
gosh> (is-a? "abc" <string>)
#t
gosh> (is-a? "abc" <sequence>)
#t
gosh> (is-a? "abc" <collection>)
#t

Schemeで定義したクラスの継承についてはどうでしょう。 単一継承なら、CPL は直截的です。そのクラスのスーパークラス、 スーパークラスのスーパークラス、 スーパークラスのスーパークラスのスーパークラス、… と <top> に到達するまで、たどっていけます。例を見てください。

 
gosh> (define-class <a> () ())
<a>
gosh> (define-class <b> (<a>) ())
<b>
gosh> (class-precedence-list <b>)
(#<class <b>> #<class <a>> #<class <object>> #<class <top>>)

Scheme定義のクラスは常に <object> を継承します。 システムが自動的に挿入します。

多重継承が使われる場合には話はすこし複雑になります。複数のスーパークラスの 複数の CPL をひとつの CPL にマージしなければなりません。このことを 線形化といい、いくつかの線形化戦略が知られています。Gauche では デフォルトで C3 線形化と呼ばれているアルゴリズムを使います。 このアルゴリズムは局所的な順位、単調性、拡張順位グラフと整合性のとれた ものです。ここでは詳細に立ち入りませんが、一般的なルールとして、 CPL 中のスーパークラスの順序は、つねにそのクラスの直接スーパークラスの 順序、それぞれのスーパークラスの CPL の順序、および、各スーパークラス の直接スーパークラスの順序、などと整合性をもちます。正確な説明について はDylanを参照してください。

もしクラスが、整合性を満した CPL を構築できないようなやり方で 複数のスーパークラスを継承すると、エラーになります。以下は多重継承の 単純な例です。

 
(define-class <grid-layout> () ())

(define-class <horizontal-grid> (<grid-layout>) ())

(define-class <vertical-grid> (<grid-layout>) ())

(define-class <hv-grid> (<horizontal-grid> <vertical-grid>) ())

(map class-name (class-precedence-list <hv-grid>))
 ⇒ (<hv-grid> <horizontal-grid> <vertical-grid>
     <grid-layout> <object> <top>)

<hv-grid> の直接スーパークラス(<horizontal-grid><vertical-grid>)の順序が保存されていることに注意してください。

以下は、すこしひねくれた例です。

 
(define-class <pane> () ())

(define-class <scrolling-mixin> () ())

(define-class <scrollable-pane> (<pane> <scrolling-mixin>) ())

(define-class <editing-mixin> () ())

(define-class <editable-pane> (<pane> <editing-mixin>) ())

(define-class <editable-scrollable-pane>
   (<scrollable-pane> <editable-pane>) ())

(map class-name (class-precedence-list <editable-scrollable-pane>))
 ⇒ (<editable-scrollable-pane> <scrollable-pane>
     <editable-pane> <pane> <scrolling-mixin> <editing-mixin>
     <object> <top>)

いったんクラス順位が決まると、定義されたクラスのスロットが以下の手順で 計算されます。スロットの定義が CPL 中のスーパークラスからサブクラスへの 順で集められます。サブクラスにスーパークラスと同じ名前のスロット定義が あった場合には、サブクラスのそのスロット定義が採用され、スーパークラス の方の定義は捨てられます。あるクラス <S> がスロット ab、および c を定義しており、あるクラス <T> が スロット cd、および e を定義し、さらに、 あるクラス <U> がスロット b および e を定義していると しよう。<U> の CPL が (<U> <T> <S> <object> <top>) と なっている場合、<U> のスロットが下の図のように計算されます。 すなわち、<U> は 5 つのスロットをもち、 b および e の定義は <U> のものを、c および d の定義は <T> 由来のものを、そして、a の定義は <S> 由来のものとなります。

 
   CPL         | スロットの定義
               |  () はシャドウされたスロットを表す
 --------------+-------------------
   <top>       |
   <object>    |
   <S>         | a  (b) (c)
   <T>         |         c   d  (e)
   <U>         |     b           e
 --------------+--------------------
 <U>のスロット | a   b   c   d   e

class-slots 関数を使ってクラスオブジェクトのスロット定義の リストを得ることができます。

上述の振舞いはデフォルトの振舞いにすぎないことに注意してください。 CPL の計算方法あるいはスロット定義の継承方法は、メタクラスを 定義することでカスタマイズ可能です。たとえば、同じスロット名の スロットオプションはどれかが他のものをシャドウしますが、これを マージすることができるようにメタクラスを書くことができます。 あるいは、サブクラスがスーパークラスのスロットをシャドウするのを 禁止するようにメタクラスを書くことができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2.3 クラスオブジェクト

クラスとは何か。Gauche ではクラスはオブジェクトをインスタンス化して ある特定の機能を実装するようなオブジェクトにすぎません。 そうなので、スロットの値を見るだけで、クラス内部を覗けます。 このように内部を覗くのに便利な手続きがいくつか用意されています。 これらの手続きがリストを返す場合、それはクラスに所属するもので、 変更してはいけないということに注意してください。

Function: class-name class

class の名前を返します。

 
(class-name <string>) ⇒ <string>
Function: class-precedence-list class

class のクラス順位リストを返します。

 
(class-precedence-list <string>)
  ⇒ (#<class <string>>
      #<class <sequence>>
      #<class <collection>>
      #<class <top>>)
Function: class-direct-supers class

class の直接スーパークラスのリストを返します。 直接スーパークラスは class が直接継承しているクラスです。

 
(class-direct-supers <string>)
  ⇒ (#<class <sequence>>)
Function: class-direct-subclasses class

class の直接サブクラスのリストを返します。 直接サブクラスは class を直接継承しているクラスです。 <T><S> の直接サブクラスであれば、 <S><T> の直接スーパークラスです。

Function: class-slots class

classスロット定義 のリストを返します。スロット定義は リストで、その car 部はスロット名、cdr 部はスロットオプションを指定する キーワード値のリストです。スロット定義内部を覗いてスロットのもつ性格を 知ることができます。詳しくは スロット定義オブジェクト を 参照してください。

与えられたクラスのスロット名のリストを得るための標準的な方法は、 (map slot-definition-name (class-slots class)) です。

Function: class-slot-definition class slot-name

クラス class 中の slot-name で指定されたスロットの スロット定義を返します。class が指定した名前のスロットを 持たなければ #f が返ります。

Function: class-direct-slots class

当該クラスで直接定義されている(つまりスーパークラスから継承された ものではない)スロット定義のリストを返します。この情報は、クラスの 初期化の際にスロットの継承を処理するために利用されます。

Function: class-direct-methods class

classを特定化子中にもつメソッドのリストを返します。

Function: class-slot-accessor class slot-name

classslot-name で指定したスロットの スロットアクセサオブジェクトを返します。 スロットアクセサオブジェクトは内部オブジェクトで与えられたスロットへの アクセス方法、変更方法、初期化の方法という情報をカプセル化しています。

メタオブジェクトプロトコルを使って特別なスロットを定義するのでなければ、 通常スロットアクセサオブジェクトを扱う必要はありません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2.4 スロット定義オブジェクト

class-slots が返すスロットの定義オブジェクト class-direct-slots および class-slot-definition はスロットに関する情報を保持しています。 現時点では Gauche はスロット定義を表現するのにリストを使っています。これは STklos や TinyCLOS と同じです。しかし、Gauche が将来にわたって、この構造を 保持するかどうかは保証のかぎりではありません。スロット定義オブジェクトの 情報を得るには以下のそれ専用のアクセサメソッドを使うべきです。

Function: slot-definition-name slot-def

スロット定義オブジェクト slot-def で与えられたスロットの名前を 返します。

Function: slot-definition-options slot-def

slot-def のスロットオプションのキーワード値リストを返します。

Function: slot-definition-allocation slot-def

slot-def:allocation オプションの値を返します。

Function: slot-definition-getter slot-def
Function: slot-definition-setter slot-def
Function: slot-definition-accessor slot-def

それぞれ、slot-def:getter:setter および :accessor スロットオプションの値を返します。

Function: slot-definition-option slot-def option :optional default

slot-def のスロットオプション option の値を返します。 そのようなオプションがない場合には、default が与えられていれば それを返し、さもなければ、エラーシグナルがあがります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2.5 クラスの再定義

define-class を使うとき、指定したクラス名がすでにあるクラスに束縛 されている場合、これは元々のクラスの再定義と看倣されます。

クラスの再定義は以下の操作を意味します。

元々のクラスと新しいクラスは別のオブジェクトであることに注意してください。 元々のクラスオブジェクトは元々どのモジュールでどの変数に束縛されていたかを 覚えており、この束縛を新しいクラスに置き換えます。どこかで、元々のクラスへ の直接参照を持っていれば、その参照は元々クラスへの参照のままです。この点 については特に注意してください。class-redefinition メソッドを 定義することによりクラス再定義の振舞いをカスタマイズできます。 詳しくは メタオブジェクトプロトコルを参照してください。

元々のクラスのインスタンスが存在している場合、それらのインスタンスは 以下のようなメソッドでアクセスあるいは変更しようとしたときに自動的に 更新されます。class-ofis-a?slot-refslot-set!ref、getterメソッド、setterメソッド。

インスタンスの更新とは、インスタンスのクラスを(旧いクラスから新しいクラスへ) 変更するということです。デフォルトでは、元々のクラスと新しいクラスで 共通のスロットの値はそのまま引き継がれます。新しいクラスで追加された スロットは新しいクラスでのそのスロットの仕様にしたがって初期化されます。 元々のクラスから削除されたスロットの値は破棄されます。この振舞いは、 change-class メソッドを書くことでカスタマイズできます。詳しくは クラスの変更 を参照してください。

スレッド安全性について

クラス再定義処理はローカルな処理ではなく、多くの副作用を行います。 複数のスレッドが同時にクラス再定義プロトコルを走らせた場合の 安全性を保証するのは困難です。そこでGauche では、一度にひとつのスレッドしか クラス再定義プロトコルに入らないように、プロセスレベルのロックを使用します。

スレッドが、別のスレッドが再定義プロトコルにいる最中にクラスを 再定義しようとした場合、たとえ、別々のクラスを再定義しようとしている 場合でも、そのスレッドはブロックされます。このようにするのは、 再定義がそのすべてのサブクラス、そのクラスとそのサブクラスに 関わるすべてのメソッドおよびジェネリック関数に影響するからで、 ふたつのクラスが完全に独立であるかどうかを決定するのは、自明では ないからです。

スレッドが他のスレッドが再定義しようとしているクラスのインスタンスに アクセスしようとした場合にも、このスレッドは再定義が完了するまで、 ブロックされます。

インスタンス更新プロトコルは直列化されません。ふたつのスレッドが 再定義されたクラスのインスタンスにアクセスしようとすると、両方の スレッドが更新プロトコルを起動します。これは好ましくない競合状態を 生じる可能性があります。このような場合がおきないようにするのは アプリケーションの責任です。インスタンスへのアクセスはどのみち システムが直列化するわけではないので、これは自然なことです。 インスタンス内に mutex を持たせる場合は特に注意が必要です。 インスタンス中のmutexにアクセスするだけでインスタンス更新プロトコルを 起動することになる可能性があるからです。

互換性に関して

クラス再定義プロトコルは CLOS風の Scheme システムとは微妙に違います。 Gauche のものは STklos のものによく似ていますが、STklos 0.56 は再定義 サブクラスの束縛を置き換えず、初期化引数を覚えたりはしないので、 再定義されたサブクラスは、元々のサブクラスが持っていた情報のなにがしかを 失ってしまう可能性があるという点にちがいがあります。Guile のオブジェクト システムはクラス再定義プロトコルの最後で、元々のクラスのアイデンティティと 再定義されたクラスをアイデンティティを入れ替えてしまします。それゆえ、 元々のクラスへの参照は、再定義されたクラスへの参照となります。筆者が 知るかぎり、クラス再定義は、STklos 0.56 においても、Guile 1.6.4 においても スレッド安全ではありません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.2.6 クラスの定義例

いくつかの例をみましょう。グラフィカルツールキットを定義しているところ だということにしましょう。<window> はスクリーン上の矩形領域で、 幅と高さを持ちます。これを階層構造に構成することが可能です。すなわち、 ある window は別の window 中に置くことができ、親 window へのポインタを もっているものとします。window の位置は、親ウィンドウの位置からの 相対座標、x と y で指定します。スクリーン全体を覆う「ルート」window を作り、これがデフォルトの親 window にもなります。ここまでで、次のように なります。

 
;; The first version
(define-class <window> ()
  (;; Pointer to the parent window.
   (parent      :init-keyword :parent :init-form *root-window*)
   ;; Sizes of the window
   (width       :init-keyword :width  :init-value 1)
   (height      :init-keyword :height :init-value 1)
   ;; Position of the window relative to the parent.
   (x           :init-keyword :x :init-value 0)
   (y           :init-keyword :y :init-value 0)
   ))

(define *screen-width* 1280)
(define *screen-height* 1024)

(define *root-window*
  (make <window> :parent #f :width *screen-width* :height *screen-height*))

:init-value および :init-form の使い方に注意してください。 <window> クラスが定義されたとき、*root-window* はまだ束縛 されていませんので、ここでは :init-value は使えません。

 
gosh> *root-window*
#<<window> 0x80db1d0>
gosh> (define window-a (make <window> :width 100 :height 100))
window-a
gosh> (d window-a)
#<<window> 0x80db1b0> is an instance of class <window>
slots:
  parent    : #<<window> 0x80db1d0>
  width     : 100
  height    : 100
  x         : 0
  y         : 0
gosh> (define window-b
        (make <window> :parent window-a :width 50 :height 20 :x 10 :y 5))
window-b
gosh> (d window-b)
#<<window> 0x80db140> is an instance of class <window>
slots:
  parent    : #<<window> 0x80db1b0>
  width     : 50
  height    : 20
  x         : 10
  y         : 5

筆者と同じ感覚の持ち主なら、*root-window* のようなグローバル 変数をツールキットのユーザに見せたいとは思わないでしょう。これを カプセル化するひとつの方法は、ルート window へのポインタをクラス変数に 保持させることです。<window> の定義に以下のようなスロットオプションを 追加すると <window> クラスの root-window スロットは同じ 格納領域を参照するようになります。

 
(define-class <window> ()
  (...
   ...
   (root-window :allocation :class)
   ...))

<window> のインスタンスに対して、slot-ref および slot-set! を、また、<window> クラスそのものに対しては、 class-slot-refclass-slot-set! を使って root-window スロットの値を取得したり、セットしたりできます。

ツールキットのユーザは window の相対座標のかわりに絶対座標(ルートウィンドウ 中の座標)が欲しいと思うことがあるでしょう。以下のようにして、絶対座標を 返す仮想スロットを提供することができます。

 
(define-class <window> ()
  (...
   ...
   (root-x :allocation :virtual
           :slot-ref  (lambda (o)
                        (if (ref o 'parent)
                            (+ (ref (ref o 'parent) 'root-x)
                               (ref o 'x))
                            (ref o 'x)))
           :slot-set! (lambda (o v)
                        (set! (ref o 'x)
                              (if (ref o 'parent)
                                  (- v (ref (ref o 'parent) 'root-x))
                                  v)))
            )
    ...))

メソッドあるいは仮想スロット経由のこのようなインタフェースを供給することは いくぶんか趣味の問題です。仮想スロットは実装の変更を隠すことができるという 利点があります。つまり、root-x を実スロットに保持するように変更し、 x をあとで仮想スロットに変更するということを <window>を 使うコードをだめにすることなくおこなえます。オブジェクト指向の主流の言語 では、通常このような「実装変更の隠蔽」はインスタンス変数を隠し、メソッドを 公開するということでおこなわれています。Gauche やその他の CLOS 風システムでは スロットは常にユーザから見えており状況はすこし違うのです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.3 インスタンス

この節では、インスタンスの生成のしかたと使い方について説明します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.3.1 インスタンスの作成

クラスオブジェクトをつかうと、ジェネリック関数 make でその クラスのインスタンスを生成できます。標準の <class> に対して 特定化されたメソッドは以下のとおり定義されています。

Generic Function: make
Method: make (class <class>) arg …

class のインスタンスを生成し、それを返します。arg … は 典型的な場合には、そのインスタンスを初期化するためのキーワード値のリストです。

概念としては、デフォルトの make メソッドは以下のように定義されて います。

 
(define-method make ((class <class>) . initargs)
  (let ((obj (allocate-instance class initargs)))
    (initialize obj initargs)
    obj))

すなわち、最初、class のインスタンス用にメモリをアロケートし、 それから、initialize メソッドを用いてそれを初期化します。

Generic Function: allocate-instance
Method: allocate-instance (class <class>) initargs

classの新規にアロケートされた、初期化されていないインスタンスを返します。

Generic Function: initialize
Method: initialize (obj <object>) initargs

<object> に対するデフォルトの初期化メソッドは以下のように働きます。

デフォルトのスロットアロケーションクラスのなかで、インスタンスアロケート スロットだけが初期化可能で、上の流れで処理されます。クラスアロケート スロット(すなわち、スロットアロケーションが :class あるいは :each-subclass のどちらかの場合)は、:init-value あるいは :init-form スロットオプションが与えられていれば、クラスオブジェクト 生成時に初期化されます。仮想スロットが、初期化されることはありません。

ユーザ定義アロケーションクラスは、初期化可能にも不可能にも設定することが できます。詳しくは メタオブジェクトプロトコル を参照してください。

initialize メソッドを特定化する場合には next-method が 確実に呼ばれるようにして、新しく生成されたインスタンスのすべての スロットにアクセスする前にデフォルトの流れで、そのスロットが正しく 初期化されるようにしてください。

定義したクラスに対応する initialize メソッドを特定化して インスタンスの初期化の方法をカスタマイズするというのが、典型的な やりかたです。

allocate-instance メソッドを特定化するというのは一般的な方法では ありません。しかしながら、make がどのように働くかを知っているなら、 make そのものを特定化して、なんらかの状況(たとえば、あらかじめ アロケートしてあるインスタンスを使うというような状況)でインスタンスの アロケーションを回避できます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.3.2 インスタンスへのアクセス

標準アクセサ

Function: slot-ref obj slot

オブジェクトobjのスロットslotの値を返します。

指定したスロットが値に束縛されていない場合、ジェネリック関数 slot-unboundが3つの引数、objのクラス、objslot を伴って呼び出されます。slot-unboundのデフォルトの振る舞いは、 エラーの通知です。

オブジェクトが指定されたスロットを持っていない場合は、ジェネリック関数 slot-missingが3つの引数、objのクラス、objslotを 伴って呼び出されます。slot-missingのデフォルトの振る舞いは、 エラーの通知です。

Function: slot-set! obj slot value

オブジェクトobjのスロットslotの値を、valueに セットします。

オブジェクトが指定したスロットを持っていない場合は、ジェネリック関数 slot-missingが4つの引数、objのクラス、objslotvalueを伴って呼び出されます。

Function: slot-bound? obj slot

オブジェクトobjのスロットslotが束縛されていれば真を、 そうでなければ偽を返します。

オブジェクトが指定したスロットを持っていない場合は、ジェネリック関数 slot-missingが3つの引数、objのクラス、objslotを伴って呼び出されます。

Function: slot-exists? obj slot

objslotを持っていれば真を返します。

Function: slot-push! obj slot value

この関数は、一般的なイディオムの実装です。 これは以下のようなコードで定義できます(が、将来のバージョンでは 最適化されるでしょう)。

 
(define (slot-push! obj slot value)
  (slot-set! obj slot (cons value (slot-ref obj slot))))
Function: slot-pop! obj slot :optional fallback

slot-push!と逆の操作です。objslotの値が ペアの場合、そのcarをslotの値から取り除き、取り除かれた値を返します。

slotの値がペアでない、あるいはslotが未束縛の場合、 fallbackが与えられていればそれが返され、そうでなければエラーが報告されます。

Method: ref (obj <object>) (slot <symbol>)
Method: (setter ref) (obj <object>) (slot <symbol>) value

これらのメソッドはそれぞれ、単に slot-ref および slot-set! を呼ぶだけです。直接 slot-refslot-set! を呼ぶよりも 効率はすこし悪いですが、プログラムコードはコンパクトになります。

フォールバックメソッド

Generic Function: slot-unbound
Method: slot-unbound (class <class>) obj slot

このジェネリック関数は束縛されていないスロットの値を取り出そうとしたときに 呼び出されます。このジェネリック関数の返り値は値を得ようとした呼出しもとに 返されます。

デフォルトのメソッドは単にエラーのシグナルをあげるだけです。

Generic Function: slot-missing
Method: slot-missing (class <class>) obj slot :optional value

このジェネリック関数は存在しないスロットの値を取り出そうとしたとき、あるいは 設定しようとしたときに呼びだされます。このジェネリック関数の返り値は、 値を得ようとした呼出しもとに返されます。

デフォルトのメソッドは単にエラーのシグナルをあげるだけです。

特殊アクセサ

Function: current-class-of obj

obj のクラスメタオブジェクトを返します。obj のクラスが 再定義されてしまった場合でも、obj がその変更に合せて更新されて いない場合には、この手続きは obj の元のクラスを返します。 この手続きは、obj を更新しません。

この手続きはめったに必要にはなりません。必要になるのは change-class メソッド内で、obj の更新のトリガーを引きたくないような場合 (無限ループを起す可能性がある場合)です。

Function: class-slot-ref class slot-name
Function: class-slot-set! class slot-name obj
Function: class-slot-bound? class slot-name obj

スロットの:allocationオプションが:classもしくは :each-subclassである場合、これらの手続きを使って、 インスタンス無しでそれらのスロットの値を取得/設定できます。

Method: slot-ref-using-class (class <class>) (obj <object>) slot-name
Method: slot-set-using-class! (class <class>) (obj <object>) slot-name value
Method: slot-bound-using-class? (class <class>) (obj <object>) slot-name

slot-refslot-set!slot-bound? のジェネリック 関数版です。classobject のクラスでなければなりません。

これらの関数は、ジェネリックであることに加えて、 obj のクラスが再定義されてもクラスの再定義を起動しない (そのような場合、classobj の元々のクラスで なければならない)という点で手続き版とは違います。

覚書: CLOS とはちがい、slot-ref などは、その中でジェネリック 関数版を呼ぶことはありません。それゆえ、slot-ref-using-class を 特定化することによって、slot-ref をカスタマイズすることはできません。 つまり、これらのジェネリック関数の主たる目的は change-class メソッドの内部で使われることです。とくに、slot-ref などは クラス再定義を再度起動する(詳細については クラスの変更 を 参照)ので、obj の再定義中には使えません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.3.3 クラスの変更

クラス変更プロトコル

CLOS系のオブジェクトシステムのユニークな機能は既存のインスタンスの クラスを変更できるということです。新旧二つのクラスに関連性がある必要は ありません。おのぞみなら、ミシンを雨がさに変更することもできます。

Generic Function: change-class
Method: change-class (obj <object>) (new-class <class>)

オブジェクト obj のクラスを new-class に変更します。 デフォルトのメソッドは単に change-object-class 手続きを呼ぶだけです。

Function: change-object-class obj orig-class new-class

オブジェクト obj のクラスを orig-class から new-class に変更します。これはジェネリック関数ではありません。 オブジェクトのクラスを変更するにはちょっとした秘密の内部的操作が必要で、 この手続きはそれを隠蔽しています。

クラスを変更する正確なステップは以下のようになっています。

  1. new-class の新しいインスタンスが allocate-instance によって アロケートされる。
  2. new-classの各スロットに対して、
    1. もし、そのスロットが old-class に存在し、obj で束縛されていれば、 その値は obj から取り出され、新しいインスタンスにセットされる。 (そのスロットは持ち越されます。)
    2. そうでなければ、新しいインスタンスのスロットは、インスタンスの作成 で 説明された手順で、標準のスロット初期化プロトコルによって初期化されます。
  3. 最後に、新しいインスタンスの内容が obj移植されます。すなわち、 obj がアイデンティティを変えることなく new-class のインスタンス となります。

objに対してnew-classinitilize メソッドは呼ばれないことに 注意してください。必要なら、独自の change-class メソッドを 定義してintializeを呼ぶようにすることができます。

change-object-classobj を返します。

ユーザは大抵の場合、change-object-classを直接呼ぶ必要は無いでしょう。 そのかわり、特定化した change-class を定義するべきです。 たとえば、旧いクラスのスロット x を 新しいクラスのスロット y へ持ち越すことは、 こんな風に書けば可能です。

 
(define-method change-class ((obj <old-class>) <new-class>)
  (let ((old-val (slot-ref obj 'x)))
    (next-method)               ;; calls default change-class
    (slot-set! obj 'y old-val)  ;; here, obj's class is already <new-class>.
    obj))

インスタンス更新のカスタマイズ

再定義されたクラスのインスタンスが更新される場合は、それもクラス変更として 扱われます。オブジェクトは通常のスロットアクセサ/モディファイア 経由でアクセスされるときに、そのクラスが再定義されたかどうかを チェックされます。もし再定義が行われていれば、 再定義されたクラスをnew-classとして change-classが呼ばれます。 すなわち、インスタンスの更新はオブジェクトの クラスを元々のものから再定義されたものへ変更することと看倣されます。

change-class を特定化することで、インスタンスを再定義された クラス用に更新する方法をカスタマイズできます。しかし、クラス再定義 用の change-class を書くには特別な注意が必要です。

まず、再定義はクラスオブジェクトのグローバルな束縛を変更してしまいます。 それゆえ、クラス再定義がおこる前の旧いクラスへの参照を保持しておく 必要があり、change-class メソッドの特定化をするには、この旧い クラスを使う必要があります。

 
;; save old <myclass>
(define <old-myclass> <myclass>)

;; redefine <myclass>
(define-class <myclass> ()
  ...)

;; define customized change-class method
(define-method change-class ((obj <old-myclass>) <myclass>)
  ...
  (next-method)
  ...)

次に、上の change-class メソッドは、slot-refslot-set!class-of などを経由して、暗黙のうちに起動される得ることに 注意してください。もし、change-class 内で、obj に対して 再度 slot-ref のような手続き使うと、インスタンス更新プロトコルが 再帰的に起動され、無限ループをひきおこすことになります。インスタンス 更新を起動しないようなメソッドしか使えません。すなわち、 slot-ref-using-classslot-set-using-class!slot-bound-using-class?current-class-of しか使えません。

仮想スロットのように、手続きによって計算される値をもつスロットを 持ち越したいのであれば、slot-ref その他が、そのスロットの値を 計算している最中に、暗黙裏に obj に対して呼ばれることがありえます。 実際のところは、change-object-class はこのような再帰を検出する 保護機構をもっています。もし、このようなことが起これば、 change-object-class はそのスロットの値を取り出すのを諦め、 新しいインスタンスのスロットを旧いスロットが未束縛であるとして、 初期化します。

インスタンス更新をカスタマイズするのは非常に強力ですがたいへん トリッキーな仕事です。Gauche のソース中のテストプログラム には自明ではないいくつかのケースが含まれています。test/object.scm 見てみてください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.4 ジェネリックファンクションとメソッド

メソッドの定義

Macro: define-generic name :key class

ジェネリック関数を生成し、name に束縛します。

通常は、これを使う必要はありません。もし、まだ存在していなければ、 define-method マクロが暗黙裏にジェネリック関数を生成してくれるからです。

キーワード引数classに、<generic>のサブクラスを渡すことで、 作られるジェネリック関数をデフォルトの<generic>クラスのインスタンスでは なく指定のクラスのインスタンスにすることができます。<generic>の サブクラスを定義してメソッド適用をカスタマイズする場合に便利です。

Macro: define-method name specs body

name という名前のメソッドを定義します。すでにグローバルに name に束縛されているジェネリック関数オブジェクトが存在していれば、生成された メソッドはそのジェネリック関数に追加されます。name が未束縛であるか またはジェネリック関数以外に束縛されているなら、新しいジェネリック関数が 生成され、name に束縛されて、新しいメソッドがそれに追加されます。

specs はこのメソッドに対応する引数とその型を指定します。これは lambda 形式の引数リストに似ていますが、それぞれの引数の型を指定できる ところが違います。

 
specs : ( arg … )
      | ( arg … . symbol )
      | ( argextended-spec …)
      | symbol

arg   : ( symbol class )
      | symbol

class は引数が所属すべきクラスを指定します。arg が単に シンボルであれば、(arg <top>) と同じです。rest 引数の 型を指定することはできません。それは常にリストに束縛されるからです。

:optional:key:rest等の拡張引数指定を使うことも できます。(拡張引数指定については手続きを作るを参照してください)。 拡張引数指定は、メソッドディスパッチに関しては rest引数と同様に扱われます。 すなわち、省略可能引数やキーワード引数にクラスを指定することはできません。

引数リストのクラスのリストはメソッド特定化子リストといい、 これを基に、ジェネリック関数は適切なメソッドを選択します。specs と それに対応する特定化子リストの例をあげておきます。 (rest引数は特定化子リストには考慮されないことに注意。リストに決まっているからです。) optionalの項はメソッドがrest引数を取るかどうかを示します。

 
specs:        ((self <myclass>) (index <integer>) value)
specializers: (<myclas> <integer> <top>)
optional:     #f

specs:        (obj (attr <string>))
specializers: (<top> <string>)
optional:     #f

specs:        ((self <myclass>) obj . options)
specializers: (<myclas> <top>)
optional:     #t

specs:        ((self <myclass>) obj :optional (a 0) (b 1) :key (c 2))
specializers: (<myclas> <top>)
optional:     #t

specs:        args
specializers: ()
optional:     #t

その特定化子リストがジェネリック関数の中のメソッドの一つに一致し、 rest引数の有無も同じである name 上のメソッドを定義すると、既存のメソッドは新しく定義された メソッドに置き換えられます。

ジェネリック関数の適用

ジェネリック関数は適用されると、まず、与えられた引数に適合する 特定化子リストを持つメソッドを選択します。たとえば、ジェネリック関数 foo が 3つのメソッドを持っており、それらの特定化子リストが それぞれ、(<string> <top>)(<string> <string>)(<top> <top>) であるとします。foo(foo "abc" 3) の ように適用されたとき、最初と 3番目のメソッドが選択されます。

選択されたメソッドは、もっとも特定化されたものから、もっとも一般的なものへ の順でソートされます。これは以下のように計算されます。

メソッドがソートされたら、最初のメソッドの本体が実引数で呼ばれます。

メソッド本体内部では、特別なローカル変数 next-methodが暗黙裏に 束縛されます。

Next method: next-method
Next method: next-method args …

この変数は、メソッド本体内部で、ソートされたメソッドリストで次のメソッド をカプセル化した特別なオブジェクトに束縛されます。

引数なしで呼ぶと、次のメソッドがこのメソッドよばれたときとと同じ引数で 起動されます。args … を明示的にわたすと、次のメソッドがその 渡された引数で起動されます。

next-method がもっとも特定化されていないメソッドで呼ばれた場合、 すなわち、「次のメソッド」がない場合、エラーシグナルがあがります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.5 メタオブジェクトプロトコル

CLOS 風のオブジェクトシステムでは、オブジェクトシステムがそれ自身の上に 構築されます。すなわち、クラス構造のようなもの、クラスをどのように 生成するか、インスタンスをどのように生成し初期化するか、メソッドをどのように ディスパッチし呼び出すか、これらはすべてオブジェクトシステムによって、 定義されます。たとえば、クラスはジェネリックな構造と標準的クラスの 振舞いを定義する <class> クラスのインスタンスです。<class> をサブクラス化すると、デフォルトのものとは違う振舞いをする、独自の クラス集合をつくることができます。これは結局、独自のオブジェクトシステムを つくることになります。

メタオブジェクトプロトコルは、どのようにオブジェクトシステムを 構築するかに関連する API 群の定義です。ブロック構築のクラス、オブジェクト システムを操作するあいだに呼ばれるジェネリック関数の名前と順序などです。 これらのクラスをサブクラス化し、これらのメソッドを特定化することは、 オブジェクトシステムの振舞いをカスタマイズすることを意味します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.5.1 クラスのインスタンシエーション

すべてのクラスはある特殊なクラスのグループのインスタンスになっています。 他のクラスのクラスになれるようなクラスのことを メタクラス と呼びます。 Gauche では <class> クラスおよびそのサブクラスのみがメタクラスに なれます。

define-class の展開

define-class マクロは基本的には <class> (あるいは指定された メタクラス)のインスタンスを生成するコードのラッパーで、それを与えられた 名前に束縛します。以下のような define-class 形式を前提とします。

 
(define-class name (supers)
  slot-specs
  options …)

これを次のように展開します。 (完全な展開形を知りたければ、ソースツリーのsrc/libobj.scm にあるdefault-classの定義を見てください。)

 
(define name
  (let ((tmp1 (make metaclass
                 :name 'name :supers (list supers)
                 :slots (map process-slot-definitions
                             slot-specs)
                 :defined-modules (list (current-module))
                 options …)))
    … check class redefinition …
    … registering accessor methods …
    tmp1))

生成されるクラスのクラス、つまり、metaclass は以下のルールで 決定されます。

  1. もし、:metaclass オプションが define-class マクロに 与えられていれば、その値を使います。その値は、<class> クラスか あるいはその子孫でなければなりません。
  2. さもなければ、クラス順位リスト中のクラスのメタクラスが試されます。

クラスの名前、スーパークラス、スロットの定義は初期化引数として ジェネリック関数 make に引き渡されます。また、 define-class のスロット定義以降に渡されたキーワード-値リストも 追加の初期化引数として make に渡されます。 初期化引数 define-modules は どのモジュールでそのクラスが定義されたかを覚えておくためにのものです。 これはこのクラスの再定義の時に使われます。

スロットの仕様 slot-specs は内部メソッド process-slot-definitions (これは直接呼び出すことはできません)で処理され、スロット定義になります。 厳密には、:init-form スロットオプションは、:init-thunk オプション になり、:getter:setter:accessor のスロットオプションは 引用されます。

クラス(metaclass のインスタンス)が生成された後、name のグローバル な束縛がチェックされます。それが、クラスに束縛されていれば、クラスの再定義 プロトコルが起動されます(クラスの再定義 参照)。

その後、slot-specs 中で、:getter:setter:accessor スロットオプションに与えられたメソッドが集められ、対応する ジェネリック関数に登録されます。

クラス構造

Class: <class>

すべてのメタクラスのベースクラスである <class> は以下のような スロットを持っています。これらのスロットは内部的な管理のためにあるので クラスが初期化された後に、これらの値を自由に変更することはできません。

クラスの情報を得るには、これらのスロットに直接 アクセスするのではなく、クラスオブジェクト にある手続きを使うことをおすすめします。

Instance Variable of <class>: name

クラスの名前、define-class マクロに与えられたシンボルです。 class-name はこの値を返します。

Instance Variable of <class>: cpl

クラス順位リストです。class-precedence-list はこの値を返します。

Instance Variable of <class>: direct-supers

直接スーパークラスのリストです。 class-direct-supers はこの値を返します。

Instance Variable of <class>: accessors

スロットアクセサの連想リストです。これは各スロットがどのようにアクセスされる べきかをカプセル化しています。

Instance Variable of <class>: slots

スロット定義のリストです。class-slots はこの値を返します。 スロット定義についての詳細は、スロット定義オブジェクト を参照してください。

Instance Variable of <class>: direct-slots

このクラスの定義で直接指定された(つまり継承したものではない)スロット定義の リストです。class-direct-slots はこの値を返します。

Instance Variable of <class>: num-instance-slots

インスタンスにアロケートされるスロットの数です。

Instance Variable of <class>: direct-subclasses

このクラスを直接継承しているクラスのリストです。 class-direct-subclasses はこの値を返します。

Instance Variable of <class>: direct-methods

このクラスを特定化子リスト中にもつメソッドのリストです。 class-direct-methods はこの値を返します。

Instance Variable of <class>: initargs

このクラスが生成されるときの初期化引数リストです。この情報は 再定義されたクラスを初期化するのに使います(クラスの再定義 参照)。

Instance Variable of <class>: defined-modules

このクラスがグローバル束縛をもつモジュールのリストです。

Instance Variable of <class>: redefined

このクラスが再定義された場合、このスロットは新しいクラスへの参照を含みます。 そうでない場合にはこのスロットは #f をもっています。

Instance Variable of <class>: category

このスロットの値は、このクラスがどのように生成されたかを示しています。 Scheme 定義のクラスは、scheme というシンボルを持っています。それ以外の 値は内部的に使用するだけです。

<class> 用の initialize メソッド

Method: initialize (class <class>) :rest initargs

The define-class macro expands into a call of (make <class> …), which allocates a class metaobject and calls initialize method. This method takes care of computing inheritance order (class precedence list) and calculate slots, and set up various internal slots. Then, at the very end of this method, it freezes the essential class slots; they became immutable.

Calculation of inheritance and slots are handle by generic fucntions. If you define a metaclass, you can define methods for them to customize how those calculations are done. Class inheritance is calculated by compute-cpl defined below. Slot calculation is a bit involved, and explained in the next subsection (see section スロットアクセスのカスタマイズ).

If your metaclass needs to initialize auxiliary slots, you can define your own initialize method, in which you call next-method first to set up the core part of the <class> structure, then you sets up metcalss-specific part. One caveat is that, after next-method handes initialization of the core <class> part, you can no longer modify essential class slots. If you need to tweak those slots, you can override class-post-initialize method, which is called right before the core class slots are frozen.

Generic function: compute-cpl class
Generic function: class-post-initialize class initargs

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.5.2 スロットアクセスのカスタマイズ

Generic Function: compute-slots class
Generic Function: compute-get-n-set class slot-definition

これらふたつのジェネリックファンクションによって、クラスの持つべきスロット、 及び各スロットがどのようにアクセスされるかが決定されます。

クラスのinitializeメソッドは、クラスの direct-superscpldirect-slotsスロットを セットしてから、compute-slotsメソッドを呼びます。 このメソッドは既にセットされた3つのスロットの情報から、 該当クラスの持つべきスロットと、 各スロットのスロットオプションを決定します。 メソッドの返り値は以下の形式のフォームで、これがクラスのslotsスロットに セットされます。

 
<slots> : (<slot-definition> ...)
<slot-definition> : (<slot-name> . <slot-options>)
<slot-name> : symbol
<slot-options> : keyword-value alternating list.

compute-slotsの返り値によってslotsスロットが設定されたら、 次に各スロットについてcompute-get-n-setが呼ばれます。 このメソッドは、各スロットをどのようにアクセスするかを決定します。 引数はクラスとスロット定義(上の<slot-definition>)です。 返り値は以下のいずれかでなけばなりません。

整数n

このスロットはn番目のインスタンススロットになります。 インスタンスにスロットを割り当てる唯一の方法です。

compute-get-n-setのベースメソッドは、それまでに割り当てられた インスタンススロットの数をクラスのnum-instance-slotsスロットに格納しています。 他の特殊化されたメソッドでこのスロットの値を参照したり変更したりすることは 避けてください(オブジェクトシステムの中身を知悉していて、そうすべき十分な理由が ある場合は別ですが。) 通常の場合、単にnext-methodを呼び出せば、ベースメソッドが インスタンススロットを新たに割り当ててそのインデックスを返してくれます。

インスタンススロットアクセスのふるまいを変更する、下に示す例も参照してください。

a list (get-proc set-proc bound?-proc initializable)

The get-proc, set-proc and bound?-proc elements are procedures invoked when this slot of an instance is accessed (either via slot-ref/slot-set!/slot-bound?, or an accessor method specified by :getter/:setter slot options). The value other than get-proc may be #f, and can be omitted if all the values after it is also #f. That is, the simplest form of this type of return value is a list of one element, get-proc.

  • When this slot is about to be read, get-proc is called with an argument, the instance. The returned value of get-proc is the value of the slot.

    The procedure may return #<undef> to indicate the slot is unbound. It triggers the slot-unbound generic function. (That is, this type of slot cannot have #<undef> as its value.)

  • When this slot is about to be written, set-proc is called with two arguments, the instance and the new value. It is called purely for the side effect; the procedure may change the value of other slot of the instance, for example.

    If this element is #f or omitted, the slot becomes read-only; any attempt to write to the slot will raise an error.

  • When slot-bound? is called to check whether the slot of an instance is bound, bound?-proc is called with an argument, the instance. It should return a boolean value which will be the result of slot-bound?.

    If this element is #f or omitted, slot-bound? will call get-proc and returns true if it returns #<undef>.

  • The last element, initializable, is a flag that indicates whether this slot should be initialized when :init-value or :init-form.
A <slot-accessor> object

Access to this slot is redirected through the returned slot-accessor object. See below for more on <slot-accessor>.

The value returned by compute-get-n-set is immediately passed to compute-slot-accessor to create a slot accessor object, which encapsulates how to access and modify the slot.

After all slot definitions are processed by compute-get-n-set and compute-slot-accessor, an assoc list of slot names and <slot-accessor> objects are stored in the class’s accessors slot.

Generic Function: compute-slot-accessor
Method: compute-slot-accessor (class <class>) slot access-specifier

Access-specifier is a value returned from compute-get-n-set. The base method creates an instance of <slot-accessor> that encapsulates how to access the given slot.

Created slot accessor objects are stored (as an assoc list using slot names as keys) in the class’s accessors slot. Standard slot accessors and mutators, such as slot-ref, slot-set!, slot-bound?, and the slot accessor methods specified in :getter, :setter and :accessor slot options, all go through slot accessor object eventually. Specifically, those functions and methods first looks up the slot accessor object of the desired slot, then calls slot-ref-using-accessor etc.

Method: compute-slots (class <class>)

The standard method walks CPL of class and gathers all direct slots. If slots with the same name are found, the one of a class closer to class in CPL takes precedence.

Method: compute-get-n-set (class <class>) slot

The standard processes the slot definition with the following slot allocations: :instance, :class, each-subclass and :virtual.

Function: slot-ref-using-accessor obj slot-accessor
Function: slot-set-using-accessor! obj slot-accessor value
Function: slot-bound-using-accessor? obj slot-accessor
Function: slot-initialize-using-accessor! obj slot-accessor initargs

The low-level slot accessing mechanism. Every function or method that needs to read or write to a slot eventually comes down to one of these functions.

Ordinary programs need not call these functions directly. If you ever need to call them, you have to be careful not to grab the reference to slot-accessor too long; if obj’s class is changed or redefined, slot-accessor can no longer be used.

Here we show a couple of small examples to illustrate how slot access protocol can be customized. You can also look at gauche.mop.* modules (in the source tree, look under lib/gauche/mop/) for more examples.

The first example implements the same functionality of :virtual slot allocation. We add :procedural slot allocation, which adds :ref, :set! and :bound? slot options.

 
(define-class <procedural-slot-meta> (<class>) ())

(define-method compute-get-n-set ((class <procedural-slot-meta>) slot)
  (if (eqv? (slot-definition-allocation slot) :procedural)
    (let ([get-proc   (slot-definition-option slot :ref)]
          [set-proc   (slot-definition-option slot :set!)]
          [bound-proc (slot-definition-option slot :bound?)])
      (list get-proc set-proc bound-proc))
    (next-method)))

A specialized compute-get-n-set is defined on a metaclass <procedural-slot-meta>. It checks the slot allocation, handles it if it is :procedural, and delegates other slot allocation cases to next-method. This is a typical way to add new slot allocation by layering.

To use this :procedural slot, give <procedural-slot-meta> to a :metaclass argument of define-class:

 
(define-class <temp> ()
  ((temp-c :init-keyword :temp-c :init-value 0)
   (temp-f :allocation :procedural
           :ref   (lambda (o) (+ (*. (ref o 'temp-c) 9/5) 32))
           :set!  (lambda (o v)
                    (set! (ref o 'temp-c) (*. (- v 32) 5/9)))
           :bound? (lambda (o) (slot-bound? o 'temp-c))))
  :metaclass <procedural-slot-meta>)

An instance of <temp> keeps a temperature in both Celsius and Fahrenheit. Here’s an example interaction.

 
gosh> (define T (make <temp>))
T
gosh> (d T)
#<<temp> 0xb6b5c0> is an instance of class <temp>
slots:
  temp-c    : 0
  temp-f    : 32.0
gosh> (set! (ref T 'temp-c) 100)
#<undef>
gosh> (d T)
#<<temp> 0xb6b5c0> is an instance of class <temp>
slots:
  temp-c    : 100
  temp-f    : 212.0
gosh> (set! (ref T 'temp-f) 450)
#<undef>
gosh> (d T)
#<<temp> 0xb6b5c0> is an instance of class <temp>
slots:
  temp-c    : 232.22222222222223
  temp-f    : 450.0

Our next example is a simpler version of gauche.mop.validator. We add a slot option :filter, which takes a procedure that is applied to a value to be set to the slot.

 
(define-class <filter-meta> (<class>) ())

(define-method compute-get-n-set ((class <filter-meta>) slot)
  (cond [(slot-definition-option slot :filter #f)
         => (lambda (f)
              (let1 acc (compute-slot-accessor class slot (next-method))
                (list (lambda (o) (slot-ref-using-accessor o acc))
                      (lambda (o v) (slot-set-using-accessor! o acc (f v)))
                      (lambda (o) (slot-bound-using-accessor? o acc))
                      #t)))]
        [else (next-method)]))

The trick here is to call next-method and compute-slot-accessor to calculate the slot accessor and wrap it. See how this metaclass works:

 
(define-class <foo> ()
  ((v :init-value 0 :filter x->number))
  :metaclass <filter-meta>)

gosh> (define foo (make <foo>))
foo
gosh> (ref foo'v)
0
gosh> (set! (ref foo'v) "123")
#<undef>
gosh> (ref foo'v)
123

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.5.3 メソッドのインスタンシエーション

Method: make (class <method>) :rest initargs

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

7.5.4 メソッド適用のカスタマイズ

Generic Function: apply-generic gf args
Generic Function: sort-applicable-methods gf methods args
Generic Function: method-more-specific? method1 method2 classes
Generic Function: apply-methods gf methods args
Generic Function: apply-method gf method build-next args

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8. ライブラリモジュール - 概要

続くいくつかの章で、Gaucheのディストリビューションに含まれる ライブラリモジュールを解説します。 これらのモジュールを使うにあたっては、特に断りのない限り、 モジュールをロードしてimportすることが必要です (通常はuseマクロが使えます。モジュールの使用参照)。

いくつかのモジュールは「オートロードされる」と表記されています。 この場合は、そのモジュールを使うにあたってあらかじめモジュールをロードしたり useしておく必要はありません。そのモジュールの束縛が使われた時点で、 モジュールが自動的にロードされインポートされます。オートロードの 詳細についてはAutoloadを参照してください。

付属ライブラリの数が増えるにつれ、必要なライブラリを探すのが難しく なりつつあります。迷ったら、必要なライブラリの探し方を 見てみて下さい。目的に合わせてライブラリをカテゴライズしています。

この章に続く4つの章で、各ライブラリについて詳細に記述します。 各章は名前でグループ化されています。

実行時にプログラムが特定のモジュールやライブラリの存在を検査できる 手続きがいくつか提供されています。 詳しくはライブラリの操作を参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1 必要なライブラリの探し方

各モジュール名は概ね何を実装しているかを示すような名前になっていて、 何のための実装になっているかは示していません。モジュールがひと つの問題を解くためにあるのなら両方は一致しますが、問題の解法が複数ある こともありますし、ひとつのアルゴリズムの実装が複数の異る問題を解くのに 役立つこともあります。したがって、モジュールに問題指向(あるいは目的指 向)の名前を付けることは困難です。

というわけで、Gaucheにはじめてふれるプログラマにとって問題を解くのに適 切なGaucheモジュールを見つけるのは簡単ではないかもしれません。やるべきことに対 応するアルゴリズムは複数ありえますから、それぞれのアルゴリズムは別々の モジュールで実装されています。

モジュールはまた、いくつもの層で実装されています。システムコールへの直接のインタフェース を提供する低レベルモジュールもあれば、複数の低レベルモジュールを使って 構築された、より抽象化され使いやすい高レベルモジュールもあります。さて どれを使えばよいでしょう。通常もっとも高レベルのものを使うのがよいでしょ う。やさしく使える抽象度の高いインタフェースを提供するというのがまさに ライブラリの目的だからです。とはいうものの、場合によっては抽象の壁をや ぶってその下にあるメカニズムに触らなければ出来ないこともあります。そ ういうときには低レベルのモジュールを直接使う必要があります。

ここではライブラリをその目的にしたがってグループ分けします。各カテゴリ ごとに関連するモジュールをあげ、簡単な説明をしています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.1 ライブラリディレクトリ - データコンテナ

ジェネリックなコンテナ操作

似たような性質をもつデータコンテナがあります。たとえば、リスト、ベクタ、 ハッシュテーブルはデータのコレクションとして見ることができます。それゆ え、すべての要素に適用できるようなジェネリックな操作があれば便利です。

Gaucheでは主にオブジェクトシステムで使うためにこのような機構をある程度 用意されています。

コンテナの実装


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.2 ライブラリディレクトリ - 文字列と文字

文字列の基本操作は文字列およびsrfi-13 - 文字列ライブラリでカバーされ ています。文字列は文字のシーケンスでもありますので、 gauche.collection - コレクションフレームワークおよびgauche.sequence - シーケンスフレームワークのメソッドを適 用可能です。

文字および文字集合の操作は 文字文字集合srfi-14 - 文字集合ライブラリがカ バーしています。

文字列を先頭から順に操作したり構築したりする場合にはインデックスによる アクセスは行いません。文字列ポート(文字列ポート参照)はさらに 効率のよいエレガントな方法を提供しています。

文字列から文字のならびを検索したり、とりだしたりするのに正規表現が使え ます。正規表現参照。

文字列の低レベル(たとえばバイトレベル)表現を扱う必要があるなら gauche.uvector - ユニフォームベクタに文字列とバイトベクタ相互の変換をおこなうツール があります。

文字の単なるシーケンスよりも高次の構造を扱いたいのですか。それなら text.*モジュールを見てください。text.parse - 入力ストリームのパージングには 基本的なスキャナの実装が用意されています。 text.tr - 文字変換はUnixのtr(1)に似た機能を実装して います。2つのテキストのdiffをとることもできます。これについては text.diff - テキストストリームの相違点を計算するを見てください。文字列の断片 から大きな文字列を構成したいのなら、string-appendを使ってはいけ ません。text.tree - 怠惰なテキスト構築参照。

大事なことを言い忘れました。Gaucheはさまざまな文字エンコーディングをサ ポートしています。基本的な関数については gauche.charconv - 文字コード変換を見てください。open-input-file のようなもっとも高次の関数のほとんどは、:encodingキーワード引数 をとり、文字の変換を暗黙におこないます。非ASCII文字でSchemeのプログラ ムを書きたい場合にはマルチバイトスクリプトを見てください。encodingと いうマジックコメントを含むGaucheのソースコードを処理したいなら コーディング認識ポートを見てください。地域化が必要なら、Gaucneには GNU gettext互換のモジュール(text.gettext - 地域化メッセージ)もあります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.3 ライブラリディレクトリ - データ交換

ほとんどのプログラムでは外界(他のプログラムや人間)とコミュニケーション する必要があります。ということは外部データを読み込んでそれがどのような フォーマットで書かれているかを判別したり、他のプログラムが理解できる フォーマットでデータを書き出したりするということです。

ネットワークに関連する外部フォーマットの多くはRFCで規定されており、そ れらのいくつかに対応するRFCモジュール(rfc.*)があります。たとえ ばよく知られたRFC2822のメッセージフォーマットを処理するには rfc.822 - RFC822メッセージ形式を見てください。

表形式のデータを交換するのにもっとも便利な方法は表の一行をプレーンテキ ストの一行に対応させることです。そしてカラムは特定の文字で区切ります。 たとえば、コンマで区切ります。これらの解析と書き出し用の基本手続につい てはtext.csv - CSVテーブルを見てください。

今どきのビジネスユーザはXMLを欲しがるというのですか。いいでしょう。まぁ 彼らが欲しいのは尖った括弧のえらい冗長なS式なんですから、XMLをS式と して読み込んで、自家籠中の car とか cdr とか map でちょちょいと処理し てえらい冗長な尖った括弧で書き出せばいいですよね。 sxml.ssaxモジュール(sxml.ssax - 関数的なXMLパーザ)はSAX XMLパーザ の実装です。これを使えば、おてがるにXMLをパーズしたり処理したり、ある いはまた、SXML(S式XML)に変換することもできます。SXPath(XPathに対応する S式(sxml.sxpath - SXMLクエリ言語))を使ってSXMLクエリを発行することもでき ます。SXMLシリアライザを使えば、すべての種類のXMLやHTMLをはきだすこと もできます(sxml.serializer - SXMLからXMLとXHTMLのシリアライゼーション)。

(でもいまどきのwebサービスならたいていはJSONも話しますし、そっちの方が 手軽だっていうなら、rfc.json - JSONのパーズと構築を見てみると良いかも しれません。)

さまざまなファイルフォーマットを処理するルーチンをfile.*という モジュールで使えるようにしようという計画はあるのですが、まだなにも準備 できていません。ひとつ自分で書いてやろうという人がいたら是非おねがいし たいので、おしらせ下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.4 ライブラリディレクトリ - ファイル

ファイルおよびディレクトリ。おおざっぱに言うと見るべきところは2箇所で す。

ファイルシステムのコア部分には下位のOSが提供しているに近いルーチンが あります。Unixのシステムプログラミングの経験があれば、よく知っている関 数名がばかりだと思うでしょう。ちなみにfcntlの機能は gauche.fcntl (gauche.fcntl - 低レベルファイル操作)にあります。

まちがいなくfile.util(file.util - ファイルシステムユーティリティ)を使いたいこと でしょう。これはシステムレベル上で実装された高次レベルのルーチン群です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.5 ライブラリディレクトリ - プロセスとスレッド

プロセス関連のルーチンも2つのレベルで提供されます。

gauche.processモジュールは高レベルのルーチン (gauche.process - 高レベルプロセスインタフェース)を提供しています。これを使えば、た とえば、データを子プロセスとの間でパイプすることが簡単にできます。

Gaucheコアはプリミティブなforkおよびexecのインタフェース を、便利なsystemコール(プロセス管理参照)同様提 供しています。やりたいことを細かくコントロールしたい場合に使うとよいで しょう。

GaucheはOSXを含む多くのUnixプラットフォームでプリエンプティブなスレッドを サポートしています。低レベルの排他制御を含む基本的なスレッドのサポートについては gauche.threads - スレッドを参照してください。data.queueモジュール (data.queue - キュー参照)では、スレッド間同期にも使えるスレッドセーフなキューを 提供しています。スレッドプールはcontrol.thread-pool (control.thread-pool - スレッドプール参照) によって提供されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.6 ライブラリディレクトリ - ネットワーク

これは抽象の層が複数あります。最下層ではソケットレベルのシステムコール に対応するAPIがあります。中間層ではホスト名の解決、コネクション、シャッ トダウンなどを自動でやるような便利なライブラリになっています。最上層で は、たとえばHTTPのような特定のプロトコルを扱うためのモジュールがいくつ か用意されています。

gauche.netモジュール(gauche.net - ネットワーキング)は最下層と中間層を提供し ています。最上層についてはrfc.*モジュールを探してみて下さい。 たとえば rfc.http (rfc.http - HTTP)です。 その他のプロトコルについてもまもなくサポートされる予定です。 rfc.ftprfc.imap4はすでにユーザの方々が書いてくれていて、 Gaucheにとりこまれるのを待っている状態にあります。おそらく次のリリース で統合されるでしょう。

さらに複数のプロトコルを抽象化してnet.*という名前のさらに高次レ ベルのライブラリの計画もあります。emailの送信あるいはURIによるリソース アクセスなどの計画があります。コードの貢献は歓迎します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.7 ライブラリディレクトリ - 入出力


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.8 ライブラリディレクトリ - 時間


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.1.9 ライブラリディレクトリ - ビットとバイト

バイナリ入出力

最下層としてGaucheはコアに含まれるブロック入出力(read-uvector, read-uvector!write-uvector)と同様、バイト入出力のプリミティブ (read-bytewrite-byte)も含んでいます (データの読み込み出力ユニフォームベクタのブロック入出力参照)。

中間層としては、binary.io(binary.io - バイナリI/O)モジュールには オプションでエンディアンを指定できる特定のデータを取得するルーチンがあ ります。

そして最上層としては、binary.pack(binary.pack - バイナリデータのパック) モジュールがあって、構造をもつバイナリデータのパック、アンパックを可能 にしています。Perlでいうpack/unpackです。

ビット操作

Gaucheコアはビットシフトおよびマスク操作を提供しています。 ビット演算を見てください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.2 ライブラリの命名規則

下の表は、モジュールの名前付けの分類を示します。 外部モジュール、及び将来予定されているモジュールも含みます。

binary.*

バイナリデータを扱うためのユーティリティ。

compat.*

互換性のためのレイヤを提供。

data.*

様々なデータ構造の実装

dbi.*, dbd.*

データベース独立インタフェース層とドライバー。

dbm.*

DBMインタフェース

gauche.*

Gauche本体の一部として考えられている機能。

gl.*

OpenGLバインディングと関連するライブラリ (外部パッケージ)。

gtk.*

GTk+バインディングと関連するライブラリ (外部パッケージ)。

file.*

ファイルとディレクトリの操作。

lang.*

言語に関するライブラリ(人工的なものも自然言語関連も)。(予定)

math.*

数学。

os.*

特定のOS向けの機能。

rfc.*

RFCで定義されているデータフォーマットやネットワークプロトコルの実装。

srfi-*

SRFIの実装

sxml.*

SXMLライブラリ。

text.*

テキストデータを扱うライブラリ

util.*

汎用的なアルゴリズムの実装。

www.*

WWWで主として使われるプロトコルやデータフォーマットの実装。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

8.3 廃止されたモジュール

Gaucheの開発の過程で、いくつかのモジュールは名前が変わったり、 合併したり、あるいはコアに取り込まれたりしてきました。 参考のため、それらのモジュールをここにリストしておきます。 新たなコードはこれらのモジュールを使うべきではありませんが、 古いコードがそのまま動きつづけるよう、Gaucheの配布物の中にはこれらの モジュールが含まれています。

Module: text.unicode

gauche.unicodeへと名前変更。gauche.unicode - Unicodeユーティリティ参照。

Module: util.list

コアに吸収されました。もう必要ありません。

Module: util.queue

data.queueへと名前変更。data.queue - キュー参照。

Module: util.rbtree

コアに吸収され、組み込みオブジェクト<tree-map>になりました。 ツリーマップ参照。

以下の手続きは、rbtreetree-mapに変えた手続きの別名と なっています。例えばrbtree-gettree-map-getと同じです。

 
make-rbtree       rbtree?           rbtree-get        rbtree-put!
rbtree-delete!    rbtree-exists?    rbtree-empty?     rbtree-update!
rbtree-push!      rbtree-pop!       rbtree-num-entries rbtree->alist
alist->rbtree     rbtree-keys       rbtree-values     rbtree-copy
rbtree-fold       rbtree-fold-right

以下の手続きはそれぞれtree-map-mintree-map-maxtree-map-pop-min!tree-map-pop-max!とほぼ同じですが、 省略可能なデフォルト引数を取り、tree-mapが空の時にそれを返す点が異なります。 デフォルト引数が与えられなければ空のtree-mapに対してはエラーを投げます。 (tree-map-min等は、tree-mapが空の場合には#fを返します)。

 
rbtree-min           rbtree-max
rbtree-extract-min!  rbtree-extract-max!

以下の手続きはtree-mapに相当する手続きを持ちません。これは与えられたtree-mapの 内部整合性を検査する手続きです。

 
rbtree-check
Module: util.sparse

data.sparseへと名前変更。data.sparse - 疎なデータコンテナ参照。

Module: util.trie

data.trieへと名前変更。data.trie - Trie参照。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9. ライブラリモジュール - Gauche拡張モジュール


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.1 gauche.array - 配列

Module: gauche.array

このモジュールは多次元配列のデータタイプとそれに関する操作を提供します。 プリミティブなAPIはSRFI-25で定義されているものに従います。 任意のSchemeオブジェクトを保持できるSRFI-25の汎用配列の他に、 均一な数値ベクタ(gauche.uvector - ユニフォームベクタ参照)を使って 数値要素を効率良く保持する配列型も提供されます。 また、SRFI-10を使った配列の外部表現も実装されます。

N次元の配列の各エレメントはN個の整数のインデックス [ i_0 i_1i_N-1 ]でアクセスされます。 配列は、各次元のインデックスの下限s_kおよび上限e_kを決める shapeを持っています。ここで、s_k <= e_kであり、 k次元目のインデックスi_ks_k <= i_k < e_k を満たすものとします。 (s_k == e_k であるような配列も作れますが、 その配列にはデータをストアすることはできません。 また、0次元の配列は作れます。それは一つだけデータを保持できます)。 Shapeはそれ自体が [ D x 2 ] の配列です。 ここでDはそのshapeが表現する配列の次元数です。

配列のプリミティブに対しインデックスを渡すにはいくつか方法があります。 各インデックスをばらばらの引数として渡すこともできますし、 ベクタや1次元の配列にパックして渡すこともできます。 後者においてインデックスがパックされたベクタや配列を「インデックスオブジェクト」 と呼ぶことがあります。Gaucheでは、配列の要素に次々とアクセスするような処理では ベクタをインデックスオブジェクトとして使うと若干効率が良いでしょう。

配列はequal?手続きで比較することが出来ます。 二つの配列のshapeが等しく、また対応する各要素がequal?の意味で 等しい場合に二つの配列はequal?であると見なされます。

内部的には、配列は1次元のインデックスでアクセスされるバッキングストレージと、 多次元のインデックスをバッキングストレージへのインデックスにマップする手続きとから 構成されています。

Class: <array-base>

配列に関する汎用操作を実装している、抽象ベースクラスです。 実際に配列のインスタンスを作るには、以下のいずれかの具体クラスを使って下さい。

Class: <array>
Class: <u8array>
Class: <s8array>
Class: <u16array>
Class: <s16array>
Class: <u32array>
Class: <s32array>
Class: <u64array>
Class: <s64array>
Class: <f16array>
Class: <f32array>
Class: <f64array>

具体配列クラスです。<array>クラスはsrfi-25互換の配列、 すなわち、任意のSchemeオブジェクトを格納できる配列を実装します。 <u8array>から<f64array>までは、 それぞれ<u8vector>から<f64vector>をバッキングストレージとして 用いる配列を実装し、制限された範囲の整数もしくは不正確な実数のみを 効率良く格納することができます。

Reader Syntax: #,(<array> shape obj …)

配列はこの形式で書き出されます。 (配列が例えば<u8array>であるなら、<array>の部分は<u8array> となります。) shapeは偶数個の整数のリストで、 2n番目の整数がn次元目のインデックスの下限を、2n+1番目の 整数がn次元目のインデックスの上限(+1)を表します。 その後に、配列の要素がrow-majorの順で書き出されます。

この構文が読み込まれると、もとの配列とequal?である配列が作成されます。

 
; 次のような配列:
;   8 3 4
;   1 5 9
;   6 7 2
#,(<array> (0 3 0 3) 8 3 4 1 5 9 6 7 2)

; 4x4の単位行列
#,(<array> (0 4 0 4) 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1)
Function: array? obj

[SRFI-25] objが配列であれば#tが、そうでなければ#fが返されます。 (is-a? obj <array-base>)と等価です。

Function: make-array shape :optional init

[SRFI-25] Shapeがshapeである配列を作成します。 Shapeは [ D x 2 ] の配列で、 0 <= k < D なる各kに対して要素 [ k 0 ] は 要素[ k 1 ]以下でなければなりません。 initが与えられた場合は、配列の各要素がinitで初期化されます。 initが与えられなかった場合の配列の要素の初期値は不定です。

 
(make-array (shape 0 2 0 2 0 2) 5)
 ⇒ #,(<array> (0 2 0 2 0 2) 5 5 5 5 5 5 5 5)
Function: make-u8array shape :optional init
Function: make-s8array shape :optional init

Function: make-f32array shape :optional init
Function: make-f64array shape :optional init

make-arrayと似ていますが、均一な数値配列を返します。

Function: shape bound …

[SRFI-25] 偶数個の正確な整数を引数に取り、配列のshapeとして使える2次元の配列を返します。

 
(shape 0 2 1 3 3 5)
 ⇒ #,(<array> (0 3 0 2) 0 2 1 3 3 5)

(shape)
 ⇒ #,(<array> (0 0 0 2))
Function: array shape init …

[SRFI-25] Shapeがshapeであるような配列を作成し、 その要素をinit …で初期化します。

 
(array (shape 0 2 1 3) 'a 'b 'c 'd)
 ⇒ #,(<array> (0 2 1 3) a b c d)
Function: u8array shape init …
Function: s8array shape init …

Function: f32array shape init …
Function: f64array shape init …

arrayと同様ですが、init …で初期化された 均一な数値配列を返します。

 
(u8array (shape 0 2 0 2) 1 2 3 4)
 ⇒ #,(<u8array> (0 2 0 2) 1 2 3 4)
Function: array-rank array

[SRFI-25] 配列arrayの次元数を返します。

 
(array-rank (make-array (shape 0 2 0 2 0 2))) ⇒ 3
(array-rank (make-array (shape))) ⇒ 0
Function: array-shape array

配列arrayのshapeを表す配列を返します。

Function: array-start array dim
Function: array-end array dim
Function: array-length array dim

[SRFI-25+] array-startは配列arraydim番目の次元の インデックスの下限を返します。 array-endは上限+1を、そしてarray-lengthは両者の差を返します。 array-startarray-endはSRFI-25で定義されています。

 
(define a (make-array (shape 1 5 0 2)))

(array-start a 0)  ⇒ 1
(array-end a 0)    ⇒ 5
(array-length a 0) ⇒ 4
(array-start a 1)  ⇒ 0
(array-end a 1)    ⇒ 2
(array-length a 1) ⇒ 2
Function: array-size array

配列arrayの全要素数を返します。

 
(array-size (make-array (shape 5 9 1 3))) ⇒ 8
(array-size (make-array (shape))) ⇒ 1
(array-size (make-array (shape 0 0 0 2))) ⇒ 0
Function: array-ref array k …
Function: array-ref array index

[SRFI-25] 配列arrayの要素を取り出します。最初の形式では、 要素は整数のインデックスk …で指定されます。 2番目の形式では、要素はベクタまたは1次元配列のインデックスオブジェクトindex で指定されます。

Function: array-set! array k … value
Function: array-set! array index value

[SRFI-25] 配列arrayの要素にvalueをセットします。 最初の形式では、 要素は整数のインデックスk …で指定されます。 2番目の形式では、要素はベクタまたは1次元配列のインデックスオブジェクトindex で指定されます。

Function: share-array array shape proc

[SRFI-25] Shapeがshapeであり、与えられた配列arrayとバッキングストレージを 共有する新しい配列を作成して返します。 procは、新しい配列へのインデックスを古い配列へのインデックスへ マップする手続きです。新しい配列の次元数をn、古い配列の次元数をmと した時、procn個の引数を取りm個の値を返す手続きでなければ なりません。さらに、各マッピングはaffineマッピング、すなわち、 出力は入力の線形合成(プラス定数)でなければなりません。 (share-arrayprocがaffineマッピングであるという事実に基づいた 最適化を行います。新しい配列にアクセスする度にprocが呼ばれるというわけでは ありません)。

Function: array-for-each-index array proc :optional index

arrayの各インデックスに対してprocを呼びます。 index引数が省略された場合は、 procはインデックス (i, j,k,…) に対して (proc i j k …) のように呼ばれます。

 
gosh> (define a (array (shape 0 2 0 2) 1 2 3 4))
a
gosh> a
#,(<array> (0 2 0 2) 1 2 3 4)
gosh> (array-for-each-index a (^(i j) (print i","j)))
0,0
0,1
1,0
1,1

この形式の呼び出しは簡単なのですが、あまり効率が良くありません。 インデックスオブジェクトを省略可能引数indexに渡すことで、 より良い性能を引き出すことができます。 インデックスオブジェクトはループの度に、各インデックスを表す値に書き換えられます。 インデックスオブジェクトに使えるのは、変更可能な、ベクタ・1次元の配列・ s8vector・s16vectorあるいはs32vectorで、その長さは配列arrayの ランクと一致していなければなりません。インデックスオブジェクトを使うと、 ループ中に一切アロケーションが行われないため速度的に有利です。 ただし、ループの度にインデックスオブジェクトの内容が書き換えられることに 注意する必要があります。

 
gosh> (array-for-each-index a (cut format #t "~s\n" <>) (vector 0 0))
#(0 0)
#(0 1)
#(1 0)
#(1 1)

gosh> (array-for-each-index a (cut format #t "~s\n" <>) (s8vector 0 0))
#s8(0 0)
#s8(0 1)
#s8(1 0)
#s8(1 1)

戻り値は未定義です。

Function: shape-for-each shape proc :optional index

シェイプshapeが表現する全ての可能なインデックスに対してprocを呼びます。 省略可能なindex引数の動作はarray-for-each-indexと同様です。 戻り値は未定義です。

 
gosh> (shape-for-each (shape 0 2 0 2) (^(i j) (print i","j)))
0,0
0,1
1,0
1,1
Function: tabulate-array shape proc :optional index

shapeが表現する各インデックスについてprocを呼び出し、 その戻り値から配列を構築して返します。省略可能なインデックスオブジェクトの用途は array-for-each-indexと同じです。 次の例は与えられたシェイプに対する単位行列を生成します:

 
(tabulate-array (shape 0 3 0 3) (^(i j) (if (= i j) 1 0)))
  ⇒ #,(<array> (0 3 0 3) 1 0 0 0 1 0 0 0 1)
Function: array-retabulate! array proc :optional index
Function: array-retabulate! array shape proc :optional index

配列arrayの各インデックスに対してprocを呼び、その戻り値で arrayの要素を置き換えます。 省略可能なインデックスオブジェクトの用途は array-for-each-indexと同じです。 二番目の呼び出し形式は第二引数にシェイプを取ります。 それはarrayのシェイプと一致しなければなりません。 意味的には冗長ですが、shapeがリテラルである場合、 何らかの最適化がなされる可能性があります。 戻り値は未定義です。

Function: array-map proc array0 array1 …
Function: array-map shape proc array0 array1 …

引数array0, array1, …は同じシェイプを持つ 配列でなければなりません。各入力配列の対応する要素について、 それらを引数としてprocが呼ばれ、その戻り値から 新たな配列が作られて返されます。 二番目の呼び出し形式は第二引数にシェイプを取ります。 それは入力配列のシェイプと一致しなければなりません。 意味的には冗長ですが、shapeがリテラルである場合、 何らかの最適化がなされる可能性があります。

 
(array-map - (array (shape 0 2 0 2) 1 2 3 4))
  ⇒ #,(<array> (0 2 0 2) -1 -2 -3 -4)
Function: array-map! array proc array0 array1 …
Function: array-map! array shape proc array0 array1 …

array-mapと似ていますが、procの結果は 与えられたarrayに格納されます。arrayのシェイプは 入力配列のシェイプと同じでなければなりません。 戻り値は未定義です。

Function: array->vector array
Function: array->list array

arrayの全要素を並べたベクタもしくはリストを作って返します。

 
(array->vector
 (tabulate-array (shape 1 3 1 4)
                 (^(i j) (+ (* 10 i) j))))
 ⇒ #(11 12 13 21 22 23)
Function: array-concatenate a b :optional dimension

指定の次元で配列を結合します。指定の次元の大きさは一致していなければなりません。 それ以外のシェイプは異なっていても構いません。配列のランクはいくつであっても 構いませんが、両配列のランクは同じでなければなりません。

 
;;  [a b]              [a b]
;;  [c d] (+)       => [c d]
;;            [e f]    [e f]
(array-concatenate
 (array (shape 0 2 0 2) 'a 'b 'c 'd)
 (array (shape 0 1 0 2) 'e 'f))
 ⇒ #,(<array> (0 3 0 2) a b c d e f)

;;  [a b]     [e]    [a b e]
;;  [c d] (+) [f] => [c d f]
(array-concatenate
 (array (shape 0 2 0 2) 'a 'b 'c 'd)
 (array (shape 0 2 0 1) 'e 'f)
 1)
 ⇒ #,(<array> (0 2 0 3) a b e c d f)

;; 結合次元の大きさが同じであればインデックスの範囲は異なっていてもよい
(array-concatenate
 (array (shape 0 2 0 2) 'a 'b 'c 'd)
 (array (shape 1 3 0 1) 'e 'f) 1)
 ⇒ #,(<array> (0 2 0 3) a b e c d f)
Function: array-transpose array :optional dim1 dim2

arrayはランク2以上の配列でなければなりません。 配列のdim1番目の次元とdim2番目の次元を転置します。 デフォルトは0番目と1番目です。

Function: array-rotate-90 array :optional dim1 dim2

arrayはランク2以上の配列でなければなりません。 配列のdim1番目の次元とdim2番目の次元をそれぞれ行と列とみなした行列を 考え、その行列を時計まわりに90度回転した新たな配列を作って返します。

 
;; [1 2 3]      [4 1]
;; [4 5 6]  =>  [5 2]
;;              [6 3]
(array-rotate-90 (array (shape 0 2 0 3) 1 2 3 4 5 6))
 ⇒ #,(<array> (0 3 0 2) 4 1 5 2 6 3)

arrayのランクが2より大きい場合は、arrayは「部分配列の行列」 と考えられます。

Function: array-flip array :optional dimension
Function: array-flip! array :optional dimension

配列の内容を、指定番目の次元 (デフォルトは0) で裏返しにします。 array-flip!arrayを直接変更してそれを返します。 array-fliparrayには触らず、裏返した内容で新たな配列を作って返します。

 
;; [1 2 3]  =>  [4 5 6]
;; [4 5 6]      [1 2 3]
(array-flip (array (shape 0 2 0 3) 1 2 3 4 5 6))
 ⇒ #,(<array> (0 2 0 3) 4 5 6 1 2 3)

;; [1 2 3]  =>  [3 2 1]
;; [4 5 6]      [6 5 4]
(array-flip (array (shape 0 2 0 3) 1 2 3 4 5 6) 1)
 ⇒ #,(<array> (0 2 0 3) 3 2 1 6 5 4)
Function: identity-array dimension :optional class

ランク2で行列ともにdimensionである単位行列を作って返します。 配列のクラスをclassに渡せば、結果はそのインスタンスになります。 デフォルトは<array>クラスです。

 
(identity-array 3)
 ⇒ #,(<array> (0 3 0 3) 1 0 0 0 1 0 0 0 1)

(identity-array 3 <f32array>)
 ⇒ #,(<f32array> (0 3 0 3) 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)
Function: array-inverse array

arrayを行列とみなし、その逆行列を返します。 arrayは2次元で、正方行列となるシェイプを持っていなければなりません。 そうでない場合はエラーが投げられます。

arrayが正則行列でない場合は#fが返されます。

Function: determinant array
Function: determinant! array

arrayを行列とみなし、その行列式を計算します。 arrayは2次元で、正方行列となるシェイプを持っていなければなりません。 そうでない場合はエラーが投げられます。

また、determinant!は計算過程でarrayの内容を破壊します。 determinantは計算の前にarrayをコピーするオーバヘッドが ありますが、arrayは変更されません。

Function: array-mul a b

配列abはともに2次元でなければなりません。 それらを行列とみなして乗算を行います。aの行数とbの列数は 一致していなければなりません。

 
;;           [6 5]
;; [1 2 3] x [4 3] => [20 14]
;; [4 5 6]   [2 1]    [56 41]

(array-mul (array (shape 0 2 0 3) 1 2 3 4 5 6)
           (array (shape 0 3 0 2) 6 5 4 3 2 1))
 ⇒ #,(<array> (0 2 0 2) 20 14 56 41)
Function: array-expt array pow

arraypow乗を返します。arrayは正方行列、 powは非負の正確な整数でなければなりません。

Function: array-div-left a b
Function: array-div-right a b

array-mulの逆を行います。 array-div-left(array-mul B M)Aと 等しくなるような行列Mを、 array-div-right(array-mul M B)Aと 等しくなるような行列Mを返します。 ABは2次元の正方行列でなければなりません。 Bが正則行列でない場合はエラーが通知されます。

Function: array-add-elements array array-or-scalar …
Function: array-add-elements! array array-or-scalar …
Function: array-sub-elements array array-or-scalar …
Function: array-sub-elements! array array-or-scalar …
Function: array-mul-elements array array-or-scalar …
Function: array-mul-elements! array array-or-scalar …
Function: array-div-elements array array-or-scalar …
Function: array-div-elements! array array-or-scalar …

要素ごとの計算をする手続きです。2つ目以降の引数は、 最初の引数の配列と同じ形の配列か、数値でなければなりません。 数値の場合は、要素が全てその数値である、最初の引数の配列と同じ形の配列だと解釈されます。

要素ごとに加算、減算、乗算、除算を行い、結果を最初の引数の配列と同じ形の配列で返します。

!で終わっている手続きは、最初の配列を結果を作るために再利用するかもしれません。 したがって最初の配列は変更可能である必要があります。ただし、必ず再利用されるとは 限らないので、呼び出し側は常に戻り値を使う必要があります。

 
(array-add-elements (array (shape 0 2 0 2) 1 2 3 4) 
                    (array (shape 0 2 0 2) 5 6 7 8) 
                    10)
 ⇒ #,(<array> (0 2 0 2) 16 18 20 22)

(array-div-elements (array (shape 0 2 0 2) 1 3 5 7)
                    100
                    (array (shape 0 2 0 2) 2 4 6 8))
 ⇒ #,(<array> (0 2 0 2) 1/200 3/400 1/120 7/800)

形が同じであれば、異なる型の配列同士での演算もできます。 結果は最初の引数の配列の型になります。

 
(array-mul-elements (make-u8array (shape 0 2 0 2) 3)
                    (array (shape 0 2 0 2) 1 3 5 7))
 ⇒ #,(<u8array> (0 2 0 2) 3 9 15 21)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.2 gauche.base - Gauche組み込み関数のインポート

Module: gauche.base

このモジュールはGaucheの組み込み手続きと構文をexportし、 gaucheモジュールを継承しない他のモジュールからもimportして Gaucheの組み込み機能を使えるようにします。

gaucheモジュールで見えるすべての束縛のうち、importを除いたものは そのままexportされます。importはR7RSのimportとの 衝突を避けるためgauche:importとしてexportされます。

このモジュールはまた、 全てのキーワード(gauche.keywordモジュールにある 束縛)もexportします。これで、Gauche関連のモジュールを継承しなくても gauche.baseをimportすれば自己束縛したキーワードを使えます。

通常のGaucheコードはこのモジュールを必要としません。組み込み関数はモジュール継承によって、 何もしなくても使えるようになっています。 新たに作られるモジュールは、何も指定しなければgaucheモジュールを 継承するからです (詳しくはモジュールの継承参照)。

しばしば、gaucheモジュールを継承しないけれどGaucheの組み込み関数を使いたい という場合があります。特にR7RSライブラリとプログラムは、そこで使う束縛をすべて 陽にimportする必要があります。従ってGaucheでも、R7RSのimportdefine-librarygaucheモジュールを継承しないモジュールを設定します。 R7RSコードでGaucheの組み込み機能を使うには、 (import (gauche base))とする必要があります。

別の用途としては、自分のモジュールで、Gaucheの組み込み機能のうちいくつかを隠したい、 といった場合があります。例えば次のコードは、Gaucheの組み込み機能のうち string-scanstring-splitだけが見えない almost-gaucheというモジュールを作ります。

 
(define-module almost-gauche
  (use scheme.r5rs)
  (use gauche.base :except (string-scan string-split)
                   :rename ((gauche:import import)))
  (extend)
  )
(select-module almost-gauche)

;; your code here

空のextendフォームに注目してください。このフォームで、 モジュールの継承リストを空にします。 (gauche.base:renameオプションは、almost-gauche内で 元のimportの名前を使えるようにするためのものです。importを直接 使わないのならこの:renameオプションは不要です。)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3 gauche.cgen - Cコードの生成

Gauche本体の多くの部分は、Gauche自身、もしくはS式ベースのDSLで書かれています。 これらのコードはビルド中にCソースに変換され、Cコンパイラで コンパイルされます。 gauche.cgenモジュールおよびそのサブモジュールは、 Gaucheビルドプロセスが使っているこの機能を一般にも使えるように公開するものです。

Cコードジェネレータに要求される機能はアプリケーションによって多種多様であり、 あまりがちがちに枠組みを固定してしまうと却って使い辛くなるでしょう。 そこで、ひとつの固定したフレームワークではなく、 ゆるく連携するいくつかのモジュールを提供し、ユーザが必要な機能を自由に 組み合わせて使えるようにしてあります。実際、Gaucheのビルド時に走る プロセスの中には、gauche.cgen.unitgauche.cgen.literalだけしか 使わないものもあります(例えば‘src/builtin-syms.scm’を見てください)。

Module: gauche.cgen

このモジュールは、簡便のために gauche.cgen.unitgauche.cgen.literalgauche.cgen.typegauche.cgen.ciseを extendしたものです。

大抵の場合、gauche.cgenをuseしておけば、個々のサブモジュールについて 考える必要はありません。 以降のセクションは主に説明のしやすさのために、サブモジュールごとに 分けて記述してあります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3.1 Cソースファイルを生成する

One of the tricky issues about generating C source is that you have to put several fragments of code in different parts of the source file, even you want to say just one thing—that is, sometimes you have to put declaration before the actual definition, plus some setup code that needs to be run at initialization time.

Creating a frame

Class: <cgen-unit>

A cgen-unit is a unit of C source generation. It corresponds to one .c file, and optionally one .h file. During the processing, a "current unit" is kept in a parameter cgen-current-unit, and most cgen APIs implicitly work to it.

The following slot are for public use. They are used to tailor the output. Usually you set those slots at initialization time. The effect is undefined if you change them in the middle of the code generation process.

Instance Variable of <cgen-unit>: name

A string to name this unit. This is used for the default name of the generated files (‘name.c’ and ‘name.h’) and the suffix of the default name of initialization function. Other cgen modules may use this to generate names. Avoid using characters that are not valid for C identifiers.

You can override those default names by setting the other slots.

Instance Variable of <cgen-unit>: c-file
Instance Variable of <cgen-unit>: h-file

The name of the C source file and header file, in strings. If they are #f (by default), the value of name slot is used as the file name, with extension .c or .h is attached, respectively.

To get the file names to be generated, use cgen-unit-c-file and cgen-unit-h-file generic functions, instead of reading these slots.

Instance Variable of <cgen-unit>: preamble

A list of strings to be inserted at the top of the generated sources. The default value is ("/* Generated by gauche.cgen */"). Each string appears in its own line.

Instance Variable of <cgen-unit>: init-prologue
Instance Variable of <cgen-init>: init-epilogue

A string to start or to end the initialization function, respectively. The default value of init-prologue is "void Scm_Init_NAME(void) {" where NAME is the value of the name slot. The default value of init-epilogue is just "}". Each string appears in its own line.

To get the default initialization function name, use cgen-unit-init-name generic function.

To customize initialization function name, arguments and/or return type, set init-prologue.

The content of initialization function is filled by the code fragments registered by cgen-init.

Parameter: cgen-current-unit

A parameter to keep the current cgen-unit.

A typical flow of generating C code is as follows:

  1. Create a <cgen-unit> and make it the current unit.
  2. Call code insertion APIs with code fragments. Fragments are accumulated in the current unit.
  3. Call emit method on the unit, which generates a C file and optionally a header file.
Generic Function: cgen-emit-c cgen-unit
Generic Function: cgen-emit-h cgen-unit

Write the accumulated code fragments in cgen-unit to a C source file and C header file. The name of the files are determined by calling cgen-unit-c-file and cgen-unit-h-file, respectively. If the files already exist, its content is overwritten; you can’t gradually write to the files. So, usually these procedures are called at the last step of the code generation.

We’ll explain the details of how each file is organized under “Filling the content” section below.

Generic Function: cgen-unit-c-file cgen-unit
Generic Function: cgen-unit-h-file cgen-unit

Returns a string that names C source and header file for cgen-unit, respectively. The default method first looks at c-file or h-file slot of the cgen-unit, and if it is #f, use the value of name slot and appends an extension .c or .h.

Generic Function: cgen-unit-init-name cgen-unit

Returns a string that names the initialization function generated to C. It is used to create the default init-prologue value.

Filling the content

There are four parts to which you can add C code fragment. Within each part, code fragments are rendered in the same order as added.

extern

This part is put into the header file, if exists.

decl

Placed at the beginning of the C source, after the standard prologue.

body

Placed in the C source, following the ’decl’ part.

init

Placed inside the initialization function, which appears at the end of the C source.

The following procedures are the simple way to put a souce code fragments in an appropriate part:

Function: cgen-extern code …
Function: cgen-decl code …
Function: cgen-body code …
Function: cgen-init code …

Put code fragments code … to the appropriate parts. Each fragment must be a string.

This is a minimal example to show the typical usage. After running this code you’ll get my-cfile.c and my-cfile.h in the current directory.

 
(use gauche.parameter)
(use gauche.cgen)

(define *unit* (make <cgen-unit> :name "my-cfile"))

(parameterize ([cgen-current-unit *unit*])
  (cgen-decl "#include <stdio.h>")
  (cgen-init "printf(stderr, \"initialization function\\n\");")
  (cgen-body "void foo(int n) { printf(stderr, \"got %d\\n\", n); }")
  (cgen-extern "void foo(int n);")
  )

(cgen-emit-c *unit*)
(cgen-emit-h *unit*)

These are handy escaping procedures; they are useful even if you don’t use other parts of the cgen modules.

Function: cgen-safe-name string
Function: cgen-safe-name-friendly string
Function: cgen-safe-string string
Function: cgen-safe-comment string

Escapes characters invalid in C identifiers, C string literals or C comments.

With cgen-safe-name, characters other than ASCII alphabets and digits are converted to a form _XX, where XX is hexadecimal notation of the character code. (Note that the character _ is also converted.) So the returned string can be used safely as a C identifier. The mapping is injective, that is, if the source strings differ, the result string always differ.

On the other hand, cgen-safe-name-friendly convers the input string into more readable C identifier. -> becomes _TO (e.g. char->integer becomes char_TOinteger), other - and _ become _, ? becomes P (e.g. char? becomes charP), ! becomes X (e.g. set! becomes setX), < and > become _LT and _GT respectively. Other special characters except _ are converted to _XX as in cgen-safe-name. The mapping is not injective; e.g. both read-line and read_line map to read_line. Use this only when you think some human needs to read the generated C code (which is not recommended, by the way.)

If you want to write out a Scheme string as a C string literal, you can use cgen-safe-string. It escapes control characters and non-ascii characters. If the Scheme string contains a character beyond ASCII, it is encoded in Gauche’s native encoding. (NB: It also escapes ?, to avoid accidenal formation of C trigraphs).

Much simpler is cgen-safe-comment, which just converts /* and */ into / * and * / (a space between those two characters), so that it won’t terminate the comment inadvertently. (Technically, escaping only */ suffice, but some simple-minded C parser might be confused by /* in the comments). The conversion isn’t injective as well.

 
(cgen-safe-name "char-alphabetic?")
  ⇒ "char_2dalphabetic_3f"
(cgen-safe-name-friendly "char-alphabetic?")
  ⇒ "char_alphabeticP"
(cgen-safe-string "char-alphabetic?")
  ⇒ "\"char-alphabetic\\077\""

(cgen-safe-comment "*/*"
  ⇒ "* / *"

If you want to conditionalize a fragment by C preprocessor #ifdefs, use the following macro:

Macro: cgen-with-cpp-condition cpp-expr body …

Code fragments submitted in body … are protected by #if cpp-expr and #endif.

If cpp-expr is a string, it is emitted literally:

 
(cgen-with-cpp-condition "defined(FOO)"
  (cgen-init "foo();"))

;; will generate:
#if defined(FOO)
foo();
#endif /* defined(FOO) */

You can also construct cpp-expr by S-expr.

 
<cpp-expr> : <string>
           | (defined <cpp-expr>)
           | (not <cpp-expr>)
           | (<n-ary-op> <cpp-expr> <cpp-expr> ...)
           | (<binary-op> <cpp-expr> <cpp-expr>)

<n-ary-op> : and | or | + | * | - | /

<binary-op> : > | >= | == | < | <= | !=
            | logand | logior | lognot | >> | <<

Example:

 
(cgen-with-cpp-condition '(and (defined FOO)
                               (defined BAR))
  (cgen-init "foo();"))

;; will generate:
#if ((defined FOO)&&(defined BAR))
foo();
#endif /* ((defined FOO)&&(defined BAR)) */

You can nest cgen-with-cpp-condition.

Submitting code fragments for more than one parts

When you try to abstract code generation process, calling individual procedures for each parts (e.g. cgen-body or cgen-init) becomes tedious, since such higher-level constructs are likely to require generating code fragments to various parts. Instead, you can create a customized class that handles submission of fragments to appropriate parts.

Class: <cgen-node>

A base class to represent a set of code fragments.

The state of C preprocessor condition (set by with-cgen-cpp-condition) is captured when an instance of the subclass of this class is created, so generating appropriate #ifs and #endifs are automatically handled.

You subclass <cgen-node>, then define method(s) to one or more of the following generic functions:

Generic Function: cgen-emit-xtrn cgen-node
Generic Function: cgen-emit-decl cgen-node
Generic Function: cgen-emit-body cgen-node
Generic Function: cgen-emit-init cgen-node

These generic functions are called during writing out the C source within cgen-emit-c and cgen-emit-h. Inside these methods, anything written out to the current output port goes into the output file.

While generating .h file by cgen-emit-h, cgen-emit-xtrn method for all submitted nodes are called in order of submission.

While generating .c file by cgen-emit-c, cgen-emit-decl method for all submitted nodes are called first, then cgen-emit-body method, then cgen-emit-init method.

If you don’t specialize any one of these method, it doesn’t generate code in that part.

Once you define your subclass and create an instance, you can submit it to the current cgen unit by this procedure:

Function: cgen-add! cgen-node

Submit cgen-node to the current cgen unit. If the current unit is not set, cgen-node is simply ignored.

In fact, the procedures cgen-extern, cgen-decl, cgen-body and cgen-init are just a convenience wrapper to create an internal subclass specialized to generate code fragment only to the designated part.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3.2 Schemeリテラルを生成する

Sometimes you want to refer to a Scheme constant value in C code. It is trivial if the value is a simple thing like Scheme boolean (SCM_TRUE, SCM_FALSE), characters (SCM_MAKE_CHAR(code)), small integers (SCM_MAKE_INT(value)), etc. You can directly write it in C code. However, once you step outside of these simple values, it gets tedious quickly, involving static data declarations and/or runtime initialization code.

For example, to get a Scheme value of a list of symbols (a b c), you have to (1) create ScmStrings for the names of the symbols, (2) pass them to Scm_Intern to get Scheme symbols, then (3) call Scm_Conses (or a convenience macro SCM_LIST3) to build a list.

With gauche.cgen, those code can be generated automatically.

NOTE: If you use cgen-literal, make sure you call (cgen-decl "#include <gauche.h>") to include ‘gauche.h’ before the first call of cgen-literal, which may insert declarations that needs ‘gauche.h’.

Function: cgen-literal obj

Returns an <cgen-literal> object for a Scheme object obj, and submit necessary declarations and initialization code to the current cgen unit.

For the above example, you can just call (cgen-literal '(a b c)) and the C code to set up the Scheme literal of the list of three symbols will be generated.

The result of cgen-literal is an instance of <cgen-literal>; the detail of the class isn’t for public use, but you can use it to refer the created literal in C code.

Generic Function: cgen-cexpr cgen-literal

Returns a C code expression fragment of type ScmObj, which represents the Scheme literal value.

The following example creates a C function printabc that prints the literal value (a b c), created by cgen-literal.

 
(define *unit* (make <cgen-unit> :name "foo"))
(parameterize ((cgen-current-unit *unit*))
  (let1 lit (cgen-literal '(a b c))
    (cgen-body
     (format "void printabc() { Scm_Printf(SCM_CUROUT, \"%S\", ~a); }"
             (cgen-c-name lit)))))
(cgen-emit-c *unit*)

If you examine the generated file ‘foo.c’, you’ll get a general idea of how it is handled.

One advantage of cgen-literal is that it tries to share the same literal whenever possible. If you call (cgen-literal '(a b c)) twice in the same cgen unit, you’ll get one instance of cgen-literal. If you call (cgen-literal '(b c)) then, it will share the tail of the original list (a b c). So you can just use cgen-literal whenever you need to have Scheme literal values, without worrying about generating excessive amount of duplicated code.

Certain Scheme objects cannot be generated as a literal; for example, an opened port can’t, since it carries lots of runtime information.

(There’s a machinery to allow programmers to extend the cgen-literal behavior for new types. The API isn’t fixed yet, though.)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3.3 SchemeとCの間の変換

In the C world, any Scheme object is uniformly of type ScmObj. But it is often the case that you need to narrow down to the specific type and convert it to a C value. Gauche maintains a database of how to typecheck and map Scheme value to C value and vice versa.

Note that the mapping isn’t one-to-one: Scheme <integer> can be mapped to C’s short, long, unsigned int, or even just ScmObj if the C routine wants to cover bignums. So each mapping has its own name. For historical reasons, each mapping is called stub type. The names of stub types look like Scheme type but its semantics differ from Scheme type. Remember: Each stub type represents a specific mapping between a Scheme type and a C type.

Each stub type has a C-predicate, a boxer and an unboxer, each of them is a Scheme string for the name of a C function or C macro. A C-predicate takes ScmObj object and returns C boolean value that if the given object has a valid type and range for the stub type. A boxer takes C object and converts it to a Scheme object; it usually involves wrapping or boxing the C value in a tagged pointer or object, hence the name. An unboxer does the opposite: takes a Scheme object and convert it to a C value. The Scheme object must be checked by the C-predicate before being passed to the unboxer.

The following table shows the predefined stub types. Note that the most of aggregate types has one to one mappings. The difficult ones are numeric types and strings. Scheme numbers can represent much wider range of numbers than C, so you have to narrow down according to the capability of C routine. Scheme strings have byte size and character length, and the body may not be NULL-terminated; so the <string> stub type maps Scheme string to ScmString*. For the convenience, you can use <const-cstring>, which creates NUL-terminated C string; beware that it may incur some copying cost.

 
Stub type    Scheme       C           Notes
-----------------------------------------------------------------
<fixnum>     <integer>    int         Integers within fixnum range
<integer>    <integer>    ScmObj      Any exact integers
<real>       <real>       double      Value converted to double
<number>     <number>     ScmObj      Any numbers

<int>        <integer>    int         Integers representable in C
<int8>       <integer>    int
<int16>      <integer>    int
<int32>      <integer>    int
<short>      <integer>    short
<long>       <integer>    long
<uint>       <integer>    uint        Integers representable in C
<uint8>      <integer>    uint
<uint16>     <integer>    uint
<uint32>     <integer>    uint
<ushort>     <integer>    ushort
<ulong>      <integer>    ulong
<float>      <real>       float       Unboxed value casted to float
<double>     <real>       double      Alias of <real>

<boolean>    <boolean>    int         Boolean value
<char>       <char>       ScmChar     Note: not a C char

<void>       -            void        (Used only as a return type.
                                        Scheme function returns #<undef>)

<string>     <string>     ScmString*  Note: not a C string

<const-cstring> <string>  const char* For arguments, string is unboxed
                                      by Scm_GetStringConst.
                                      For return values, C string is boxed
                                      by SCM_MAKE_STR_COPYING.

<const-cstring-safe> <string> const char*  Like <const-cstring>,
                                      but when converting from Scheme,
                                      reject a string with NUL chars in it.

<pair>       <pair>       ScmPair*
<list>       <list>       ScmObj
<string>     <string>     ScmString*
<symbol>     <symbol>     ScmSymbol*
<keyword>    <keyword>    ScmKeyword*
<vector>     <vector>     ScmVector*
<uvector>    <uvector>    ScmUVector*
<s8vector>   <s8vector>   ScmS8Vector*
<u8vector>   <u8vector>   ScmU8Vector*
<s16vector>  <s16vector>  ScmS16Vector*
<u16vector>  <u16vector>  ScmU16Vector*
<s32vector>  <s32vector>  ScmS32Vector*
<u32vector>  <u32vector>  ScmU32Vector*
<s64vector>  <s64vector>  ScmS64Vector*
<u64vector>  <u64vector>  ScmU64Vector*
<f16vector>  <f16vector>  ScmF16Vector*
<f32vector>  <f32vector>  ScmF32Vector*
<f64vector>  <f64vector>  ScmF64Vector*

<hash-table> <hash-table> ScmHashTable*
<tree-map>   <tree-map>   ScmTreeMap*

<char-set>   <char-set>   ScmCharSet*
<regexp>     <regexp>     ScmRegexp*
<regmatch>   <regmatch>   ScmRegMatch*
<port>       <port>       ScmPort*
<input-port>  <input-port> ScmPort*
<output-port> <output-port> ScmPort*
<procedure>  <procedure>  ScmProcedure*
<closure>    <closure>    ScmClosure*
<promise>    <promise>    ScmPromise*

<class>      <class>      ScmClass*
<method>     <method>     ScmMethod*
<module>     <module>     ScmModule*
<thread>     <thread>     ScmVM*
<mutex>      <mutex>      ScmMutex*
<condition-variable> <condition-variable> ScmConditionVariable*

A stub type can have a maybe variation, denoted by ? suffix; e.g. <string>?. It is a union type of the base type and boolean false (for <string>?, it can be either <string> or #f.) In the C world, boolean false is mapped to NULL pointer. It is convenient to pass a C value that allowed to be NULL back and forth—if you pass #f from the Scheme world it comes out NULL to the C world, and vice versa. The maybe variation is only meaningful when the C type is a pointer type.

Class: <cgen-type>

An instance of this class represents a stub type. It can be looked up by name such as <const-cstring> by cgen-type-from-name.

Function: cgen-type-from-name name

Returns an instance of <cgen-type> that has name. If the name is unknown, #f is returned.

Function: cgen-box-expr cgen-type c-expr
Function: cgen-unbox-expr cgen-type c-expr
Function: cgen-pred-expr cgen-type c-expr

c-expr is a string denotes a C expression. Returns a string of C expression that boxes, unboxes, or typechecks the c-expr according to the cgen-type.

 
;; suppose foo() returns char*
(cgen-box-expr
 (cgen-type-from-name '<const-cstring>)
 "foo()")
 ⇒ "SCM_MAKE_STR_COPYING(foo())"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3.4 CiSE - S式で書くC

Some low-level routines in Gauche are implemented in C, but they’re written in S-expression. We call it “C in S expression”, or CiSE.

The advantage of using S-expression is its readability, obviously. Another advantage is that it allows us to write macros as S-expr to S-expr translation, just like the legacy Scheme macros. That’s a powerful feature—effectively you can extend C language to suit your needs.

The gauche.cgen.cise module provides a set of tools to convert CiSE code into C code to be passed to the C compiler. It also has some support to overcome C quirks, such as preparing forward declarations.

Currently, we don’t do rigorous check for CiSE; you can pass a CiSE that yields invalid C code, which will cause the C compiler to emit errors. The translater inserts line directives by default so the C compiler error message points to the location of original (CiSE) source instead of generated code; however, sometimes you need to look at the generated code to figure out what went wrong. We hope this will be improved in future.

In Gauche source code, CiSE is extensively used in precompiled Scheme files and recognized by the precompiler (precomp). However, gauche.cgen.cise is an independent module only relies on gauche.cgen basic features, so you can plug it to your own C code generating programs.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3.4.1 CiSE overview

Before diving into the details, it’s easier to grasp some basic concepts.

A CiSE fragment is an S-expression that follows CiSE syntax (see section CiSE syntax). A CiSE fragment can be translated to C code by cise-render to a C code fragment. Note that some translation may not be local, meaning it may want to emit forward declarations before other C code fragments. So, the full translation requires buffering—you process all the CiSE fragments and saves output, emit forward declarations, then emit the saved C code fragments. We have a wrapper procedure, cise-translate, to take care of it, but for your purpose you may want to roll your own wrapper.

A CiSE macro is a Scheme code that translates a CiSE fragment to another CiSE fragment. There are number of predefined CiSE macros. You can add your own CiSE macros by utilities such as define-cise-stmt and define-cise-expr.

A CiSE ambient is a bundle of information that affects fragment translation. It contains CiSE macro definitions, and also it keeps track of forward declarations.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3.4.2 CiSE syntax


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.3.4.3 CiSE procedures

Parameter: cise-ambient
Function: cise-default-ambient
Function: cise-ambient-copy ambient
Function: cise-ambient-decl-strings ambient
Parameter: cise-emit-source-line
Function: cise-render cise-fragment :optional port context
Function: cise-render-to-string cise-fragment :optional context
Function: cise-render-rec cise-fragment stmt/expr env
Function: cise-translate inp outp :key environment
Function: cise-register-macro! name expander :optional ambient
Function: cise-lookup-macro name :optional ambient
Macro: define-cise-stmt name [env] clause … [:where definition …]
Macro: define-cise-expr name [env] clause … [:where definition …]
Macro: define-cise-toplevel name [env] clause … [:where definition …]
Macro: define-cise-macro (name form env) body …
Macro: define-cise-macro name name2

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4 gauche.charconv - 文字コード変換

Module: gauche.charconv

与えられたデータストリームの文字エンコーディングを変換するための手続き群を提供する モジュールです。

このモジュールはまた、open-input-filecall-with-output-file等のファイルストリームを作成する手続きに :encodingキーワード引数が与えられた場合に暗黙にロードされます。

リリース0.5.6より、Gaucheは日本語の主要エンコーディング (ISO2022JP, ISO2022JP-3, EUC-JP (EUC-JISX0213), Shift_JIS (Shift_JISX0213), UTF-8 (Unicode 3.2))間の変換を自前で持つようになりました。 上記以外のコードとの変換はiconv(3)を利用します。 サポートされるコーディングシステムに関する詳細は サポートされる文字エンコーディングを参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4.1 サポートされる文字エンコーディング

CESの名前は文字列またはシンボルで指定します。大文字小文字の違いは無視されます。 同じCESにいくつかの別名がついていることがあります。

CES名 "none" は特殊です。Gaucheのネイティブエンコーディングがnoneの 場合、Gaucheは文字列を単なるバイト列として扱い、そのエンコーディングの解釈は アプリケーションに任されます。したがって、CES "none" への変換、および "none" からの変換は「何もしない」変換として扱われます。

特定の変換がシステムでサポートされているかどうかは次の手続きで調べることができます。

Function: ces-conversion-supported? from-ces to-ces

CES from-cesからto-cesへの変換がサポートされていれば#tを、 そうでなければ#fを返します。

システムがfrom-cesからto-cesへの変換を部分的にしか サポートしていない場合でもこの手続きは#tを返すことに注意して下さい。 そのような場合、実際の変換作業はfrom-cesにあってto-cesにない コードを置換文字に置き換える等で情報を失うかもしれません (例えば、UnicodeからEUC-JPへの変換はサポートされていますが、 UnicodeにはEUC-JPにない文字も含まれています)。

from-cesto-cesが "none" ならばこの手続きは常に#tを 返します。なぜならそのような変換は常に成功するからです(何も変換しないわけですが)。

 
;; 内部エンコーディングからEUC-JPに変換できるか?
(ces-conversion-supported? (gauche-character-encoding) "euc-jp")

また、CESを扱う二つの便利な手続きが用意されています。

Function: ces-equivalent? ces-a ces-b :optional unknown-value

CES ces-aces-bがシステムの知る限りで等価だった場合に #tを、等価でない場合に#fを返します。 システムがその等価性を判断出来ない場合はunknown-valueに与えられた 値を返します。そのデフォルトは#fです。

CES名 "none" はワイルドカードのように動作します。それはどんなCESとも 等価と見倣されます。(従って、ces-equivalent? は遷移的ではありません。 この手続きは、二つのCESを知った時に相互の変換が 必要かどうかを判断するためのものです)。

 
(ces-equivalent? 'eucjp "EUC-JP")            ⇒ #t
(ces-equivalent? 'shift_jis "EUC-JP")        ⇒ #f
(ces-equivalent? "NoSuchEncoding" 'utf-8 '?) ⇒ ?
Function: ces-upper-compatible? ces-a ces-b :optional unknown-value

CES ces-bでエンコードされた文字列が、システムの知る限りで 変換無しにces-aでエンコードされたものと見倣せる場合に#tを 返します。見倣せない場合は#fを返します。 システムが判断できない場合はunknown-valueに与えられた 値を返します。そのデフォルトは#fです。

ces-equivalent?と同様に、CES名 "none" はワイルドカードとして 働きます。ces-aces-b が "none" であれば、 常に#tが返されます。

 
(ces-upper-compatible? "eucjp" "ASCII")             ⇒ #t
(ces-upper-compatible? "eucjp" "utf-8")             ⇒ #f
(ces-upper-compatible? "utf-8" "NoSuchEncoding" '?) ⇒ ?

日本語の文字セットJIS X 0201及びJIS X 0213のエンコーディング (EUC_JP、Shift JIS、UTF-8及びISO2022-JP) 間の変換に関しては、 Gaucheが内部に変換アルゴリズムを持っています (詳細は下の注記を参照)。 それ以外のCESに関しては、リンクされていればGaucheはiconv(3)を用いて 変換を行います。

出力CESへマップ不可能な入力文字に出会った場合、Gaucheの変換ルーチンは その文字が入力CESでマルチバイトである場合はgeta mark ’〓’ (U+3013) に、 入力CESでシングルバイトである場合は ’?’ に置換します。 iconvによる変換でマップ不可能な文字に出会った場合の処理はiconvの実装に 依存します(例えばglibcではエラーとなります)。

変換ルーチンが、入力CESとして不正な入力文字列に出会った場合は エラーが報告されます。

Gaucheの内部変換アルゴリズムの詳細: EUC_JP、Shift JIS、及びISO2022JP間の変換は可能な限り計算で行います。 文字が未定義のコードポイントも計算式に従って変換されます。 Unicode(UTF-8)とEUC_JP間の変換はテーブルルックアップによって行われます。 UnicodeとShift JISまたはISO2022JP間の変換は、入力CESを一度EUC_JPに 変換し、それを出力CESに変換することによって行います。 入力と出力のCESがGaucheの内部アルゴリズムがサポートする範囲で等しかった場合、 Gaucheの変換ルーチンはエンコーディングの正当性はチェックせず、 単に入力を出力にコピーします。

EUC_JP, EUCJP, EUCJ, EUC_JISX0213

ASCII, JIS X 0201カナ、JIS X 0212、及びJIS X 0213文字セットをカバーします。 JIS X 0212は、単にJIS X 0213と重ならないコードを使っているためにサポート されていますが、他のCESへの変換は行われないので注意して下さい。 なるべくJIS X 0213の使用をおすすめします。

SHIFT_JIS, SHIFTJIS, SJIS

Shift_JISX0213をカバーします。但し、0x5cと0x7eに関しては JIS X 0201 Roman (YEN SIGNとOVERLINE) ではなく ASCII (REVERSE SOLIDUSとTILDE) にマップされます。

UTF-8, UTF8

Unicode 3.2です。JIS X 0213中の文字のいくつかはExtension B (U+20000〜) にマップされます。JIS X 0213中の他の文字のいくつかは2つのUnicode文字 (base character + combining character)にマップされます。

ISO2022JP, CSISO2022JP, ISO2022JP-1, ISO2022JP-2, ISO2022JP-3

これらのエンコーディングは、CSISO2022JPがISO2022JPのエイリアスであることを除き 異なるエスケープシーケンスを使いますが、Gaucheの中では同じルーチンで処理されます。 これらのエンコーディングのいずれかが入力CESに指定された場合、 Gaucheは全てのバリエーションのエスケープシーケンスを認識します。 但し、ISO2022JP-2に定義された日本語以外の言語のエスケープシーケンスに関しては、 Gaucheはエスケープシーケンスの認識だけを行い、 文字は’?’あるいは’〓’ に置換します。

出力に関してはGaucheは出来る限りISO2022JPでエンコードし、 JIS X 0212文字に出会ったらISO2022JP-1のエスケープシーケンスを、 JIS X 0213の第2面の文字に出会ったらISO2022JP-3のエスケープシーケンスを 用います。したがって、文字列がJIS X 0208の範囲だけで構成されていた場合は 出力はISO2022JP互換となります。 厳密には、JIS X 0213では包摂基準の変更により、 「JIS X 0208と同じコードポイントを用いるがJIS X 0208のエスケープシーケンスを 使ってはいけない文字」というのが定義されています。 Gaucheでは互換性のため、これらの文字もJIS X 0208のエスケープシーケンスを 用いてエンコードします (これは、Emacs-Muleにおけるiso2022jp-3-compatible モードと同じ方針です)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4.2 文字エンコーディングの自動判定

しばしば、入力のCESが未知であり、可能性のあるいくつかの候補から入力のCESを 推測しなければならない場合があります。推測するアルゴリズムはいくつも考えられるので、 それぞれに名前がついています(ワイルドカードCES)。 今のところ、一つのアルゴリズムしか実装されていません。

"*JP"

日本語のテキストのCESを、ISO2022-JP(-1,2,3), EUCJP, SHIFT_JIS または UTF-8の いずれかであるとして推測する。

ワイルドカードCESは、いくつかの変換関数においてCES名の代わりに使うことができます。

Function: ces-guess-from-string string scheme

文字列stringのCESを、CES推測アルゴリズムscheme ("*JP"など) を使って推測し、結果のCES名を返します。もしどのCESにも該当しないデータが 含まれていれば#fが返されます。複数のCESが可能である場合、 ネイティブエンコーディングが含まれていればそれを、 そうでなければ可能なCESからどれかひとつが選んで返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.4.3 変換ポート

Function: open-input-conversion-port source from-code :key to-code buffer-size owner?

文字が符号化方法from-codeで符号化されているデータを読み出せる 入力ポートsourceを取り、符号化方法to-codeで符号化されたデータを 読み出せるポートを作成して返します。

to-codeが省略された場合はネイティブのCESと見なされます。

buffer-sizeは変換のための内部バッファのサイズを指定します。 省略時のサイズは1Kバイト程で、通常の使用には問題ないサイズです。

入力のCESが不明な場合、"*JP"などのCES推測アルゴリズム名をfrom-code として指定することができます。変換ポートは最高でバッファサイズまでのデータを先読みし、 CESを推測しようとします。そのアルゴリズムで推測されるどのCESにも該当しないデータが 検出された場合はエラーが報告されます。もし複数のCESが可能であるようなデータであった 場合は、Gaucheのネイティブエンコーディングが可能なCESに含まれていればそれが選ばれ、 そうでなければいずれかのCESが適当に選ばれます。従って、バッファサイズが小さすぎると 誤った判定をする可能性が高くなります。大抵のテキストドキュメントに対して、 既定のバッファサイズは十分ですが、大きなテキストのほとんどがASCII文字で最後だけ マルチバイト文字が現われるようなテキストでは誤判定の可能性があります。 最悪の場合でも結果を保証するには、テキスト全体が格納できるバッファサイズを指定すると 良いでしょう。

通常、open-input-conversion-portは変換が全て終了した後でもsource はオープンしたままにします。キーワード引数owner?に真の値を指定すると、 EOFが読まれた後でsourceはクローズされます。

例を示しましょう。以下のコードは未知のCES(但しEUC-JP, SJIS, ISO2022-JP, UTF8の いずれかであることは分かっている)で書かれたテキストファイル‘unknown.txt’を 読みだし、文字エンコーディングをEUC-JPに変換して‘eucjp.txt’に書き出します。

 
(call-with-output-file "eucjp.txt"
  (lambda (out)
    (copy-port (open-input-conversion-port
                 (open-input-file "unknown.txt")
                 "*jp"             ;guess code
                 :to-code "eucjp"
                 :owner? #t)       ;close unknown.txt afterwards
               out)))
Function: open-output-conversion-port sink to-code :key from-code buffer-size owner?

文字のエンコーディングをfrom-codeからto-codeに変換して 出力ポートsinkに書き出すような出力変換ポートを作成して返します。 from-codeが省略された場合はネイティブのCESと見なされます。 from-codeにもto-codeにも、CES推測アルゴリズム名を 使用することはできません。

buffer-sizeは内部で変換に使われるバッファサイズを指定します。 出力変換ポートに書き出された文字は、flushを使って明示的に フラッシュするか出力変換ポートが閉じられるまで、バッファ内に残っている可能性があります。

通常、出力変換ポートがクローズされてもsinkはクローズされません。 owner?に真の値を与えれば、出力変換ポートのクローズ時にsinkを クローズするようにできます。

Function: ces-convert string from-code :optional to-code

from-codeでエンコーディングされた文字列stringto-codeでエンコーディングされた文字列に変換します。 to-codeがネイティブエンコーディングで無い場合、返される文字列は バイト文字列(不完全な文字列)であるかもしれません。

from-codeにはCES推測アルゴリズム名("*JP"など)を与えることができます。 to-codeが省略された場合はネイティブエンコーディングと見なされます。

Function: call-with-input-conversion iport proc :key encoding conversion-buffer-size
Function: call-with-output-conversion oport proc :key encoding conversion-buffer-size

この2つの手続きを使うと、元のポートのエンコーディングとは異る一時的な エンコーディングをもつ文字I/Oを行うことが可能です。

call-with-input-conversion は、文字エンコードencoding を もつ入力ポート iport をとり、procを一つの引数、すなわち、 変換入力ポートとともに呼びだします。このポートから、proc は文字を Gauche の内部エンコーディングで読み込むことができます。いったん、 procが呼ばれたら、EOFまでのすべての文字を読み込まなくてはなりません。 後述の注意を参照してください。

call-with-output-conversion は、文字エンコーディング encoding を期待する出力ポート oport をとり、procを一つの引数、すなわち、 一時的な変換出力ポートとともに呼びだします。このポートへ、procは Gauche の内部エンコーディングをもつ文字を書き込むことができます。 proc が戻るか、エラーで抜けると、この一時的な変換出力ポートは フラッシュされて、クローズされます。call-with-output-conversion の呼出し側は、その後、元のエンコーディングのポートoportを使い 続けること可能です。

どちらの手続きも proc が返す値を返します。encoding のデフォルト値は Gauche の内部エンコーディングです。この2つの手続きは 必要がなければ、変換ポートを作成しません。もし conversion-buffer-size が与えられていれば、変換ポートがオープン されるときに、buffer-size 引数として使われます。

proc がアクティブであるあいだは、iport/oport を直接 使うべきではありません。文字のエンコーディングはステートフルなプロセスで 変換ポート(から/へ)の入出力を混在させると元にあるポートの状態がおかしく なってしまいます。

注意: call-with-input-conversion については、 proc が EOF を読むまでは、iport を再度利用することはできません。 これは、変換ポートがバッファを必要とし、proc がもどったときに iportへのバッファ付き入力をアンドゥする方法がないからです。

Function: with-input-conversion iport thunk :key encoding conversion-buffer-size
Function: with-output-conversion oport thunk :key encoding conversion-buffer-size

call-with-*-conversion と似ていますが、この2つの手続きは、 thunk を引数なしで呼びます、また、変換ポートはそれぞれ標準入力、標準 出力のポートにセットされます。 キーワード引数の意味は call-with-*-conversion と同じです。

Function: wrap-with-input-conversion port from-code :key to-code owner? buffer-size
Function: wrap-with-output-conversion port to-code :key from-code owner? buffer-size

不要な変換ポートを作らないようにする便利な手続きです。 各手続きはそれぞれopen-input-conversion-portopen-output-conversion-portのように動作します。 但し、指定されたCES間の変換が必要でないとシステムが知っている場合は、 変換ポートは作られず、portがそのまま返されます。

変換ポートが作られた場合、portは常に変換ポートに所有されます (:owner #tとなる)。ポートを閉じる場合はportではなく 常にwrap-with-*-conversionが返したポートを閉じるようにして下さい。 元のportが先に閉じられた場合、変換中の結果が書き出されない可能性があります。 (変換によっては、ポートが閉じられる時点で終了シーケンスを書き出すものがあるので、 単にポートをflushするだけでは不十分です)。

:buffer-size 引数はそのまま 変換ポート作成手続きに渡されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.5 gauche.collection - コレクションフレームワーク

Module: gauche.collection

このモジュールは、様々なコレクションに対して繰り返し処理を行う総称関数を提供します。 Schemeの規格はmapfor-eachなどの繰り返し手続きを定義しており、 またSRFI-1(srfi-1 - リストライブラリ参照)は更に数多くの繰り返し手続きを提供しますが、 それらはリストに対してしか動作しません。

このモジュールはオブジェクトシステムのメソッドディスパッチを利用して、 これらの手続きをベクタやハッシュテーブルのような一般のコレクションタイプに対しても 効率良く動作するように拡張します。また、ユーザ定義のクラスにこれらの操作を実装するための 簡単な方法も提供します。今のところ、次のような総称関数が提供されています。

マッピング

fold, fold2, fold3, map, map-to, map-accum, for-each

選択と探索

find, find-min, find-max, find-min&max, filter, filter-to, remove, remove-to, partition, partition-to group-collection

変換

coerce-to

その他

size-of, lazy-size-of

基礎的なイテレータ構築メソッド

call-with-iterator, call-with-builder, with-iterator, with-builder, call-with-iterators.

これらの操作は、コレクションとそのサブクラスである シーケンスに対して動作します。コレクションは、その要素を全て 訪れる方法が用意されているようなオブジェクトの集合です。 シーケンスは、要素間に全順序関係が定義されておりインデックスで要素を取り出すことが できるようなコレクションです。

次にあげるGaucheの組み込みオブジェクトはシーケンスあるいはコレクションとして動作します。

<list>

シーケンス

<vector>

シーケンス

<string>

文字のシーケンス

<hash-table>

コレクション。各要素はキーと値のペア。

<s8vector>, <u8vector>, … <f64vector>

シーケンス。メソッドはsrfi-4モジュール内で定義されます。 srfi-4 - 単一型のベクタ参照。

gauche.sequence - シーケンスフレームワークも参照してください。シーケンス特有のメソッドが 追加されます。

オブジェクトの集合を返すようなメソッド、すなわち mapfilterremoveおよびpartitionは、 リストを返します。対応する“-to”がつくメソッド (map-tofilter-toremove-topartition-to) はコレクションクラスも引数に取り、そのクラスのコレクションを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.5.1 コレクションに対するマッピング

これらのジェネリックファンクションは標準のマッピング手続きを拡張します。 要素だけでなくそのインデックスも必要な場合はシーケンス上のマップを 参照して下さい。

Generic function: fold proc knil coll coll2 …

fold (他のリスト手続き参照) の自然な拡張です。

コレクションcollの各要素Eiに対して、手続きprocが (proc Ei Ri-1) のように呼ばれます。ここで、 Ri-1i > 0 に対しては (i-1)番目のprocの呼び出しの 結果であり、R0knilです。最後のprocの戻り値を返します。

 
(fold + 0 '#(1 2 3 4)) ⇒ 10
(fold cons '() "abc")  ⇒ (#\c #\b #\a)

collがシーケンスでもある場合、要素はシーケンスの順にprocに渡されます。 そうでなければ繰り返しの順序は未定義です。

註:コレクションに対するfold-rightは提供されません。コレクションでは 要素の順序は定義されないため、意味のあるトラバースをするのには foldだけあれば十分だからです。 しかし、シーケンスに対してはfold-rightが定義されます。 シーケンス上のマップを参照して下さい。

複数のコレクションをfoldに渡すこともできます (但し、その全てがシーケンスで なければあまり意味のある操作では無いでしょう)。 k番目のコレクションのi番目の要素をE(k, i)とするとき、 procは以下のように呼ばれます。

 
(proc E(0,i) E(1,i) … E(K-1,i) Ri-1)

異なる型のコレクションを混ぜて扱うことができます。

 
(fold acons '() "abc" '#(1 2 3))
  ⇒ ((#\c 3) (#\b 2) (#\a 1))

;; 二つのベクタの内積を計算
(fold (lambda (a b r) (+ (* a b) r)) 0
      '#(3 5 7) '#(2 4 6))
  ⇒ 68

複数のコレクションが与えられた場合、foldは少なくともひとつのコレクションが 終了した時点で終了します。

Generic function: fold2 proc knil1 knil2 coll coll2 …
Generic function: fold3 proc knil1 knil2 knil3 coll coll2 …

foldと似ていますが、1つではなくそれぞれ2, 3個の状態値を 持ち回ります。状態値はknilNによって初期化されます。 手続きprocはコレクションcollNの各要素値と状態値を 引数として取り、fold2の場合は2個、fold3の場合は3個の 値を返さねばなりません。返された値が次の繰り返しでの状態値として 使われます。最後に返された値がfold2, fold3の戻り値と なります。

 
(fold2 (lambda (elt a b) (values (min elt a) (max elt b)))
       256 0 '#u8(33 12 142 1 74 98 12 5 99))
 ⇒ 1 and 142  ;; find minimum and maximum values

下のmap-accumも参照。

Generic function: map proc coll coll2 …

組み込み手続きmap (リストをたどる手続き参照) を拡張します。 コレクションcollの各要素に手続きprocを適用し、その結果をリストにして 返します。

collがシーケンスでもある場合、要素はシーケンスの順にprocに渡されます。 そうでなければ繰り返しの順序は未定義です。

複数のコレクションが与えられた場合、procは各コレクションからの要素を引数として 呼び出されます。mapはひとつでもコレクションの最後に到達したら終了します。 複数のコレクションを渡すのは、コレクションの全てがシーケンスでないとあまり意味がないでしょう。

 
(map (lambda (x) (* x 2)) '#(1 2 3))
  ⇒ #(2 4 6)

(map char-upcase "abc")
  ⇒ (#\A #\B #\C)

(map + '#(1 2 3) '#(4 5 6))
  ⇒ (5 7 9)

mapは常にリストを返します。別のコレクション型で結果を得たい場合は、 次に示すmap-toを使って下さい。何故(map char-upcase "abc")"ABC"を返さないのか疑問なら、この最後にあるディスカッションを参照してください。

Generic function: map-to class proc coll coll2 …

mapと同じように動作しますが、結果はクラスclassのインスタンスとして返されます。 classはコレクションクラスでなければなりません。 また、ビルダーインタフェースを持っている必要があります (基礎的なイテレータ構築メソッド参照).

 
(map-to <vector> + '#(1 2 3) '#(4 5 6))
  ⇒ #(5 7 9)

(map-to <string> char-upcase "def")
  ⇒ "DEF"

(map-to <vector> char=? "bed" "pet")
  ⇒ #(#f #t #f)
Generic function: map-accum proc seed coll1 coll2 …

状態値を持ち回りながらprocのコレクションの各要素への呼び出しを集めます。 procは次のように呼ばれます。

 
(proc elt1 elt2seed)

ここでelt1 elt2 …は coll1 coll2 …の各要素です。 procは2つの値を返さねばなりません。最初の値がmapのように リストへと集められます。2つ目の値は次のprocの呼び出しのseed として使われます。

いずれかのコレクションの要素を使い切った時点で、map-accumは 2つの値を返します。最初の値はprocの最初の戻り値をリストにしたもの、 2番目の値はprocの最後の呼び出しの2番目の戻り値です。

もし与えられたコレクションがシーケンスであった場合は、 procはシーケンスの順序通りに適用されます。

この手続きはHaskellのmapAccumLと似ています。但し、 procの引数と戻り値の順が逆転していることに注意して下さい。

Generic function: for-each proc coll coll2 …

組み込み手続きfor-each (リストをたどる手続き参照) を拡張します。 コレクションcollの各要素に手続きprocを適用します。 procの結果は捨てられます。for-eachの結果は未定義です。

collがシーケンスでもある場合、要素はシーケンスの順にprocに渡されます。 そうでなければ繰り返しの順序は未定義です。

複数のコレクションが与えられた場合、procは各コレクションからの要素を引数として 呼び出されます。for-eachはひとつでもコレクションの最後に到達したら終了します。 複数のコレクションを渡すのは、コレクションの全てがシーケンスでないとあまり意味がないでしょう。

Generic Function: fold$ proc
Generic Function: fold$ proc knil
Generic Function: map$ proc
Generic Function: for-each$ proc

foldmapfor-eachの部分評価版です。

Discussion: mapがリスト以外に対して適用されたとき、どういう コレクション型を返すべきでしょう。 (map * '#(1 2) '#(3 4)) がベクタを返し、 (map char-upcase "abc") が文字列を返すようにするほうが「自然」でしょうか。

そのようなインタフェースは単純な場合には動作するように思えますが、 一般的な拡張は困難です。文字列とベクタが同時に渡されたらどうします? 更に、コレクションクラスによっては繰り返しインタフェースは持っていても ビルダーインタフェースを持っていない場合があり、結果をそのコレクションクラスとして 返せない場合もあります (データベースレコードのコレクションに対してマップする、 といった用法を考えてみて下さい)。また、Schemeプログラマはmapが リストを返すという事実に慣れ親しんでおり、既存のコードもmapの戻り値を リストを受け取る手続きに渡すことがよく行われています。

そこで、結果の型を明示的に指定するmap-toという別のメソッドを定義しました。 結果の型を渡すのは、CommonLispのmap関数にならっていますが、 Gaucheではクラスメタオブジェクトを渡すようにしたため、メソッドディスパッチを使って 拡張することが容易です。“-to” のつくメソッドは結果のコレクションのクラスを 取るというインタフェースはコレクションフレームワーク中で統一的に使われています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.5.2 コレクションからの選択と探索

Generic function: find pred coll

predをコレクションcollの要素に適用してゆきます。predが 真の値を返したらそこで打ち切り、その要素を返します。predが真の値を返す 要素が無かった場合は#fを返します。

collがシーケンスでもある場合、要素はシーケンスの順にprocに渡されます。 そうでなければ繰り返しの順序は未定義です。

 
(find char-upper-case? "abcDe") ⇒ #\D
(find even? '#(1 3 4 6)) ⇒ 4
(find even? '(1 3 5 7))  ⇒ #F
Generic function: find-min coll :key key compare default
Generic function: find-max coll :key key compare default

コレクションcollから最小もしくは最大の要素を探して返します。

コレクションの各要素に対し、1引数の手続きkeyが適用され、 その戻り値が比較対象となります。keyのデフォルトはidentityです。 比較対象の値は2引数の手続きcompareで比較されます。 compareのデフォルトは<です。コレクション中の要素数が 1つ以下の場合はcompareは呼ばれません。

コレクションが空の場合は、defaultで指定した値が返されます。 defaultのデフォルト値は#fです。

 
(find-min '((a . 3) (b . 9) (c . -1) (d . 7)) :key cdr) ⇒ (c . -1)
Generic function: find-min&max coll :key key compare default default-min default-max

find-minfind-maxの動作を同時に行い、 最小と最大の要素をふたつの値として返します。 キーワード引数keycomparedefaultの意味は find-minfind-maxと同じです。 また、default-mindefault-maxを使って 最小要素と最大要素のデフォルト値を別々に指定することもできます。

Generic function: filter pred coll

コレクションcoll中の要素のうち、述語手続きpredが真の値を返したものの リストを返します。コレクションがシーケンスであれば、結果の要素の順序は元のシーケンスの 順序と同じになります。

 
(filter char-upper-case? "Hello, World")
  ⇒ (#\H #\W)
(filter even? '#(1 2 3 4)) ⇒ (2 4)
Generic function: filter-to class pred coll

filterと同じですが、結果のコレクションがclassのインスタンスで 返されます。

 
(filter-to <vector> even? '#(1 2 3 4)) ⇒ #(2 4)
(filter-to <string> char-upper-case? "Hello, World")
  ⇒ "HW"
Generic function: remove pred coll

コレクションcoll中の要素のうち、述語手続きpredが偽の値を返したものの リストを返します。コレクションがシーケンスであれば、結果の要素の順序は元のシーケンスの 順序と同じになります。

 
(remove char-upper-case? "Hello, World")
  ⇒ (#\e #\l #\l #\o #\, #\space #\o #\r #\l #\d)
(remove even? '#(1 2 3 4)) ⇒ (1 3)
Generic function: remove-to class pred coll

removeと同じですが、結果のコレクションがclassのインスタンスで 返されます。

 
(remove-to <vector> even? '#(1 2 3 4)) ⇒ #(1 3)
(remove-to <string> char-upper-case? "Hello, World")
  ⇒ "ello, orld"
Generic function: partition pred coll

filterremoveを同時に行います。 二つのリストを返します。最初のリストはコレクションcollの要素のうち 述語手続きpredが真の値を返したものから構成され、二つ目のリストは そうでない要素から構成されます。

 
(partition char-upper-case? "PuPu")
  ⇒ (#\P #\P) and (#\u #\u)
(partition even? '#(1 2 3 4))
  ⇒ (2 4) and (1 3)
Generic function: partition-to class pred coll

partitionと同じですが、結果がクラスclassのコレクションとして 返されます。

 
(partition-to <string> char-upper-case? "PuPu")
  ⇒ "PP" and "uu"
(partition-to <vector> even? '#(1 2 3 4))
  ⇒ #(2 4) and #(1 3)
Generic function: group-collection coll :key key test

partitionを汎化したものです。coll内の要素を同じキーを 持つ値同士でグループ化し、リストのリストにして返します。 キーの値は要素に手続きkeyを適用することで得られます。keyの デフォルト値はidentityです。collの各要素に対して、 keyは正確に一回だけ呼ばれます。 キーの等価性判定には手続きtestが使われます。デフォルト値はeqv?です。

collがシーケンスである場合、結果の各グループに含まれる要素の順は もとのシーケンス内での順と同じになります。

 
(group-collection '(1 2 3 2 3 1 2 1 2 3 2 3))
  ⇒ ((1 1 1) (2 2 2 2 2) (3 3 3 3))

(group-collection '(1 2 3 2 3 1 2 1 2 3 2 3) :key odd?)
  ⇒ ((1 3 3 1 1 3 3) (2 2 2 2 2))

(group-collection '(("a" 2) ("b" 5) ("c" 1) ("b" 3) ("a" 6))
  :key car :test string=?)
  ⇒ ((("a" 2) ("a" 6)) (("b" 5) ("b" 3)) (("c" 1)))

gauche.sequencegroup-sequenceも参照して下さい (その他のシーケンス上の操作参照)。 隣り合う要素同士でグループ化するものです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.5.3 コレクションに対する様々な操作

Generic function: size-of coll

コレクションの要素数を返します。 デフォルトのメソッドは、コレクション中の要素をすべて数え上げるものですが、 あまり効率は良くないでしょう。また、無限個の要素を持つコレクションでは 帰ってきません。多くのコレクションクラスはより効率の良い方法でこのメソッドを定義しています。

Generic function: lazy-size-of coll

コレクションの要素数か、もしくはそれを計算するプロミスを返します。 このメソッドの目的は、要素数の計算が高価な場合にそれを避けることにあります。 しばしば、呼び出し側では最適化のための参考値として要素数が欲しい場合があり、 そういった場合は要素数を計算するために時間を費すのは望ましくありません。 このメソッドを代わりに呼び出して、結果がプロミスであればそれを使わない、 という選択ができます。

Generic function: coerce-to class coll

コレクションcollを、クラスclassのインスタンスである 別のコレクションへと変換します。collがシーケンスであり、 classがシーケンスクラスであれば、元のシーケンスの順序は保存されます。

 
(coerce-to <vector> '(1 2 3 4))
  ⇒ #(1 2 3 4)

(coerce-to <string> '#(#\a #\b #\c))
  ⇒ "abc"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.5.4 基礎的なイテレータ構築メソッド

ここに挙げるメソッドは、他のコレクションメソッドの基礎となるものです。 メソッドのインタフェースは一般のコードで使われることよりも、 効率良く他の繰り返しメソッドを記述するのに便利なように設計されています。 何故このインタフェースを基礎のメソッドとして選んだかについてはこの章の最後に説明します。

Generic function: call-with-iterator collection proc :key start

基礎となるイテレータ構築メソッドです。このメソッドはコレクションcollection から繰り返しのための二つの手続きを作成し、それらを引数として手続きprocを 呼びます。作られる最初の手続きは終了判定手続きで、引数無しで呼び出され、繰り返しが 終了していれば#tを、まだ要素が残っていれば#fを返します。 作られる二番目の手続きはインクリメント手続きで、呼ばれる度に現在の要素を返し、 内部のポインタを次の要素へと進めます。終了判定手続きが#tを返した後に インクリメント手続きを呼んだ場合の動作は未定義です。

コレクションがシーケンスでもある場合、インクリメント手続きはシーケンスの順番に要素を取り出します。 キーワード引数startが与えられていればイテレーションの範囲は start番目の要素から最後の要素までとなります。シーケンスでないコレクションに 対してはstart引数は意味を持ちません。

call-with-iteratorのメソッド実装は、イテレータのエクステントを そのメソッドのダイナミックスコープ内に限ることを許されます。例えば、 メソッドはprocを呼ぶ前に何らかのリソースを確保し(データベースへのコネクションなど)、 procから戻った後でそれを解放するということができます。

このメソッドは proc が返した値をそのまま返します。

 
(call-with-iterator '(1 2 3 4 5)
  (lambda (end? next)
    (do ((odd-nums 0))
        ((end?) odd-nums)
      (when (odd? (next)) (inc! odd-nums)))))
 ⇒ 3

下に示すwith-iteratorマクロも参照してください。

Macro: with-iterator (collection end? next args …) body …

call-with-iteratorを簡潔に呼び出すマクロです。

 
(with-iterator (coll end? next args …) body …)
 ≡
(call-with-iterator coll
  (lambda (end? next) body …)
   args …)
Function: call-with-iterators collections proc

N-aryのイテレータメソッドを書くのに便利な手続きです。 この手続きはコレクションのリストcollectionsの各コレクションに対して call-with-iteratorを呼び、二つのリストを作ります。最初のリストには 終了判定手続きが順に集められており、二つ目のリストにはインクリメント手続きが 順に集められています。そして、これらのリストを引数としてprocを呼び出します。 procが返した値を返します。

Generic function: call-with-builder collection-class proc :key size

基礎的なビルダー構築メソッドです。ビルダーはコレクションをインクリメンタルに 作成する方法です。コレクションクラスによってはこの手続きを提供しないものもあります。

Collection-classは作成されるコレクションのクラスです。 このメソッドは、追加手続きと結果手続きの二つの手続きを作成し、それらを 引数としてprocを呼びます。追加手続きは一つ引数を取り、それを作成中の コレクションに追加します。結果手続きは引数を取らず、作成されたコレクションを返します。 結果手続きが呼ばれた後で追加手続きを呼んだ場合の動作は未定義です。

作られるコレクションのサイズが分かっている場合、キーワード引数sizeを与える ことができます。コレクションクラスによってはその情報を使って効率的にコレクションを 作成することができます。その情報を単に無視するコレクションクラスもあります。 size個より多くの要素が追加されたり、size個の要素が追加される前に 結果手続きが呼ばれたりした場合の動作は未定義です。

コレクションクラスがシーケンスクラスであった場合、追加手続きは要素を シーケンスの順に追加してゆきます。

コレクションクラスによっては、コレクションオブジェクトの初期化のために 他のキーワード引数を取るかもしれません。

このメソッドはprocが返す値を返します。

 
(call-with-builder <list>
  (lambda (add! get)
    (add! 'a) (add! 'b) (add! 'c) (get)))
 ⇒ (a b c)

(call-with-builder <vector>
  (lambda (add! get)
    (add! 'a) (add! 'b) (add! 'c) (get)))
 ⇒ #(a b c)

下に示すwith-builderマクロも参照してください。

Macro: with-builder (collection add! get args …) body …

call-with-builderを簡潔に呼び出すマクロです。

 
(with-builder (coll add! get args …) body …)
 ≡
(call-with-builder coll
  (lambda (add! get) body …)
  args …)

Discussion: 他のイテレータメソッドは全てこのcall-with-iteratorとcall-with-builderの上に構築可能です。 最低限これらのメソッドを定義すれば、そのクラスはコレクションとして振舞うことができます。 もちろん最適化のために他のイテレータメソッドを定義しても構いませんが。

どの操作を基礎的なメソッドとするかには議論の余地があります。 Gaucheでは、作者がよく見るパターンで最も効率が良くなるように考えて現在のスタイルを 選びました。以下に、他の基礎的なメソッドの可能性を検討します。

fold

foldを最も基礎的なメソッドとして、他のイテレータメソッドをその上に 構築することも可能です。繰り返しの状態はスタックに置かれるので効率良く走ります。 foldを基礎とした繰り返し関数を最適化する方法は良く知られています。 しかし、foldを元にしてジェネレータスタイルのインタフェースを 作成するのは複雑です。また、複数のコレクションに対しての繰り返しを書くのも 面倒です。

CPS

繰り返しの中身の手続きに対し、繰り返しを続けるための継続手続きを渡す方法です。 繰り返しを続けたくなければ、手続きは継続を呼ばすにそのまま戻ります。 Oleg Kiselyovの記事(OLEG2)に指摘されているような、 リソース管理の問題があります。

Iterator object

C++のイテレータやCommon Lispのジェネレータのようなオブジェクトを使う方法です。 ループを書くのは容易ですが、終了判定や要素取り出しの度にメソッドディスパッチが 起こってしまいます。

Series

Common Lispのシリーズはコンパイラがシリーズの使われかたを追跡できれば 非常に効率の良いコードに変換できます。Gaucheのコンパイラはそこまでのデータフロー解析を 行っていません。また、それをやったとしても、コレクションクラスを拡張するための方法が Gaucheのオブジェクトシステムとはうまく調和しません。

Macros

効率を気にするなら、イテレータをマクロで書いてしまう方法もあります (例えばScheme48のiteratorマクロなど)。 効率は良いのですが、拡張するにはマクロを書くことが必要となり、 Gaucheのオブジェクトシステムとうまく調和しません。

現在の実装はイテレータオブジェクトアプローチに近いですが、イテレータオブジェクトを 作る代わりにクロージャを使うことで内部のループでのメソッドディスパッチを 避けています。また、現在のインタフェースはリソース管理の問題を解決しています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.5.5 コレクションの実装

コレクションクラスの実装に最低限要求されるものには、以下のものがあります。

これにより、mapfor-eachfindfilterなどの イテレータメソッドが動作するようになります。

建設的なメソッド(例えば、コレクションを作るためのmap-toなど)を 作るためには、メソッドcall-with-builderも実装しなければなりません。 メソッドcall-with-builderは、クラスによりディスパッチされるクラスメソッドの 一種で、インスタンスによりディスパッチされる通常のメソッドとは異なります。 Gaucheでは、これはメタクラスを使うことによって実装できます。 最小限のコードは次のようになります。

 
(define-class <your-collection-meta> (<class>) ())

(define-class <your-collection> (<collection>)
 (...) ;; slots
 :metaclass <your-collection-meta>)

(define-method call-with-iterator
    ((coll <your-collection>) proc . options)
  …
  )

(define-method call-with-builder
     ((coll <your-collection-meta>) proc . options)
  …
  )

パフォーマンスの最適化のために、他のジェネリック関数をオーバロードすることも できます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.6 gauche.config - コンフィグレーション情報

Module: gauche.config

このモジュールは、gauche-config プログラム経由で得られるのと 同等の情報をSchemeプログラムから取得すること可能にします。

Function: gauche-config option

optionに対応するコンフィグレーション値を返します。

有効なオプションについては、gauche-config のマニュアルページを 見るか、シェルで gauche-config を引数なしで実行して下さい。

 
(gauche-config "--cc")
  ⇒ "gcc"
(gauche-config "-L")
  ⇒ "-L/usr/lib/gauche/0.6.5/i686-pc-linux-gnu"
(gauche-config "-l")
  ⇒ "-ldl -lcrypt -lm -lpthread"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.7 gauche.configure - ビルド用ファイルの生成

Module: gauche.configure

このモジュールは‘configure’スクリプトを書くためのユーティリティライブラリです。 ‘configure’スクリプトはシステムの属性をチェックして、 ビルド用のファイル(通常は‘makefile’)をテンプレートから生成します。

主な目的は、Gauche拡張パッケージにおいて、autoconfが生成する ‘configure’シェルスクリプトを置き換えることです。

autoconfを使う利点は、生成されるスクリプトが最小限のシェル機能と基本的なunixコマンドにしか 依存しないため、ほとんど何も入れないunix上でも実行できることです。 けれども、Gauche拡張パッケージをビルドする時には、 既にGaucheはインストールされているわけですから、これを使わない手はありません。

configure’スクリプトがGaucheで直接書ければ、配布前に ‘configure’スクリプトを生成するという余分な手間が省けます。 ‘configure’スクリプト自体をリポジトリにチェックインしておけますし、 誰でもソースツリーをチェックアウトしたら(autoconfを使わずに)すぐに ‘configure’を実行できます。

ただ、今のところgauhce.confiugreはautoconfの機能の小さなサブセット しかカバーしないので、複雑な機能テストが必要ならautoconfに戻らなければ ならない場合もあります。必要に応じて機能テストを足して行く予定です。

gauche.configureのコアとなる機能は、テンプレート(例: ‘Makefile.in’) から、@VAR@のように書かれたパラメータを置換して ファイル(例: ‘Makefile’)を生成することです。 autoconf用に書いた‘Makefile.in’はそのまま流用できるはずです。

autoconfのAC_*マクロに対応する機能は、プレフィクスをcf-に置き換えた 名前で提供されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.7.1 configureスクリプトの構成とビルドファイル

configure’スクリプトは、それを実行しているシステムの特性を調べて パラメータの値を決定し、ビルドファイルのテンプレート中のパラメータを 置換してビルドファイルを生成します。

通常、テンプレートファイルにはサフィックス.inがつけられ、 対応する出力ファイルはそのサフィックスが除かれたものになります。 例えばMakefile.inMakefileを生成するテンプレートです。

テンプレートファイル中には@PARAMETER_NAME@という形式の パラメータを含めることができます。以下は典型的なMakefileのテンプレートです。

 
GAUCHE_PACKAGE = "@GAUCHE_PACKAGE@"
SOEXT          = @SOEXT@
LOCAL_PATHS    = "@LOCAL_PATHS@"

foo.$(SOEXT): $(foo_SRCS)
        $(GAUCHE_PACKAGE) compile \
          --local=$(LOCAL_PATHS) --verbose foo $(foo_SRCS)

configureがこのテンプレートを処理する際に、@GAUCHE_PACKAGE@@SOEXT@@LOCAL_PATHS@はそれぞれ適切な値に 置換されます。autoconfを知っている読者には馴染み深い動作でしょう。

Gaucheのconfigureスクリプトはautoconfのconfigure.inと 似たような構造をとりますが、フルセットのSchemeの力を使うことができます。 ごく簡単なconfigureスクリプトを示します。

 
#!/usr/bin/env gosh
(use gauche.configure)

;; Argument declarations
(cf-arg-with 'local
             (cf-help-string
              "--with-local=PATH:PATH..."
              "For each PATH, add PATH/include to the include search
  paths and PATH/lib to the library search paths.  Useful if you have some
  libraries installed in non-standard places. ")
             (^[with-local]
               (unless (member with-local '("yes" "no" ""))
                 (cf-subst 'LOCAL_PATHS with-local)))
             (^[] (cf-subst 'LOCAL_PATHS "")))

;; Initialization
(cf-init)

;; Tests & other parameter settings
(cf-path-prog 'GOSH "gosh")

;; Output
(cf-make-gpd)
(cf-echo (cf$ 'PACKAGE_VERSION) > "VERSION")
(cf-output "Makefile")

cf-* APIの呼び出しを、このようにトップレベルに書くかわりに、 関数としてまとめることもできます。コードのまとめかたは自由ですが、 実際にスクリプトが実行される際に、以下のステップを踏む必要があります。

  1. 追加の引数の宣言(省略可): --with-PACKAGE--enable-FEATUREで 処理すべきオプションを、それぞれcf-with-argcf-enable-argで 宣言します。
  2. 初期化。cf-initを呼ぶと、グローバルなコンテキストが設定され、 configureに与えられた引数がパーズされます。また、 ‘package.scm’が存在していれば、そこからパッケージメタ情報が読み込まれます。
  3. テストとパラメータの設定 (省略可): システムの特性を調べ、 置換すべきパラメータやCプリプロセッサ定義の値を設定します。
  4. 出力の生成。cf-outputを呼び出し、テンプレートファイルを処理します。

多くのcf-* APIは、autoconfのAC_*もしくはAS_*マクロに 対応しています。引数の宣言がcf-initより前になされなければならないのは、 1パスでcf-initがカスタム引数の説明を含んだヘルプメッセージを 生成する必要があるからです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.7.2 Configure API

初期化

Function: cf-init :optional package-name package-version maintainer-email homepage-url

configureシステムを初期化します。これはconfigureスクリプト中で、 全ての機能テスト手続きに先立って必ず1回呼ばれなければなりません。

まずこの手続きは、configureスクリプトと同じディレクトリに‘package.scm’という ファイルがあるかどうか調べ、あればGaucheパッケージ記述をそこから読み込みます。 パッケージ記述には、パッケージ名、バージョン、依存関係などがかかれています。 詳しくはgauche.package - パッケージメタ情報を参照してください。

次にこの手続きはコマンドライン引数をパーズし、configureの環境を設定し、 (もし‘package.scm’が依存関係を定義していれば)必要なパッケージが 既にあるかどうかもチェックします。

省略可能引数は以前のバージョンとの互換性のためのものです。‘package.scm’が 無い場合、少なくともpackage-namepackage-versionを与える 必要があります。これらはconfigure変数のPACKAGE_NAMEPACKAGE_VERSION を置換するのに使われます。他の引数maintainer-emailhomepage-urlPACKAGE_BUGREPORTPACKAGE_URLに 使われます。これらの引数はautoconfのAC_INITマクロと互換です。

推奨される呼び出し方法は、‘package.scm’を用意し、cf-initには 引数を渡さないことです。そうすれば、パッケージのメタ情報を一ヶ所(‘package.scm’)で 管理できます。‘package.scm’が読まれた場合、PACKAGE_BUGREPORTは パッケージ記述のmaintainersの最初のエントリで、また PACKAGE_URLはパッケージ記述のhomepageで初期化されます。 パッケージ記述についての詳細はgauche.package - パッケージメタ情報を見てください。

もし‘package.scm’が存在し、かつcf-initに引数が与えられた場合、 それぞれ対応する情報は一致していなければなりません。一致していなかった場合、 cf-initはエラーを投げます。これは、‘package.scm’への移行の途中で、 どちらかの情報をアップデートし忘れたケースを捕まえるためです。

コマンドライン引数

Function: cf-arg-enable feature help-string :optional proc-if-given proc-if-not-given
Function: cf-arg-with package help-string :optional proc-if-given proc-if-not-given

Make the configure script accept feature selection argument and package selection argument, respectively. The corresponding autoconf macros are AC_ARG_ENABLE and AC_ARG_WITH.

Those procedures must be executed before calling cf-init.

The feature and package arguments must be a symbol.

A feature selection argument is in a form of either --enable-feature=val, --enable-feature, or --disable-feature. The latter two are equivalent to --enable-feature=yes and --enable-feature=no, respectively. It is to select an optional feature provided with the package itself.

A package selection argument is in a form of either --with-package=val, --with-package and --without-package. The latter two are equivalent to --with-package=yes and --with-package=no, respectively. It is to select an external software package to be used with this package.

When cf-init finds these arguments, it adds entry of feature or package to the global tables, with the value val. Those global tables can be accessed with cf-feature-ref and cf-package-ref procedures below.

The help-string argument must be a string and is used as is to list the help of the option in part of usage message displayed by configure --help. You can use cf-help-string below to create a help string that fits nicely in the usage message.

If optional proc-if-given argument is given, it must be a procedure that accepts one argument, val. It is called when cf-init finds one of those arguments.

If optional proc-if-not-given argument is given, it must be a procedure that accepts no arguments. It is called when cf-init doesn’t find any of those arguments.

Function: cf-help-string item description

Return a string formatted suitable to show as an option’s help message. The result can be passed to help-string argument of cf-arg-enable and cf-arg-with. This corresponds to autoconf’s AS_HELP_STRING.

Call it as follows, and it’ll indent and fill the description nicely.

 
(cf-help-string "--option=ARG" "Give ARG as the value of option")
Function: cf-feature-ref name
Function: cf-package-ref name

Lookup a symbol name from the global feature table and the global package table, respectively. These can be called after cf-init.

For example, if you’ve called cf-arg-enable with foofeature, and the user has invoked the configure script with --with-foofeature=full, then (cf-feature-ref 'foofeature) returns "full". If the user hasn’t given the command-line argument, #f is returned.

メッセージ

The cf-init procedure opens the default log drain that goes to config.log, and you can use log-format to write to it (See section gauche.logger - ユーザレベルのロギング, for the details of logging).

However, to have consistent message format conveniently, the following procedures are provided. They emits the message both to log files and the current output port (in slightly different formats so that the console messages align nicely visually.)

Function: cf-msg-checking fmt arg …

Writes out “checking XXX...” message. The fmt and arg … arguments are passed to format to produce the “XXX” part.

For the current output port, this does not emit the trailing newline, expecting cf-msg-result will be called subsequently.

Here’s an excerpt of the source that uses cf-msg-checking and cf-msg-result:

 
(define (compiler-can-produce-executable?)
  (cf-msg-checking "whether the ~a compiler works" (~ (cf-lang)'name))
  (rlet1 result ($ run-compiler-with-content
                   (cf-lang-link-m (cf-lang))
                   (cf-lang-null-program-m (cf-lang)))
    (cf-msg-result (if result "yes" "no"))))

This produces a console output like this:

 
checking whether the C compiler works... yes

while the log file records more info:

 
checking: whether the C compiler works
... whatever logging message from run-compiler-with-content ...
result: yes

This corresponds to autoconf’s AC_MSG_CHECKING.

Function: cf-msg-result fmt arg …

The fmt and arg … are passed to format, and the formatted message and newline is written out. For the log file, it records “result: XXX” where XXX is the formatted message. Supposed to be used with cf-msg-checking.

This corresponds to autoconf’s AC_MSG_RESULT.

Function: cf-msg-warn fmt arg …
Function: cf-msg-error fmt arg …

Produces “Warning: XXX” and “Error: XXX” messages, respectively. The fmt and arg … are passed to format to generate XXX part. These corresponds to autoconf’s AC_MSG_WARN and AC_MSG_ERROR.

Function: cf-echo arg … [> file][>> file]

Convenience routine to replace shell’s echo command.

If the argument list ends with > file or >> file, where file is a string file name, then this works just like shell’s echo; that is, args except the last two are written to file, space separated, newline terminated. Using > supersedes file, while >> appends to it.

If the argument list doesn’t end with those redirection message, it writes out the argument to both the current output port and the log file, space separated, newline terminated. For the log file, the message is prefixed with “Message:”.

パラメータと定義

The configure script maintains two global tables, definition tables and substitution tables. Definition tables is used for C preprocessor definitions, and substitution tables are used for @PARAMETER@ substitutions.

Function: cf-define symbol :optional value

Registers C preprocessor definition of symbol with value. Value can be any Scheme objects, but it is emitted to a command line (in -DSYMBOL=VALUE form) or in ‘config.h’ (in #define SYMBOL VALUE form) using display, so you want to avoid including funny characters. If value is omitted, 1 is assumed.

This corresponds to autoconf’s AC_DEFINE.

Function: cf-subst symbol value

Registers substitution parameter symbol with value. Value can be any Scheme objects; it’s display representation is used to substitute @SYMBOL@ in the template.

This corresponds to autoconf’s AC_SUBST, but we require the value (while autoconf can refer to the shell variable value as default).

Function: cf-have-subst? symbol

Returns true iff symbol has a substitution registered by cf-subst.

Function: cf-arg-var symbol

Lookup the environment variable symbol and if it is found, use its value as the substitution value. For example, if you call (cf-arg-var 'MYCFLAGS), then the user can provide the value of @MYCFLAGS@ as MYCFLAGS=-g ./configure.

This corresponds to autoconf’s AC_ARG_VAR, but we lack the ability of setting the help string. That’s because cf-arg-var must be run after cf-init, but the help message is constructed within cf-init.

Function: cf-ref symbol :optional default

This looks up the value of the substitution parameter symbol. If there’s no such substitution parameter registered, it returns default when it’s provided, otherwise throws an error.

Function: cf$ symbol

Looks up the value of the substitution parameter cf-ref, but it returns empty string if it’s unregistered. Useful to use within string interpolation, e.g. #"gosh ~(cf$ GOSHFLAGS)".

定義済みテスト

Function: cf-check-prog sym prog-or-progs :key value default paths filter
Function: cf-path-prog sym prog-or-progs :key value default paths filter

Check if a named executable program exists in search paths, and if it exists, sets the substitution parameter sym to the name of the found program. The name to search is specified by prog-or-progs, which is either a string or a list of strings.

The difference of cf-check-prog and cf-path-prog is that cf-check-prog uses the basename of the found program, while cf-path-prog uses its full path. These corresponds to autoconf’s AC_CHECK_PROG, AC_CHECK_PROGS, AC_PATH_PROG and AC_PATH_PROGS.

For example, the following feature test searches either one of cc, gcc, tcc or pcc in PATH and sets the substitution parameter MY_CC to the name of the found one.

 
(cf-check-prog 'MY_CC '("cc" "gcc" "tcc" "pcc"))

If multiple program names is given, the search is done in the following order: First, we search for the first item (cc, in the above example) for each of paths, then the second, etc. For example, if we have /usr/local/bin:/usr/bin:/bin in PATH and we have /usr/local/bin/tcc and /usr/bin/gcc, the above feature test sets MY_CC to "gcc". If you use cf-path-prog instead, MY_CC gets "/usr/bin/gcc".

If no program is found, sym is set to the keyword argument default if it is given, otherwise sym is left unset.

If the value keyword argument is given, its value is used instead of the found program name to be set to sym.

The list of search paths is taken from PATH environment variable. You can override the list by the paths keyword argument, which must be a list of directory names. It may contain nonexistent directory names, which are siently skipped.

The filter keyword argument, if given, must be a predicate that takes full pathname of the executable program. It is called when the procedure finds matching executable; the filter procedure may reject it by returning #f, in which case the procedure keeps searching.

Note: If the substitution parameter sym is already set at the time these procedure is called, these procedures do nothing. Combined with cf-arg-var, it allows the configure script caller to override the feature test. For example, suppose you have the following in the configure script:

 
(cf-arg-var 'GREP)
(cf-path-prog 'GREP '("egrep" "fgrep" "grep"))

A user can override the test by calling configure like this:

 
$ ./configure GREP=mygrep
Function: cf-prog-cxx

A convenience feature test to find C++ compiler. This searches popular names of C++ compilers from the search paths, sets the substitution parameter CXX to the compiler’s name, then tries to compile a small program with it to see it can generate an executable.

This corresponds to autoconf’s AC_PROG_CXX.

CXX is cf-arg-var’ed in this procedure. If a user provide the value when he calls configure, the searching is skipped, but the check of generating an executable is still performed.

If the substitution parameter CXXFLAGS is set, its value is used to check if the compiler can generate an executable. CXXFLAGS is cf-arg-var’ed in this procedure.

This procedure also emulates autoconf’s AC_PROG_CXX behavior— if CXX is not set, but CCC is set, then we set CXX by the value of CCC and skip searching.

Function: cf-check-header header :key includes

Check if a header file header exists and usable, by compiling a source program of the current language that includes the named header file. This is intended to be used as a predicate—returns #t if the header is usable, #f if not. This corresponds to autoconf’s AC_CHECK_HEADER.

If header requires other headers being included or preprocessor symbosl defined before it, you can pass a list of strings to be emitted before the check in the includes keyword arguments. The given strings are just concatenated and used as a C program fragment. The default value is provided by cf-includes-default.

The following example sets C preprocessor symbol HAVE_CRYPT_H to 1 if ‘crypt.h’ is available. (Note: For this kind of common task, you can use cf-check-headers below. The advantage of using cf-check-header is that you can write other actions in Scheme depending on the result.)

 
(when (cf-check-header "crypt.h")
  (cf-define "HAVE_CRYPT_H" 1))
Function: cf-check-headers headers :key includes if-found if-not-found

Codify a common pattern of checking the availability of headers and sets C preprocessor definitions. This corresponds to autoconf’s AC_CHECK_HEADERS.

See this example:

 
(cf-check-headers '("unistd.h" "stdint.h" "inttypes.h" "rpc/types.h"))

This checks availability of each of listed headers, and sets C preprocessor definition HAVE_UNISTD_H, HAVE_STDINT_H, HAVE_INTTYPES_H and HAVE_RPC_TYPES_H to 1 if the corresponding header file is available.

A list of strings given to includes are emitted to the C source file before the inclusion of the testing header. You can give necessary headers and/or C preprocessor definitions there; if omitted, cf-includes-default provides the default list of such headers.

The keyword argument if-found and if-not-found are procedures to be called when a header is found to be available or to be unavailable, respectively. The procedure receives the name of the header.

The name of the C preprocessor definition is derived from the header name by upcasing it and replacing non-alphanumeric characters for _. Note that this substitution is not injective: Both ‘gdbm/ndbm.h’ and ‘gdbm-ndbm.h’ yield GDBM_NDBM_H. If you need to distinguish such files you have to use cf-check-header.

Function: cf-includes-default

Returns a list of strings that are included in the check program by default. It is actually a combination of C preprocessor #ifdefs and #includes, and would probably be better to be called cf-prologue-default or something, but the corresponding autoconf macro is AC_INCLUDES_DEFAULT so we stick to this name.

Usually you don’t need to call this explicitly. Not giving the includes argument to cf-check-header and cf-check-headers will make cf-includes-default called implicitly.

コンパイラを走らせる

The gauche.configure module provides a generic mechanism to construct a small test program, compile it, and run it. Currently we only support C and C++; we’ll add support for other languages as needed.

Parameter: cf-lang
Function: cf-lang-program prologue body

Returns a string tree that consists a stand-alone program for the current language. Prologue and body must be a string tree. Prologue comes at the beginning of the source, and body is included in the part of the program that’s executed. If the current language is C, the code fragment:

 
(use text.tree)
(write-tree (cf-lang-program "#include <stdio.h>\n" "printf(\"()\");\n"))

would produce something like this:

 
#include <stdio.h>

int main(){
printf("()");

; return 0;
}
Function: cf-lang-io-program

This is a convenience routine. It returns a string tree of a program in the current language, that creates a file named ‘conftest.out’, then exits with zero status on success, or nonzero status on failure.

Function: cf-lang-call prologue func-name
Function: cf-try-compile prologue body
Function: cf-try-compile-and-link prologue body

出力

Function: cf-output file …
Function: cf-show-variables :key formatter
Function: cf-make-gpd

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.8 gauche.dictionary - ディクショナリフレームワーク

Module: gauche.dictionary

ディクショナリはキーから値への写像ができるオブジェクトを表わす抽象クラ スです。このモジュールではディクショナリに対してよく使うジェネリック関数、 および他のディクショナリクラスの上に構築される汎用的なディクショナリクラスを 提供します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.8.1 ディクショナリのためのジェネリック関数

これらのジェネリック関数は、ディクショナリ(離散的で有限なキーの集合から値の集合への 写像を表現するデータ構造)に対して共通するアルゴリズムを書くのに便利です。 (理論的には連続した、また無限なキーの集合を考えることもできますが、 実装上は有限集合に限る方がずっと簡潔になります。)

組み込みクラスでは、<hash-table><tree-map>が ディクショナリのインタフェースを実装しています。dbmモジュール群の 提供する<dbm>クラスもそうです。

自分の定義したクラスにディクショナリのインタフェースを実装するには、 最低限、dict-getdict-put!dict-delete!dict-folddict-comparatorの メソッドを実装してください (データ型がエントリの削除を許さない場合は、dict-delete!の実装を 省略することができます。) 他のジェネリック関数には、これらの基本的なメソッドを使ったデフォルト実装が 提供されます。ただ、他のジェネリック関数についても 自分のクラスに最適化した実装を書くと性能上有利になるでしょう。

註:ディクショナリはコレクションを継承しているので、コレクションを 扱うジェネリック関数はディクショナリに対しても使えます。 例えばエントリの数を得るにはsize-ofジェネリック関数が使えます。

Generic function: dict-get (dict <dictionary>) key :optional default

keyに関連付けられた値を返します。もしkeyを持つエントリが ディクショナリに無い場合、defaultが与えられていればそれを返し、 そうでなければエラーが通知されます。

Generic function: dict-put! (dict <dictionary>) key value

keyからvalueへの関連づけをディクショナリに追加します。

Generic function: (setter dict-get) (dict <dictionary>) key value

dict-put!と同じ動作です。

Generic function: dict-exists? (dict <dictionary>) key

ディクショナリがkeyをキーに持つエントリを保持していれば#tを、 そうでなければ#fを返します。

Generic function: dict-delete! (dict <dictionary>) key

ディクショナリからkeyをキーに持つエントリを除去します。 keyを持つエントリが無ければ何もしません。

Generic function: dict-clear! (dict <dictionary>)

ディクショナリを空にします。通常、全キーをループしてひとつづつ消していくよりも ずっと高速です。

Generic function: dict-comparator (dict <dictionary>)

キーを比較するのに使われるcomparatorを返します。

Generic function: dict-fold (dict <dictionary>) proc seed

dictの各要素に対してprocを呼びシード値を次に渡します。 procは引数を3つとります。エントリーのキー、エントリーの値、それ にシード値です。最初のシード値はseedです。procからの返り値 は次のprocの呼び出しでシード値として使われます。最後のproc の呼び出しの結果がdict-foldの返り値として返されます。

dict<ordered-dictionary>であれば、procは以下のよ うな結合で呼ばれます。ここで、キーはK0(最小)からKn(最大)ま でで、それに対応する値がV0からVnまでであるとします。

 
(proc Kn Vn (proc Kn-1 Vn-1 ... (proc K0 V0 seed)))
Generic function: dict-fold-right (dict <ordered-dictionary>) proc seed

dict-foldと同じですが、procを適用する結合の順が以下のよう に逆になります。

 
(proc K0 V0 (proc K1 V1 ... (proc Kn Vn seed)))

このジェネリック関数は<ordered-dictionary>上にのみ定義されてい ます。

Generic function: dict-for-each (dict <dictionary>) proc

ディクショナリdictの各エントリーのキーと値に対してprocを呼 びます。順序付きディクショナリに対してはprocがキーの昇順に呼ばれ ることが保証されています。

Generic function: dict-map (dict <dictionary>) proc

ディクショナリdictの各エントリーのキーと値に対してprocを呼 び、結果をリストにまとめて返します。順序付きディクショナリに対しては結 果が最初のキーの順にならびます(が、procがキーの昇順に呼ばれるこ とを保証するものではありません)。

Generic function: dict-keys (dict <dictionary>)
Generic function: dict-values (dict <dictionary>)

それぞれdict内にあるすべてのキーのリスト、すべての値のリストを返 します。順序付きディクショナリについてはリストの要素はキーの昇順になら んでいます。

Generic function: dict->alist (dict <dictionary>)

ディクショナリ中の各キーと値の対のリストを返します。対の順番は未定義です。

Generic function: dict-push! (dict <dictionary>) key value

(dict-put! dict key (cons value (dict-get dict key '()))) と同じ動作をするメソッドです。具体的な実装によってはもっと効率が良いかもしれません (keyを2度検索しなくても良い、など)。

Generic function: dict-pop! (dict <dictionary>) key :optional fallback

(dict-get dict key)がペアpであれば、 そのエントリの値が(cdr p)で置き換えられ、(car p)の値が 戻り値となります。keyに該当するエントリが無かったり、ペアで無かった場合は ディクショナリは変更されず、fallbackがあればそれが戻り値となり、 無ければエラーが報告されます。

Generic function: dict-update! (dict <dictionary>) key proc :optional fallback

次のコードのような動作をしますが、具体的な実装はkeyを一度しかルックアップ しないなどより効率良くなっている場合があります。

 
(rlet1 x (proc (dict-get dict key fallback))
  (dict-put! dict key x))
Macro: define-dict-interface dict-class method proc method2 proc2 …

Many dictionary-like datatypes already has their own procedures that directly corresponds to the generic dictionary API, and adding dictionary interface tends to become a simple repetition of define-methods, like this:

 
(define-method dict-put! ((dict <my-dict>) key value)
  (my-dict-put! key value))

The define-dict-interface macro is a convenient way to define those methods in a batch. Each method argument is a keyword that corresponds to dict-method, and proc is the name of the datatype-specific procedure. Here’s the definition of dict interface for <tree-map> and you’ll get the idea. You don’t need to provide every dictionary interface.

 
(define-dict-interface <tree-map>
  :get        tree-map-get
  :put!       tree-map-put!
  :delete!    tree-map-delete!
  :clear!     tree-map-clear!
  :comparator tree-map-comparator
  :exists?    tree-map-exists?
  :fold       tree-map-fold
  :fold-right tree-map-fold-right
  :for-each   tree-map-for-each
  :map        tree-map-map
  :keys       tree-map-keys
  :values     tree-map-values
  :pop!       tree-map-pop!
  :push!      tree-map-push!
  :update!    tree-map-update!
  :->alist    tree-map->alist)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.8.2 汎用ディクショナリ

Class: <bimap>

Provides a bidirectional map (bimap), a relation between two set of values, of which you can lookup both ways.

Internally, a bimap consists of two dictionaries, left map and right map. Think a bimap as a relation between xs and ys. The left map takes an x as a key and returns corresponding y as its value. The right map takes an y as a key and returns corresponding x as its value.

Currently, <bimap> only supports strict one-to-one mapping. Mutating interface (bimap-*-put!, bimap-*-delete! etc) modifies both left and right maps to maintain this one-to-one mapping. (In future, we may provide an option to make many-to-one and many-to-many mappings).

A bimap can be used as a dictionary, with the generic dictionary functions such as dict-get. In such cases, the left map takes precedence; that is, the key given to dict-get etc. is regarded as the key to the left map.

Function: make-bimap left-map right-map :key on-conflict

Creates a new bimap consists of two dictionaries, left-map and right-map. It is the caller’s responsibility to choose appropriate type of dictionaries; for example, if you want to create a relation between a string and a number, you man want to create it like this:

 
(make-bimap (make-hash-table 'string=?)  ; string -> number
            (make-hash-table 'eqv?))     ; number -> string

The keyword argument on-conflict specifies what will happen when the added entry would conflict the existing entries. The following values are allowed:

:supersede

This is the default behavior. Duplicate relations are silently removed in order to maintain one-to-one mapping. For example, suppose a bimap between strings and numbers has had ("foo", 1) and ("bar", 2). When you try to put ("bar", 2) with this option, the first two entries are removed. Returns #t.

:error

Raises an error when duplicate relations are found.

#f

When duplicate relations are found, does nothing and returns #f.

Note: At this moment, an attempt to add a relation exactly same as the existing one is regareded as a conflict. This limitation may be lifted in future.

Function: bimap-left bimap
Function: bimap-right bimap

Returns the left or right map of bimap, respectively. Do not mutate the returned map, or you’ll break the consistency of the bimap.

Function: bimap-left-get bimap key :optional default
Function: bimap-right-get bimap key :optional default

Lookup the value corresponding to the key in the left or right map of bimap. If no entry is found for key, default is returned if provided, otherwise an error is raised.

Function: bimap-left-exists? bimap key
Function: bimap-right-exists? bimap key

Returns #f if the left or right map of bimap has an entry of the key, #t otherwise.

Function: bimap-put! bimap x y :key on-conflict

Put a relation (x, y) into the bimap. After this, (bimap-left-get x) will return y, and (bimap-left-get y) will return x.

If the bimap already have relations with x and/or y, the conflict is handled according to the value of on-conflict; see make-bimap for the possible values and their meanings. The on-conflict keyword argument can override the bimap’s default setting specified at its creation time.

Function: bimap-left-delete! bimap key
Function: bimap-right-delete! bimap key

Deletes an relation with the given left key or right key from bimap. Both left and right maps are modified so that the consistency is maintained. If there’s no relations with given key, these are noop.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.9 gauche.fcntl - 低レベルファイル操作

Module: gauche.fcntl

アドバイザリ・ファイルロックを含む、 fcntl(2)へのインタフェースを提供します。

Function: sys-fcntl port-or-fd operation :optional arg

port-or-fd で指定されたファイルに対して特定の操作をおこないます。 ファイル指定は、ポートオブジェクトもしくはシステムのファイルディスクリプタ である整数でなくてはなりません。それがポートである場合には、すでにオープン されたファイルを結びついていなければなりません。 (port-typefile を返すようなポートということです。 ポート共通の操作参照。)

操作は整数 operation で指定します。 いくつかの変数が定義されいて、operation に使用できます。

F_GETFD

port-or-fd のファイルディスクリプタに結びついているフラグを返します。 オプション引数のargは使われません。返り値は整数で、その定義はシステム によります。ただし、FD_CLOEXECだけは例外で、これはexecで クローズされたことを示しています。 詳しくはあなたのシステムのfcntl(2)のマニュアルエントリを 参照してください。

F_SETFD

port-or-fdのファイルディスクリプタのフラグを与えられたargに します。たとえば、FL_CLOEXECフラグを設定する、互換性のある方法では 次のようにします。

 
(sys-fcntl port F_SETFD
          (logior FD_CLOEXEC
                  (sys-fcntl port F_GETFD)))
F_GETFL

port-or-fdによって指定されたオープンしたファイルに結びついている フラグを返します。このフラグには以下のような情報が含まれています。

  • ファイルのアクセスモード。O_ACCMODE でマスクしたとき、 これは、O_RDONLYO_WRONLYO_RDWRのうちどれかひとつです。
  • ファイルの作成オプション。O_CREATO_EXCLのどちらか、または両方。 O_TRUNC.
  • O_APPENDでマスクしたとき、追記が許されているかどうか。
  • O_NONBLOCKでマスクしたとき、I/O が非ブロックモードかブロックモードか。
  • O_NOCTTYでマスクしたとき、端末制御を切離しているかどうか。

システムが、そのシステム特有のフラグを定義していることがあります。

F_SETFL

port-or-fdで指定したオープンされたファイルのフラグを設定します。 上に列挙されたフラグのうち、O_NONBLOCKO_APPEND だけが 変更可能です。

F_GETFD/F_SETFD はファイルディスクリプタそのものに結びついた フラグについてであり、一方、F_GETFL/F_SETFL はオープンされた ファイルそのものに結びついているフラグについての操作であることに注意して ください。この違いは、2つ以上のファイルディスクリプタが同じオープンされた ファイルを指しているような場合にあらわれます。

F_DUPFD

port-or-fdで参照しているのと同じファイルを指す新しいファイル ディスクリプタを生成します。 argで整数を与えなければなりません。この数は割り当てられる ファイルディスクリプタの最小値を指定します。

F_GETLK

3つめの引数を与えなければなりません。これは、以下に述べる<sys-flock> オブジェクトのインスタンスでなければなりません。argで指定された ロック情報を検索し、それにしかるべく変更します。

F_SETLK
F_SETLKW

3つめの引数を与えなければなりません。これは、以下に述べる<sys-flock> オブジェクトのインスタンスでなければなりません。argにしたがって、 アドバイザリロックを設定します。ロックの取得に成功すれば、#tが 返ります。もし、別のプロセスがロックをもっていて要求したものと衝突した 場合には F_SETLK のときは #f を返します。一方、F_SETLKW のときはロックが利用可能になるまで待ちます。

F_GETOWN

当該ファイルディスクリプタ上のイベント用シグナル、SIGIO および SIGURG を 受けとることになるプロセスのプロセスIDあるいはプロセスグループを返します。 プロセスグループは負の値で表示されます。このフラグはシステムにこのような 機能がある場合にのみ利用可能です(BSD や Linux にはこの機能があります)。

F_SETOWN

当該ファイルディスクリプタ上のイベント用シグナル、SIGIO および SIGURG を 受けとることになるプロセスのプロセスIDあるいはプロセスグループを設定します。 プロセスグループは負の値で表示されます。このフラグはシステムにこのような 機能がある場合にのみ利用可能です(BSD や Linux にはこの機能があります)。 詳細については、お使いのシステムの fcntl(2) のマニュアルページをチェック してください。

これ以外の値を operation で指定しようとするとエラーになります。

Builtin Class: <sys-flock>

POSIX のアドバイザリレコードロックを表わす構造体です。アドバイザリレコードロック というのは、適切なロックをもたないプロセスが対象ファイルを操作しても システムは関知しない、ということです。すべてのプロセスが、共有される可能性 のあるファイルを操作する前にロックをfcntlを使って検査することが 期待されています。

fcntlによるロックは、プロセスごと、ファイルごとであることに注意してください。 同一プロセス内で同じファイルを複数回ロックすることはできますが、再帰ロックではないので いずれかのロックがアンロックされるか、どこかでそのファイルがクローズされるかすると プロセスはそのファイルに対するロックを失います。これは、fcntlロックを ライブラリで使うことを難しくします。プロセス間排他制御を実現する別の方法として with-lock-file (ロックファイル参照) があります。

Instance Variable of <sys-flock>: type

ロックのタイプを表す整数です。利用可能な値として以下の変数があらかじめ 定義されています。

F_RDLCK

読み込みのロック

F_WRLCK

書き込みのロック

F_UNLCK

F_SETLKによるロックを除去するか、あるいはレコードがF_GETLK でロックされていないことを示す。

Instance Variable of <sys-flock>: whence

startの計測が開始される位置を示す。

Instance Variable of <sys-flock>: start

ロックされる領域の開始位置オフセット

Instance Variable of <sys-flock>: len

ロックされるバイト数。ゼロは「EOFまで」という意味。

Instance Variable of <sys-flock>: pid

当該のロックをもっている整数のプロセスID。F_GETLKでのみ使用。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.10 gauche.generator - ジェネレータ

Module: gauche.generator

ジェネレータは、値の列の生成器として動作する、引数を取らない手続きです。 呼ばれる度に列の次の値を返します。列の終端に達した場合はEOFが返されます。 例えば、read-charは、現在の入力ポートからの入力を一文字づつ返す ジェネレータと考えることができます。

値の列の生成源を手続きによってジェネレータとして抽象化するのは広く使われるテクニックなので、 このようなジェネレータに共通して使えるユーティリティがあると便利です。 このモジュールはそのために作られました。

Srfi-121 (Generators) はこのモジュールのサブセットです。 gauche.generatorはsrfi-121より前からあったので、 いくつかの手続きについては異なる名前を使っていました。互換性のため、 それらの手続きは両方の名前を定義しています。

ジェネレータは、必要になったら計算を行うようなシステムを簡単に実現でき、 効率も極めて良いですが、副作用に頼った抽象化であることには注意が必要です。 例えば、途中まで列を生成した後に、最初にもどってやり直す、といったことはできません。 より関数的にオンデマンドの計算を行うためには、ジェネレータを使って 構築された、遅延シーケンスを使うのが便利です (遅延シーケンス参照)。

ジェネレータの典型的な使い方は次の通りです。まず値の源となる ジェネレータを作ります。これはジェネレータの生成手続きを使っても良いですし (ジェネレータの生成参照)、自分で一から定義しても良いでしょう。 次に、生成される値を流れの途中で加工するジェネレータ操作手続きを 必要に応じて繋いでゆきます(ジェネレータの操作参照)。 最終的には、ジェネレータから具体的な値を取り出して消費する必要があります。 そのために便利なジェネレータ消費手続きも用意してあります ジェネレータの消費参照)。このように、ジェネレータ手続きを組み合わせた パイプライン(あるいは有向非循環グラフ)を作ることで、怠惰な値伝搬ネットワークを 実現できます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.10.1 ジェネレータの生成

ジェネレータは特別なデータタイプではなく、普通の手続きに過ぎません。つ まり、ジェネレータはlambdaを使って作ることができます。 このモジュールは、あると便利な 良く使われるジェネレータの構築子を提供します。

自分で定義した手続きをジェネレータとして使うこともできますが、 ジェネレータを扱う手続きは、 一度EOFを返したジェネレータを繰り返して呼び出す可能性があることに注意してください。 その手続きがいったんEOFを返したら、それ以降の呼び出しに対してEOFを 返し続けるよう書かなければなりません。

ジェネレータ構築子が返すのは単なる手続きであり、そのまま印字しても 中身が何かはわかりません。この節の例では、generator->listを使って ジェネレータをリストに変換しています。generator->listの説明は、 ジェネレータの消費を参照してください。

Function: null-generator

空ジェネレータです。呼ばれるたびにEOFオブジェクトを返します。

Function: circular-generator arg …

与えられた引数を繰り返し生成する、無限ジェネレータを返します。

 
(generator->list (circular-generator 1 2 3) 10)
  ⇒ (1 2 3 1 2 3 1 2 3 1)

この例では、変換後のリストの長さを10に制限しています。そうしなければ、 generator->listは制御を返さないでしょう。

Function: giota :optional (count +inf.0) (start 0) (step 1)

iota (see section リストの作成)のような、startで始まり stepずつ増加する、count個の級数のジェネレータを作成します。

 
(generator->list (giota 10 3 2))
  ⇒ (3 5 7 9 11 13 15 17 19 21)

startendがともに正確数であれば、ジェネレータは 正確数を生成します。そうでなければ非正確数を生成します。

 
(generator->list (giota +inf.0 1/2 1/3) 6)
  ⇒ (1/2 5/6 7/6 3/2 11/6 13/6)
(generator->list (giota +inf.0 1.0 2.0) 5)
  ⇒ (1.0 3.0 5.0 7.0 9.0)
Function: grange start :optional (end +inf.0) (step 1)

giotaと同様、級数ジェネレータを作成します。この級数は startに始まり、stepずつ増加してend直前まで続きます。

 
(generator->list (grange 3 8))
  ⇒ (3 4 5 6 7)
Function: generate proc

コルーチンからジェネレータを作成します。

引数procは、引数yieldひとつを取る手続きです。 generateは、呼ばれるとジェネレータGをただちに返します。 Gは呼ばれると、その中でyieldが呼ばれるまでprocを 実行します。yieldが呼ばれるとprocの実行は中断され、 yieldに渡した値がGから返ります。

いったんprocが返ると、それが列の終端となります—それ以降、 GはEOFオブジェクトを返します。procが返す値は無視されます。

次に挙げるコードは、0、1、2からなる級数を生成するジェネレータを作成し (事実上、(giota 3)と同じ)、gに束縛しています。

 
(define g
  (generate
   (^[yield] (let loop ([i 0])
               (when (< i 3) (yield i) (loop (+ i 1)))))))

(generator->list g) ⇒ (0 1 2)
Function: list->generator lis :optional start end
Function: vector->generator vec :optional start end
Function: reverse-vector->generator vec :optional start end
Function: string->generator str :optioanl start end
Function: uvector->generator uvec :optional start end
Function: bytevector->generator u8vector :optional start end

[SRFI-121+] 実引数の各要素を生成するジェネレータを返します。 reverse-*は、逆順で値を生成します。 srfi-121はuvector->generator以外の手続きを定義しています。 uvector->generatorは全ての種類のユニフォームベクタを取ることができます。 一方、srfi-121も定義するbytevector->generatoru8vectorだけを 対象とします。

 
(generator->list (list->generator '(1 2 3 4 5)))
  ⇒ (1 2 3 4 5)
(generator->list (vector->generator '#(1 2 3 4 5)))
  ⇒ (1 2 3 4 5)
(generator->list (reverse-vector->generator '#(1 2 3 4 5)))
  ⇒ (5 4 3 2 1)
(generator->list (string->generator "abcde"))
  ⇒ (#\a #\b #\c #\d #\e)
(generator->list (uvector->generator '#u8(1 2 3 4 5)))
  ⇒ (1 2 3 4 5)

いったん全ての要素を取り出してしまえば そのジェネレータは空になります;省略可能引数startendで ジェネレータがたどる範囲を制限することができます;startで 左の境界を、endで右の境界を指定します。

正順のジェネレータでは、最初の値としてジェネレータが生成するのは start番目の要素であり、end番目の要素の直前の要素が最後に生成する 値となります。逆順のジェネレータでは、最初の値はend番目の要素の すぐ隣の要素であり、最後の値がstart番目の要素となります。

 
(generator->list (vector->generator '#(a b c d e) 2))
  ⇒ (c d e)
(generator->list (vector->generator '#(a b c d e) 2 4))
  ⇒ (c d)
(generator->list (reverse-vector->generator '#(a b c d e) 2))
  ⇒ (e d c b)
(generator->list (reverse-vector->generator '#(a b c d e) 2 4))
  ⇒ (d c)
(generator->list (reverse-vector->generator '#(a b c d e) #f 2))
  ⇒ (b a)
Function: bits->generator n :optional start end
Function: reverse-bits->generator n :optional start end

これらの手続きは正確な整数を引数に取り、 integer->listと同様に(srfi-60 - 整数に対するビット操作参照)、 それを真偽値のシーケンス(0が偽、1を真)として扱います。 bits->generatorはLSBから、reverse-bits->generatorは MSBからビットを取り出します。

 
(generator->list (bits->generator #b10110))
 ⇒ (#f #t #t #f #t)
(generator->list (reverse-bits->generator #b10110))
 ⇒ (#t #f #t #t #f)

省略可能引数startendはビットフィールドの範囲を指定します (LSBが0)。list->generator等の同名の引数と違って、 startが右端(含まれる)、endが左端(含まれない)を指定します。 この指定方法は整数のビットフィールドにアクセスする他の手続きと一貫しています (srfi-60 - 整数に対するビット操作参照)。

 
(generator->list (bits->generator #x56 0 4)
  ⇒ (#f #t #t #f)  ; takes bit 0, 1, 2 and 3
(generator->list (bits->generator #x56 4 8)
  ⇒ (#t #f #t #f)  ; takes bit 4, 5, 6 and 7

(generator->list (reverse-bits->generator #x56 4 8)
  ⇒ (#f #t #f #t)  ; takes bit 7, 6, 5 and 4 
Function: port->sexp-generator input-port
Function: port->line-generator input-port
Function: port->char-generator input-port
Function: port->byte-generator input-port

それぞれ文字、バイトを与えられた入力ポートから読み取るジェネレータを 返します。つまり、それぞれ (cut read input-port)(cut read-line input-port)(cut read-char input-port)(cut read-byte input-port) と同じですが、完全性のために提供されています。

Generic function: x->generator obj

任意のコレクションobjを、objをたどるジェネレータに変換する ジェネリック関数版です。さらに、objが入力ポートである場合は、 port->char-generatorを呼びます。

Function: file->generator filename reader . open-args

ファイルfilenameをオープンし、入力ポートを引数に取る手続き readerによってそのファイルから読み取るジェネレータを返す。 引数open-argsは、open-input-fileに渡されます (ファイルポート参照)。

ファイルは、ジェネレータが尽きたところで閉じられます。最後 まで読み取る前にジェネレータが破棄された場合は、そのジェネレータ がガベージコレクタに回収されるまでファイルは開かれたままです。 特定の時点までにファイルが確実にクローズしたい場合は、with-input-from-file の動的なエクステントの中で、リーダー手続きをジェネレータとして 使うのが良いでしょう。

Function: file->sexp-generator filename . open-args
Function: file->char-generator filename . open-args
Function: file->line-generator filename . open-args
Function: file->byte-generator filename . open-args

ファイルfilenameからそれぞれS式、文字、行、バイトの列を読むジェネ レータを返します。これらは、file->generatorreader引数にreadread-charread-lineread-byteを渡して特定化したものです。

file->generator同様、open-argsはそのままopen-input-fileに 引き渡されます(ファイルポート参照)。ジェネレータが使い切られると、 ファイルは閉じられます。

Function: gunfold p f g seed :optional tail-gen

unfoldに似たジェネレータの構築子です (SRFI-1 リスト操作関数参照)。

Pはシード値を引数に取り、いつ止まるかを決める述語です。 Fはシード値から値を計算する手続きです。Gは現在の シード値から次のシード値を計算する手続きです。Tail-gen は最後のシード値を取り、残りを生成するジェネレータを返します。

この関数から返されたジェネレータが呼ばれるたびに、pが現在の シード値とともに呼ばれます。これが真を返せば、やるべき事が終わっ たことがわかり、(もし与えられていたなら)tail-genが残りを 生成するジェネレータを得るために呼ばれます。さもなければ、生成する 値を得るために現在のシード値にfが適用され、シード値を更新 するためにgが使われます。

 
(generator->list (gunfold (^s (> s 5)) (^s (* s 2)) (^s (+ s 1)) 0))
  ⇒ '(0 2 4 6 8 10)

SRFI-121 compatible procedures

Function: generator item …

[SRFI-121] item … を返すジェネレータを作成します。

Function: make-iota-generator count :optional start step

[SRFI-121] giotaとほぼ同じですが、count引数は必須です。

Function: make-range-generator start :optional end stop

[SRFI-121] grangeと同じです。

Function: make-coroutine-generator proc

[SRFI-121] generateと同じです。

Function: make-bits-generator n

[SRFI-121] (make-bits-generator n)(bits->generator n)と同じです。 省略可能なstart/end引数は取りません。 reverse-bits->generatorに相当する手続きはsrfi-121にはありません。

Function: make-for-each-generator for-each obj

[SRFI-121]

 
(define (make-for-each-generator for-each coll)
  (generate (^[yield] (for-each yield coll))))

作られたジェネレータが全ての値を取り出す前にobjが変更された場合の振る舞いは、 渡されたfor-each手続きがそのケースをどのように処理するかに依存します。 それは安全かもしれないし、安全でないかもしれません。一般的に、ジェネレータが EOFを返す前にobjを変更することは避けるのが賢明です。 ジェネレータがEOFを返した後にobjを変更するのは安全です。

Function: make-unfold-generator stop? mapper successor seed

[SRFI-121] 省略可能なtail-gen引数を取らないことを除けば、 gunfoldと同じです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.10.2 ジェネレータの操作

以下に挙げる手続きは、どれもジェネレータ(gengen2と記述されて います)を受け取ってジェネレータを返します。便宜上、これらの手続きは gengen2としてコレクションも受けつけます;ジェネレータが 想定されているところにコレクションが渡されると、暗黙のうちにジェネレータ へと変換されるのです。

(註:これはGauche独自の拡張です。ポータブルなsrfi-121プログラムは、 この振る舞いに依存してはいけません。明示的にコレクションをジェネレータに 変換してください。)

Function: gcons* item … gen

[SRFI-121] genの前にitemを追加するジェネレータを返します。

 
(generator->list (gcons* 'a 'b (giota 2)))
 ⇒ (a b 0 1)
Function: gappend gen …

[SRFI-121] 最初に与えたジェネレータが生成する値を生成し、それが尽きたら2番目に与えた ジェネレータが生成する値を生成し、という具合に、与えられたジェネレータが 生成する値を順番に生成するジェネレータを返します。

 
(generator->list (gappend (giota 3) (giota 2)))
 ⇒ (0 1 2 0 1)

(generator->list (gappend))
 ⇒ ()
Function: gconcatenate gen

gen引数は、ジェネレータやシーケンスを生成するジェネレータです。 この関数は、genが生成する最初のジェネレータ/シーケンスの要素を次々に 生成し、それが尽きたら二番目のジェネレータ/シーケンスの要素を次々に生成し… というジェネレータを作って返します。

(apply gappend (generator->list gen))と似た動作ですが、 gconcatenategenが無限ジェネレータであっても動作するという 利点があります。

 
($ generator->list $ gconcatenate
   $ list->generator `(,(giota 3) ,(giota 2)))
 ⇒ (0 1 2 0 1)
Function: gflatten gen

引数genはリストを生成するジェネレータです。 この手続きは、入力が生成するリストの各要素をひとつづつ生成するようなジェネレータを 作成して返します。

例: ゲームのテトリスは、次に落ちてくるピース (テトリミノ) を次の アルゴリズムで決めています: 各種類(O, I, T, S, Z, L, J)のテトリミノが 一つづつ入った袋をとり、そこからランダムに一つづつ取り出す。袋が空になったら 新たにテトリミノの袋をとり繰り返す。このアルゴリズムは次に示すとおり ジェネレータのパイプラインで実装できます。(テトリスはThe Tetris Companyの 登録商標です)

 
(use gauche.generator)
(use data.random) ; for permutations-of

(define g
  ($ gflatten $ permutations-of
     $ (circular-generator '(O I T S Z L J))))

(generator->list g 21)
  ⇒
  (L O Z T J S I J L Z T I O S T L Z S I J O)

この例と、上ののgconcatenateの例を比べてみてください。微妙な違いが あります。gconcatenateは、ジェネレータを生成するジェネレータを取りますが、 gflattenはリストを生成するジェネレータを取ります。

Haskell風の型表記を使うと、これら似た手続きの違いをわかりやすく整理することができるでしょう:

 
gappend            :: (Generator a, Generator a, ...) -> Generator a
(pa$ apply append) :: [(Generator a)] -> Generator a
gconcatenate       :: Generator Generator a -> Generator a
gflatten           :: Generator [a] -> Generator a
Function: gmerge less-than gen gen2 …

入力ジェネレータから生成される要素を、手続きless-thanで決められる順 に生成するジェネレータを作って返します。 less-thanは入力ジェネレータの二つの要素a, bに対して 呼び出され、abに先行すべき時のみ#tを返します。

入力のジェネレータはそれぞれが要素を正しい順で生成しなければなりません。 そうでない場合、出力が正しい順になっていることは保証されません。

入力が一つだけ渡された場合は、(それをジェネレータへと型変換した後で)それがそのまま 返され、less-thanは呼ばれません。

 
(generator->list (gmerge < '(1 3 8) '(5) '(2 4)))
  ⇒ '(1 2 3 4 5 8)
Function: gmap proc gen gen2 …

与えられたジェネレータから得られる値にprocを適用して得られる値を 生成するジェネレータを返します。返り値となるジェネレータは、引数として 与えられたジェネレータのどれかが尽きたら終了します。

注意: この手続きは、generator-map (生成された値の畳み込み参照) とは違います。generator-mapは一度に全ての値を消費し、結果をリストとして返しますが、 gmapはすぐには入力を消費せずにジェネレータを返すのです。

Function: gmap-accum proc seed gen gen2 …

状態を持つマッピングを行うmap-accum (see section コレクションに対するマッピング)のジェネレータ版 です。

引数procは、入力となるジェネレータの個数プラス1個の引数を取る 手続きで、(proc v v2 … seed)のように呼ばれます。 v, v2, … は、入力ジェネレータが生成する値であり、 seedは現在のシード値です。この手続きは2つの値を返さなければい けません。生成する値と、次のシード値です。

註:これはsrfi-121のgcombineと同じものです。

Function: gcombine proc seed gen gen2 …

[SRFI-121] gmap-accumの別名です。srfi-121との互換性のため提供されています。

Function: gfilter pred gen
Function: gremove pred gen

[SRFI-121] ソースジェネレータgenが生成する値のうち、 predがfalseを返すもの(gfilterの場合)、 またはpredが真の値を返すもの(gremoveの場合)を 除いた値を生成するジェネレータを返します。

 
(generator->list (gfilter odd? (grange 0)) 6)
 ⇒ (1 3 5 7 9 11)
(generator->list (gremove odd? (grange 0)) 6)
 ⇒ (0 2 4 6 8 10)
Function: gdelete item gen :optional =

[SRFI-121] ソースジェネレータgenが生成する値のうち、itemと等しいものを 取り除いた値を生成するジェネレータを返します。 比較は省略可能引数=に渡す手続きで行われ、省略時にはequal?が使われます。

 
;; Note: This example relies on auto-coercing list to generator.
;; SRFI-121 requires list->generator for the second argument.
(generator->list (gdelete 3 '(1 2 3 4 3 2 1)))
  ⇒  (1 2 4 2 1)
Function: gdelete-neighbor-dups gen :optional =

[SRFI-121] ソースジェネレータgenが生成する値を生成するジェネレータを返します。 但し、連続して等しいものはそのうち1つだけが生成されます。 比較は省略可能引数=に渡す手続きで行われ、省略時にはequal?が使われます。

 
;; Note: This example relies on auto-coercing list to generator.
;; SRFI-121 requires string->generator for the second argument.
(generator->list (gdelete-neighbor-dups "mississippi"))
  ⇒ (#\m #\i #\s #\i #\s #\i #\p #\i)
Function: gfilter-map proc gen gen2 …

(gfilter values (gmap proc gen gen2 …)) と同様に動作しますが、若干効率的です。

Function: gstate-filter proc seed gen

この手続きは、一連の値に対するステートフルなフィルタリングを可能にします。 引数procはソースジェネレータからの値vと、シード値を取る手続きでなければ なりません。この手続きが真を返す場合に、返り値となるジェネレータは vを生成します。そうでない場合、ジェネレータはprocが真を返すか、ソース ジェネレータが尽きるまで、シード値を更新しながらprocを呼び出し続けます。

次に挙げる例は、振動する値を生成するジェネレータを受け取り、直前の値よりも 大きな値のみを生成するジェネレータを返します。

 
(generator->list
 (gstate-filter (^[v s] (values (< s v) v)) 0
                (list->generator '(1 2 3 2 1 0 1 2 3 2 1 0 1 2 3))))
 ⇒ (1 2 3 1 2 3 1 2 3)
Function: gbuffer-filter proc seed gen :optional tail-gen

この手続きは入力と出力がn対mで対応するようなフィルタを作ります。つまり、 入力のいくつかを見て、それに応じて1個以上の出力を生成するようなフィルタです。

手続きprocは、次の入力およびシード値を受け取り、 二つの値を返します:出力値のリスト、及び次のシード値です。 出力を決定するためにもっと入力を読むことが必要なら、 第一の返り値を()にします。

入力が終端に達したら、手続きtail-genが、その時点でのシード値を引数として 呼び出されます。tail-genは出力の終端となる値のリストを返します。 tail-genが省略された場合は、入力がなくなった時点で最後のprocが 返した出力のリストが終端となります(最後のシード値は捨てられます)

例えば、テキストファイルがあり、各行にコマンドがかかれているとしましょう。 ただし行がバックスラッシュで終わった場合、次の行へと継続しているものとみなします。 以下のコードは、入力ファイルの各行を読むジェネレータから、 論理行(継続行をくっつけたもの)をひとつづつ返すジェネレータを生成します。

 
(gbuffer-filter (^[v s]
                  (if-let1 m (#/\\$/ v)
                    (values '() (cons (m 'before) s))
                    (values `(,(string-concatenate-reverse (cons v s))) '())))
                '()
                (file->line-generator "input-file.txt")
                (^[s] `(,(string-concatenate-reverse s))))
Function: gtake gen k :optional padding
Function: gdrop gen k

[SRFI-121] それぞれ、ソースジェネレータgenが生成する値のうち最初からk個の値を生成する、 および最初からk個の値を除いた次の値から生成するようなジェネレータを 作成して返します。

これらの手続きはソースジェネレータがk個の値を生成する前に尽きたとしても、 何も文句を言いません。デフォルトでは、gtakeが返すジェネレータはソース ジェネレータが終端に達した時点で終了します。しかし、省略可能引数paddingを与えた 場合、返されるジェネレータは足りない分をpaddingで補うことで 常にk個の値を生成します。

註: fill引数を渡した場合、たとえ入力ジェネレータが終端に達していたとしても gtakeが返すジェネレータはk個の値を生成します。つまりその場合、 入力が使い尽くされたかどうかを判定する汎用的な方法はありません。 「入力が尽きるまでk個の要素を次々に取り出したい」という場合は 下に説明するgslicesの方が使い勝手が良いかもしれません。

互換性への註: 0.9.4までは、gtakefill?paddingの ふたつの省略可能引数を取りました。これはもともとGauche組み込みtake*に合わせて いたのですが、srfi-121に採用されたgtakeとは非互換になりました。 srfi-121の方が簡潔で直感的なので、0.9.5からは元のgtakeを(take*との 類似性を強調して)gtake*とリネームし、gtakeはsrfi-121に合わせることと しました。 移行をスムースにするため、gtakeは二つの省略可能引数(合計4つの引数)を取ることを 許します。その場合、gtake*が呼ばれたのと同じ動作になります。 従って、gtakeに4引数を渡している従来のコードは、0.9.5以前でも以降でも 動作します。

Function: gtake* gen k :optional fill? padding

gtakeのバリエーションで、一つの省略可能引数paddingのかわりに、 take*と同じように二つの省略可能引数を取ります (リストへのアクセスと変更参照)。 0.9.4まではこれがgtakeと呼ばれていました。 互換性のために名前を変えて残してあります。

Function: gtake-while pred gen
Function: gdrop-while pred gen

[SRFI-121] ジェネレータ版の take-whiledrop-while (リストへのアクセスと変更参照) です。 gtake-whileが返すジェネレータは、ソースジェネレータが生成する値に対して predが真を返す限り、その値を生成します。gdrop-whileが返す ジェネレータは、まずソースジェネレータから値を読み取り、その値に対して predが真を返したら、値の生成を開始します。

Function: gslices gen k :optional (fill? #f) (padding #f)

slicesのジェネレータ版です (リストへのアクセスと変更参照。 入力のジェネレータgenからk要素づつ読み出して、そのリストを生成する ジェネレータを返します。

 
(generator->list (gslices (giota 7) 3))
  ⇒ ((0 1 2) (3 4 5) (6))

fill?引数とpadding引数は、gtakeのそれと同様に、 入力が足りなかった場合の処理を指定します。デフォルトの、fill?#fの場合、入力がk要素に満たなければ、出力の要素も 切り詰められます (上の例参照)。fillが真の値であれば、 入力が足りない分はpaddingが当てられ、出力の最後のリストもk要素になります。

 
(generator->list (gslices (giota 6) 3 #t 'x))
  ⇒ ((0 1 2) (3 4 5))
(generator->list (gslices (giota 7) 3 #t 'x))
  ⇒ ((0 1 2) (3 4 5) (6 x x))
Function: grxmatch regexp gen

genは暗黙の変換後、文字を生成するジェネレータでなければなりません。

この手続きが返すジェネレータは、genが生成する文字のシーケンスに対して、 regexpをマッチさせようとする。そして一度マッチすると、その位置を記憶して #<rxmatch>オブジェクトを返します。さらにマッチすることがなければ、 ジェネレータは尽きます。

 
($ generator->list
   $ gmap rxmatch-substring
   $ grxmatch #/\w+/ "The quick brown fox jumps over the lazy dog.")
 ⇒ ("The" "quick" "brown" "fox" "jumps" "over" "the" "lazy" "dog")

注意: この手続きはgenが文字列の場合、ジェネレータへの変換がバイパスされるため 効率的です。genが文字列ではない場合、現在の実装では、genが生成する文字の シーケンス全体の長さがnの場合に、係数が小さいとはいえ、必要となるregexpの適用 回数は、O(n^2)となるかもしれません。この点について、将来改良されるかもしれませんが、 大きな入力に対してこの関数を使う場合は、注意してください。

もう一点注意: genが文字列でない場合、rxmatchはバッファリングされた 部分的な入力に対して適用されます。このため、返されたマッチのrxmatch-after は、マッチ後の“入力の残り全部”を表現しません。単にバッファの中にある文字列の 残りになります。

Function: gindex vgen igen

[SRFI-121] 引数はどちらもジェネレータです。igenは単調増加する正確な非負整数を 生成しなければなりません。

vgenから生成される値のうち、igenからの数字をインデックスとする 値のみを取り出して返すようなジェネレータを作成して返します。 どちらかの入力ジェネレータが終端に達したら、返されるジェネレータも終端に達します。

igenが条件を満たさない値を生成した場合はその時点でエラーが投げられます。

 
;; This example takes advantage of Gauche's auto-coercing
;; list to generator.  For portable srfi-121 programs,
;; you need list->generator for each argument:
(generator->list (gindex '(a b c d e) '(0 2 3)))
  ⇒ (a c d)
Function: gselect vgen bgen

[SRFI-121] 引数はどちらもジェネレータです。vgenからの要素のうち、 対応するbgenの要素が真の値であるものだけを選んで返す ようなジェネレータを作成して返します。

ソースジェネレータの一つが終端に達した時に、作成された ジェネレータも終端に達します。

 
;; This example takes advantage of Gauche's auto-coercing
;; list to generator.  For portable srfi-121 programs,
;; you need list->generator for each argument:
(generator->list (gselect '(a b c d e) '(#t #t #f #t #f)))
  ⇒ (a b d)

ビットジェネレータと一緒に使うと、gselectでビットマスク により要素を抽出することができます。

 
(generator->list (gselect '(a b c d e)
                           (reverse-bits->generator #x1a)))
  ⇒ (a b d)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.10.3 ジェネレータの消費

いくつかのジェネレータ消費手続きは組み込みになっています。 generator-foldgenerator-fold-rightgenerator-for-eachgenerator-mapgenerator-findについては、 生成された値の畳み込みを参照してください。

Function: generator->list generator :optional k
Function: generator->reverse-list generator :optional k

[SRFI-121] generatorから項目を読み取り、それらを要素とするリストを返します (generator->reverse-listでは要素が逆順になります)。 デフォルトでは、ジェネレータを使い切るまで読み取ります。省略可能 引数kを与える場合、それは非負整数でなければならず、結果のリスト は、k個の項目を読み取るか、ジェネレータを使い切ったところで終わり となります。

無限ジェネレータを渡す時は必ずkを指定しましょう。 さもなくば、この手続きは制御を返さず、全てのメモリを食い尽して クラッシュするでしょう。

Function: generator->vector gen :optional k
Function: generator->string gen :optional k

[SRFI-121] genから、k要素もしくはgenが終端に達するまで要素を読み出し、 それらの要素からなるベクタまたは文字列を作って返します。

generator->stringの場合、genが文字以外のものを生成したら エラーが報告されます。

Function: generator->vector! vector at gen

[SRFI-121] vectorを、インデックスatから、genが生成する値によって 埋めてゆきます。genが終端に達するか、ベクタの最後まで埋められた時点で 終了し、生成された要素の個数が返されます。

 
(define v (vector 'a 'b 'c 'd 'e))

(generator->vector! v 2 (giota))
  ⇒ 3

v ⇒ #(a b 0 1 2)
Macro: glet* (binding …) body body2 …

これは、ジェネレータコードに頻繁に現れるモナド的なパターンを表現 するものです。and-let*の発想に似ていますが、#fのかわりにEOFを 返す式を評価すると直ちに返ります。

binding部分は(var expr)( expr )の形をとります。 実際の定義を見れば、この構文が明解に理解できるでしょう。

 
(define-syntax glet*
  (syntax-rules ()
    [(_ () body body2 ...) (begin body body2 ...)]
    [(_ ([var gen-expr] more-bindings ...) . body)
     (let1 var gen-expr
       (if (eof-object? var)
         var
         (glet* (more-bindings ...) . body)))]
    [(_ ([ gen-expr ] more-bindings ...) . body)
     (let1 var gen-expr
       (if (eof-object? var)
         var
         (glet* (more-bindings ...) . body)))]))
Macro: glet1 var expr body body2 …

これとglet*の関係は、let1let*のそれと同じです。言葉を 変えれば、これは(glet* ([var expr]) body body2 …)と同じです。

Macro: do-generator (var gexpr) body …

これは、ジェネレータ版のdolistでありdotimesです(変数束縛参照)。

gexprはジェネレータを生成する式であり、一度だけ評価されます。 結果となるジェネレータはEOFを返すまで繰り返し呼ばれます。ジェネレータ が呼ばれる毎に、ジェネレータが生成する値に束縛されるvarの スコープ内でbody …が評価されます。

dolistdotimesがそうであるように、このマクロも副作用のため のものです。同じことはfor-each族を使っても書けますが、このマクロを 使って命令的に記述したコードの方が読みやすいこともあるのです。

 
(do-generator [line (file->line-generator "filename")]
  ;; lineを使って何か副作用のある作業をする
  )
Function: generator-any pred gen
Function: generator-every pred gen

[SRFI-121] Like any and every (see section リストをたどる手続き), but works on a generator.

Function: generator-count pred gen

[SRFI-121] genが生成する要素ののうち、predを満たすものの個数を返します。 副作用としてgenは使い尽くされます。

Function: generator-unfold gen unfold arg …

[SRFI-121]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.11 gauche.hook - フック

Module: gauche.hook

フックオブジェクトを提供します。これは、特定のタイミングで 呼び出されるクロージャのリストを管理するものです。

フックのAPIはGuileのそれと上方互換で、下記のように拡張されています。

Class: <hook>

フックのクラスで、一度に呼び出される手続きのリストを保持しています。

<hook>クラスにはobject-applyメソッドが定義されているので、 あたかもそれが手続きであるかのようにフックオブジェクトを“適用”できます。 フックオブジェクトを適用すると、登録されている全ての手続きが呼び出されます。

Function: make-hook :optional (arity 0)

与えられたアリティ(正の整数でなければなりません)とともに新しい フックオブジェクトを作成します。

Function: hook? obj

objがフックオブジェクトであれば、真を返します。

Function: hook-empty? hook

hookの手続きリストが空なら、真を返します。

Method: add-hook! (hook <hook>) proc :optional (append? #f)

手続きprochookに追加します。 append?が与えられ真であれば、procはリストの最後に 追加されます。そうでなければ、procはリストの最初に追加 されます。 procは、make-hook時に与えられたアリティとともに 呼ばれなければなりません。

Method: delete-hook! (hook <hook>) proc
Method: remove-hook! (hook <hook>) proc

hookの手続きリストからprocを削除します。 Guileとの互換性のためだけに、remove-hook!delete-hook!の エイリアスとなっています。

Method: reset-hook! (hook <hook>)

hookの手続きリストを空にします。

Method: hook->list (hook <hook>)

hookの手続きリストのコピーを返します。

Method: run-hook (hook <hook>) arg …

hookの手続きを、arg …を引数として順番に呼び出します。 引数の数は、make-hook時に与えられたアリティにマッチしなければ なりません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.12 gauche.interactive - インタラクティブセッション

Module: gauche.interactive

インタラクティブセッションで便利なユーティリティ手続きを提供します。

goshをインタラクティブモードで起動した場合、 このモジュールは自動的にロードされます。

また、このモジュールはgauche.reload (gauche.reload - モジュールの再ロード参照)で定義される 手続きに対してautoloadを設定し、それらの手続きが インタラクティブな開発時にデフォルトで使えるようにします。

Macro: apropos pattern :optional module

名前がpatternにマッチするような定義された変数のリストを表示します。 moduleにモジュールオブジェクトまたはモジュール名を与えた場合は、 そのモジュール内で定義されている変数のみが表示されます。moduleが 省略された場合は、カレントモジュールから「見える」変数が全て表示されます。

patternはシンボルか正規表現オブジェクトでなければなりません。 シンボルの場合、そのシンボル名を部分文字列として名前に含むような変数が リストされます。正規表現オブジェクトの場合は、その正規表現にマッチする 名前を持つ変数がリストされます。

いくつか例を示します。

 
;; "string"を名前に含む変数を表示
(apropos 'string)

;; srfi-14モジュールの中のみを検索
(apropos 'char 'srfi-14)
Generic Function: describe :optional obj
Generic Function: d :optional obj

Schemeオブジェクトobjの詳細情報を表示します。 既定メソッドはobjのクラスを表示し、さらにobjがスロットを 持っていればその名前と内容をリストします。 このメソッドをスペシャライズすることによりクラス毎に表示をカスタマイズできます。 いくつかの組み込み型は既にメソッドを持っています (下の例で整数がどのように表示されるか見てください)。

objが省略された場合は、*1に束縛されている、直前のREPLの結果が 使われます。(REPLでの開発参照)

 
gosh> (sys-stat "Makefile")
#<<sys-stat> 0x1e7de60>
gosh> (d)
#<<sys-stat> 0x1e7de60> is an instance of class <sys-stat>
slots:
  type      : regular
  perm      : 436
  mode      : 33204
  ino       : 3242280
  dev       : 2097
  rdev      : 0
  nlink     : 1
  uid       : 500
  gid       : 500
  size      : 19894
  atime     : 1435379061
  mtime     : 1432954340
  ctime     : 1432954340
gosh> (d 1432954340)
1432954340 is an instance of class <integer>
  (#x556925e4, ~ 1.4Gi, 2015-05-30T02:52:20Z as unix-time)
Function: info symbol

Gaucheのinfoドキュメント中から、 symbolで指定される手続きか構文要素の定義を含んでいるページを表示します。 infoドキュメントは、もし環境変数INFOPATHが定義されていればそこに 示されるディレクトリ中から探され、そうでなければgoshのライブラリディレクトリ から推測されるディレクトリ中から探されます。infoドキュメントが見付からなかったり、 見付かってもsymbolがIndexページ中に無かった場合はエラーとなります。 つまり、この手続きはinfoファイルがインストールされていないと動作しません。

現在の出力ポートが端末である場合、infoドキュメントの該当ページは ページングプログラムを用いて表示されます。環境変数PAGERが指定されていれば それを用い、そうでなければコマンドサーチパスからless及びmoreを この順で探します。いずれも見付からなかった場合や、出力ポートが端末では ない場合には、単にページがそのまま出力されます。

この手続きのセッション中での最初の呼び出しは、infoファイルをパーズするために 多少時間がかかります。

註: lessをページャに使う場合、lessがutf-8文字を扱えるように 設定してください (例えばLESSCHARSET環境変数をUTF-8にする)。 そうでないと画面にエスケープされた文字が表示されて見づらいです。

Function: ed filename-or-procedure :key editor load-after

外部エディタを起動し、与えられた名前のファイル、もしくは与えられた手続きの定義がある ファイルを開きます。後者については、与えられた手続きを source-locationに渡して定義箇所を調べます(デバッグ補助参照)。 定義箇所がわからなければファイルは開かれません。

起動するエディタの名前は次の順序で決定されます。

  1. キーワード引数editor
  2. userモジュール中の変数*editor*の値。 これは、.gaucherc内で設定しておくのが便利です。
  3. 環境変数GAUCHE_EDITORの値
  4. 環境変数EDITORの値

上記のどの値も定義されていないか#fなら、 手続きedはユーザにエディタ名を入力するように促します。

エディタの名前がわかったら、それが子プロセスとして次の形式で実行されます。

 
EDITOR +lineno filename

linenoは整数の行番号で、1が先頭行です。エディタは指定された行の頭に カーソルを移動することが期待されます。

エディタプロセスが終了したら、edはファイルが更新されたかどうかを調べ、 更新された場合はload-after引数の値によってそのファイルをロードするかどうかを 決定します。load-after引数の値は以下のいずれかです。

#t

ファイルが更新されていれば自動的にロード

#f

ファイルをロードしない

ask

シンボルaskの場合は、ロードすべきかどうかユーザに尋ねる。 これがデフォルトの動作です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.13 gauche.lazy - 遅延シーケンスユーティリティ

Module: gauche.lazy

This module provides utility procedures that yields lazy sequences. For the details of lazy sequences, see 遅延シーケンス.

Since lazy sequences are forced implicitly and indistinguishable from ordinary lists, we don’t need a separate set of procedures for taking lists and lazy sequences; we can use find to search in both ordinary lists and lazy sequences.

However, we do need a separate set of procedures for returning either lists or lazy sequences. For example, lmap can take any kind of sequences, and returns lazy sequence (and calls the procedure on demand).

This distinction is subtle, so I reiterate it. You can use both map and lmap on lazy sequences. If you want the result list at once, use map; it doesn’t have overhead of delayed calculation. If you don’t know you’ll use the entire result, or you know the result will get very large list and don’t want to waste space for an intermediate list, you want to use lmap.

Function: x->lseq obj

A convenience function to coerce obj to (possibly lazy) list. If obj is a list, it is returned as it is. If obj is other type of collection, the return value is a lazy sequence that iterates over the collection.

If you try x->lseq in REPL, it looks as if it just converts the input collection to a list.

 
(x->lseq '#(a b c)) ⇒ (a b c)

But that’s because the lazy sequence is forced by the output routine of the REPL.

Function: lunfold p f g seed :optional tail-gen

A lazy version of unfold (see section SRFI-1 リスト操作関数). The arguments p, f and g are procedures, each of which take one argument, the current seed value. The predicate p determines when to stop, f creates each element, and g generates the next seed value. The seed argument gives the initial seed value. If tail-gen is given, it should also be a procedure that takes one argument, the last seed value (that is, the seed value (p seed) returned #f). It must return a (possibly lazy) list, that forms the tail of the resulting sequence.

 
(lunfold ($ = 10 $) ($ * 2 $) ($ + 1 $) 0 (^_ '(end)))
  ⇒ (0 2 4 6 8 10 12 14 16 18 end)
Function: lmap proc seq seq2 …

Returns a lazy sequence consists of values calculated by applying proc to every first element of seq seq2 …, every second element of them, etc., until any of the input is exhausted.

Function: lmap-accum proc seed seq seq2 …

The procedure proc takes one element each from seq seq2 …, plus the current seed value. It must return two values, a result value and the next seed value. The result of lmap-accum is a lazy sequence consists of the first values returned by each invocation of proc.

This is a lazy version of map-accum (see section コレクションに対するマッピング), but lmap-accum does not return the final seed value. We only know the final seed value when we have the result sequence to the end, so it can’t be calculated lazily.

Function: lappend seq …

Returns a lazy sequence consists of elements in seq ….

Function: lconcatenate seqs

The seqs argument is a sequence of sequences. Returns a lazy sequence that is a concatenation of all the sequences in seqs.

This differs from (apply lappend seqs), for lconcatenate can handle infinite number of lazy seqs.

Function: lappend-map proc seq1 seq …

Lazy version of append-map. This differs not only from (apply lappend (lmap proc seq1 seq …)), which would evaluate the result of lmap to the end before passing it to lappend, but also differ from (lconcatenate (lmap proc seq1 seq …)) in the subtle way.

Remember that Gauche’s lazy sequence evaluates one element ahead? lconcatenate does that to the result of lmap. To see the effect, let’s define a procedure with a debug print:

 
(define (p x) #?=(list x x))

You can see that (apply lappend (lmap ...)) wouldn’t delay any of application of p:

 
gosh> (car (apply lappend (lmap p '(1 2 3))))
(car (apply lappend (lmap p '(1 2 3))))
#?="(standard input)":4:(list x x)
#?-    (1 1)
#?="(standard input)":4:(list x x)
#?-    (2 2)
#?="(standard input)":4:(list x x)
#?-    (3 3)
1

How about lconcatenate?

 
gosh> (car (lconcatenate (lmap p '(1 2 3))))
(car (lconcatenate (lmap p '(1 2 3))))
#?="(standard input)":4:(list x x)
#?-    (1 1)
#?="(standard input)":4:(list x x)
#?-    (2 2)
1

Oops, even though we need only the first element, and the first result of lmap, (1 1), provides the second element, too, p is already applied to the second input.

This is because the intermediate lazy list of the result of lmap is evaluated “one element ahead”. On the other hand, lappend-map doesn’t have this problem.

 
gosh> (car (lappend-map p '(1 2 3)))
(car (lappend-map p '(1 2 3)))
#?="(standard input)":4:(list x x)
#?-    (1 1)
1
Function: linterweave seq …

Returns a lazy seq of the first items from seq …, then their second items, and so on. If the length of shortest sequence of seqs is N, the length of the resulting sequence is (* N number-of-sequences). If all of seqs are infinite, the resulting sequence is also infinite.

 
(linterweave (lrange 0) '(a b c d e) (circular-list '*))
 ⇒ (0 a * 1 b * 2 c * 3 d * 4 e *)
Function: lfilter proc seq

Returns a lazy sequence that consists of non-false values calculated by applying proc on every elements in seq.

Function: lfilter-map proc seq seq2 …

Lazy version of filter-map.

Function: lstate-filter proc seed seq

Lazy version of gstate-filter.

Function: ltake seq n :optional fill? padding
Function: ltake-while pred seq

Lazy versions of take* and take-while (see section リストへのアクセスと変更). Note that ltake works rather like take* than take, that is, it won’t complain if the input sequence has less than n elements. Because of the lazy nature of ltake, it can’t know whether input is too short or not before returning the sequence.

There are no ldrop and ldrop-while; if you apply drop and drop-while on lazy sequence, they return lazy sequence.

Function: lrxmatch rx seq

This is a lazy sequence version of grxmatch (see section ジェネレータの操作).

The seq argument must be a sequence of characters. The return value is a lazy sequence of <rxmatch> objects, each representing strings matching to the regular expression rx.

This procedure is convenient to scan character sequences from lazy character sequences, but it may be slow if you’re looking for rarely matching string from very large input. Unless seq is a string, lrxmatch buffers certain

Function: lslices seq k :optional fill? padding

Lazy version of slices (see section リストへのアクセスと変更).

 
(lslices '(a b c d e f) 2)
  ⇒ ((a b) (c d) (e f))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.14 gauche.listener - リスナー

Module: gauche.listener

このモジュールは、 複数のread-eval-printループ(repl)を並行して動作させるのに便利な機能を提供します。

複数のreplを実現する自明な方法は、セッション毎にスレッドを生成して 各スレッド上でread-eval-print-loop (eval と repl参照)を 呼ぶことですが、シングルスレッドでの実装が好ましい場合もあります。 例えばアプリケーションがMT-safeでないライブラリに大きく依存していたり、 既にアプリケーションがselectやpollをベースにしたディスパッチメカニズムを 持っているような場合です。

シングルスレッドのselectionベースのアプリケーションでreplを実装するには、 通常、listenしているポートにデータが到着した時に呼ばれるハンドラを登録します。 ハンドラはポートからデータを読み、内部バッファに追加します。 そして内部バッファをスキャンし、データが完全な式を構成した場合は それをS式として読みだし、評価して結果をポートに流します。 このモジュールの<listener>クラスはこのハンドラのメカニズムを提供するので、 アプリケーション側はそれを自分のディスパッチメカニズムに登録するだけで済みます。

註:場合によっては、出力もバッファリングする必要があるかもしれませんが、 現在はそれは実装されていません。

Listener API

Class: <listener>

Replセッションの状態を維持するオブジェクトです。 オブジェクトのふるまいをカスタマイズするためにいくつもの外部スロットがあります。 これらのスロットの値は、スロットの名前と同名のキーワード引数を用いて オブジェクトの構築時に指定することもできますし、オブジェクト構築後に slot-set!で設定することもできますが、listener-read-handler を呼ぶ前に確定していなければなりません。

Instance Variable of <listener>: input-port

リスナーが入力を受けとる入力ポートを指定します。デフォルト値は、 オブジェクトが構築された時のカレント入力ポートです。

Instance Variable of <listener>: output-port

リスナーが出力をおこなう出力ポートを指定します。デフォルト値は、 オブジェクトが構築された時のカレント出力ポートです。

Instance Variable of <listener>: error-port

リスナーのエラーメッセージを出力するポートを指定します。デフォルト値は、 オブジェクトが構築された時のカレントエラー出力ポートです。

Instance Variable of <listener>: reader

引数を取らない手続きです。呼び出し時のカレント入力ポートからScheme式を 読み込まなければなりません。デフォルト値はシステムのread手続きです。

Instance Variable of <listener>: evaluator

Scheme式と環境指定子のふたつの引数をとる手続きです。 式を与えられた環境で評価し、0個以上の値を返さなければなりません。 デフォルト値はシステムのeval手続きです。

Instance Variable of <listener>: printer

0以上の引数をとり、カレント出力ポートに印字する手続きです。 デフォルト値は、各値をwriteで印字したのち、改行する手続きです。

Instance Variable of <listener>: prompter

引数をとらない手続きです。カレント出力ポートにプロンプトを 印字しなければなりません。リスナーは、この手続きが面倒を見なくてよいように、 出力をフラッシュします。 デフォルトの手続きは "listener> " を印字します。

Instance Variable of <listener>: environment

式を評価する環境指定子です。デフォルト値は(interaction-environment)が 返す値です。

Instance Variable of <listener>: finalizer

input-portからEOFが読み込まれたときに呼び出されるサンクです。 finalizer実行中は、現在の入力、出力、エラー出力ポートは、 listener-read-handlerが呼ばれたときのものに戻っています。

このような手続きが必要ない場合は#fにしておいて構いません。 デフォルトの値は#fです。

Instance Variable of <listener>: error-handler

エラー例外をひとつ引数としてとる手続きです。read-eval-printの最中に エラーが発生すると、エラーシグナルが発生すのと同じ動的環境で、 呼び出されます。 デフォルト値はエラー例外をreport-errorを使って印字する手続きです。

Instance Variable of <listener>: fatal-handler

エラー例外をひとつの引数としてとる手続です。 fatalエラー(詳しい定義については後述)が発生したときに呼び出され ます。このハンドラが呼び出された場合、リスナーセッションは安全に続ける ことはできないと思ってください。クライアントへのメッセージを書くこと もできません。このハンドラはこういう状況でログを残したりリスナーの クリーンアップするためのものです。fatal-handlerを実行中は現在の 入力、出力、エラー出力はlistener-read-handlerが呼ばれたときのも のに戻ります。

fatal-handler#fを返す場合、finalizerは後で呼ばれ ます。これを利用してfinalizerで共通のクリーンアップを実行するよ うな実装ができます。fatal-handlerが真の値を返した場合には、 finalizerは呼ばれません。

Method: listener-read-handler (listener <listener>)

リスナーのinput-portから読み込んだデータが正しい時に 呼ばれるサンクを返します。

返されたサンク(readハンドラ)は、以下のように実行されます。 この手続きは最初のプロンプトを印字しないことに 注意してください。これについては後述のlistener-show-promptを 参照してください。

  1. input-portにあるデータを読み、これをリスナーの内部バッファに 連結する。
  2. バッファをスキャンして、それが完結したS式であるかを確かめ、 完結していなければ、リターンする。
  3. バッファからそのS式を読み込み、そのバッファからは そのデータを除去する。
  4. そのS式を評価して、結果をoutput-portへ印字する。 output-port.
  5. プロンプタ手続きを使って、プロンプトをoutput-portに印字したのち、 output-portをフラッシュする。
  6. 2から繰り返す。
Method: listener-show-prompt (listener <listener>)

リスナーの出力ポートにプロンプトを表示します。表示にはリスナーの プロンプタ手続きを使います。通常この手続きは最初のプロンプトを 印字するために使います。たとえば、クライアントがリスナーソケットに 接続してきたときです。

Function: complete-sexp? str

もし、strが完結したS式を含んでいれば#tを返します。 このユーティリティ手続きは、他の目的にも有用なので、 他の手続きといっしょにエクスポートしてあります。

この手続きは構文のチェックをするだけで、(不正な文字名を含んだり、 登録されていないSRFI-10のタグを含むなど)誤りのある式を判別するわけでは ないことに注意してください。この手続きは入力が’#<’文字の シーケンスを含むと、エラーを発生させます。

エラー処理

エラー状況によりリスナーのエラー処理法が異ります。

リスナーの例

以下のコード断片は、サーバソケットをオープンし、クライアントが 接続してきたときにSchemeの対話的セッションをオープンするというものです。 (註: このコードはdemo用のコードです。決して、外のネットワークから アクセス可能なマシン上で走らせてはいけません!)

 
(use gauche.net)
(use gauche.selector)
(use gauche.listener)

(define (scheme-server port)
  (let ((selector (make <selector>))
        (server   (make-server-socket 'inet port :reuse-addr? #t))
        (cid      0))

    (define (accept-handler sock flag)
      (let* ((client (socket-accept server))
             (id     cid)
             (input  (socket-input-port client :buffering :none))
             (output (socket-output-port client))
             (finalize (lambda ()
                         (selector-delete! selector input #f #f)
                         (socket-close client)
                         (format #t "client #~a disconnected\n" id)))
             (listener (make <listener>
                         :input-port input
                         :output-port output
                         :error-port output
                         :prompter (lambda () (format #t "client[~a]> " id))
                         :finalizer finalize))
             (handler (listener-read-handler listener))
             )
        (format #t "client #~a from ~a\n" cid (socket-address client))
        (inc! cid)
        (listener-show-prompt listener)
        (selector-add! selector input (lambda _ (handler)) '(r))))

    (selector-add! selector
                   (socket-fd server)
                   accept-handler
                   '(r))
    (format #t "scheme server started on port ~s\n" port)
    (do () (#f) (selector-select selector))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.15 gauche.logger - ユーザレベルのロギング

Module: gauche.logger

プログラムからログを書き出す簡単なインタフェースを提供します。 情報は指定されたファイルへ書き出されるか、もしくはsyslog(3)を 使ってシステムログへと送られます。 ファイルに書き出される場合は、 syslogに似たプレフィクスが各メッセージの前に追加されます。 プレフィクスはカスタマイズすることもできます。 また、ログファイルの排他制御も行われます (下のlock-policyの説明を 参照して下さい)。

Class: <log-drain>

ログメッセージの行き先を表現するオブジェクトです。 デフォルトのログの行き先として、グローバルな<log-drain>のインスタンスが ひとつ作られます。 ログをいくつかにわけて出力する場合などはmakeメソッドを使って いくつでも<log-drain>のインスタンスを作ることができます。

Instance Variable of <log-drain>: path

ログファイルのパス名か、#t#f、あるいはシンボルsyslog の値を取ります。このスロットが#tの場合、ログメッセージは現在のエラーポートに 書き出されます。#fの場合はlog-formatは ログを書き出すかわりにフォーマットされたログメッセージを文字列として返します。 シンボルsyslogの場合はメッセージがシステムログへと送られます。

このスロットの初期値は#fです。

Instance Variable of <log-drain>: prefix

各メッセージの前に追加されるプレフィクス文字列を指定します。 メッセージが複数行に渡る場合、プレフィクスは各行の頭に追加されます。 またこのスロットには手続きをセットすることもできます。 その場合、プレフィクス文字列が必要とされる度にその手続きが<log-drain> を引数として呼び出され、戻り値の文字列がプレフィクスとして使われます。

pathスロットの値がシンボルsyslogの場合は、 このスロットの値は無視されます。システムのログ機構が適切なプレフィクスを付けるからです。

prefixスロットの値が文字列の場合、以下に挙げる文字シーケンスは特別な意味を持ち、 log-formatによって置換されます。

~T

現在の時刻を"Mmm DD hh:mm:ss"という形式で表したもの。 ここで"Mmm" は英語の月名の省略形、"DD" は日、"hh"、"mm"、 "ss" は時間(24時間制)、分、秒です。 このフォーマットはシステムログと同じです。

~Y

4桁で表した現在の西暦。

~P

プログラム名。初期値は(car (command-line))の値のbasenameが使われます (コマンドライン引数参照)。 下に述べるprogram-nameスロットの値を変えれば ここに現れる文字列を変えることができます。

~$

このプログラムのプロセスID。

~U

このプロセスの実行ユーザ名。

~H

このプロセスが走っているホスト名。

このスロットの初期値は"~T ~P[~$]: "です。例えば "this is a log message.\nline 2\nline 3"というメッセージが 与えられると、次のようなログが書き出されます。

 
Sep  1 17:30:23 myprogram[441]: this is a log message
Sep  1 17:30:23 myprogram[441]: line 2
Sep  1 17:30:23 myprogram[441]: line 3
Instance Variable of <log-drain>: program-name

プレフィクス文字列の~Pが置換されるプログラム名を指定します。

Instance Variable of <log-drain>: lock-policy

ログファイルのロックの方法を指定します。 このスロットの値がシンボルfcntlだった場合は、 fcntl()によるファイルロックが使われます (gauche.fcntl - 低レベルファイル操作参照)。 このスロットの値がシンボルfileだった場合は、 ログファイルのパス名に ".lock" を追加したロックファイルを作成することによる 排他制御が行われます。この場合、ログを行うプロセスはログファイルの 置かれるディレクトリに書き込み権限が必要です。 また、プロセスがログファイル書き込み中に強制的に終了させられた場合、 ロックファイルが残ってしまう恐れがあります。 Log-formatはロックファイルが十分に古い場合(現在は10分をリミットとしています) ロックファイルを自動的に削除します。 このスロットの値が#fだった場合は、排他制御は行われません。

デフォルトの値はfcntlです。但しMacOSXではfcntlスタイルのロックが サポートされていないため、fileがデフォルト値となります。

ログの行き先がファイルでない場合は排他制御は行われません。

Instance Variable of <log-drain>: syslog-option
Instance Variable of <log-drain>: syslog-facility
Instance Variable of <log-drain>: syslog-priority

これらのスロットの値は、ログの行き先がシステムログの場合にのみ使われます。 これらの値に関する説明はgauche.syslog - Syslogを参照して下さい。 デフォルトの値はそれぞれLOG_PIDLOG_USERLOG_INFOです。

Function: log-open path :key prefix program-name

デフォルトのログの行き先をpathに指定します。 pathは文字列かboolean値あるいはシンボルsyslogで、 上のpathスロットで述べたものと おなじ意味を持ちます。またプレフィクスとプログラム名をキーワード引数で 指定することもできます。

名前に"open"とありますが、この手続きは指定されたファイルをオープンしません。 ファイルはlog-formatが呼ばれるたびにオープンされクローズされます。

Parameter: log-default-drain

引数無しで呼ばれると、log-formatが使う、 現在のデフォルトのログの行き先が返されます。 まだデフォルトのログの行き先がlog-openで指定されていない場合は #fが返ります。

新たな<log-drain>オブジェクトか#fを引数にして呼び出すと、 デフォルトのログの行き先がそれに変更されます。 parameterize (gauche.parameter - パラメータ) を使えば、一時的にデフォルトの ログの行き先を変更できます。

Method: log-format (format <string>) arg …
Method: log-format (drain <log-drain>) (format <string>) arg …

ログメッセージをformat手続きでフォーマットし(出力参照)、 指定された行き先に書き出します。最初の形式ではデフォルトの行き先が使われます。

ファイルはこの手続きが呼ばれるたびにオープンされクローズされます。 したがって、ログファイルに書き出すプログラムが走っている最中でも ログファイルをmoveすることができます。 また、log-formatsys-fcntl (gauche.fcntl - 低レベルファイル操作参照) を使ってファイルロックを獲得します。

log-openが呼ばれる前にlog-formatが最初の形式で呼ばれた場合、 log-formatは何もしません。したがって、log-formatをデバッグに 活用し、必要なくなったらlog-openだけをコメントアウトする、というような 使用法も可能です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.16 gauche.mop.propagate - スロットアクセスの伝播

Module: gauche.mop.propagate

Provides a metaclass to add :propagated slot allocation option.

When a slot allocation has :propagated, access to the slot is redirected to other object’s slot. It is handy for composite objects to keep external interface simple, for access to the slot of inner objects can be disguised as if it is a slot of the parent object.

An example would work better than explanation. Suppose you have a <rect> class to represent generic rectangular area, and you want to use it when you create a <viewport> class by composition, instead of inheritance. A simple way would be as follows:

 
(define-class <rect> ()
  ((width  :init-keyword :width)
   (height :init-keyword :height)))

(define-class <viewport> ()
  ((dimension :init-form (make <rect>))
   ;;   ... other slots ...
   ))

With this definition, whenever you want to access the viewport’s width or height, you have to go through <rect> object, e.g. (~ viewport'dimension'width). This is not only cumbersome, but the users of viewport class have to know that how the viewport is composed (it’s not necessarily a bad thing, but sometimes you may want to hide it).

Using gauche.mop.propagate, you can define slots width and height in <viewport> class that are proxies of <rect>’s slots.

 
(use gauche.mop.propagate)

(define-class <rect> ()
  ((width  :init-keyword :width)
   (height :init-keyword :height)))

(define-class <viewport> (<propagate-mixin>)
  ((dimension :init-form (make <rect>))
   (width     :allocation :propagated :propagate 'dimension
              :init-keyword :width)
   (height    :allocation :propagated :propagate 'dimension
              :init-keyword :height)))

With :propagated allocation, the slots are not actually allocated in <viewport> instance, and accesses to the slots are redirected to the object in the slot specified by :propagate slot option—in this case, the dimension slot. It is somewhat similar to the virtual slots, but it’s more convenient for you don’t explicitly write procedures to redirect the access.

Now you can treat width and height as if they are slots of <viewport>. You can even make them initialize via init-keyword (but you can’t use :init-form or :init-value; if you want to specify default values, give the default values to the actual object).

 
gosh> (define vp (make <viewport> :width 640 :height 480))
vp
gosh> (d vp)
#<<viewport> 0xc5a1e0> is an instance of class <viewport>
slots:
  dimension : #<<rect> 0xc5a130>
  width     : 640
  height    : 480
gosh> (set! (~ vp'width) 800)
#<undef>
gosh> (~ vp'width)
800

Here’s two classes that enables this feature. Usually all you have to do is to inherit <propagate-mixin> class.

Class: <propagate-meta>

Adds :propagated slot allocation. The propagated slot has to have :propagate slot option which specifies the name of the slot that points to an object that actually holds the value of the slot. If a slot has :propagated slot allocation but does not have :propagate slot option, an error is signaled.

The :propagate slot option should have a value of either a symbol, or a list of two symbols.

If it is a symbol, it names the slot that contains an object, whose slot with the same name of the propagate slot holds the value.

If it is a list of two symbols as (X Y), then the access to this propagated slot actually works as (slot-ref (slot-ref obj X) Y).

If you want to make a propagated slot initializable by init-keywords, make sure the slot holding the actual object comes before the propagated slots. Slot initialization proceeds in the order of appearance by default, and you want the actual object is created before setting values.

Class: <propagate-mixin>

This is a convenience mixin class. Instead of giving :metaclass <propagate-meta>, you can just inherit this calss to make propagated slots available.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.17 gauche.mop.singleton - シングルトン

Module: gauche.mop.singleton

シングルトンクラスを定義するためのメタクラスを提供します。

Class: <singleton-meta>

シングルトンクラスを生成します。シングルトンクラスはそのインスタンスが ただ一つしかないことを保証するクラスです。 make の 最初の呼び出しで、唯一のインスタンスを生成し、その後の呼出しでは 最初に生成したものと同一のインスタンスを返します。

 
(define-class single () () :metaclass <singleton-meta>)

(define a (make single))
(define b (make single))

(eq? a b) ⇒ #t

インスタンスのスロットは最初の make の呼び出し時に 初期化されます。makeへの初期引数は最初の呼び出しのときに だけ効果があり、それ以降の呼び出しでは無視されます。

初期化ルーチンの呼び出しはスレッドセーフです。

Method: instance-of (class <singleton-meta>) :rest initargs

このメソッドは渡された引数とともに make を呼んでいるだけです。 シングルトンを扱うプログラム中ではこちらの方が分りやすいでしょう。

Class: <singleton-mixin>

<singleton-meta>のインスタンスです。<singleton-meta>define-class:metaclass 引数として指定せずに、 このクラスを継承することで、定義しようとするクラスにシングルトンの 性質を与えることができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.18 gauche.mop.validator - Validator付きスロット

Module: gauche.mop.validator

:validatorおよび:observer スロットオプションを追加するメタクラスを提供するモジュールです。

Class: <validator-meta>

このメタクラスは、スロットの値がセットされる前後に呼ばれるコールバックを 指定する機能を提供します。例えば、あるスロットに常に文字列のみが セットされているようにしたい場合、そのスロットに、slot-ref経由であれ アクセサメソッド経由であれ値がセットされようとした時に指定の手続きが呼ばれるように し、その手続きの中で、値が文字列でなかったらエラーとするか、強制的に値を 文字列に変換してしまうことができます。その手続きが返した値が実際にスロットに セットされます。

スロットに値がセットされる前に呼ばれるvalidator手続きは、 :validatorスロットオプションによって指定できます。 手続きは、インスタンスとセットされようとしている値の二つを引数に取ります。 その手続きが返した値が実際にスロットにセットされる値となります。

スロットに値がセットされた後に呼ばれるobserver手続きは :observerスロットオプションによって指定できます。 手続きは、インスタンスとセットされた値の二つを引数に取ります。 戻り値は捨てられます。

下の例を参照して下さい。

 
(define-class <v> ()
  ((a :accessor a-of
      :validator (lambda (obj value) (x->string value)))
   (b :accessor b-of
      :validator (lambda (obj value)
                   (if (integer? value)
                       value
                       (error "integer required for slot b")))))
  :metaclass <validator-meta>)

(define v (make <v>))
(slot-set! v 'a 'foo)
(slot-ref v 'a) ⇒ "foo"

(set! (a-of v) 1234)
(a-of v) ⇒ "1234"

(slot-set! v 'b 55)
(slot-ref v 'b) ⇒ 55

(slot-set! v 'b 3.4) ⇒ error
(set! (b-of v) 3.4)  ⇒ error

:validator が指定されたスロットにも、:init-value等を 使って既定の初期値を指定しておくことができます。 その場合、インスタンスの初期化ルーチンは、 init-keywordによる初期値指定が無ければ既定の初期値を引数としてvalidator手続きを 呼び出します。

 
(define-class <v> ()
  ((a :initform 'foo :init-keyword :a
      :validator (lambda (obj value) (x->string value)))))

(slot-ref (make <v>) 'a)        ⇒ "foo"
(slot-ref (make <v> :a 555) 'a) ⇒ "555"

仮想スロットに似ていますが、validatorを持つスロットはインスタンス内に実際に 値を格納している点が異なります。

このモジュールはまた、スロットのアクセスをメタオブジェクトプロトコルを使って カスタマイズする例でもあります。この機能は20行ほどのコードで実現されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.19 gauche.net - ネットワーキング

Module: gauche.net

BSDソケットに基づいたネットワーキングに必要な手続きを提供するモジュールです。

2つのレベルのAPIが定義されています。低レベルの手続きはbind(2)のような BSDソケットインタフェースに近いインタフェースを提供し、高レベルの手続きは 典型的なコネクション指向のクライアントサーバアプリケーションに便利な インタフェースを提供します。

また、ホストネームやプロトコルに関する情報にアクセスするAPIも定義されます。

Gaucheは、--enable-ipv6コンフィギュアオプションつきで コンパイルされていれば、IPv6を扱うことができます。 IPv6が使えるかどうかを調べるには、 cond-expand中でgauche.net.ipv6 feature identifierを 使うことができます。次の例を見てください。

 
(use gauche.net)
(cond-expand
  (gauche.net.ipv6
    ... ipv6を使うコード ...)
  (else
    ... ipv4のみのコード ...))

cond-expandの詳細については機能条件式を 参照してください。

ネットワークを使うポータブルなコードを書きたい場合は、 srfi-106を見てください (srfi-106 - 基本的なソケットインタフェース参照)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.19.1 ソケットアドレス

ソケットアドレスオブジェクト

Builtin Class: <sockaddr>

ソケットアドレスの抽象ベースクラスです。 ソケットアドレスファミリはこのクラスのサブクラスとして実装されます。

ソケットアドレスはビルトインクラスですが、makeメソッドで 特定のソケットアドレスファミリのインスタンスを作成することができます。

Generic Function: sockaddr-family addr

ソケットアドレスaddrのファミリを表すシンボルを返します。

Generic Function: sockaddr-name addr

ソケットアドレスaddrの名前を表す文字列を返します。

Builtin Class: <sockaddr-in>

AF_INETファミリのソケットアドレスです。このクラスのインスタンスを 作成するには、次のようにしてmakeメソッドを呼びます。

 
(make <sockaddr-in> :host host :port port)

hostは文字列、整数のIPアドレス、u8vectorのIPアドレス、 もしくは:any:broadcast:none:loopbackの いずれかのキーワードでなければなりません。 文字列の場合、それはホスト名かIPアドレスのドット表記です。 Gaucheはgethostbyname(3)を使って実際のIPアドレスを得ます。 この値がキーワード:any:broadcastなら、それぞれ INADDR_ANYINADDR_BROADCASTがアドレスとして使われます。 キーワード:loopbackはIPv4のループバックアドレス"127.0.0.1"を表します。

portはポート番号を示す正の整数です。 2つ以上のプロトコルスタックを持つようなマシン上で複数のソケットアドレスを 生成するには、後述の make-sockaddrs も参照してください。

Method: sockaddr-family (addr <sockaddr-in>)

シンボルinetを返します。

Method: sockaddr-name (addr <sockaddr-in>)

文字列"a.b.c.d:port"を返します。 コロンの前はIPアドレスのドット表記で、portはポート番号です。

Method: sockaddr-addr (addr <sockaddr-in>)
Method: sockaddr-port (addr <sockaddr-in>)

それぞれ、IPアドレスとポート番号を整数で返します。

Builtin Class: <sockaddr-un>

AF_UNIXファミリのソケットアドレスです。このクラスのインスタンスを作成するには、 makeメソッドを次のように呼んで下さい。

 
(make <sockaddr-un> :path path)

pathはソケットのパス名を表す文字列です。

Method: sockaddr-family (addr <sockaddr-un>)

シンボルunixを返します。

Method: sockaddr-name (addr <sockaddr-un>)

ソケットアドレスのパス名を返します。

Builtin Class: <sockaddr-in6>

AF_INET6ファミリのソケットアドレスです。 このクラスはgaucheが–enable-ipv6つきでconfigureされている場合に使えます。 コンストラクタとスロットは<sockaddr-in>と同じです。 2つ以上のプロトコルスタックを持つようなマシン上で複数のソケットアドレスを 生成するには、後述の make-sockaddrs も参照してください。

Function: make-sockaddrs host port :optional proto

これは上位レベルのユーティリティ手続きで、プロトコル protohost:port を指す、可能なすべてのドメインソケット アドレスを生成します。特に、指定したホストが IPv4 および IPv6 の両方の アドレスを持ち、稼働中のシステムがその両方をサポートしている場合、IPv4 と IPv6 の両方のソケットアドレスが返ります。もし、host が複数の IP アドレスをもつ場合、ソケットアドレスはそれぞれのIPアドレス毎に生成 されます。自分のネットワークアプリケーションを異るネットワークスタックの 設定の間でこれまでよりはるかにポータブルなものにすることができます。

host#f を渡すと、ローカルな(サーバ)アドレスが生成されます。 port 引数に整数のかわりに、サービス名(たとえば、"http")を わたすこともできます。proto の値は、tcp あるいは udp のどちらかのシンボルになります。デフォルトでは、tcp です。

この手続きは常にソケットアドレスのリストを返します。もし、host の 検索に失敗した場合には、空リストが返ります。

アドレスと文字列との変換

Function: inet-string->address address

文字列で表現されたインターネットアドレスaddressを整数のアドレス に変換します。addressのパーズが成功した場合には値を2つ返します。 ひとつは整数で表現されたアドレスの値、もうひとつは認識されたプロトコル (定数値で、2 (= AF_INET)ならIPv4アドレス、10 (= AF_INET6)ならIPv6アドレス)です。addressのパーズに失敗した ら、#f#fとの2つが返ります。

 
(inet-string->address "192.168.1.1")
 ⇒ 3232235777 and 2
(inet-string->address "::1")
 ⇒ 1 and 10
(inet-string->address "::192.168.1.1")
 ⇒ 3232235777 and 10
(inet-string->address "ffe0::1")
 ⇒ 340116213421465348979261631549233168385 and 10
(inet-string->address "::192.168.1.1")
 ⇒ 3232235777 and 10
Function: inet-string->address! address buf

inet-string->addressと同じですが、整数値を返すかわりに、与えら れたu8vectorであるbufをパーズしたアドレスで埋めるところが違いま す。inetアドレスの整数表現はbignumと同じやりかたです。この関数を使えば bignumを生成しなくてすみます。与えられたu8vectorbufは変更可能で なければなりません。変換に成功した場合はプロトコルが、失敗した場合は #fが返ります。

呼び出し側は格納に十分なバッファを用意しなければなりません。buf が必要な分より大きい場合には結果は先頭から詰められ、ベクタの他の部分は そのままになります。

 
(let* ((buf (make-u8vector 16 0))
       (proto (inet-string->address! "192.168.1.1" buf)))
  (list proto buf))
 ⇒ (2 #u8(192 168 1 1 0 0 0 0 0 0 0 0 0 0 0 0))
Function: inet-address->string address protocol

与えれれたaddressをプロトコルprotocolの文字列表現に変換し ます。プロトコルとしては2 (定数AF_INET)または10 (定数AF_INET6)が指定可能です。addressとしては整数または u8vectorが使えます。u8vectorをつかった場合には必要な部分だけが読み込ま れます。したがって、必要な長さ以上あるベクタでもかまいません。

 
(inet-address->string 3232235777 AF_INET)
  ⇒ "192.168.1.1"

(inet-address->string '#u8(192 168 1 1) AF_INET)
  ⇒ "192.168.1.1"

(inet-address->string 3232235777 AF_INET6)
  ⇒ "::c0a8:101"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.19.2 高レベルネットワーク手続き

Builtin Class: <socket>

通信の終端であるところのソケットを表すクラスです。

コネクション型のソケットには、入力用と出力用の2つのポートが結び付いており、 それらを使って通信路にアクセスできます。 socket-input-portsocket-output-portはそれぞれ 入力用、出力用のポートを返します。

以下の3つは手軽にコネクション型のソケットを作成する ための手続きです。 大抵の場合はこれらの手続きで間に合いますが、 より細かい制御が必要な場合は低レベルAPIを使用して下さい。

Function: make-client-socket :optional address-spec …

引数address-spec …によって指定されるアドレスと接続する クライアントソケットを作成して返します。

(make-client-socket 'unix path)

pathで待っているUnixドメインのサーバーソケットに接続します。

(make-client-socket 'inet host port)

ホストhostのポートportにTCPで接続します。 hostはIPv4アドレスのドット表記でもホスト名でも 構いません。Gaucheが–enable-ipv6でコンパイルされていれば、 IPv6形式のアドレス表記も受け付けます。 portはポート番号を指定する正確な整数か、文字列のサービス名 ("http"等)でなければなりません。

Gaucheが–enable-ipv6でコンパイルされており、ホスト名が渡されて、 そのホスト名がIPv6とIPv4の両方のアドレスを持っていた場合は、 最初にIPv6での接続が試みられ、それが失敗した場合にIPv4での接続が試みられます。

(make-client-socket host port)

上と同じです。この形式はSTkとの互換性のために提供されています。

(make-client-socket sockaddr)

<sockaddr>クラスのインスタンスが渡された場合には、それに対応する ソケットをオープンし、そのアドレスへ接続します。

ソケットを作成できなかったり、指定されたアドレスに接続できなかった場合は エラーが報告されます。

 
(make-client-socket 'inet "www.w3.com" 80)
  ⇒ ;a socket connected to www.w3.com, port 80
(make-client-socket "127.0.0.1" 23)
  ⇒ ;a socket connected to localhost, port 23
(make-client-socket 'unix "/tmp/.sock"
  ⇒ ;a socket connected to a unix domain socket "/tmp/.sock"
Function: make-server-socket :optional address-spec …

address-specにて接続を待つサーバソケットを作成して返します。

(make-server-socket 'unix path [:backlog num])

パス名pathを持つUnixドメインソケットが作成されます。 キーワード引数backlogに渡された数値はsocket-listenに渡され、 サーバが接続要求を貯めておくキューの最大長を指定します。 デフォルトは5です。多忙なサーバーで、"connection refused"が頻発する場合は この数値を増やしてみて下さい。

(make-server-socket 'inet port [:reuse-addr? flag] [:sock-init proc] [:backlog num])

ポートportにて接続を待つInetドメインのTCPソケットが作成されます。 portは非負の正確な整数か、文字列のサービス名("http"等)でなければなりません。 portが零の場合はシステムが適当なポート番号を割り当てます。 キーワード引数reuse-addr?に真の値が与えられた場合は、 ソケットにSO_REUSEADDRオプションがセットされます。 その場合、他のプロセスが解放したばかりの(TCP)ポートでも エラーとならずに使うことができます。

あるいは、正の正確な整数のリストをportに渡すことも出来ます。 その場合、Gaucheは与えられたポート番号で順にbindを試し、成功したらそのソケットを返します。

キーワード引数sock-initが与えられた場合、proc が出来たての ソケットとそのソケットアドレスを引数にして呼び出されます。 つまり、proc はそのような2つの引数を取る手続きでなければなりません。 ソケットに特殊なオプションを設定したいような場合に便利です。

キーワード引数backlogはunixソケットと同じです。上の記述を参照して下さい。

(make-server-socket port [:reuse-addr? flag] [:sock-init proc][:backlog num])

これは、portが整数でなければならないことを除けば、 上の形式と同じ動作をします。STkのmake-server-socketとの 互換性のために提供されています。

(make-server-socket sockaddr [:reuse-addr? flag][:sock-init proc][:backlog num])

この形式は、listenするソケットアドレスを<sockaddr>のインスタンスで 明示的に指定します。

 
(make-server-socket 'inet 8080)
  ⇒ #<socket (listen "0.0.0.0:8080")>
(make-server-socket 8080)
  ⇒ #<socket (listen "0.0.0.0:8080")>
(make-server-socket 'inet 0)
  ⇒ #<socket (listen "0.0.0.0:35628")>
(make-server-socket 'unix "/tmp/.sock")
  ⇒ #<socket (listen "/tmp/.sock")>
Function: make-server-sockets host port :key reuse-addr? sock-init

host が持つすべての利用可能なネットワークインタフェース上の port で接続を待つソケットを生成し、それらのリストを返します。 port には数字のポート番号のほか、"http"などの サービス名も指定できます。

この手続きは、ホストが複数のプロトコルスタック(IPv4とIPv6など)を 持つ場合に特に便利です。その場合、この手続きはIPv4用のソケットと IPv6用のソケットのリストを返すかもしれません。 (OSによっては、一つのソケットでIPv4もIPv6も両方listenできるものが あります。そういったOSでは単一のソケットのリストが返るでしょう。)

キーワード引数の意味はmake-server-socketのと同じです。

make-server-socketと同様、portに0を渡すことで、 システムに空いているポートを自動的にアサインしてもらうことができます。 portに0を渡して複数のソケットが返される場合、それらのソケットは 同じポート番号を持つことが保証されます。

上記の手続きによって返されたソケットオブジェクトに対して、 以下のようなアクセサがあります。

Function: socket-address socket

ソケットsocketのソケットアドレスを返します。 ソケットにアドレスがまだバインドされていない場合は#fが返ります。

Function: socket-input-port socket :key (buffering :modest)
Function: socket-output-port socket :key (buffering :line)

それぞれ、socketからデータを読みだす入力ポート、および socketにデータを書き出す出力ポートを返します。

キーワード引数bufferingはポートのバッファリングモードを 指定します。バッファリングモードの説明はファイルポートにあります。

Function: socket-close socket

ソケットsocketをクローズします。socketの入出力ポートも クローズされます。

注意: リリース 0.7.2 より、この手続きは接続をシャットダウンしないように なりました。その理由は、socket が fork した別プロセスから参照され ている可能性があり、既存の接続を妨害することなくクローズしたい場合がある からです。socket-shutdown を呼べば接続を明示的にシャットダウンできます。

Function: call-with-client-socket socket proc :key input-buffering output-buffering

socketは接続されたクライアントソケットでなければなりません。 procがソケットから読み出す入力ポートと、 ソケットに書き出す出力ポートを引数として呼ばれます。 procが正常終了するか、エラーを投げた場合にソケットはクローズされます。

キーワード引数input-bufferingoutput-bufferingが与えられた場合、 それらはそれぞれsocket-input-portsocket-output-portbufferingキーワード引数へと渡されます。

これは高レベルソケット手続きを使った、非常に単純なhttpクライアントです。

 
#!/usr/bin/env gosh
(use gauche.net)

(define (usage)
  (display "Usage: swget url\n" (current-error-port))
  (exit 1))

;; Returns three values: host, port, and path.
(define (parse-url url)
  (rxmatch-let (rxmatch #/^http:\/\/([-A-Za-z\d.]+)(:(\d+))?(\/.*)?/ url)
      (#f host #f port path)
    (values host port path)))

(define (get url)
  (receive (host port path) (parse-url url)
    (call-with-client-socket
        (make-client-socket 'inet host (string->number (or port "80")))
      (lambda (in out)
        (format out "GET ~a HTTP/1.0\r\n" path)
        (format out "host: ~a\r\n\r\n" host)
        (flush out)
        (copy-port in (current-output-port))))))

(define (main args)
  (if (= (length args) 2)
      (get (cadr args))
      (usage))
  0)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.19.3 低レベルソケットインタフェース

ここにある関数群はシステムコールと類似した API を提供します。 ソケット API プログラミングに慣れていれば、ソケットをより細かく 制御できるので、役に立つでしょう。

Function: make-socket domain type :optional protocol

パラメータで指定したソケットを返します。

Constant: PF_UNIX
Constant: PF_INET
Constant: PF_INET6

これらの定数はそれぞれシステムが提供するPF_UNIXPF_INETPF_INET6に束縛されています。 これらの値をmake-socketdomain引数に使うことができます。

(PF_INET6はオペレーティングシステムがIPv6をサポートしている場合にのみ 定義されます。)

Constant: AF_UNIX
Constant: AF_INET
Constant: AF_INET6

これらの定数はそれぞれ、AF_UNIXAF_INETAF_INET6 に束縛されています。

(AF_INET6はオペレーティングシステムがIPv6をサポートしている場合にのみ 定義されます。)

Constant: SOCK_STREAM
Constant: SOCK_DGRAM
Constant: SOCK_RAW

これらの定数はそれぞれ、SOCK_STREAMSOCK_DGRAMSOCK_RAW に束縛されており、 make-sockettype引数に使うことができます。

Function: socket-fd socket

ソケットのもつ整数のシステムファイルディスクリプタを返します。

Function: socket-status socket

socket の内部状態を、以下のシンボルのどれかで返します。

none生成直後
boundsocket-bind によって、あるアドレスに束縛されている
listeningsocket-listen によって、接続をリッスンしている
connectedsocket-connect あるいは socket-accept によって接続されている
shutdownsocket-shutdown によってシャットダウンされた
closedsocket-close によってクローズされた
Function: socket-bind socket address

socket をローカルネットワークアドレス address に束縛します。 通常は、特定のアドレスをこのサーバのポートに結びつけるのに用います。 もし、束縛が失敗したら(多くの場合、そのアドレスが既に使用されている)、 エラーシグナルが発生します。

INET ドメインアドレスの場合には port=0 とした address を 渡せます。システムがポート番号を割当て、socketaddress スロットに実際のアドレスをセットします。

Function: socket-listen socket backlog

socket をリッスンします。ソケットは既になんらかのアドレスに 束縛されていなければなりません。backlog はキューに入れる 接続要求の最大数を指定します。

Function: socket-accept socket

socketに来た接続要求をアクセプトします。リモートエンティティへ 接続している新しいソケットを返します。元の socket は引き続き 次の接続要求を待ちます。接続要求がないとき、これの呼出しは要求が 一つ来るまで待ちます。

接続要求をペンディングしているかどうかをチェックするのに sys-selectが使えます。

Function: socket-connect socket address

socket をリモートアドレス address に接続します。 これは、クライアントソケットをリモートエンティティに接続するための 方法です。

Function: socket-shutdown socket how

socket の接続をシャットダウンします。howSHUT_RD (0) なら、 socket の受信チャネルが不許可となります。howSHUT_WR (1)なら、 socket の送信チャネルが不許可となります。howSHUT_RDWR (2)なら、 socket の送受信チャネルの両方が不許可となります。 接続していないソケットに対して、この手続きを呼ぶとエラーになります。

ソケットの送信チャネルをシャットダウンすると、リモート側の受信チャネル に EOF があらわれます。これは、リモート側が何かを送り返す前に EOF を受けとることを期待している場合、便利です。

Function: socket-getsockname socket

socketのローカル側アドレスを表す<sockaddr>インスタンスを返します。

Function: socket-getpeername socket

socketの通信相手のアドレスを表す<sockaddr>インスタンスを返します。

Function: socket-send socket msg :optional flags
Function: socket-sendto socket msg to-address :optional flags.

それぞれ、send(2) および sendto(2) へのインタフェース。 msg の内容を socket を通じて送出します。 msgは文字列もしくはユニフォームベクタでなければなりません。 バイナリパケットを送る場合はユニフォームベクタの使用を推奨します。

実際に送出されたオクテット数を返します。

socket-send を使うときには、socket は既に接続されて いなければなりません。他方、socket-sendto は未接続の ソケットに対して使用でき、送出先アドレスは <sockaddr> のインスタンス to-address で指定します。

オプション引数 flags は整数定数 MSG_* のビット毎のORで 指定できます。詳しくはシステムの man ページ send(2) および sendto(2) を見て下さい。

Function: socket-sendmsg socket msghdr :optional flags

sendmsg(3)を使って、msghdrで記述されるパケットをsocket を通じて送ります。msghdr引数は文字列かu8vectorで、 struct msghdr構造体のバイナリ表現でなければなりません。 msghdr引数に適したデータを構築する確かな方法は、下に述べる socket-buildmsgを使うことです。

flags引数はsocket-sendおよびsocket-sendtoと同じです。

送り出されたオクテット数を返します。

この手続きはWindowsネイティブ環境では(まだ)サポートされません。 これが使えるかどうかは、機能識別子gauche.os.windowsを使って判定 できます (プラットフォーム依存の機能参照)。

Function: socket-buildmsg addr iov control flags :optional buf

struct msghdr構造体のバイナリ表現を構築します。 作られたmsghdrはsocket-sendmsgに渡すことができます。 この手続きを理解するには、sendmsg(3)の動作をよく知っていることが必要です。

addr引数は<sockaddr>のインスタンスか#fでなければ なりません。<sockaddr>のインスタンスである場合、そのアドレスが msghdrのmsg_nameフィールドに使われます。

iov引数はベクタか#fでなければなりません。ベクタの場合、 各要素は文字列かu8vectorでなければなりません。これはmsghdrの msg_iovフィールドに使われます。各要素の内容はカーネル内で 結合されてペイロードとなります。

control引数は補助データ(cmsg)を指定します。補助データが必要ない 場合は#fを渡せます。そうでなければ、control引数は 次の形式を持つリストでなければなりません。

 
((level type data) …)

leveltypeは正確な整数で、dataは文字列かu8vectorの いずれかです。 最初の2つはそれぞれcmsgのcmsg_levelcmsg_typeフィールドを指定します。dataはcmsgのデータとなります。 (cmsg_lendataから計算されます)。

flags引数はmsg_flagsフィールドに使われます。

buf引数が省略されるか#fの場合、msghdrを構築するのに 必要なメモリがアロケートされます。書き換え可能なu8vectorがbuf に渡されれば、socket-buildmsgはできるだけそれをバッファに使って msghdrを構築しようとします。bufで領域が不足する場合のみ 新たなメモリをアロケートします。

この手続きは構築されたmsghdrをu8vectorとして返します。

この手続きはWindowsネイティブ環境では(まだ)サポートされません。 これが使えるかどうかは、機能識別子gauche.os.windowsを使って判定 できます (プラットフォーム依存の機能参照)。

Function: socket-recv! socket buf :optional flags

recv(2)へのインタフェースです。socketからメッセージを 受信し、それを変更可能なユニフォームベクタbufへと書き込みます。 そして実際に書き込まれたバイト数を返します。 socketは既にコネクトされていなければなりません。 bufの大きさが受信したメッセージより小さい場合、socketの タイプによっては残りのメッセージは捨てられる可能性があります。

オプション引数 flags は整数定数 MSG_* のビット毎のORで 指定できます。詳しくはシステムの man ページ recv(2)を見て下さい。

Function: socket-recvfrom! socket buf addrs :optional flags

recvfrom(2)へのインタフェースです。socketからメッセージを 受け取り、変更可能なユニフォームベクタへ書き込みます。socketは コネクトされていなくても構いません。socket-recvと同様に、 bufの大きさがメッセージ全てを保持するのに十分でない場合、 socketのタイプによっては残りのメッセージは捨てられます。

ふたつの値を返します。実際にbufに書かれたバイト数と、 送信者のアドレスを示す<sys-sockaddr>のサブクラスのインスタンスです。

addrs引数はソケットアドレスインスタンスのリストでなければなりません。 ただし、リストの終端 (最後のcdr) は#tであっても許されます。 (この特殊な場合として、addrs引数にただ#tを渡すことも許されます)。 addrs引数に渡されたソケットアドレスの中身は何でも構いません。 送信者のアドレスファミリーに一致するソケットアドレスインスタンスがaddrs 中にあった場合、socket-recvfrom!はそのインスタンスに直接 送信者のアドレスを書き込みます。受信し得る全てのアドレスファミリのソケットアドレスを リストで渡しておけば、socket-recvfrom!が(成功時には) メモリアロケーションを行わないことが保証されます。これは 速度が重要となるタイトなループ内でsocket-recvfrom!を呼ぶ場合に 重要です。

送信者のアドレスファミリに一致するソケットアドレスがあたえられなかった場合、 socket-recvfrom!の振舞いはaddrsのリスト終端の値によります。 addrs()で終端されていた場合 (addrsが真性リストで あった場合)、送信者のアドレスは捨てられ、socket-recvfrom!は 二番めの値として#fを返します。addrs#tで終端 されていた場合、socket-recvfrom!は新たなソケットアドレス インスタンスを作成して返します。

簡単な場合として次の二つがあります:addrs()が渡された場合、 送信者のアドレスは常に捨てられます。socketがコネクトされていれば 送信者のアドレスは既にわかっているので、そういう場合に便利でしょう。 一方addrs#tを渡せば、常に新しいソケットアドレスが アロケートされて返されます。メモリアロケーションについて気にしなくても 良い場合は便利でしょう。

オプション引数 flags は整数定数 MSG_* のビット毎のORで 指定できます。詳しくはシステムの man ページ recvfrom(2)を見て下さい。

Function: socket-recv socket bytes :optional flags
Function: socket-recvfrom socket bytes :optional flags

socket-recv!socket-recvfrom!と似ていますが、 受け取ったメッセージを(不完全かもしれない)文字列として返します。 bytesは受信する上限のバイト数を指定します。 socket-recvfromはそれに加えて、送信者のアドレスのために 常に新たなソケットアドレスオブジェクトを生成します。

これらの手続きの使用はお薦めしません。というのは、バイナリメッセージを 不完全な文字列の形で扱うことになりがちだからです。文字列で バイナリメッセージを取り扱うことは多くの落とし穴を作ります。 バイナリデータを扱う場合はユニフォームベクタ (特にu8vector) を使うべきでしょう。 (これらの手続きが文字列を返すのは単に歴史的な理由によります)。

Variable: MSG_CTRUNC
Variable: MSG_DONTROUTE
Variable: MSG_EOR
Variable: MSG_OOB
Variable: MSG_PEEK
Variable: MSG_TRUNC
Variable: MSG_WAITALL

socket-sendsocket-sendtosocket-recv および socket-recvfromflag として使う定義済み整数定数。 これらの定数のいくつかは、基盤のOSが提供していなければ未定義に なっています。

以下に解説する getsockopt/setsockopt インタフェースを使えば、 ソケットやプロトコルをさらに制御することができます。

Function: socket-setsockopt socket level option value
Function: socket-getsockopt socket level option rsize

これらは、setsockopt() および getsockopt() を呼ぶための インタフェースです。このインタフェースが少々ぎこちないのは、 低レベルの呼び出しすべてにアクセスできるようにしているためです。

socket および option は、処理する対象の プロトコルスタックのレベルおよびオプションを指定する 正確な整数です。以下にリストアップしたシステム定数に束縛された 変数があります。

ソケットオプションを設定するには、value に正確な整数を渡す方法と 文字列を渡す方法があります。それが、整数なら、その値は、C の int として setsockopt (2) に渡されます。文字列なら、バイト列が そのまま渡されます。どの型の値が要求されるかは、オプションに依存します。 Gauche 側では渡された値が setsockopt (2) で期待された型であるかどうかは 知ることができません。正しい値が渡されるかどうかはプログラマの責任です。

ソケットオプションを知るには、結果として期待する最大の長さを rsize を使って教える必要があります。Gauche 側は返されるそれぞれのオプションの 合計を知らないからです。 socket-getsockopt はオプションの値をバイト列として返します。 オプションの値が整数だと、わかっているなら rsize に0を渡すこと ができます。その場合 socket-getsockopt は正確な整数として値を返します。

関数名に関する註: これらの関数の名前を socket-{set|get}opt あるいは socket-{set|get}-option にしようかとも思いましたが、結局、命名の 一貫性をとることにしました。それで、"sock" が重複しています。

以下のような定義済みの変数が用意されています。 すべてのプラットフォームで利用できるわけではないものも含まれている ことに注意してください。 これらの値の正確な使用は、お使いのシステムの man ページ socket(7)tcp(7) あるいは ip(7) を参照してください。

“level” 引数

Variable: SOL_SOCKET
Variable: SOL_TCP
Variable: SOL_IP

これらの変数は、それぞれ、SOL_SOCKETSOL_TCP および SOL_IP に束縛されています。

“option” 引数

Variable: SO_KEEPALIVE

整数が期待されています。0でなければ、コネクション指向ソケットに キープアライブ(keep-alive)メッセージを送ることができます。

Variable: SO_OOBINLINE

整数が期待されています。0でなければ、帯域外データは直接 受信データストリームに乗ります。さもなければ、帯域外データは、受信中に MSG_OOBフラグが設定されてたときにのみ渡されます。

Variable: SO_REUSEADDR

整数が期待されています。0ではない場合、socket-bindは、 ローカル・アドレスが、アクティブソケットにリッスンされていない 場合にかぎり、そのアドレスを再利用することを許可されます。

Variable: SO_TYPE

ソケットの型(sock_streamなど)を整数として取得します。 socket-getsockoptでのみ使用可能です。

Variable: SO_BROADCAST

整数が期待されています。0でなければ、データグラムソケットは ブロードキャストパケットを送受信することを許されます。

Variable: SO_PRIORITY

整数が期待されています。このソケットで送信されるすべてのパケットに ついてプロトコル定義の優先順位を指定します。

Variable: SO_ERROR

保留状態のソケットエラー(整数値)を取得し、それをクリアします。 socket-getsockoptでのみ使用可能です。

Function: inet-checksum packet size

ユニフォームベクタで与えられるpacketのインターネットチェックサム(RFC1071) の1の補数を計算して返します。packetの最初のsizeバイトのみが 計算の対象となります。返される値はネットワークバイトオーダ (ビッグエンディアン) です。packetより大きな値をsizeに指定するとエラーとなります。

註: 使われているアルゴリズムは、packetがそれほど大きくないこと (< 64K) を 仮定しています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.19.4 Netdbインタフェース

Builtin Class: <sys-hostent>

このクラスはネットワークホストのオブジェクト用で、 C の struct hostent に対応しています。以下のスロットは読み込み専用です。

Instance Variable of <sys-hostent>: name

ホストの正式名(文字列)

Instance Variable of <sys-hostent>: aliases

ホストの別名リスト(文字列のリスト)

Instance Variable of <sys-hostent>: addresses

アドレスのリスト(文字列のリスト)。現時点では IPv4 のアドレスのみサポート しています。それぞれのアドレスは、ドットつき十進記法で表記されています。

Function: sys-gethostbyname name

nameという名前のホストを探し、見つかれば、<sys-hostent> オブジェクトを返します。見つからなければ、#f を返します。

 
(let ((host (sys-gethostbyname "www.w3c.org")))
  (list (slot-ref host 'name)
        (slot-ref host 'aliases)
        (slot-ref host 'addresses)))
  ⇒ ("www.w3.org" ("www.w3c.org") ("18.29.1.34" "18.29.1.35"))
Function: sys-gethostbyaddr addr proto

protoというプロトコルの、addrというアドレスを持つホストを 探します。addrはアドレスの自然な文字列表現で、IPv4 については、 ドットつき十進記法です。protoはプロトコル番号で、現時点では AF_INETのみサポートされています。ホストが見つかれば、 <sys-hostent>オブジェクトを返します。 見つからなければ、#f を返します。

 
(let ((host (sys-gethostbyaddr "127.0.0.1" AF_INET)))
  (list (slot-ref host 'name)
        (slot-ref host 'aliases)
        (slot-ref host 'addresses))
  ⇒ ("localhost" ("localhost.localdomain") ("127.0.0.1"))
Builtin Class: <sys-servent>

ネットワークサービスデータベースのエントリです。 C の struct serventに対応しています。 以下のスロットは読み込み専用です。

Instance Variable of <sys-servent>: name

サービスの正式名(文字列)

Instance Variable of <sys-servent>: aliases

サービスの別名リスト(文字列のリスト)

Instance Variable of <sys-servent>: port

サービスに割り当てられたポート番号(正確な整数)

Instance Variable of <sys-servent>: proto

このサービス用のプロトコル名(文字列)

Function: sys-getservbyname name proto

ネットワークサービスデータベースをサービス名 name および プロトコル proto で検索します。name および proto は文字列でなければなりません。サービスが見つかれば、<sys-servent> のインスタンスを返します。見つからなければ、#f を返します。

 
(let ((serv (sys-getservbyname "http" "tcp")))
  (list (slot-ref serv 'name)
        (slot-ref serv 'aliases)
        (slot-ref serv 'port)
        (slot-ref serv 'proto)))
  ⇒ ("http" () 80 "tcp")
Function: sys-getservbyport port proto

ネットワークサービスデータベースをサービス名 port および プロトコル proto で検索します。port は正確な整数でなければ なりません。また、proto は文字列でなければなりません。 サービスが見つかれば、<sys-servent> のインスタンスを返します。見つからなければ、#f を返します。

 
(let ((serv (sys-getservbyport 6000 "tcp")))
  (list (slot-ref serv 'name)
        (slot-ref serv 'aliases)
        (slot-ref serv 'port)
        (slot-ref serv 'proto)))
  ⇒ ("x-server" () 6000 "tcp")
Builtin Class: <sys-protoent>

プロトコルデータベースのエントリです。C の struct protoentに 対応しています。以下のスロットは読み込み専用です。

Instance Variable of <sys-servent>: name

プロトコルの正式名(文字列)

Instance Variable of <sys-servent>: aliases

プロトコルの別名のリスト(文字列のリスト)

Instance Variable of <sys-servent>: proto

プロトコル番号(正確な整数)

Function: sys-getprotobyname name

名前 name でネットワークプロトコルデータベースを検索します。 名前 name は文字列でなければなりません。 プロトコルが見つかれば、<sys-protoent> のインスタンスを返します。見つからなければ、#f を返します。

 
(let ((proto (sys-getprotobyname "icmp")))
  (list (slot-ref proto 'name)
        (slot-ref proto 'aliases)
        (slot-ref proto 'proto)))
  ⇒ ("icmp" ("ICMP") 1)
Function: sys-getprotobynumber number

プロトコル番号 number でネットワークプロトコルデータベースを検索 する。プロトコル番号 number は正確な整数でなければなりません。 プロトコルが見つかれば、<sys-protoent> のインスタンスを返します。見つからなければ、#f を返します。

 
(let ((proto (sys-getprotobynumber 17)))
  (list (slot-ref proto 'name)
        (slot-ref proto 'aliases)
        (slot-ref proto 'proto)))
  ⇒ ("udp" ("UDP") 17)
Builtin Class: <sys-addrinfo>

アドレス情報を保持する新しいインタフェースです。C の struct addrinfo に対応しています。これは gauche が –enable-ipv6 オプションで設定され、 ビルドされた場合にのみ利用可能です。以下のスロットが提供されます。

Instance Variable of <sys-addrinfo>: flags
Instance Variable of <sys-addrinfo>: family
Instance Variable of <sys-addrinfo>: socktype
Instance Variable of <sys-addrinfo>: protocol
Instance Variable of <sys-addrinfo>: addrlen
Instance Variable of <sys-addrinfo>: addr
Function: sys-getaddrinfo nodename servname hints

与えられた nodenameservname および hints から <sys-addrinfo> のインスタンスのリストを返します。 これは gauche が –enable-ipv6 オプションで設定され、 ビルドされた場合にのみ利用可能です。

Function: sys-ntohs integer
Function: sys-ntohl integer
Function: sys-htons integer
Function: sys-htonl integer

16bit (s)もしくは32bit (l) の整数を ネットワークバイト順 (n) とホストバイト順 (h) の間で 相互変換するユーティリティ関数です。

netdbインタフェースのScheme APIは必要な箇所ではこれらの関数を内部的に 呼んでいるので、Cでプログラミングしている時ほどこれらの関数を必要とする ことはないでしょう。ただ、バイナリデータパケットを構築したり解析したり する際は便利かもしれません。バイナリデータの扱いについては binary.pack - バイナリデータのパックも参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.20 gauche.package - パッケージメタ情報

Module: gauche.package

Gauche manages extra libraries and extension modules as packages.

Each package source tree has ‘package.scm’ on top directory, which contains define-gauche-package form that provides metainformation about the package—the package name, veirson, author, dependencies, etc.

When the package is installed, the standard installation process copies that information, with additional information such as the version of Gauche used to build the package, into ‘.packages’ subdirectory of the library installation path, with the name ‘PACKAGENAME.gpd’, where PACKAGENAME is the name of the package.

We collectively call ‘package.scm’ and ‘*.gpd’ as package description file.

This module provides utility procedures to read and write package description files, and search installed ‘*.gpd’ files.

define-gauche-package form

configure script and *.gpd file generation

Utility procedures

Function: <gauche-package-description>
Function: path->gauche-package-description filename
Function: write-gauche-package-description description :optional oport
Function: make-gauche-package-description
Function: gauche-package-description-paths :key all-versions
Function: find-gauche-package-description name :key all-versions

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.21 gauche.parameter - パラメータ

Module: gauche.parameter

「パラメータ」は基本的にゼロもしくは一個の引数を取る状態を持つ手続きとみなせます。 引数が与えられなかった場合、それは内部に保持した値を返します。 一つの引数が与えられた場合、その値が新たなパラメータ内部の値となり、 変更前の値が返されます。 パラメータは状態を保持する手段として、 単なるグローバル変数に比べていくつかの利点を持っています。

パラメータの値はスレッド毎に保持されるので、 パラメータをスレッドローカルな格納領域として利用することができます。 新たに作られたスレッドは、その作成元のスレッドのパラメータのコピーを 受け取ります。

パラメータに値がセットされるときに、指定の「フィルタ手続き」を呼んで 値を検査することができます。これにより、パラメータの値が常に正当なもので あることを保証できます。

マクロparameterizeにより、特定のダイナミックスコープの中だけ パラメータの値を変えることができます。これは、実質的にダイナミックスコープを 持つ変数を置き換えることができます。

また、パラメータの値が変更されたときに呼ばれるコールバック手続きを 登録しておくこともできます。これによって実質的に、いわゆる 「traceされた変数」を置き換えることができます。

パラメータオブジェクトの基本的な機能はSRFI-39で定義されています。 この機能はまた、ChezScheme、Chicken、MzSchemeなどいくつかのScheme処理系に 見られます。GaucheのパラメータはSRFI-39の上位互換になっています。

Class: <parameter>

パラメータのクラスです。object-applyメソッドが定義されているので、 パラメータオブジェクトは普通の手続きと同様に使うことが出来ます。

 
;; p is a parameter with initial value 2
(define p (make-parameter 2))

;; calling p with no arguments returns the current value
(p) ⇒ 2

;; modify p's value to 3
(p 3)

(p) ⇒ 3

;; you can also use generalized set (srfi-17)
(set! (p) 5)

(p) ⇒ 5

;; using parameterize dynamically changes p's value.

(define (get-p) (p))

(parameterize ([p 7])
  (get-p)) ⇒ 7

(get-p)    ⇒ 5
Function: make-parameter value :optional filter

[SRFI-39] 初期値がvalueであるパラメータを作成します。 もし省略可能な引数filterが与えられた場合、 それは一つの引数を取る手続きでなければなりません。 パラメータの値が変更されようとした時、filterは与えられた値を 引数として呼ばれ、filterが返した値がパラメータの新しい値と なります。filterはエラーを報告したりパラメータの値を変えずに置くことも 可能です。

Macro: parameterize ((param value) …) body …

[SRFI-39] body …を評価します。 但し、body … の実行中のみ、パラメータparamの値を valueに変更します。最後bodyの返した値を返します。

例:

 
(define a (make-parameter 1))
(a) ⇒ 1
(a 2) ⇒ 1
(a) ⇒ 2
(parameterize ((a 3))
  (a)) ⇒ 3
(a) ⇒ 2
Method: parameter-observer-add! (p <parameter>) proc :optional when where

procをパラメータpの“オブザーバ”手続きに追加します。 オブザーバ手続きは、(1)パラメータに新しい値がセットされる直前、 あるいは(2)パラメータに新しい値がセットされた直後、に呼ばれます。 (1)の場合、コールバックが呼ばれる前にフィルタ手続きがすでに適用 されています。どちらの場合も、オブザーバ手続きは、古い値と新しい 値の2引数を伴って呼ばれます。オブザーバ手続きの戻り値は無視されます。

オプショナル引数whenは、シンボルbeforeafterの いずれかでなければならず、値が変更された前と後のどちらでprocが 呼ばれるかを指定します。省略されると、afterを指定したものと されます。

オプショナル引数whereは、シンボルappendprependの いずれかでなければならず、procが既存のオブザーバ手続きのリストの 前に追加されるか後に追加されるかを指定します。省略されると、append を指定したものとされます。

注意: パラメータの値自身がスレッドローカルであっても、 オブザーバのリストは全てのスレッドで共有されます。

Method: parameter-observer-delete! (p <parameter>) proc :optional when

パラメータpのオブザーバ手続きのリストからprocを削除します。 procがリストになければ、何も起こりません。 when引数にシンボルbeforeafterを与えて、 procをどちらのリストから削除するかを指定することができます。 when引数が省略されると、procは両方のリストから削除されます。

Method: parameter-pre-observers (p <parameter>)
Method: parameter-post-observers (p <parameter>)

“before”か“after”オブザーバをそれぞれキープするフックオブジェクト (gauche.hook - フック参照)を返します。

注意: パラメータの値自身がスレッドローカルであっても、 オブザーバのリストは全てのスレッドで共有されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.22 gauche.parseopt - コマンドライン引数の解析

Module: gauche.parseopt

このモジュールでは、コマンドラインオプションをパースするための便利な方法を定義 しています。インタフェースは Perl にヒントを受けたもので、複数のオプション引数を 伴う長い形式のオプションを便利に扱うことができます。

実際、Gauche でコマンドラインオプションをパースするにはいくつかの選択肢が あります。SRFI-37 (srfi-37 - args-fold プログラム引数処理参照)では、 POSIX/GNU 互換の引数構文をパースするための関数的なインタフェースを提供しています。 SLIB は、getopt 互換のユーティリティを持っています。 要求される機能はアプリケーションごとに異なるので、 あなたの要求にフィットするものを選んで下さい。

High-level API

Macro: let-args args (bind-spec … [. rest]) body …

このマクロはコマンドライン引数処理の最も典型的なパターンを扱います。 引数のリストargsを取り、bind-specで示される仕様をもとに コマンドラインオプションを探してその値を変数に束縛し、それから body …を実行します。

まず簡単な例を見てみましょう。このフォームが何をするか、だいたい想像できるのでは ないかと思います。(より多くの例については下の“Examples”の項を参照して ください)。

 
(define (main args)
  (let-args (cdr args)
      ((verbose     "v|verbose")
       (outfile     "o|outfile=s")
       (debug-level "d|debug-level=i" 0)
       (help        "h|help" => (cut show-help (car args)))
       . restargs
      )
    ....))

(define (show-help progname)
  ...)

ローカル変数verboseは、コマンドラインオプション-v--verbose が与えられれば#tに、そうでなければ#fに束縛されます。 変数outputはオプション引数を取ると指定されており、例えば -o out.txtのようにコマンドラインに指定されれば値"out.txt"が 束縛されます。debug-levelも似ていますが、オプション引数は 整数へと変換され、またデフォルト値0が指定されています。 help節では、単に値を束縛するだけでなくアクションを起動しています。

(註: 今のところlet-args-v--vを区別しません。 -verbose--verboseも同様です。将来、getopt_long(3)と 互換になるオプションを追加するかもしれません。)

最後のドットの後のrestargsは、オプションでないコマンドライン引数のリストを 受け取ります。

bind-specについて詳しく見てみましょう。 bind-specは次のいずれかの形式でなければなりません。

 
1. (var option-spec)
2. (var option-spec default)
3. (var option-spec => callback)
4. (var option-spec default => callback)

5. (else => handler)
6. (else formals body ...)

コマンドライン引数のリストがargsに渡されると、それが option-specに基づいてパーズされます。該当するオプションが見付かれば、 変数varが次に述べる値に束縛されます:

 
(a) bind-specの形式が上の1. または2. の場合:
  (a1) option-specが引数を要求しない場合は、#t。
  (a2) option-specがひとつの引数を要求する場合は、その引数の値。
  (a3) option-specがそれ以上の引数を要求する場合は、引数の値のリスト。
(b) bind-specの形式が上の3. または4. の場合、callbackを
  引数の値を伴って呼び出し、その戻り値。

option-specの詳細については後で説明します。

特別な場合として、var#fとすることもできます。 その場合、値は無視されます。 callbackでの副作用だけに関心がある場合に使えるでしょう。

対応するオプションがargsに与えられなかった場合、 vardefaultが与えられていればその値に、 そうでなければ#fに束縛されます。

最後のbind-specには5か6の形式も許されます。 この節は、どのoption-specにもマッチしないコマンドラインオプションが 与えられた場合に選択されます。 5番目の形式では、handlerが3つの引数を伴って呼び出されます。 引数は、マッチしなかったコマンドラインオプション、 残りのコマンドライン引数、そして引数処理に戻るための継続手続きです。 handlerは与えられたコマンドラインオプションを処理した後、 オプション処理を続けたければ継続手続きに残りのコマンドライン引数を渡し、 オプション処理を打ちきりたければ残りのコマンドライン引数を戻り値として 返します。返された値は通常の(オプションでない)コマンドライン引数として扱われます。

束縛リストは、最後のcdrにシンボルを持つ不完全なリストであっても良く、 その場合はコマンドライン引数の残りのリストがその変数にと束縛されます。

defaultcallbackelse節のフォーム等は varの束縛の外側のスコープで評価されることに注意して下さい。

C における典型的な getoptgetopt_long の実装とは異なり、 let-args は与えられたコマンドライン引数の順番を変えません。 オプションでない引数(ハイフンで始まらない引数)に遭遇した時点でパースを 中止します。

パーサは、ハイフン2つのみの引数 ‘--’ に遭遇すると、引数パーシングを 中止して‘--’ の後の引数のリストを返します。

全ての束縛が終了した後、body … が評価されます。 bodyは内部defineで始まっていても構いません。

Option spec

option-spec は、オプションの名前とそのオプションがどのように引数を取るか を指定する文字列です。オプションの名前には、アルファベット文字、数字、 アンダースコア、プラス記号、ハイフンが許されますが、ハイフンは最初の文字としては 使えません。すなわち、有効なオプションの名前は、#/[\w+][-\w+]*/ という 正規表現にマッチするものです。

オプションが引数を取る場合、名前の後ろに等号文字と引数の型を表現する文字を 付けることで指定できます。オプションは一つ以上の引数を取ることができます。 以下の文字がオプションの引数の型を表現するものとして認識されます。

s

文字列。

n

数値。

f

実数 (flonumに変換されます)。

i

正確な整数。

e

S式。

y

シンボル (引数はstring->symbolにより変換される)。

option-specの例を見てみましょう:

"name"

引数を取らないオプションの name を指定します。

"name=s"

オプション name は引数を一つ取り、それは文字列として渡されます。

"name=i"

オプション name は引数を一つ取り、それは正確整数として渡されます。

"name=ss"

オプション name は引数を二つ取り、両方とも文字列です。

"name=iii"

オプション name は3つの整数の引数を取ります。

"name=sf"

オプション name は2つの引数を取ります。一つ目は文字列で、 二つ目は数値です。

オプションにいくつかの別名がある場合は、"|" でつなげて書いておくことができます。 例えば"h|help"というoption-specは "h"にも"help"にもマッチします。

コマンドラインでは、オプションは一つか二つのハイフンに続いて与えられます。 オプションの引数は、オプションそのものと等号記号でつながれていても構いません。 例えば、以下の全てのコマンドライン引数は、オプションの仕様、"prefix=s" に マッチします。

 
-prefix /home/shiro
-prefix=/home/shiro
--prefix /home/shiro
--prefix=/home/shiro

Error handling

Condition Type: <parseopt-error>

let-argsが、option-specに従わない引数を見つけた場合は、 コンディションタイプ<parseopt-error>のエラーを投げます。 例えば、必須のオプション引数が与えられていなかったり、異なる型であった 場合などです。

 
(let-args '("-a" "foo") ((a "a=i")) ; option a requires integer
  (list a))
 ⇒ parseopt-error

このコンディションはあくまでargsに渡された引数をパーズする際に発生するものです。 option-specが不正であった場合は通常のエラーが投げられます。

Examples

これはgauche-installスクリプトから取った例です。 modeオプションは8進数のオプション引数を取るので、コールバック 手続きを使って変換しています。また、認識できないオプションをelse節で 処理しています。

 
  (let-args (cdr args)
      ((#f      "c")        ;; ignore for historical reason
       (mkdir   "d|directory")
       (mode    "m|mode=s" #o755 => (cut string->number <> 8))
       (owner   "o|owner=s")
       (group   "g|group=s")
       (srcdir  "S|srcdir=s")
       (target  "T|target=s")
       (utarget "U|uninstall=s")
       (shebang "shebang=s")
       (verb    "v")
       (dry     "n|dry-run")
       (#f      "h|help" => usage)
       (else (opt . _) (print "Unknown option : " opt) (usage))
       . args)
    ...)

次の例はelse節の使い方を示す小さなプログラムです。 コマンドラインオプションを変数rに集めてゆきますが、 オプション -c に出会うとオプション処理を中止して残りを restargsへと渡します。

 
(use gauche.parseopt)

(define (main args)
  (let1 r '()
    (let-args (cdr args)
      ((else (opt rest cont)
         (cond [(equal? opt "c") rest]
               [else (push! r opt) (cont rest)]))
       . restargs)
     (print "options: " (reverse r))
     (print "restargs: " restargs)
     0)))

上のスクリプトの実行例です (‘example’というファイル名で保存されていると します)。

 
$ ./example -a -b -c -d -e foo
options: (a b)
restargs: (-d -e foo)
$ ./example -a -b -d -e foo
options: (a b d e)
restargs: (foo)

Low-level API

The followings are lower-level API used to build let-args macro.

Macro: parse-options args (option-clause …)

args は、コマンドライン引数のリストを含む式です。 このマクロは、コマンドラインオプション(‘-’ で始まる引数)をスキャンし、 option-clause の指定に従って処理し、残りの引数を返します。

それぞれの option-clause は、option-spec とそのアクションのペアで 構成されます。

与えられたコマンドラインオプションが option-spec の一つにマッチすると、 関連付けられたアクションが評価されます。アクションは以下のフォームの一つです。

bind-spec body

bind-spec は、ラムダリストのような変数の正しいリストかドット対リストです。 オプションの引数は bind-spec に束縛され、body … が評価されます。

=> proc

コマンドラインオプションが option-spec にマッチすると、 proc がオプションの引数のリストとともに呼び出されます。

シンボル elseoption-spec の位置にある場合、その節は、 与えられたコマンドラインオプションにマッチする他のオプション節が ない場合に選択されます。その節には3つの“引数”が関連付けられます。 それらは、マッチしなかったオプション、引数の残り、オプションパーサを 表す手続きです。

Macro: make-option-parser (option-clause …)

これは低レベルのインタフェースです。option-clause は、 parse-options と同じです。このマクロは、コマンドラインオプションを 後でパースするために使うことができる手続きを返します。

返される手続きは、一つの必須の引数と一つのオプション引数を取ります。 必須の引数は、与えられたコマンドライン引数としての文字列のリストです。 オプションの引数は、三つ以上の引数を取る手続きで、それが与えられると 手続きはそれが else オプション節のボディであるかのように使われます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.23 gauche.partcont - 部分継続

Module: gauche.partcont

Gaucheは内部的に、部分継続(限定継続と呼ばれることもあります)を ネイティブでサポートしています。このモジュールはその機能を 一般的に使えるように公開するものです。

註: 部分継続はふたつのオペレータ、resetshiftを使います。 これらは元の論文で導入された名前ですが、既に用語として定着した感があります。 ライブラリ関数名としては一般的に過ぎる名前なので、よりわかりやすい名前を つけようかとも考えたのですが、部分継続を話題にする際にはこれらの用語が 使われるのが普通なので、最終的にこの名前をキープすることにしました。 プログラム中で他の識別子とぶつかったり紛らわしい場合は、モジュールのimport 時に:prefixインポート指示子(モジュールの使用参照)を 次のように使うと良いでしょう。

 
;; Add prefix pc: to the 'reset' and 'shift' operators.
(use gauche.partcont :prefix pc:)

(pc:reset ... (pc:shift k ....) )
Macro: reset expr …

現在の継続を保存し、expr … を空の継続を伴って実行します。 空の継続はshiftオペレータが捕捉する継続の終端になります。

暗黙の限定継続について: Gaucheは内部的に、reset相当の操作を行う場合があります。 CルーチンがSchemeを継続渡し形式でない方法で呼び出す場合です。 (C APIをご存知の方へ: Scm_EvalRec(), Scm_Apply*Rec(), Scm_Eval()、およびScm_Apply()が相当する関数です。) これらの関数はCの呼び出し側へ、値をたかだか1度だけ返すことが期待されています。 Schemeの継続は無限エクステントを持ち、一度返ったルーチンから再び返ることが あり得るため、こういったC関数とは相性が良くありません。 これらの関数を呼び出す時に、Gaucheは限定継続を自動的に作成します。

例えば、goshmain関数はSchemeのREPLを Scm_Eval()を通じて呼び出します。ということは、 REPL全体がresetで囲まれているということです。 従ってresetの外側でshiftを呼び出すと、そのshiftの 継続はREPL全体の継続と同じになります。すなわち、goshが終了するということです。 暗黙の限定継続に気づかないと、この振る舞いにはびっくりするかもしれません。

他に暗黙の限定継続が作られる例をいくつかあげます。 仮想ポートのハンドラ (gauche.vport - 仮想ポート参照)、 writedisplayから呼ばれるobject-applyメソッド、 glut-display-funcにより登録されたGUIコールバック (詳しくはGauche-glのマニュアル参照)などです。

そのような暗黙の限定継続を心配する必要は滅多にありません。Cで実装された 組み込み関数や拡張関数のほとんどは継続渡し形式でSchemeを呼んでいるため、 通常継続も限定継続も制限なく使うことができます。

Macro: shift var expr …

この式からもっとも最近のresetにより切り取られた空の継続までの継続を 手続きに包み、それをvarに束縛します。 そしてもっとも近いreset式の継続を伴ってexpr …を実行します。

すなわち、expr …を実行後、その結果の値はもっとも最近のreset の戻り値を待っている式に直ちに渡されます。 varに束縛されている部分継続が実行されると、それに渡された値は shiftの戻り値を待っている継続に直ちに渡されます。 その部分継続の実行が終了すると、その結果の値はvarを呼び出した式の 戻り値となります。

Function: call/pc proc

これはshiftのラッパーです。 (shift k expr …)(call/pc (lambda (k) expr …)) 等価です。call/ccと似た形の呼び出し形式の方が便利な場合があるので 用意しました。

さてと… もしあなたが、継続の国の血を引く珍しい種族の一員でなければ、 たぶんここまで読んできて、脳みそがこんがらがっていることでしょう。 何が起きているか、厳密ではないが直感的な説明を試みてみます。

手続きAが式Bを呼び出すとします。AがBの戻り値を受け取ってさらに計算を続ける場合、 Bが戻ってからの残りの計算部分をA’として分離することにすれば、全体の制御の流れは 次のように一本の鎖で表現できるでしょう。

 
A -> B -> A'

A -> Bは手続き呼び出しで、B -> A'は手続きからのリターンですが、 手続き呼び出しとリターンは本質的には同じものでしたね。

Bはその中から別の手続きCをさらに呼び出しているかもしれません。 コードのある部分に着目した場合、そこにある制御の鎖を 次のようにイメージすることができるでしょう。

 
... -> A -> B -> C -> .... -> C' -> B' -> A' -> ...

魔法の手続きcall/ccは、そのフォームの直後に来る鎖の先頭 (下の図で*で示されている部分)を取り上げて、それを 与えられた手続きに引数として渡すものです (下の図のk)。 従って、kが呼び出されると、制御は直ちに*へとジャンプします。

 
... -> A -> B -> (call/cc -> (lambda (k) ... ) ) -> B' -> A' -> ...
                                      |             ^
                                      \-----------> *

call/ccで難しいのは、制御の鎖の片方しか取り出せないことです。 もう一方、鎖の右側がどこにつながっているのか、コードは知ることができません。 そもそも、鎖の右側に何が来るかは、プログラム全体を知らないとわからないのです。 この、グローバルな性質が、call/ccを扱い辛いものにしています。

resetプリミティブはこの継続の鎖を切断します。 元の鎖 (下の図のxで示される端) は保存され、 reset式の継続自体は宙ぶらりんになります (下の図のoで示される端)。

 
... -> A -> B -> (reset ... ) -> o

                                 x -> B ' -> A' -> ...

ここでひとつ規則を導入します。制御がoの端に達した場合、 直近に保存されたxの端から制御を再開するとします。 従って、単にresetを挿入するだけでは、目に見える違いは生じません。

resetの中にshiftを挿入したらどうなるでしょう。 resetの内部の鎖にshiftを形式的に挿入してみるとこうなります。

 
... -> (reset -> X -> Y -> (shift k ... ) -> Y' -> X' ) -> o

実際には次のような動作となります。

  1. shiftは、resetの終端までの仕事の残りの部分を取り出し、 それを変数kに束縛します。
  2. shift自身の継続は、空の継続となります。したがってshiftから抜けると、 shift以降対応するresetまでの操作はスキップされます。
 
... -> (reset -> X -> Y -> (shift k ... ) ---------> ) -> o
                                  |
                                  \-------> Y' -> X' ) -> o

別の言い方をすれば、resetフォームをひとつの仕事の単位とすれば、 その中のshiftは、その仕事の残りの部分を一時保存して 仕事を中断して戻ってくるのです。

例を見てみます。次に示すwalker引数は、 「手続きと何らかのコレクションを取り、手続きをコレクションの各要素に 適用してゆく」という手続きとします。 walkerの戻り値は無視します。

 
(define (inv walker)
  (lambda (coll)
    (define (continue)
      (reset (walker (lambda (e) (shift k (set! continue k) e)) coll)
             (eof-object)))
    (lambda () (continue))))

walkerの典型的な例はfor-eachです。手続きとリストを取り、 リストの各要素に手続きを適用するからです。 for-eachを上のinvに渡すと、for-each裏返した手続きが得られます。どういうことでしょう? 下のやりとりを見てみましょう。

 
gosh> (define inv-for-each (inv for-each))
inv-for-each
gosh> (define iter (inv-for-each '(1 2 3)))
iter
gosh> (iter)
1
gosh> (iter)
2
gosh> (iter)
3
gosh> (iter)
#<eof>

リストをinv-for-eachに渡すと、呼ばれる度にリストの各要素を順に返す イテレータ手続きが得られます。というのも、iterが呼ばれる度に、 invの中で作られたshiftリストの残りの部分をたどって行く という仕事の残りをcontinueに束縛して、現在の要素eを返すからです。

walkerはリストを取る必要はありません。次に示すfor-each-leafは 木を取って、fをペアでない要素に適用してゆく手続きです。

 
(define (for-each-leaf f tree)
  (match tree
   [(x . y) (for-each-leaf f x) (for-each-leaf f y)]
   [x (f x)]))

これもfor-eachと同じように裏返すことができます。

 
gosh> (define iter2 ((inv for-each-leaf) '((1 . 2) . (3 . 4))))
iter2
gosh> (iter2)
1
gosh> (iter2)
2
gosh> (iter2)
3
gosh> (iter2)
4
gosh> (iter2)
#<eof>

util.combinationsモジュール (util.combinations - 組み合わせ参照) には、 コレクションの全ての並べ替えに対して与えられた手続きを呼び出す手続きがあります。 それをinvに渡せば、呼ばれる度に並べ替えを返す手続きが得られます。

 
gosh> (define next ((inv permutations-for-each) '(a b c)))
next
gosh> (next)
(a b c)
gosh> (next)
(a c b)
gosh> (next)
(b a c)
gosh> (next)
(b c a)
gosh> (next)
(c a b)
gosh> (next)
(c b a)
gosh> (next)
#<eof>

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.24 gauche.process - 高レベルプロセスインタフェース

Module: gauche.process

このモジュールは、sys-forksys-exec などの低レベルな システムコールの上に実装された、Unix プロセス制御の高レベル API を提供します。 また、このモジュールは、サブプロセスに情報を送ったり、サブプロセスから 情報を受け取ったりするのに便利な「プロセスポート」を提供します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.24.1 Running subprocess

Function: run-process cmd/args :key redirects input output error fork wait directory host sigmask

サブプロセスで、cmd/argsに与えられたコマンドと引数を実行し、 <process>オブジェクトを返します。 <process>オブジェクトについては次節で説明します。

cmd/args引数はリストで、そのcarがコマンド名を、 cdrがコマンドラインに渡す引数を指定します。

コマンド名がスラッシュを含んでいた場合、それは実行可能ファイルへの パス名と解釈されます。そうでなければ、コマンド名がPATH環境変数 にあるディレクトリから探されます。

cmd/argsの各要素は、x->stringで文字列に変換されます。

例えば、次の式はls -alを実行します。

 
(run-process '(ls -al))

上の式をREPLで実行したなら、おそらく戻り値がlsの出力より先に表示されるでしょう。 デフォルトでは、run-processは子プロセスの 終了を待たずに、直ちに戻ります。子プロセスの終了を待ちたい場合はwaitキーワード 引数を使います。

 
(run-process '(ls -al) :wait #t)

あるいは戻り値の<process>オブジェクトを保持しておいて、後で process-waitを呼ぶことによって子プロセスの終了を待つこともできます。 process-waitについてはProcess objectで説明します。

 
(let1 p (run-process '(ls -al))
  ... do some other work ...
  (process-wait p))

ところで、-iは虚数として読まれることに注意してください。 -iを引数として渡したい場合は文字列にするか、|-i|のように エスケープしてシンボルにする必要があります。

 
(run-process '(ls "-i"))

注:外部プロセスを走らせる方法にはもうひとつ、sys-systemがあります。 こちらはコマンド行を単一の文字列で渡し、シェルを呼び出して解釈させます (プロセス管理参照)。 したがって、入出力のリダイレクトや、パイプでつないだコマンド等もそのまま渡せます。 書き捨てのスクリプトを素早く書くときはそちらの方が便利でしょう。

一方、sys-systemを使う場合には、コマンドの引数を実行時に可変にしたい場合に きちんとエスケープされているかどうかを確認したり (実はgauche.process モジュール中にそれをする関数があります。下のshell-escape-stringを 参照してください)、またsys-systemsystem(3)経由で 呼び出す/bin/shのプラットフォーム間の違いを気にしたりする必要があります。 原則として、sys-systemの使用は固定コマンドを呼び出す簡単な場合に 止めておき、他の仕事にはrun-processを使うのが良いでしょう。

注:以前のバージョンのこの手続きは引数の取りかたが若干異なっており、 例えば(run-process "ls" "-al" :wait #t)のように呼び出しました。 これはSTkとの互換性によるものです。現在でもこの呼び出しはサポートされていますが、 非推奨です。

子プロセスの実行を細かく制御するために、run-processはたくさんの キーワード引数を取ります。以下でそれらをいくつかのカテゴリに分類して説明します。

同期

run-process argument: wait flag

flag が真の場合、run-process はサブプロセスが終了するまで待ちます。 そうでなければ、サブプロセスは非同期に実行され、run-process は すぐに返ります。これがデフォルトの振る舞いになります。

サブプロセスが非同期に実行している場合、その終了ステータスを回収するために 適切なタイミングで process-wait を呼ぶことは、呼び出し側の責任であることに 注意してください。

 
;; This returns after wget terminates.
(define p (run-process '(wget http://practical-scheme.net/) :wait #t))

;; Check the exit status
(let1 st (process-exit-status p)
  (cond [(sys-wait-exited? st)
         (print "wget exitted with status " (sys-wait-exit-status st))]
        [(sys-wait-signaled? st)
         (print "wget interrupted by signal " (sys-wait-termsig st))]
        [else
         (print "wget terminated with unknown status " st)]))
run-process argument: fork flag

flag が真の場合、run-process はサブプロセスを実行するために フォークします。これはデフォルトの振る舞いです。flag が偽の場合、 run-process は直接 sys-exec を呼ぶので、それは返りません。

I/Oリダイレクト

run-process argument: redirects (iospec …)

子プロセスのI/Oをどのようにリダイレクトするかを指定します。 各iospecは次の形式のいずれかです。ここでfd, fd0およびfd1子プロセスにおけるファイルディスクリプタを指定する非負の整数です。

(註:簡単にコマンドを走らせて結果を文字列で得たい場合は、 process-output->stringが使えます(Process ports参照)。 また、複数のコマンドをパイプでつなぎたい場合は Running multiple processesを見てください。)

(< fd source)

sourceは文字列、シンボル、キーワード:null、整数、入力ポートのいずれかです。

文字列の場合、それはファイル名を指定します。そのファイルが読み込み用にオープンされ、 子プロセスはfdからその内容を読み込みます。指定されたファイルが存在しないか 読み込み用にオープンできなければエラーが報告されます。

シンボルの場合、一方向のパイプが作られ、その読み出し側の端が子プロセスのfdへと 接続されます。書き込み側の端は (process-input process source) を呼び出すことで入手できます。

:nullの場合、fdはシステムのヌルデバイスからの 読み込みになります。

整数の場合は、現プロセスの読み込み用ファイルディスクリプタを指定します。 そのファイルディスクリプタの読み込み元がdupされて、子プロセスからはfdとして 見えます。

入力ポートの場合は、入力元のファイルディスクリプタがdupされて 子プロセスのfdになります。ファイルディスクリプタを持たないポートを渡した 場合はエラーになります(ポート共通の操作port-file-number参照)。

(<< fd value)
(<<< fd obj)

valueまたはobjを子プロセスの入力ファイルディスクリプタfdへの 入力とします。

<<を使う場合、valueは文字列かユニフォームベクタ (see section gauche.uvector - ユニフォームベクタ)でなければなりません。その内容がそのまま 子プロセスの入力へと送られます。ユニフォームベクタはバイナリデータを渡すのに便利です。

<<<を使う場合、objには任意のSchemeオブジェクトを渡せます。 (write-to-string obj)の結果の文字列が子プロセスの入力へと送られます。

(<& fd0 fd1)

子プロセスのファイルディスクリプタfd0が、 同じく子プロセスのファイルディスクリプタfd1が指しているものと 同じ入力を参照するようにします。 <との違いに注意してください。(< 3 0)とすると、 現プロセスのstdin (ファイルディスクリプタ0) が子プロセスからはファイルディスクリプタ3 として見えるようになります。(<& 3 0)とすると、子プロセスのファイルディスクリプタ3 は子プロセスのstdinと同じものを見るようになります (それは他のiospecによって ファイル等にリダイレクトされているかもしれません)。

<&の処理の順番について、下の方にある註も参照してください。

(> fd sink)
(>> fd sink)

sinkは文字列、シンボル、キーワード:null、整数、あるいは ファイル出力ポートでなければなりません。

文字列の場合、それはファイル名です。指定されたファイルが書き込み用にオープンされ、 子プロセスのファイルディスクリプタfdからそこに書き込むことができます。 指定のファイルが既に存在している場合、>はファイルをまず空にするのに対し、 >>はファイルの後に追加します。 >>>の違いが出るのはsinkがファイルの場合のみです。

sink:nullなら、fdはシステムのヌルデバイスへの 書き出しになります。

整数の場合、それは現プロセスの書き込み用ファイルディスクリプタを 指定します。それがdupされて子プロセスのfdとなります。

出力ポートの場合は、出力先のファイルディスクリプタがdupされて 子プロセスのfdになります。ファイルディスクリプタを持たないポートを渡した 場合はエラーになります(ポート共通の操作port-file-number参照)。

(>& fd0 fd1)

子プロセスのファイルディスクリプタfd0が、子プロセスのファイルディスクリプタfd1 が参照するのと同じ出力先を参照するようにします。 >との違いに注意: (> 2 1)は子プロセスのstderrが現プロセスのstdout と同じところに送られるようにするのに対し、(>& 2 1)は子プロセスのstderr が子プロセスのstdoutに送られます (それは別のiospecによってリダイレクト されているかもしれません)。

 
;; 子プロセスのstdoutとstderrを一緒にして読み込む
(let1 p (run-process '(command arg)
                     :redirects '((>& 2 1) (> 1 out)))
  (begin0 (port->string (process-output p 'out))
          (process-wait p)))

註: 入力元や出力先にパイプを使う場合、同じ名前(シンボル)を複数の入出力に 指定することはできません。例えば次のコードはエラーとなります。

 
(run-process '(command) :redirects '((> 1 out) (> 2 out))) ; error!

複数の出力を一つの出力先にマージするには>&を、 一つの入力元を複数の入力で読み込みには<&を使ってください。

 
(run-process '(command) :redirects '((> 1 out) (>& 2 1)))

同じファイル名を複数の入出力に指定することは、Unixのシェル同様、可能です。 ただしその場合、ファイルはそれぞれの入出力ごとに別々にオープンされる ことに注意してください。例えば同じファイルを複数の出力からオープンして 書き込んだ場合、求める結果が得られないかもしれません(通常のファイルであれば、 一方の出力がもう一方の出力を上書きしてしまうでしょう)。

註: I/Oリダイレクト指定は、unixのシェルと違って、 全て同時に処理されます。つまり、以下の式はどちらも同じように、 stdoutとstderrをファイル‘out’に書き出す処理となります。

 
(run-process '(command arg) :redirects '((>& 2 1) (> 1 "out")))
(run-process '(command arg) :redirects '((> 1 "out") (>& 2 1)))

unixのシェルではリダイレクト指定は順に処理されるので、 次の二つのコマンドラインは異なる動作となります。最初の例では 子プロセスのstderrが現時点でのstdout (それは現プロセスのstdoutでもある) へと向けられ、次に子プロセスのstdoutがファイル‘out’に向けられます。 従ってエラーメッセージは現プロセスのstdoutへと現れます。二番目の例では 最初に子プロセスのstdoutがファイル‘out’へ向けられるので、 2>&1が処理される時にはstderrの行き先はやはり‘out’となります。

 
$ command arg 2>&1 1>out
$ command arg 1>out 2>&1

run-processredirects引数の順番にかかわらず 常に後者のように動作すると言ってもよいでしょう。

もし前者の例のように子プロセスのstderrだけを親プロセスのstdoutに 向けたいのであれば、次のように>を使うことで実現できます。

 
(run-process '(command arg) :redirects '((> 2 1) (> 1 "out")))
run-process argument: input source
run-process argument: output sink
run-process argument: error sink

サブプロセスの標準入出力を制御します。 source及びsinkは、文字列、キーワード :pipe、 キーワード:null、整数のファイルディスクリプタ、もしくはシンボルです。

実のところ、これらはredirects引数の略記にすぎません。

 
:input x   ≡ :redirects '((< 0 x))
:output x  ≡ :redirects '((> 1 x))
:error x   ≡ :redirects '((> 2 x))

キーワード:pipeは互換性のためだけにサポートされています。 それぞれ、シンボルstdinstdoutstderrが 指定されたかのように振る舞います。

 
:input :pipe   ≡ :redirects '((< 0 stdin))
:output :pipe  ≡ :redirects '((> 1 stdout))
:error :pipe   ≡ :redirects '((> 2 stderr))

すなわち、パイプが作成され、その一方が子プロセスの標準入出力のいずれかに接続されます。 もう一方の端は(process-input process)(process-output process)および (process-error process)によって得ることができます。 (process-input, process-outputname引数が 省略されるとそれぞれstdin, stdoutをデフォルトとし、 また(process-error p)(process-output p 'stderr)と 等価だからです)

引数の意味の詳しい説明については上のredirectsの項を参照してください。

実行環境

run-process argument: directory directory

directoryに文字列が与えられた場合、 そのディレクトリが起動されるプロセスのワーキングディレクトリとなります。 #fが与えられた場合はの引数は何もしません。 文字列か#f以外が与えられた場合、もしくは文字列が存在するディレクトリの 名前でない場合はエラーが報告されます。

hostキーワード引数も与えられている場合、この引数は リモートプロセスのワーキングディレクトリを指定します。

註: run-processdirectoryが有効な値であることを事前に チェックしますが、実際のchdir(2)exec(2)の直前に 行われます。事前のチェックにもかかわらずchdirが失敗する可能性が あります。その時点ではrun-processの呼び出し元にエラーを伝える 確実な方法が無いため、Gaucheは標準エラー出力にメッセージを印字して exitします。頑健なプログラムを書く場合、そのようなケースにも留意して下さい。

run-process argument: sigmask mask

mask<sys-sigset>のインスタンス、整数のリスト、 あるいは#fでなければなりません。 <sys-sigset>のインスタンスである場合、それが実行する プロセスのシグナルマスクになります。整数のリストの場合は各整数が マスクすべきシグナル番号とみなされます。マルチスレッドアプリケーションで run-processを使う場合はシグナルマスクを適切に設定することが重要です。 sys-execの説明を参照して下さい (プロセス管理)。

hostキーワード引数が与えられている場合は、この引数は ローカル側のプロセス(ssh)のみのシグナルマスクをセットします。

run-process argument: detached flag

真の値が渡されると、作られるプロセスは親プロセスのプロセスグループから 切り離され、独自のプロセスグループを作ります。 デーモンプロセスを作る際に便利です。detached引数の詳しい動作については、 sys-fork-and-exec を見てください (プロセス管理参照)。

run-process argument: host hostspec

この引数は、commandをリモートホストで実行させるのに使います。 hostspecの完全な構文はprotocol:user@hostname:portで、 protocol:user@:portの部分は省略可能です。

protocolはリモートに接続するプロトコルを指定します。現在のところ sshだけがサポートされており、また省略された場合もsshが 使われます。userはリモートでのユーザ名を、hostnameは リモートホスト名を指定します。portprotocolのデフォルト 以外のポートを使いたい場合に指定します。

コマンドライン引数はリモートホスト上で解釈されます。 一方、I/Oリダイレクトはローカル側で処理されす。 例えば、次のコードはリモートマシンの‘/foo/bar’の内容を読み、 それをローカルのワーキングディレクトリ内のファイル‘baz’へとコピーします。

 
(run-process '(cat "bar")
             :host "remote-host.example.com"
             :directory "/foo"
             :output "baz")

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.24.2 Running multiple processes

Function: run-process-pipeline commands :key input output error wait directory sigmask

A convenience routine to run pipeline of processes at once. Example:

 
(run-process-pipeline '((ls "src/")
                        (grep "\\.c$")
                        (wc -l)))

This is equivalent to shell command pipeline ls src/ | grep '\.c$' | wc -l, i.e. shows the number of C source files in the ‘src’ subdirectory.

The commands argument is a list of lists. Each list must be cmd/args argument run-process can accept. At least one command must be specified.

The specified commands are run concurrently, with the stdout of the first command is connected to the stdin of the second, and stdout of the second to the stdin of the third, and so on. The stdin of the first command is fed from the source specified by the input keyword argument, and the stdout of the last command is sent to the sink specified by the output keyword argument. The default values of these are the calling process’s stdin and stdout, respectively. See run-process, for the possible values of these arguments.

The stderr of all the processes are sent to the sink specified by the error keyword argument, which is defaulted by the calling process’s stderr.

The wait keyword argument specifies whether run-process-pipeline waits for the completion of the last process. If a true value is given, run-process-pipeline won’t return until the last process exits. If it is #f, run-process-pipeline returns immediately after all the processes are spawned.

The directory and sigmask keyword arguments are applied to all the processes; see run-process for the description of these arguments.

The return value of run-process-pipeline is a list of <process> objects, in the order as given to commands arguments.

Note that the exit status of processes won’t be automatically taken (except for the last one, when wait is true), so the caller must call process-wait on those process objects to clean up the processes.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.24.3 Process object

Class: <process>

子プロセスの状態を保持するためのオブジェクト。以下で説明される run-process 手続きにより、プロセスを作ることができます。 次章で説明するプロセスポートもプロセスオブジェクトを用いています。

<process>クラスは、run-processopen-input-process といった高レベルAPIで作られた子プロセスの状態を管理しています。 それらの子プロセスの終了ステータスをとるには、 process-waitprocess-wait-anyといった 高レベルAPIを利用してください。これらの手続きはシステムコール以外の情報管理も 行います。sys-waitsys-waitpidといった低レベルAPIで 直接子プロセスの終了ステータスを取ると、<process>クラスの 内部状態に矛盾が生じます。

Class: <process-abnormal-exit>

主にプロセスポートユーティリティ関数で使われるコンディション型。 <error>を継承。このコンディション型は高レベルプロセスポートユーティ リティが子プロセスが非ゼロのexitステータスで終了したことを検知したとき に投げられます。

Instance Variable of <process-abnormal-exit>: process

プロセスオブジェクト。

注: Unix用語では,exitステータスにかかわらず,プロセスがcalling exit(2)を呼ぶか,main()から帰った場合を「正常な終了」と しています。コマンドによっては非ゼロのexitステータスで何らかの正常な実行結果を 示すものもあります(grep(1)など)。しかし,ほとんどのコマ ンドでは,非ゼロの exit ステータスは要求された操作が実行できなかったこ とを表わします。それゆえ上のような場合を例外的な場合として扱います。

Function: process? obj

(is-a? obj <process>)

Method: process-pid (process <process>)

サブプロセス process のプロセスIDを返します。

Method: process-command (process <process>)

サブプロセス process 内で起動されたコマンドを返します。

Method: process-input (process <process>) :optional name
Method: process-output (process <process>) :optional name

プロセスの入力もしくは出力に一方の端がつながれたパイプの、もう一方の端を取り出します。 namerun-processredirects引数に与えた 識別用の名前です。次の例を見てください。

 
(let1 p (run-process '(command arg)
                     :redirects '((< 3 aux-in)
                                  (> 4 aux-out)))
  (let ([auxin  (process-input p 'aux-in)]
        [auxout (process-output p 'aux-out)])
    ;; feed something to the child's input
    (display 'something auxin)
    ;; read data from the child's output
    (read-line auxout)
    …
    )
  (process-wait p))

シンボルaux-inaux-outがパイプを識別するのに 使われています。process-inputが返すのは出力ポートであり、 process-outputが返すのは入力ポートであることに注意してください。

nameが省略された場合、process-inputstdinを、 process-outputstdoutを使います。これらは 子プロセスの標準入力/出力をそれぞれ:input :pipe/:output :pipeで リダイレクトした場合に使われる名前です。

名前に対応するパイプが無い場合は#fが返ります。

 
(let* ((process (run-process '("date") :output :pipe))
       (line (read-line (process-output process))))
  (process-wait process)
  line)
 ⇒ "Fri Jun 22 22:22:22 HST 2001"
Method: process-error (process <process>)

これは(process-output process 'stderr)と等価です。

Function: process-alive? process

process が生きている場合は真を返します。process-wait によって 明示的にチェックされない限り、Gauche はサブプロセスのステータスを知ることが できないことに注意してください。

Function: process-list

アクティブなプロセスのリストを返します。プロセスは、その終了ステータスが process-wait によって明示的に回収されない場合は、アクティブなまま 残ります。 ひとたび終了ステータスが回収され、プロセスの状態がインアクティブに 変更されると、そのプロセスはprocess-listが返すリストからは除かれます。

Function: process-wait process :optional nohang error-on-nonzero-status

サブプロセス process の終了ステータスを取得し、process のstatusスロットに値を格納します。statusスロットの値は process-exit-statusで得ることができます。

デフォルトでは、この手続きはprocess が終了するまで実行を一時停止します。 しかし、nohangに真の値が与えられた場合は、processが終了して いない場合にも直ちに返ります。

オプショナル引数error-on-nonzero-statusに真の値が与えられた場合、 この手続きは得られた終了ステータスが0で無い場合に <process-abnormal-exit>エラーを投げます。

この呼び出しによってprocessの終了ステータスが実際に取得された場合は #tを、そうでなければ#fを返します。

Function: process-wait-any :optional nohang

run-processで作られたサブプロセスのどれかの終了ステータスを取得します。 終了ステータスが取得できたプロセスのプロセスオブジェクトを返します。

真の値がnohangに与えられた場合は、どの子プロセスも終了していない場合は 直ちに#fを返します。そうでなければ、この手続きはいずれかの子プロセスが 終了するまで待ちます。

子プロセスが存在しない場合は、この手続きは直ちに#fを返します。

Function: process-exit-status process

process-waitによって取得されたprocessの終了ステータスを 返します。processに対してprocess-waitを呼ぶ前にこの手続きを 呼んだ場合の結果は未定義です。

終了ステータスの解釈はプラットフォームに依存します。プロセスが自発的に (exitを呼んで)終了したか、それともシグナルによって終了させられたかを 確かめるにはsys-wait-exited?sys-wait-signaled?を 使ってください。また、終了コードもしくは終了と原因となったシグナルを 知るにはsys-wait-exit-statussys-wait-termsig 使ってください (プロセス管理参照)。

Function: process-send-signal process signal

サブプロセス process にシグナル signal を送ります。 signal は正確整数のシグナルナンバーでなければなりません。 シグナルの定義済み変数については、シグナルを参照して下さい。

Function: process-kill process
Function: process-stop process
Function: process-continue process

それぞれ、process に、SIGKILL、SIGSTOP、SIGCONT を送ります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.24.4 Process ports

Function: open-input-process-port command :key input error encoding conversion-buffer-size

command を子プロセスで非同期に実行します。 走らせた子プロセスの標準出力につながれた入力ポートと、 プロセスオブジェクトの二つの値を返します。

commandは文字列かリストです。

文字列の場合、それは/bin/shに渡されます。 環境変数の置換やグロブパターン、リダイレクトなどのシェルの機能が 文字列中で使えます。 文字列をつなぎ合わせてコマンドラインを作成する場合、 特殊文字をシェルに解釈してほしくなければ、それを正しくエスケープするのは 呼び出し元の責任です。下で説明するshell-escape-stringは 助けになるかもしれません。

commandがリストの場合は、各要素がx->stringで文字列に 変換された後に、sys-execを使って直接コマンドを起動します (リストのcarがコマンドのパス名とargv[0]の両方に使われます)。 シェルの介入を避けたい場合はこの形式を使うと良いでしょう。 特殊文字をエスケープする必要はありません。

デフォルトでは、子プロセスの標準入力は/dev/nullにリダイレクトされ、 標準エラー出力は呼び出したプロセスと共有されます。 inputerrorキーワード引数にパス名を与えることで、 これらの出力をリダイレクトすることができます。

また、プロセスの出力の文字エンコーディングを指定するために encodingキーワード引数を与えることもできます。 それがGaucheの内部エンコーディングと異なっていた場合、 open-input-process-portは文字コード変換ポートを挿入します。 encodingが与えられた場合、conversion-buffer-sizeキーワード引数で 変換バッファの大きさを指定することも可能です。文字コード変換の詳細については gauche.charconv - 文字コード変換を参照して下さい。

 
(receive (port process) (open-input-process-port "ls -l Makefile")
  (begin0 (read-line port)
          (process-wait process)))
 ⇒ "-rw-r--r--   1 shiro    users        1013 Jun 22 21:09 Makefile"

(receive (port process) (open-input-process-port '(ls -l "Makefile"))
  (begin0 (read-line port)
          (process-wait process)))
 ⇒ "-rw-r--r--   1 shiro    users        1013 Jun 22 21:09 Makefile"

(open-input-process-port "command 2>&1")
 ⇒ ;the port reads both stdout and stderr

(open-input-process-port "command 2>&1 1>/dev/null")
 ⇒ ;the port reads stderr

サブプロセスの終了ステータスは自動的に回収されません。 process-wait を呼ぶことは呼び出し側の責任であり、これを怠ると サブプロセスはゾンビプロセスになります。それが面倒であれば、以下の 手続きを使うことができます。

Function: call-with-input-process command proc :key input error encoding conversion-buffer-size on-abnormal-exit

子プロセスでcommand を実行し、その標準出力と入力ポートを パイプで繋ぎ、そのポートを引数として proc を呼び出します。 proc が返るとその終了ステータスを回収し、proc が返した 結果を返します。proc がエラーを通知しても、クリーンアップは 行われます。

キーワード引数on-abnormal-exitは子プロセスが0以外の終了ステータス を返した場合の振舞いを指定します。その値は:error(デフォルト)、 :ignore、もしくは一引数の手続きでなければなりません。 値が:errorの場合、0以外の終了ステータスは <process-abnormal-exit>エラーコンディションを発生させます。 コンディションオブジェクトのprocessスロットには子プロセスオブジェクトが 保持されます。値が:ignoreの場合、0以外の終了ステータスに対して 特別なアクションは取られません。値が手続きの場合、0以外の終了ステータスに対して 子プロセスオブジェクトを引数にしてその手続きが呼ばれます。その手続きが 戻れば、call-with-input-processは正常動作と同じように戻ります。

commandおよび他のキーワード引数の意味はopen-input-process-portと 同じです。

 
(call-with-input-process "ls -l *"
  (lambda (p) (read-line p)))
Function: with-input-from-process command thunk :key input error encoding conversion-buffer-size on-abnormal-exit

子プロセスで command を実行し、コマンドの標準出力に 接続された現在の入力ポートとともに thunk を呼び出します。 thunkが終了するかエラーを投げた後に、コマンドの終了ステータスが 回収されます。

commandおよびキーワード引数の意味はcall-with-input-processと 同じです。

 
(with-input-from-process "ls -l *" read-line)
Function: open-output-process-port command :key output error encoding conversion-buffer-size

子プロセスで command を非同期に実行します。 子プロセスの標準入力に接続された出力ポートと、 プロセスオブジェクトの二つの値を返します。

command引数、およびencodingconversion-buffer-sizeの 意味は、open-input-process-portと同じです。

デフォルトでは、子プロセスの標準出力は/dev/nullにリダイレクトされ、 標準エラー出力は呼び出したプロセスと共有されます。 outputerrorキーワード引数にパス名を与えることで、 これらの出力をリダイレクトすることができます。

サブプロセスの終了ステータスは自動的には回収されません。 適切なタイミングで、サブプロセスに対して process-wait を呼ぶ 必要があります。

Function: call-with-output-process command proc :key output error encoding conversion-buffer-size on-abnormal-exit

command を子プロセスで実行し、コマンドの標準入力に 接続された出力ポートとともに proc を呼び出します。 コマンドの終了ステータスは、proc が返るかエラーを通知した 後に回収されます。

キーワード引数の意味はopen-output-process-portと同じです。 ただしon-abnormal-exitについてはcall-with-input-process で説明したのと同じ意味です。

 
(call-with-output-process "/usr/sbin/sendmail"
  (lambda (out) (display mail-body out)))
Function: with-output-to-process command thunk :key output error encoding conversion-buffer-size on-abnormal-exit

コマンドの標準入力に接続された出力ポートが、thunk の実行中は 現在の出力ポートにセットされることを除いて、call-with-output-process と同じです。

Function: call-with-process-io command proc :key error encoding conversion-buffer-size on-abnormal-exit

command をサブプロセスで実行し、proc を2つの引数と ともに呼び出します。最初の引数は入力ポートで、コマンドの標準出力に 接続されたものです。2番目の引数は出力ポートでコマンドの標準入力に 接続されたものです。コマンドからのエラー出力は、errorキーワード 引数でパス名が指定されない限り、呼び出したプロセスのエラー出力が共有されます。

コマンドの終了ステータスは、procが戻るかエラーを投げた場合に 回収されます。

Function: process-output->string command :key error encoding conversion-buffer-size on-abnormal-exit
Function: process-output->string-list command :key error encoding conversion-buffer-size on-abnormal-exit

command を実行し、その(標準出力への)出力を回収して返します。 process-output->stringcommand からの全ての出力を連結し 1つの文字列とします。その際、空白文字からなるシーケンスは1つの空白に 置換されます。このアクションは、シェルスクリプトにおける「コマンド置換」 に似たものです。 process-output->string-listcommand からの出力を行ごとに 回収し、それらをリストにしたものを返します。改行文字は削除されます。

内部的には、commandcall-with-input-process により 実行されます。キーワード引数はcall-with-input-processに そのまま渡されます。

 
(process-output->string '(uname -smp))
  ⇒ "Linux i686 unknown"

(process-output->string '(ls))
  ⇒ "a.out foo.c foo.c~ foo.o"

(process-output->string-list '(ls))
  ⇒ ("a.out" "foo.c" "foo.c~" "foo.o")
Function: shell-escape-string str :optional flavor

strがシェルのコマンドライン引数解析に影響を与える文字を含んでいる場合、 それらがシェルによって解釈されないようにエスケープされた文字列を返します。 そうでなければstr自体を返します。

省略可能なflavor引数はシンボルを取り、プラットフォームを指定します。 現在はwindowsposixが指定可能です。シェルが エスケープやクオートを処理する方法がこの二つのプラットフォームで大きく 異なるからです。windowsフレーバーの場合はMVCSランタイムの 引数パージングに合わせ、posxiフレーバーの場合はIEEE Std 1003.1に 合わせます。 省略された場合は、プロセスが走っているプラットフォームが デフォルトの値となります。(Cygwinはposixとみなされます。)

自分でコマンドライン文字列を組み立てる必要がある場合に使ってください。 (単一のコマンドライン文字列でなく、コマンドライン引数のリストを渡す場合は エスケープの必要はありません。引数はシェルを通さずに子プロセスに渡される からです。)

Function: shell-tokenize-string str :optional flavor

シェルがやるように、文字列strを引数リストに分割します。

 
(shell-tokenize-string "grep -n -e \"foo bar\" log")
 ⇒ ("grep" "-n" "-e" "foo bar" "log")

省略可能なflavor引数はシンボルwindowsposixを取り、 構文を指定します。windowsの場合はMVCSランタイムの 引数パージングに合わせ、posxiフレーバーの場合はIEEE Std 1003.1 Shell Command Languageに合わせます。 省略された場合は、プロセスが走っているプラットフォームが デフォルトの値となります。(Cygwinはposixとみなされます。)

この手続きはシェルの変数置換のような高度な機能は持っていません。 もしそういった解釈が必要なメタ文字に出会った場合はエラーが投げられます。 言い換えれば、メタ文字はstrの中で全て適切にクオートされている 必要があります。

 
(shell-tokenize-string "echo $foo" 'posix)
  ⇒ signals error

(shell-tokenize-string "echo \"$foo\"" 'posix)
  ⇒ still signals error

(shell-tokenize-string "echo '$foo'" 'posix)
  ⇒ ("echo" "$foo")

(shell-tokenize-string "echo \\$foo" 'posix)
  ⇒ ("echo" "$foo")

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.25 gauche.record - レコード型

Module: gauche.record

This module provides a facility to define record types, user-defined aggregate types. The API is upper compatible to SRFI-9 (Defining Record Types) and SRFI-99 (ERR5RS Records).

Record types are implemented as Gauche’s classes, but have different characteristics from the general classes. See section はじめに, for when you want to use record types.

The record API consists of three layers, following SRFI-99 and R6RS design.

The syntactic layer is the define-record-type macro that conveniently defines a record type and related procedures (a constructor, a predictate, accessors and modifiers) all at once declaratively. Knowing this macro alone is sufficient for most common usage of records.

The inspection layer defines common procedures to query information to the records and record types.

The procedural layer is a low-level machinery to implement the syntactic layer; you don’t usually need to use them in day-to-day programming, but they might be handy to create record types on-the-fly at runtime.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.25.1 はじめに

Gauche provides a general way for users to define new types as new classes, using object system (see section オブジェクトシステム), and indeed record types are implemented as Gauche’s classes. However, using record types instead of classes has several advantages.

The disadvantage of record types is that they don’t obey Gauche’s class redefinition protocol (see section クラスの再定義). That is, if you redefine a record with the same name, it creates a new record type unrelated to the old one. The record instances created from the old definition won’t be updated according to the new definition.

More importantly, record constructors, accessors and modifiers are tend to be inlined where they are used, to achieve better performance. Since they are inlined, the code that uses those procedures are not affected when the record type is redefined. This means if you redefine a record type, you have to reload (recompile) the sources that uses any of record constructors, accessors or modifiers.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.25.2 Syntactic Layer

Macro: define-record-type type-spec ctor-spec pred-spec field-spec …

[SRFI-9][SRFI-99+] Defines a record type, and optionally defines a constructor, a predicate, and field accessors and modifiers.

The type-spec argument names the record type, and optionally specifies the supertype (parent).

 
type-spec : type-name | (type-name parent)

type-name : identifier
parent : expression

The type-name identifier will be bound to a record type descriptor, or rtd, which can be used for introspection and reflection. See Inspection layer and Procedural layer for possible operations for record types. In Gauche, a record type descriptor is a <class> with a metaclass <record-meta>.

The parent expression should evaluate to a record type descriptor. If given, the defined record type inherits it; that is, all the slots defined in the parent type are available to the type-name as well, and the instance of type-name answers #t to the predicate of the parent type.

Since a record type is also a class, parent type is also a superclass of the defined record type. However, record types are limited to have single inheritance.

You can give a pseudo record base type as parent to define a pseudo record type, which allows you to access ordinary aggregates like vectors as records. See Pseudo record types for more details.

The ctor-spec defines the constructor of the record instance.

 
ctor-spec : #f | #t | ctor-name
          | (ctor-name field-name …)

ctor-name : identifier
field-name : identifier

If it is #f, no constructor is created. If it is #t, a default constructor is created with a name make-type-name. If it is a single identifier ctor-name, a default constructor is created with the name. The default constructor takes as many arguments as the number of fields of the record, including inherited ones if any. When called, it allocates an instance of the record, and initialize its fields with the given arguments in the order (inherited fields comes first), and returns the record.

The last variation of ctor-spec creates a custom constructor with the name ctor-name. The custom constructor takes as many arguments as the given field-names, and initializes the named fields. If the inherited record type has a field of the same name as the ancestor record type, only the inherited ones are initialized. In Gauche, uninitialized fields remains unbound until some value is set to it.

The pred-spec defines the predicate of the record instance, which takes one argument and returns #t iff it is an instance of the defined record type or its descendants.

 
pred-spec : #f | #t | pred-name

pred-name : identifier

If it is #f, no predicate is created. If it is #t, a predicate is created with a name type-name?. If it is a single identifier, a predicate is created with the given name.

The rest of the arguments specify fields (slots) of the record.

 
field-spec
 : field-name   ; immutable, with default accessor
 | (field-name) ; mutable, with default accessor/modifier
 | (field-name accessor-name); immutable
 | (field-name accessor-name modifier-name); mutable

field-name    : identifier
accessor-name : identifier
modifier-name : identifier

The first and the third forms define immutable fields, which can only be intialized by the constructor but cannot be modified afterwards (thus such fields don’t have modifiers). The second and the fourth forms define multable fields.

The third and fourth forms explicitly name the accessor and modifier. With the first and second forms, on the other hand, the accessor is named as type-name-field-name, and the modifier is named as type-name-field-name-set!.

Let’s see some examples. Here’s a definition of a record type point.

 
(define-record-type point #t #t
  x y z)

The variable point is bound to a record type descriptor, which is just a class. But you can take its class and see it is indeed an instance of <record-meta> metaclass.

 
point            ⇒ #<class point>
(class-of point) ⇒ #<class <record-meta>>

You can create an instance of point by the default constructor make-point. The predicate is given the default name point?, and you can access the fields of the created record by point-x etc.

 
(define p (make-point 1 2 3))

(point? p)  ⇒ #t
(point-x p) ⇒ 1
(point-y p) ⇒ 2
(point-z p) ⇒ 3

Since we defined all fields immutable, we cannot modify the instance p.

Here’s a mutable version of point, mpoint. You can modify its fields by modifier procedures and generalized set!.

 
(define-record-type mpoint #t #t
  (x) (y) (z))

(define p2 (make-mpoint 1 2 3)) ; create an instance

(mpoint-x p2)  ⇒ 1

(mpoint-x-set! p2 4)            ; default modifier
(mpoint-x p2)  ⇒ 4

(set! (mpoint-x p2) 6)          ; generalized set! also works
(mpoint-x p2)  ⇒ 6

Next one is an example of inheritance. Note that the default constructor takes arguments for fields of the parent record as well.

 
(define-record-type (qpoint mpoint) #t #t
  (w))

(define p3 (make-qpoint 1 2 3 4))

(qpoint? p3)  ⇒ #t      ; p3 is a qpoint
(mpoint? p3)  ⇒ #t      ; ... and also an mpoint

(mpoint-x p3) ⇒ 1       ; accessing inherited field
(mpoint-y p3) ⇒ 2
(mpoint-z p3) ⇒ 3
(qpoint-w p3) ⇒ 4

A small caveat: Accessors and modifiers for inherited fields (e.g. qpoint-x etc.) are not created.

Gauche’s convention is to enclose class name by <>. You can follow the convention and still explicitly gives simpler names (instead of make-<point> or <point>-x):

 
(define-record-type <point> make-point point?
  (x point-x)
  (y point-y)
  (z point-z))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.25.3 Inspection layer

This layer provides common procedures that operates on record type descriptors and record instances.

Note that a record type descriptor is a class in Gauche, so you can also use operators on classes (e.g. class-name, class-slots etc.) on record type descriptors as well. However, these procedures are more portable.

Function: record? obj

[SRFI-99][R6RS] Returns #t iff obj is an instance of record type, #f otherwise.

Function: record-rtd record

[SRFI-99][R6RS] Returns the record type descriptor of the record instance.

Function: rtd-name rtd

[SRFI-99] Returns the name of the record type descriptor rtd.

Function: rtd-parent rtd

[SRFI-99] Returns the parent type of the record type descriptor rtd. If rtd doesn’t have a parent, #f is returned.

Function: rtd-field-names rtd

[SRFI-99] Returns a vector of symbols, each of which is the names of the direct fields of the record represented by rtd. The result doesn’t include inherited fields.

Function: rtd-all-field-names rtd

[SRFI-99] Returns a vector of symbols, each of which is the names of the fields of the record represented by rtd. The result includes all inherited fields.

Function: rtd-field-mutable? rtd field-name

[SRFI-99] Returns #t iff the field with the name field-name of a record represented by rtd is mutable.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.25.4 Procedural layer

These procedures are low-level machinery on top of which define-record-type is implemented. They can be used to create a new record type at runtime.

Function: make-rtd name field-specs :optional parent

[SRFI-99] Creates and returns a new record type descriptor with name name and having fields specified by field-specs. If parent is given, it must be a record type descriptor or #f. If it is a record type descriptor, the created record type inherits from it.

The field-specs argument must be a vector, each element of which is a field specifier. A field specifier can be a symbol, a list (mutable symbol), or a list (immutable symbol). The symbol names the field. A single symbol or (mutable symbol) format makes the field mutable, and (immutable symbol) format makes the field immutable.

Note: Gauche does not implement the extension suggested in SRFI-99 yet, which is sealed, opaque and uid arguments.

Function: rtd? obj

[SRFI-99] Returns #t if obj is a record type descriptor, #f otherwise.

Function: rtd-constructor rtd :optional field-specs

[SRFI-99] Returns a procedure that creates an instance record of the record type represented by rtd. Without field-specs, it returns the default constructor, which takes as many arguments as the number of fields of the record to initialize them.

You can give a vector of symbols as field-specs. The n-th symbol specifies which field of the instance should be initialized by the n-th argument. The field-specs vector cannot contain duplicate names. If the record type defines a field with the same name as the one in the parent record type, the custom constructor can only initialize the field of the derived type’s instance.

Function: rtd-predicate rtd

[SRFI-99] Returns a predicate to test an object is an instance of rtd.

If rtd is a pseudo record type, the predicate merely tests the given object is in an appropriate type and has enough size to hold the contents. See Pseudo record types for the details.

Function: rtd-accessor rtd field-name

[SRFI-99] Returns a procedure that takes one argument, an instance of rtd, and returns the value of the field-name of the instance.

An error is signaled if the record type doesn’t have the field of name field-name.

If rtd is inherits other record types, and it defines a field of the same name as inherited ones, then the accessor returned by this procedure retrieves the value of the field of the derived record.

Function: rtd-mutator rtd field-name

[SRFI-99] Returns a procedure that takes two arguments, an instance of rtd and a value, and sets the latter as the value of the field-name of the instance.

An error is signaled if the record type doesn’t have the field of name field-name, or the named field is immutable.

Like rtd-accessor, if the record has a field with the same name as inherited one, the modifier returned by this procedure only modifies the field of the derived record.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.25.5 Pseudo record types

A pseudo record type is a record type that does not create an instance of its own type. Instead it treats an object of other collection types, such as a vector, as if it had named fields. It’s easier to understand by an example:

 
(define-record-type (vpoint (pseudo-rtd <vector>)) #t #t
  (x) (y) (z))

(make-vpoint 1 2 3)  ⇒ #(1 2 3)
(vpoint-x '#(1 2 3)) ⇒ 1

(rlet1 v (make-vpoint 1 2 3)
  (set! (vpoint-y v) -1))
 ⇒ #(1 -1 3)

To create a pseudo record type, specify another pseudo record type as a parent. The procedure pseudo-rtd can be used to obtain a base pseudo record type of the suitable instance class.

Function: pseudo-rtd instance-class

Returns a pseudo rtd suitable to use instance-class as a pseudo record.

Currently, <list>, <vector>, and uniform vector classes (<u8vector> etc.) are supported as instance-class.

The predicates of a pseudo record return #t if the given object can be interpreted as the pseudo record. In the above example of vpoint record, the predicate vpoint? returns #t iff the given object is a vector with 3 or more elements:

 
(vpoint? '#(0 0 0))   ⇒ #t
(vpoint? '#(0 0))     ⇒ #f
(vpoint? '(0 0 0))    ⇒ #f
(vpoint? '#(0 0 0 0)) ⇒ #t

We allow more elements so that the pseudo record can be used to interpret the header part of the longer data.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.26 gauche.reload - モジュールの再ロード

Module: gauche.reload

開発のサイクルのなかで、モジュールを再ロードしなければならないことは 頻繁におこります。このモジュールはそれを支援するものです。

プログラムのいくつかの部分のセマンティクスはモジュールのロードの順に 依存します。それで、任意のモジュールを再ロードすることはプログラムの ふるまいを予期せぬものに変えてしまう可能性があります。このモジュールは 自分がなにをしようとしているか判っている開発者向けのものです。

再定義ルール: モジュールを再ロードすると、デフォルトでは、その モジュール内のすべての束縛はリセットされます。 しかしながら、場合によってこれは望ましいことではありません。たとえば、 中間結果をある変数に残したままにしておきたい場合です。 再ロード手続きにどの束縛を保存するかを決めるためのルールを指定することが できます。

このルールは以下の構文で記述します。

 
  <module-rules> : (<module-rule> …)
  <module-rule>  : (<module-pattern> <rule> …)
  <module-pattern> : a symbol module name, or a symbol containing glob pattern
  <rule>         : procedure | symbol | regexp
                 | (and <rule> …)
                 | (or  <rule> …)
                 | (not <rule>)

<module-rules> はモジュールごとのルールをきめるグローバルルール です。<module-pattern> はモジュール名シンボルまたは(mylib.* のような)グロブパターンを含むシンボルのどちらかです。<rule> が 手続きであれば、述語として使われ、この述語を満す値の束縛は再定義から 保護されます。<rule> がシンボルであれば、それと同じ名前の 変数の束縛は保護されます。<rule> が正規表現の場合、この正規表現に マッチする名前の変数は保護されます。

再定義からの保護機構は、場当たり的なもので、セマンティクスとしても 不透明なものであることに注意してください。特にdefineの右辺式は 評価されてしまうので、そこで起きる副作用は効果を持ちます (例えば define-classはクラスの再定義を行ってしまいます)。 これは利便のためだけのものだと考えて下さい。 正確な振舞いを知りたければ、コードを見てください。

Function: reload module-name :optional rule …

指定されたモジュールを再ロードします。オプションで rule … をつかって再定義ルールを指定することができます。 ここで、それぞれの rule は上の <rule> で定義されたものです。

Function: reload-modified-modules :optional module-rules

前回ロードしたときより変更のあったモジュールを再ロードします。 オプション引数 module-rules が与えられていれば、再ロードした モジュールに適用する再定義ルールを決定するために使います。 module-rulesが省略された場合、現在のルールが適用されます。 デフォルトの現在のルールは空です。現在のルールは module-reload-rulesをつかって設定できます。

Function: module-reload-rules :optional module-rules

これは一種の「パラメータ」(gauche.parameter - パラメータ参照)で、 reload-modified-modulesに対応するデフォルトのモジュールルールを 保持しています。引数なしで呼ばれたときには、現在のモジュールルールを 返します。module-rulesとともに呼ばれたきにはその引数を 現在のモジュールルールにします。

Function: reload-verbose :optional flag

これは再ロード手続きのメッセージ出力レベルを制御するパラメータです。 引数なしで呼ばれると現在の出力レベルフラグを返します。flagと ともに呼ばれると現在の出力レベルをこれに設定します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.27 gauche.selector - 簡単なディスパッチャ

Module: gauche.selector

このモジュールは、sys-select (I/Oの多重化参照)に基づき、 登録されたハンドラにI/Oイベントをディスパッチするためのシンプルな インタフェースを提供します。

Class: <selector>

ディスパッチャのインスタンスで、ハンドラを携えてI/Oポートを監視します。 makeメソッドで新しいインスタンスを作れます。

Method: selector-add! (self <selector>) port-or-fd proc flags

セレクタにハンドラprocを追加します。procは、port-or-fd (ポートオブジェクトかシステムのファイルディスクリプタを表す整数)が flagsで指定された条件に合致した場合に呼ばれます。 flagsは、以下のシンボルの1つ以上のリストでなければなりません。

r

port-or-fdが読み取り可能になった時点でprocが呼ばれます。

w

port-or-fdが書き込み可能になった時点でprocが呼ばれます。

x

port-or-fdで例外的な状況が発生した場合にprocが呼ばれます。

procは、2引数で呼ばれます。1つ目はport-or-fdそのもので、 2つ目は条件を表すシンボル、rwあるいはxです。

同じ条件の下ですでにport-or-fdにハンドラが関連付けられていた場合は、 以前のハンドラがprocで置き換えられます。

Method: selector-delete! (self <selector>) port-or-fd proc flags

port-or-fdprocflagsにマッチするハンドラエントリを 削除します。引数のいずれかが#fである場合は、それが何でも良いことを 意味します。例えば、

 
(selector-delete! selector the-port #f #f)

the-portに関連付けられた全てのハンドラを削除します。

 
(selector-delete! selector #f #f '(w))

書き込み可能になるのを待っている全てのハンドラを削除します。

Method: selector-select (self <selector>) :optional (timeout #f)

ディスパッチャのボディです。selfに登録された条件を待ち、条件が合致すると それに関連付けられたハンドラを呼びます。 timeout引数が省略されるか偽の場合、このメソッドは永久に待ちます。 タイムアウトの値を与える場合は、マイクロ秒を表す実数か、秒とマイクロ秒を 表す2つの整数のリストを与えます。

戻り値は、ハンドラが呼ばれた回数です。0(ゼロ)は、セレクタがタイムアウト したことを意味します。

ハンドラの中でselfを変更することは安全です。その変更は、次回の selector-selectの呼び出し以降に反映されます。

echoサーバのシンプルな例を示します。

 
(use gauche.net)
(use gauche.selector)
(use gauche.uvector)

(define (echo-server port)
  (let ((selector (make <selector>))
        (server   (make-server-socket 'inet port :reuse-addr? #t)))

    (define (accept-handler sock flag)
      (let* ((client (socket-accept server))
             (output (socket-output-port client)))
        (selector-add! selector
                       (socket-input-port client :buffering #f)
                       (lambda (input flag)
                         (echo client input output))
                       '(r))))

    (define (echo client input output)
      (let ((str (read-uvector <u8vector> 4096 input)))
        (if (eof-object? str)
            (begin (selector-delete! selector input #f #f)
                   (socket-close client))
            (begin (write-uvector str output)
                   (flush output)))))

    (selector-add! selector
                   (socket-fd server)
                   accept-handler
                   '(r))
    (do () (#f) (selector-select selector))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.28 gauche.sequence - シーケンスフレームワーク

Module: gauche.sequence

シーケンスに関するジェネリックな操作を提供するモジュールです。 シーケンスとは、コレクションのうち要素の順序が規定されているものです。 コレクションの操作全てに加え、シーケンスに対しては、 各要素に整数のインデックスを関連づけること、 それから要素の順序が影響する操作を適用することができます。

このモジュールはgauche.collectionを継承しています (gauche.collection - コレクションフレームワーク参照)。 コレクションに使えるジェネリックな操作は全てシーケンスに対しても適用可能です。

Gauche組み込みクラスのうち、リスト、ベクター、そして文字列は シーケンスであり、このモジュールでメソッドが定義されます。 またgauche.uvectorのユニフォームベクタ等、 いくつかの拡張データタイプはシーケンスとなっています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.28.1 基本的なシーケンスのアクセサ

Method: ref (seq <sequence>) index :optional fallback

シーケンスseqindex番目の要素を返します。 このメソッドによって、全てのシーケンスが統一的にアクセスできます。

indexが負値だったりシーケンスのサイズ以上だった場合は、 fallbackが与えられていればそれが返され、 そうでなければエラーとなります。

 
(ref '(a b c) 1)  ⇒ b
(ref '#(a b c) 1) ⇒ b
(ref "abc" 1)     ⇒ #\b
Method: (setter ref) (seq <sequence>) index value

統一的なシーケンスの変更メソッドです。 シーケンスseqindex番目の要素にvalueをセットします。

註: シーケンスによってはインデックスによる変更をサポートしていない 場合があります。例えば「ソートされた整数」を表すシーケンスがあった場合、 i番目の要素を適当な整数で置き換えることはできないでしょう。 そのようなシーケンスでも、要素の挿入や削除など、別の方法でシーケンスを 変更する手段が与えられるかもしれません。

 
(let ((x (list 'a 'b 'c)))
  (set! (ref x 1) 'z)
  x) ⇒ (a z c)

(let ((x (vector 'a 'b 'c)))
  (set! (ref x 1) 'z)
  x) ⇒ #(a z c)

(let ((x (string #\a #\b #\c)))
  (set! (ref x 1) #\z)
  x) ⇒ "azc"
Method: referencer (seq <sequence>)
Method: modifier (seq <sequence>)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.28.2 シーケンスのスライス

Method: subseq (seq <sequence>) :optional start end

シーケンスseqの、start番目の要素からend番目の要素の直前 までの部分シーケンスを返します。endが省略された場合はシーケンスの 最後までが取られます。返されるシーケンスの型はseqと同じになります。

 
(subseq '(a b c d e) 1 4)   ⇒ (b c d)
(subseq '#(a b c d e) 1 4)  ⇒ #(b c d)
(subseq "abcde" 1 4)        ⇒ "bcd"

(subseq '(a b c d e) 3)     ⇒ (d e)
Method: (setter subseq) (seq <sequence>) start end value-seq
Method: (setter subseq) (seq <sequence>) start value-seq

value-seqの各要素を、シーケンスseqstart番目から end番目の直前まで順にセットします。 value-seqはどんなシーケンスでも構いませんが、 (end - start) よりは長くなくてはなりません。

2番目の形式では、endvalue-seqの長さから算出されます。

 
(define s (vector 'a 'b 'c 'd 'e))
(set! (subseq s 1 4) '(4 5 6))
s ⇒ #(a 4 5 6 e)
(set! (subseq s 0)   "ab")
s ⇒ #(#\a #\b 5 6 e)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.28.3 シーケンス上のマップ

シーケンスはまたコレクションでもあるので、シーケンスについて foldmapfor-eachや他のジェネリック関数を 拡張することができます。しかし、時にはイテレーション中に要素そのものと そのインデックスを知りたいことでしょう。そのためのジェネリック関数が いくつかあります。

Method: fold-with-index kons knil (seq <sequence>) …

ジェネリックなfoldと似ていますが、konsにはseqの インデックス内から、第1引数としてseqの要素と増加する値が渡る 点が異なります。

 
(fold-with-index acons '() '(a b c))
  ⇒ ((2 . c) (1 . b) (0 . a))
Method: map-with-index proc (seq <sequence>) …
Method: map-to-with-index class proc (seq <sequence>) …
Method: for-each-with-index proc (seq <sequence>) …

mapmap-tofor-eachと似ていますが、procが 第1引数としてインデックスを受け取る点が違います。

 
(map-with-index list '(a b c d) '(e f g h))
  ⇒ ((0 a e) (1 b f) (2 c g) (3 d h))

(map-to-with-index <vector> cons '(a b c d))
  ⇒ #((0 . a) (1 . b) (2 . c) (3 . d))
Method: find-with-index pred (seq <sequence>)

findのように、seqの中でpredを満足する最初の要素を 探しますが、2つの値、要素のインデックスと要素自身を返します。 predを満足する要素がなかったら、2つの#fが返ります。

 
(find-with-index char-upper-case? "abraCadabra")
  ⇒ 4 and #\C

(find-with-index char-numeric? "abraCadabra")
  ⇒ #f and #f
Method: find-index pred (seq <sequence>)

findに似ていますが、seqの中でpredを満足する最初の、 要素自身ではなくインデックスを返す点が異なります。 seqの中にpredを満足する要素がなかったら、#fが返ります。

 
(find-index char-upper-case? "abraCadabra")
  ⇒ 4

(find-index char-numeric? "abraCadabra")
  ⇒ #f

SRFI-1 (SRFI-1 リスト操作関数参照)のlist-indexも見て下さい。

Method: fold-right kons knil (seq <sequence>) …

リストに対するfold-rightの総称関数版です。 foldと同じように、このメソッドは種となる値 (初期値はknil) を受渡しながら、高階関数konsを与えられたシーケンスの各要素に 適用してゆきます。foldfold-rightの違いは 要素間の結合の順序にあります。

ひとつだけのシーケンス[E0, E1, ..., En]に適用する場合、 foldfold-rightはそれぞれ以下のように動作します。

 
fold:
  (kons En (kons En-1 (kons ... (kons E1 (kons E1 knil)) ...)))

fold-right
  (kons E0 (kons E1 (kons ... (kons En-1 (kons En knil)) ...)))

このメソッドは<collection>に対しては提供されていません。 コレクションは要素の順序を規定しないからです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.28.4 その他のシーケンス上の操作

Selection and searching

Generic function: sequence-contains haystack needle :key test

needlehaystackはシーケンスです。 haystackの中で、needleに一致するシーケンスを左から探します。 もし見つかれば、haystack中のneedleが始まる場所のインデックスを、 見つからなければ#fを返します。キーワード引数testは 要素を比較するのに使われます。デフォルトはeqv?です。

 
(sequence-contains '#(a b r a c a d a b r a) '#(b r a))
  ⇒ 1

(sequence-contains '#(a b r a c a d a b r a) '#(c r a))
  ⇒ #f

これは、srfi-13のstring-containsを一般化したものとみることもできます (文字列の探索参照)。

Function: break-list-by-sequence list needle :key test
Function: break-list-by-sequence! list needle :key test

シーケンスneedlelistから探し、見つかればlistneedleの直前までとそれ以降に分割して二つの値として返します。 listはリストでなければなりませんが、 needleには任意のシーケンスを渡せます。 各要素はtest手続きで比較されます。デフォルトはeqv?です。

 
(break-list-by-sequence '(a b r a c a d a b r a) '(c a d))
  ⇒ (a b r a) and (c a d a b r a)

needlelist中に見つからなかった場合は、 listそのものと()が返されます。 これはspanbreakの動作に合わせてあります (SRFI-1 リスト操作関数参照)。 これらの手続きは要素に対する述語でもってリストを分割しますが、 分割条件が満たされなかった場合はリスト全体を返します。

 
(break-list-by-sequence '(a b r a c a d a b r c a) '(c a z))
  ⇒ (a b r a c a d a b r c a) and ()

break-list-by-sequence!はその場で更新するバージョンで、 必要ならlistを破壊的に変更して返り値を生成します。渡すリストは変更可能で なければなりません。listが変更されない場合もあるので、 呼出側は副作用に頼らず常に返り値を利用する必要があります。

Function: sequence->kmp-stepper needle :key test

これは、KMPアルゴリズムを使って大きなシーケンス中から 部分シーケンスneedleを探すための内部ルーチンです。 sequence-containsbreak-list-by-sequencebreak-list-by-sequence!の中で使われています。

KMPアルゴリズムの1ステップを行う手続きを返します。 返される手続きは二つの引数、要素eltとインデックスkを取り、 elt(~ needle k)を比較します。二つの値を返します: 次に比較すべきインデックスと、マッチが終了したかどうかのフラグです。 マッチが終了した場合、次のインデックスとして返される値はneedleの 長さと等しいです。

エッジケースとして、needleが空のシーケンスの場合、 sequence->kmp-stepper#fを返します。

要素はtestで比較されます。デフォルトはeqv?です。

以下に、sequence->kmp-stepperを使った探索コードの骨格を示します。 この例ではhaystackはリストで、単にneedleが見つかったかどうか (あるいはneedleが空か)だけを返しています。 実際の応用では、他の情報をループで持ち回ることになるでしょう (例えばsequence-containsではhaystack中の インデックスをトラックして、見つかった場所を返しています。)

 
(if-let1 stepper (sequence->kmp-stepper needle)
  (let loop ([haystack haystack]
             [k 0])
    (if (null? haystack)
      'not-found
      (receive (k found) (stepper (car haystack) k) ; KMP step
        (if found
          'found
          (loop (cdr kaystack) k)))))
  'needle-is-empty)

コレクションに対する選択と探索はシーケンスにも使えます。 コレクションからの選択と探索を参照して下さい。

Grouping

Generic function: group-sequence seq :key key test

シーケンスseqの連続する要素で、同じキー値を持つもの同士を グループ化します。 キーの値は要素に手続きkeyを適用することで得られます。keyの デフォルト値はidentityです。sedqの各要素に対して、 keyは正確に一回だけ呼ばれます。 キーの等価性判定には手続きtestが使われます。デフォルト値はeqv?です。

 
(group-sequence '(1 1 1 2 3 4 4 2 2 3 1 1 3))
  ⇒ ((1 1 1) (2) (3) (4 4) (2 2) (3) (1 1) (3))

(group-sequence '(1 1 1 2 3 4 4 2 2 3 1 1 3)
                :key (cut modulo <> 2)))
  ⇒ ((1 1 1) (2) (3) (4 4 2 2) (3 1 1 3))

(group-sequence '#("a" "a" "b" "b" "c" "d" "d")
                :test string=?)
  ⇒ (("a" "a") ("b" "b") ("c") ("d" "d"))

(group-sequence "aabbcdd"
                :test char=?)
  ⇒ ((#\a #\a) (#\b #\b) (#\c) (#\d #\d))

このメソッドはHaskellのgroupと似ています。 隣り合っていない要素もグループ化したい場合は、 group-collection (コレクションからの選択と探索参照)を使って下さい。

Prefix

Generic function: common-prefix (a <sequence>) (b <sequence>) :key key test

シーケンスabに共通するプレフィクスを、aと同じ型の 新たなシーケンスで返します。abの型は異なっていても構いません。 aの型はビルダーを持っている必要があります。

abのそれぞれ対応する要素について、まずkey手続きが 呼ばれ、その結果がtest手続きで比較されます。省略時にはそれぞれ identityおよびeqv?が使われます。

 
(common-prefix '(a b c d e) '(a b c e f))
  ⇒ (a b c)

(common-prefix "abcef" '#(#\a #\b #\c #\d #\e))
  ⇒ "abc"

文字列については、srfi-13に似た機能を持つ手続き string-prefix-lengthがあります。 (文字列のプリフィックスとサフィックス参照)。

Generic function: common-prefix-to (class <class>) (a <sequence>) (b <sequence>) :key key test

シーケンスabに共通するプレフィクスを、classの インスタンスとして返します。abの型は異なっていても構わず、 ビルダーを持っていなくても構いません。classはビルダーを備えた シーケンスのクラスである必要があります。

キーワード引数についてはcommon-prefixと同じです。

 
(common-prefix-to <list> "abcde" "ABCEF" :test char-ci=?)
  ⇒ '(#\a #\b #\c)

Permutation and shuffling

Generic function: permute (src <sequence>) (permuter <sequence>) :optional fallback

シーケンスsrcの要素をpermuterに従って並べ替えた、新たなシーケンスを 作って返します。返されるシーケンスはsrcと同じ型です。

permuterは正確な整数のシーケンスです。pertmuterk番目の 要素がiであれば、結果のk番目の要素が(ref src i) になります。従って結果のシーケンスの長さはpermuterと長さと同じになります。 permuterの型はsrcの型と違っていて構いません。

同じインデックスiが複数回permuterに現れても構いません。

 
(permute '(a b c d) '(3 2 0 1))     ⇒ (d c a b)
(permute '(a b c d) '(0 2))         ⇒ (a c)
(permute '(a b c d) '(0 0 1 1 2 2)) ⇒ (a a b b c c)

permuterの要素の整数がsrcの有効なインデックスの範囲外であった場合、 デフォルトではエラーが通知されます。しかしfallbackが与えられた場合は、 srcの要素の読み出しが(ref src i fallback) として行われます。これは通常、iが範囲外であった場合にfallbackを 返します。

 
(permute '#(a b c) '(3 2 1 0) 'foo) ⇒ #(foo c b a)

(permute "!,HWdelor" #(2 5 6 6 7 1 -1 3 7 8 6 4 0) #\space)
  ⇒ "Hello, World!"
Generic function: permute-to (class <class>) (src <sequence>) (permuter <sequence>) :optional fallback

結果のシーケンスがsrcの型でなくclassになること以外は、 permuteと同じです。

 
(permute-to <string> '(#\a #\b #\c #\d #\r)
            '(0 1 4 0 2 0 3 0 1 4 0))
  ⇒ "abracadabra"
Generic function: permute! (src <sequence>) (permuter <sequence>) :optional fallback

これもpermuteと似ていますが、結果は新たに作られるシーケンスではなく srcを破壊的変更して格納されます。srcは変更可能でなければならず、 またsrcpermuterは同じ長さでなければなりません。

Generic function: shuffle (src <sequence>) :optional random-source

srcと同じ型、長さのシーケンスで、srcの要素の順序がランダムに 置き換えられたものを返します。

 
(shuffle '(a b c d e))  ⇒ (e b d c a)
(shuffle "abcde")       ⇒ "bacde"

このジェネリックファンクションはsrfi-27 (srfi-27 - ランダムビットのソース参照) を使っています。デフォルトでは乱数源としてdefault-random-sourceが 使われますが、省略可能引数に乱数源を渡すこともできます。

Generic function: shuffle-to (class <class>) (src <sequence>) :optional random-source

結果をsrcの型ではなくclassのインスタンスとして返すshuffleです。

Generic function: shuffle! (src <sequence>) :optional random-source

結果をsrcを破壊的変更して格納するshuffleです。 srcは変更可能でなければなりません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.28.5 シーケンスを実装する


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.29 gauche.syslog - Syslog

Module: gauche.syslog

このモジュールは syslog(3) のシステムロガーのインタフェースを提供します。

一般的なアプリケーションについては、gauche.logger モジュールの ほうが使いやすいでしょう(gauche.logger - ユーザレベルのロギング参照)。 このモジュールは syslog の API に直接アクセスする必要のある人向きです。

手続き群は基盤になっているシステムで syslog がサポートされている場合に のみ定義されます。

Function: sys-openlog ident option facility

[POSIX] システムロガーへのコネクションをオープンします。文字列の引数 ident はログの接頭辞に使われます。通常はプログラムを名前にします。option は整数のフラグで、ログの振舞いを制御します。facility はプログラムの タイプを指定する整数です。

optionのフラグは以下の整数定数の1つまたは複数を logior で 合成することができます: LOG_CONSLOG_NDELAYLOG_NOWAITLOG_ODELAYLOG_PERROR および LOG_PID です (これらの定数のいくつかは基盤となるシステムがサポートしていなければ 定義されません)。

facility 引数は以下の整数定数のどれかひとつです。 LOG_AUTHLOG_AUTHPRIVLOG_CRON, LOG_DAEMONLOG_FTPLOG_KERNLOG_LOCAL0、 から LOG_LOCAL7LOG_LPRLOG_MAILLOG_NEWS, LOG_SYSLOGLOG_USERLOG_UUCP までです。 (これらの定数のいくつかは基盤となるシステムがサポートしていなければ 定義されません)。

これらの定数の詳しい説明については、システムの openlog(3) の マニュアル・ページを見てください。

Function: sys-syslog priority message

[POSIX] 文字列 message をログに記録します。syslog(3) とは違って、この手続きは フォーマットを行いません。フォーマットされた文字列を生成するなら format (出力参照)が使えます。あるいは、より高水準の ルーチン log-format (gauche.logger - ユーザレベルのロギング参照) を使うこともできます。

整数の引数 priority は上述の facility 定数のどれかひとつ と、以下のlevel定数とlogiorして、合成することができます: LOG_EMERGLOG_ALERTLOG_CRITLOG_ERRLOG_WARNINGLOG_NOTICELOG_INFOLOG_DEBUG

Function: sys-closelog

[POSIX] ロギングシステムとのコネクションをクローズします。

Function: sys-setlogmask mask

[POSIX] どの sys-syslogへの呼び出しを記録できるかを決める、プロセスのログ 優先度マスクを設定します。 優先度マスクmasksys-sysloglevel引数に 対応するビットマスクをlogiorすることで作成できます。 レベルに対応するビットマスクは下のsys-logmaskから 取得することができます。

Function: sys-logmask level

[POSIX] ログレベル level から sys-setlogmask に対する ビットマスク整数を返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.30 gauche.termios - 端末の制御

Module: gauche.termios

このモジュールは端末の制御のための手続きを提供します。 Unix環境では、低レベルAPIはモジュール名からわかるように POSIX termiosへのインタフェースを提供しています。 さらに、このモジュールでは、システムがサポートしている場合には 擬似 tty のインタフェースも提供しています。

Windowsネイティブ環境ではPOSIX termiosインタフェースは使えません。 Windows console APIは大きく違っているので、意味のあるエミュレーションを 提供するのが難しいためです。低レベルのWindows console APIは os.windowsモジュールで使うことができます(os.windows - Windowsのサポート参照)。 ただし、このモジュールの高レベル端末制御手続きは、 Windowsネイティブ環境でも使うことができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.30.1 POSIX termiosインタフェース

ここに挙げる手続きは、機能シンボルgauche.os.windowsが定義されていない 場合にのみ利用可能です。機能シンボルによってコードを切り替える方法は 機能条件式cond-expandを参照してください。

Builtin Class: <sys-termios>

POSIX termios(7) の構造体です。

Instance Variable of <sys-termios>: iflag
Instance Variable of <sys-termios>: oflag
Instance Variable of <sys-termios>: cflag
Instance Variable of <sys-termios>: lflag
Instance Variable of <sys-termios>: cc

iflagoflagcflaglflagの各スロットは、 対応するビットマスクを非負の整数で表現した値を保持しています。

また、ccスロットはstruct termiosc_cc配列の コピーを返します。 c_cc配列の値を変えたい場合は、変更したu8vectorを明示的に ccスロットにset!して下さい。

このセクションを通じて、引数 port-or-fd はポートオブジェクトか システムのファイルディスクリプタを表現する小さい整数かのどちらかです。 portがシステム端末に結びついていないければ、エラーになります。 (sys-isatty?を使えばportが端末と結びついているか どうかチェックできます。 他のファイル操作参照)

Function: sys-tcgetattr port-or-fd

port-or-fd と結びついている <sys-termios>オブジェクトの 端末パラメータを返します。

Function: sys-tcsetattr port-or-fd when termios

port-or-fdと結びついている端末のパラメータを termios に設定します。 termios<sys-termios> のインスタンスでなければなりません。

整数の引数whenはいつ変更を有効にするかを指定します。 この引数用に3つの変数があらかじめ定義されています。

TCSANOW

変更を直ちに反映します。

TCSADRAIN

変更を、すべてのペンディングになっている出力がフラッシュされた後に反映します。

TCSAFLUSH

変更を、すべてのペンディングになっている出力がフラッシュされ、かつ、 すべてのペンディングになっている入力が破棄されたあとに反映します。

Function: sys-tcsendbreak port-or-fd duration

ゼロストリームを指定した時間、port-or-fdと結びついている端末に 送出します。時間の単位はシステム依存です。詳しくは、お使いの システムのマニュアルページ tcsendbreak(3)を参照してください。

Function: sys-tcdrain port-or-fd

port-or-fdへのすべての出力が送出されるまで待ちます。

Function: sys-tcflush port-or-fd queue

port-or-fdのバッファ内のデータを破棄します。queueには以下の値の どれかを指定します。

TCIFLUSH

受信データしたが読み込んではいないデータを破棄します。

TCOFLUSH

書き出したが送出していないデータを破棄します。

TCIOFLUSH

TCIFLUSHTCOFLUSH の両方の動作をします。

Function: sys-tcflow port-or-fd action

port-or-fdのデータフローをactionで制御します。actionは 以下の値のうちどれかです。

TCOOFF

出力の送出をサスペンドします。

TCOON

出力の送出を再開します。

TCIOFF

端末デバイスがシステムへの送出を止めるよう STOP 文字を送出する。

TCION

端末デバイスがシステムへの送出を再開するよう START 文字を送出する。

Function: sys-tcgetpgrp port-or-fd

port-or-fdの結びついている端末のプロセスのグループIDを返します。

Function: sys-tcsetpgrp port-or-fd pgrp

port-or-fdの結びついている端末のプロセスのグループIDをpgrpに 設定します。

Function: sys-cfgetispeed termios
Function: sys-cfsetispeed termios speed
Function: sys-cfgetospeed termios
Function: sys-cfsetospeed termios speed

termios内の入出力スピード(ボーレート)を取得/設定します。スピードは 以下の定義ずみの数値で表現されてます: B0B50B75B110B134B150B200B300B600B1200B1800B2400B4800B9600B19200B38400

もっと速いボーレート、たとえば、B57600B115200 あるいは B230400 をサポートしている システムもあります。symbol-bound?をつかえば、これらの オプションが定義されているかどうかをチェックできます。B0 はコネクションを終了するのに使われます。

Function: sys-openpty :optional term

擬似 tty のペア、マスターとスレーブをオープンし、2つの ファイルディスクリプタの整数を返します。オプション引数 term が渡される場合は、<sys-termios>オブジェクトでなければなりません。 これは、pty パラメータを設定します。

open-input-fd-portopen-output-fd-portの両方またはどちらか を返されたファイルディスクリプタに対するポートを生成するために使うことが できます(ファイルポート参照)。擬似端末の名前を得るには sys-ttyname を使います(他のファイル操作参照)。

この関数はシステムが openpty(3) をサポートしている場合にのみ利用 可能です。

Function: sys-forkpty :optional term

擬似 ttyのペア、マスターとスレーブをオープンし、スレーブ sty を ログイン端末になるよう設定し、fork(2) します。

二つの整数、最初の値は親プロセスに対しては子の pid の値で、0ならば 子プロセスです。ふたつ目の値はマスター pty のファイルディスクリプタの 値です。

オプション引数 termが渡される場合は、それは<sys-termios>オブジェクト でなければなりません。これはスレーブ pty のパラメータを設定します。

この関数はシステムが forkpty(3) をサポートしている場合にのみ利用 可能です。

注意: sys-forkptyにはsys-forkと同様のマルチスレッドハザー ドの危険性があります。(詳細についてはプロセス管理参照 してください)。マルチスレッドプログラムでは後述の sys-forkpty-and-execを利用してください。

Function: sys-forkpty-and-exec command args :key iomap term sigmask

sys-forkptyして、ただちに指定したcommandを引数 argsで子プロセスでexecします。この関数にはマルチスレッド 環境でもハザードを起こしません。

引数commandargsiomapsigmaskの意味は sys-execのものと同じです。(プロセス管理を見て ください)。キーワード引数termが与えられれば、スレーブptyの初期化 に使われます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.30.2 共通の高レベル端末制御

Function: without-echoing iport proc

If iport is an input port connected to a terminal, sets the terminal mode non-echoing and call proc with iport as an argument. Before returning from without-echoing, or throwing an error, the terminal mode is reset to the original state when this procedure is called. The procedure returns whatever value(s) proc returns.

You can also pass #f to iport. In that case, this procedure tries to open a console (/dev/tty on Unix, CON on Windows) and set the console mode, then calls proc with the opened input port. An error is thrown if the procedure can not open a console.

If iport is other than above, this procedure simply calls proc with iport. This allows the caller to read password from redirected input, for example.

Note: Because of an implementation issue, on Windows native platforms this procedure always changes console mode of the standard input handle when iport is either #f or a terminal input port.

Function: has-windows-console?

Returns #t iff the running Gauche is Windows-native and the process has attached console. On POSIX platforms this procedure always returns #f.

The reason that cond-expand isn’t enough is that on Windows the program may start without console, but you can attach console afterwards. See section Windows console API, for the details.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.31 gauche.test - 単体テスト

Module: gauche.test

テストスクリプトを書くための手続きを提供します。 テストスクリプトは次のような形になります。

 
(use gauche.test)
(test-start "my feature")
(load "my-feature")  ; テストすべきプログラムをロード
(import my-feature)  ; モジュールを定義している場合はインポート

(test-module 'my-feature) ; モジュールの一貫性チェック

(test-section "feature group 1")
(test "feature 1-1" EXPECT (lambda () TEST-BODY))
(test "feature 1-2" EXPECT (lambda () TEST-BODY))
 …

(test-section "feature group 2")
(define test-data ...)
(test "feature 2-1" EXPECT (lambda () TEST-BODY))
(test "feature 2-2" (test-error) (lambda () TEST-THAT-SIGNALS-ERROR))
 …

(test-end :exit-on-failure #t)

このテストスクリプトは、バッチ処理でもインタラクティブセッションからでも 実行できます。インタラクティブセッションの場合はこのテストスクリプトを ただロードすれば、各テストの結果とサマリーが報告されます。 バッチテストの場合は、標準出力を別のファイルにリダイレクトしておくと良いでしょう。 標準出力が端末でない場合、テスト手続きは詳しい結果をstdoutに出力し、簡単な メッセージを標準エラー出力に書き出します。

モジュールやプログラムを書いたら、Makefileに"check"ターゲットを作ることを お薦めします。ルールはこんな感じになるでしょう:

 
check :
        gosh my-feature-test.scm > test.log

テストファイルの構造

Function: test-start module-name

テストの状態を初期化して、ヘッダをログに書き出します。テストを呼ぶ前に呼んで下さい。 module-nameはログのために使われる文字列です。

Function: test-section section-name

一群のテストの開始をマークします。単にログに使われるだけです。

Function: test-log fmtstr args …

この手続きも単なるログのためのものです。 formatと同じようにfmtstrargsからフォーマット済み文字列を 作り、;; を前に、改行文字を後につけて現在の出力ポートへと出力します。

典型的なMakefileでのテスト起動では、テストスクリプトの標準出力をログに流すので、 このメッセージもログにのみ記録されることになります。

この手続きは、自動化テストで検査することは出来ないけれどトラブルシューティングに 役に立つかもしれない情報をダンプしておくのに使えます。 例えば、謎のテスト失敗報告が来て、でも手元ではどうしても再現できず、 実行システムに固有のある側面が影響しているのではないかと推測できたとしましょう。 その場合、test-logでそういった情報をダンプするコードをテストスクリプトに 仕込んでおいて、もう一度報告者のマシンでテストを走らせてもらい、そのログを解析する ことができるでしょう。

Function: test-end :key exit-on-failure

失敗したテストのリストを報告します。exit-on-failure#fか 省略された場合は、この手続きは失敗したテストの数を返します。

そうでなければ、この手続きはexitを呼んでgoshを 終了させます。exit-on-failureにfixnumが渡された場合は、 それがプロセスの終了ステータスとして使われます。exit-on-failureが 他の真となる値であった場合は、終了ステータスは1となります。

Function: test-record-file file

テストスクリプトがいくつかある場合を考えます。通常は、それらのテストスクリプト をひとつづつ走らせて結果を確かめるのではなく、全部を一気に走らせて 簡単な結果のまとめだけを知りたいと思うでしょう。

test record fileはテスト結果を集積するための補助ファイルです。 その内容は次のような一行のサマリになっています。

 
Total:  9939 tests,  9939 passed,     0 failed,     0 aborted.

test record fileが既に存在していると、test-startは それを読みこんで数字を覚えておきます。そしてtest-endが そのスクリプト中のテスト結果の数字を加算して、同じtest record fileに書き戻します。

makefileのcheckターゲットを次のように書いておけば、 make checkを実行するたびにテスト結果の一行サマリを 得ることができます。 ただし、‘test1.scm’、‘test2.scm’、‘test3.scm’はいずれも test-start呼び出し前に(test-record-file "test.record")を 評価しているものとします。

 
check:
        @rm -f test.record test.log
        gosh test1.scm >> test.log
        gosh test2.scm >> test.log
        gosh test3.scm >> test.log
        @cat test.record

test-record-file手続きがうまく動作するためには、それが test-startより前に呼ばれなければならないことに注意してください。

この手続きのかわりに、環境変数GAUCHE_TEST_RECORD_FILEを使って test record fileを指定することもできます。

Environment Variable: GAUCHE_TEST_RECORD_FILE

テストスクリプトが走る時にこの環境変数が指定されていれば、 その値がtest record fileの名前として使われます。

ただしテストスクリプト中にtest-record-fileの呼び出しがあると そちらが優先され、この環境変数は無視されます。

Function: test-summary-check

テストレコードファイルが(test-record-fileもしくは環境変数 GAUCHE_TEST_RECORD_FILEによって)設定されていた場合、 それを読み込んで、失敗カウントとアボートカウントがともに0でなければ 終了ステータス1でexitします。テストレコードファイルが設定されていなければ 何もしません。

これは、複数のテストスクリプトを持っていて、どれかが失敗したらmakeに それを伝えたいけれども、テストスクリプト自体はすべて走らせたい、という場合に便利です。 各テストスクリプトでtest-end:exit-on-failureを 使ってしまうと、makeは失敗したテストスクリプトのところで処理を打ち切って しまいます。そこで:exit-on-failureを使うのを避け、 テストレコードファイルを使い、ビルドの最後にこの関数を呼ぶようにします:

 
check:
   rm -f $GAUCHE_TEST_RECORD_FILE test.log
   gosh test1.scm >> test.log
   gosh test2.scm >> test.log
   cat $GAUCHE_TEST_RECORD_FILE /dev/null
   gosh -ugauche.test -Etest-summary-check -Eexit

こうしておくとmakeは失敗があろうともすべてのテストスクリプトを 実行し (goshは常に終了ステータス0で終了するので)、 最後の行でテストレコードファイルを参照して、失敗があった場合に makeにそれを伝えることができます。

個々のテスト

Macro: test* name expected expr :optional check

exprをlambdaでくるんでくれる便利なマクロです。

 
(test* name expected expr)
  ≡ (test name expected (lambda () expr))
Function: test name expected thunk :optional check

thunkを呼び、その結果がexpectedに沿っているかを checkを次のとおり呼び出すことで確認します。

 
(check expected result-of-thunk)

この手続きは、渡された結果が期待する値と合致する場合に#tを、 そうでなければ#fを返さなければなりません。 デフォルトのcheck手続きは下で述べるtest-checkです。 これは、expectedがいくつかの特殊なテストオブジェクトである場合をのぞき、 expectedresult-of-thunkequal? である場合に#tを返します。すなわち、通常はテスト式の結果が 期待するものとequal?であればテストは成功である、ということです。 (特別な場合については下の “曖昧な結果をテストする” および “異常系をテストする” の項を見てください)。

特別な比較手続きのひとつの用法は、不正確な数値を、多少の誤差を許して 比較するような場合です。

 
(test "test 1" (/ 3.141592653589 4)
      (lambda () (atan 1))
      (lambda (expected result)
        (< (abs (- expected result)) 1.0e-10)))

nameはログに残すためのテストの名前です。

thunk内で捕捉されないエラーが発生した場合、それは捕捉され、 特別なエラーオブジェクト<test-error>に置き換えられます。 その結果を、下で説明するtest-error手続きで作った<test-error> オブジェクトと比較することにより、エラーが期待されたものであるか、 また適切な例外が上がっているかをテストすることができます。

Function: test-check expected result :optional fallback

テスト式の結果が期待された値に合致していることをチェックするために testtest*が使うデフォルトの手続きです。 基本的に、test-checkexpectedresultfallbackに渡された手続きで比較するだけです。fallbackの デフォルト値はequal?です。 ただし、以下に述べるとおり、expectedが特殊なテストオブジェクト だった場合には特別な振る舞いをします。

曖昧な結果をテストする

Function: test-one-of choice …

時々、テスト式exprの結果が外部の環境に左右されるため、 ひとつの確定した値を期待値expectedとして書いておけない場合があります。 この手続きはそのようなテストを簡単に書けるようにします。

choice … のいずれか を表現する特別なオブジェクトを返します。 デフォルトの検査手続きであるtest-checkは、expected引数に そのオブジェクトが渡ってきた場合には、テスト結果とchoice …を ひとつづつ照合し、どれかがマッチすれば真を返します。

例えば次のテストは、procが1か2を返せば成功となります。

 
(test* "proc returns either 1 or 2" (test-one-of 1 2) (proc))
Function: test-none-of choice …

choice … のいずれでもない を表現する特別なオブジェクトを返します。 テストは、テスト結果がchoiceのどれにもマッチしなかった時に成功となります。

異常系をテストする

Function: test-error :optional (condition-type <error>)

与えられたcondition-typeと適合する<test-error>オブジェクト とマッチするような、新たな<test-error>オブジェクトを作成して返します。

テスト結果をチェックするtest-check手続きは<test-error>オブジェクトを 特別に扱います。err-expectederr-actualが ともに<test-error>のインスタンスであるとき、 (test-check err-expected err-actual)err-expectedの持つcondition typeがerr-actualの それと同じであるか、スーパータイプである場合に#tを返します。

例えばfooの呼び出しが<io-error> (もしくはそのサブタイプ) の コンディションを投げるかどうかをテストしたければ、次のように書くことができます。

 
(test "see if foo raises <io-error>" (test-error <io-error>) (foo))
Variable: *test-error*

(非推奨) condition typeとして<error>を持つ<test-error> オブジェクトに束縛されています。この変数は互換性のためにのみ残されています。 新しいコードは上に述べたtest-error手続きを使ってください。

Variable: *test-report-error*

この変数が真であれば、testルーチンはエラーを捕捉した際に スタックトレースをカレントエラーポートに出力します。 期待しない状況でtest-errorオブジェクトが返された際に、そのエラーが どこで起こったかを知るのに役立つでしょう。

この変数はgauche.testモジュールが読み込まれた時点で 環境変数GAUCHE_TEST_REPORT_ERRORの値により初期化されます。 例えば、テストスクリプト中の予期せぬエラーを調べるのに、 次のようにすることができるでしょう (環境変数がセットされていれば、値は関係ありません)。

 
env GAUCHE_TEST_REPORT_ERROR=1 gosh mytest.scm

準静的検査

Schemeは動的型付けで、これはREPLでインクリメンタルあるいは実験的な開発を するには便利なのですが、コードが実際に走るまでエラーに気づかないということになりがちです。 しばらく走らせた後で変数名のタイポで止まってしまうと腹立ちますよね。

Gaucheでは、こういった種類のエラーをテスト時に検査する方法を用意しています。 これは完全に静的な検査ではありません(対象のモジュールやスクリプトをロードするので、 トップレベル式は実行されてしまいます)し、網羅的でもありません (複数のモジュールを見たり、実行時に追加される情報に依存するものは検査できません)。 それでも、変数名の間違いや引数の個数の誤りなどよくあるミスはこの検査で大抵見つけることが できます。

test-moduletest-script手続きはそれぞれ、 指定されたモジュールもしくはスクリプトをロードし (その過程でSchemeコードは VM命令列へとコンパイルされます)、次にコンパイルされたVMコードを走査して 以下のテストを行います。

  1. 関数内から参照されているグローバル変数は(該当モジュール内、もしくはインポートしたモジュールで) 全て定義されているか
  2. グローバル変数が関数として使われているなら、その引数の数が 関数の定義と合致しているか。
  3. autoloadに設定されている変数が実際にロードできるか。
  4. モジュールをテストしている場合、exportされているシンボルが 定義されているか。

今のところ検査はヒューリスティックで、エラーを見逃すこともあれば、 エラーでないものをエラーと報告してしまう可能性もあります。後者については、 偽陽性となるシンボルを列挙して検査から外すようにできます。

Function: test-module module :key allow-undefined bypass-arity-check

モジュールをロードし、準静的な一貫性チェックを行います。 Moduleはモジュール名のシンボルかモジュールでなければなりません。

しばしば、プラットフォームやコンパイルオプションによって グローバル変数が定義されるかどうかが異なる場合があります。 コード中では実行時にその変数の存在を確認してから使うように コーディングしてあったとしても、test-moduleは そのようなロジックを追わないため、未定義変数の参照を報告して しまいます。そのような場合は、チェックから外す変数名のリストを allow-undefinedキーワード引数に渡して下さい。

引数の個数のチェックも、偽陽性のエラーをあげる可能性があります。 モジュールがロードされた後のグローバルな関数の変更を当てにしている場合などです (例えば、コード中であるジェネリックファンクションに渡されている引数の個数が、 モジュールのロード時点では不正なものだったとしても、そのコードが実行される までに該当引数のメソッドが追加されれば、正しいコードとなるわけです)。 自分のコードは確かに正しく、チェックが誤りであるような場合は、 該当関数の名前のリストをbypass-arity-checkキーワード引数に 渡してください。

Function: test-script filename :key allow-undefined bypass-arity-check

filenameで指定されるスクリプトを新しい無名モジュールにロードし、 準静的な一貫性チェックを行います。 filenameはスクリプトファイル名を指定する文字列でなければなりません。

キーワード引数の意味はtest-moduleと同じです。

filename中のトップレベルフォームは評価されるので、 トップレベルフォームのアクションに依存したスクリプトは望ましくない副作用を生じるでしょう。 この検査は、スクリプトがsrfi-22形式、 つまりアクションをmain手続きから呼ぶようになっているとうまくいきます。 R7RSスクリプトはトップレベルフォームのアクションに頼らざるを得ないので この手続きではうまく検査できません。

スクリプトがuserモジュールにロードされることに依存して書かれている場合も、 この手続きではうまくいきません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.32 gauche.threads - スレッド

Gaucheでは、コンパイル時に有効にしていれば、POSIXスレッド(pthreads)か Windowsスレッド上に構築されるスレッドを使うことができます。

Module: gauche.threads

スレッドを扱うAPIを提供します。コンパイル時にスレッドのサポートを 指定したか否かに関わらず、このモジュールを’use’することができます。 スレッドがサポートされていない場合は、多くのスレッド関連の手続きは 単に“not supported”エラーを通知するだけです。

pthreadが使えるかどうかによって実行するコードを切り替えたい場合は、 cond-expandフォームでgauche.sys.threadsという feature identifierを使うことができます (機能条件式参照)。

 
(cond-expand
 [gauche.sys.threads
   ;; Thread APIを使うコード (gauche.threadsはこの時点で
   ;; 自動的にロードされます).
  ]
 [else
   ;; Thread APIを使わないコード
  ])

また、pthreadプラットフォームとWindowsスレッドプラットフォームで それぞれ、gauche.sys.pthreadsgauche.sys.wthreads というfeature identifierも定義されています。 ただ、Schemeレベルではこれら下位の実装の違いを意識する必要は ほとんど無いでしょう。スレッドサポートの有無によりコードを切り替える時は、 gauche.sys.threadsを使うのが良いでしょう。

コンパイル時ではなく実行時にスレッドが有効かどうかをチェックするためには、 次の手続きを使います。

Function: gauche-thread-type

サポートされているスレッドのタイプを表すシンボルを返します。 以下のいずれかのシンボルが返されます。

none

スレッドはサポートされていません。

pthread

スレッドはPOSIXのpthreadsを使って実装されています。

win32

スレッドはWin32スレッドを使って実装されています。

(註:pthreadプラットフォームではシンボルpthreadではなく pthreadsを返すべきでした。そうすれば、 gauche-thread-typeの返り値はコンフィグレーション時に --enable-threadsに与えた値と対応するものになっていたでしょう。 互換性のために、残念ながらこの見過ごしは修正されないでしょう。

SchemeレベルのスレッドAPIはSRFI-18、“マルチスレッドサポート” ([SRFI-18])を満たし、Gaucheのオブジェクトの インターフェースでラップされます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.32.1 スレッドプログラミングTips

Gaucheのスレッドをどう使うか

スレッドのAPIは外見上シンプルでポータブルに見えますが、 その機能の潜在的な力を活用するためには、スレッドがどのように 実装されているかを知る必要があります。 いくつかの言語では言語組み込みの機能としてスレッドをサポートし、 プログラマによるスレッドの利用を推奨しています。 しかし、多くの場合、実現したいアルゴリズムをスレッドを使わずに 実装する方法があります。 スレッドを使うことの利点と欠点を、そのスレッドがシステムによってどのように 実現されているかを考慮した上で比較する必要があります。

Gaucheでは、スレッドを使う一番の目的は、他の方法で表現することが 難しい、プリエンプティブなスケジューリングを必要とする プログラムを書くことです。プリエンプティブなスレッドは、 例えば、中断できないブロッキングI/Oを行うモジュールを 使わなければならないときや、実行時間の分からない計算に 割り込みを行いたいときなどに必要となります。

それぞれのGaucheのスレッドには、個別の仮想マシンが割り当てられ、 専用のPOSIXスレッドにより実行されます。したがって、コンテキスト スイッチのオーバヘッドは、ネイティブスレッドと同等です。 しかし、スレッドの生成は、例えばcall/ccによる軽量スレッドよりは ずっとコストのかかる処理です。 このように、Gaucheのプリエンプティブなスレッドは、 きめ細かい計算のために幾千ものスレッドを生成したいアプリケーション 向けではありません

推奨される使用方法は、いわゆる“スレッドプール”と呼ばれる テクニックです。つまり、スレッドの集合を作って長時間それを 保持し、必要になったときにジョブをそこへディスパッチする というものです。Gaucheはcontrol.thread-poolモジュール (control.thread-pool - スレッドプール参照) でスレッドプールの実装を提供しています。

プリエンプティブなスレッドには他にも難しい点があり (FairThreads参照)、 しばしばネイティブなプリエンプティブスレッドよりも より良くフィットする代替策があります。

もちろん、これらのテクニックはネイティブスレッドとは相互排他ではありません。 例えば、“スレッドプール”テクニックと一緒にディスパッチャを使うこともできます。 それらの機能を実現するために、ネイティブスレッドが唯一の方法ではないということを 心に留め置いて下さい。

スレッドボディ内で捕捉されないエラー

シングルスレッドのプログラムが予期せぬ(捕捉されない)エラーを起こした場合、 デフォルトではGaucheはエラーメッセージとスタックトレースを表示します。 ところが、スレッドで捕捉されないエラーが生じスレッドが終了しても、 何も表示されません。

それはこういうわけです。スレッド本体でエラーが起き、それがスレッド中で処理されなければ、 スレッドは終了し、エラー自体はそのスレッドの終了を待つスレッドへと伝達される仕組みだからです。 エラーで終了したスレッドに対してthread-join!を呼んだ時点で、 元のエラーを<uncaught-exception>でラップしたエラーが投げられます。 この動作はSRFI-18で規定されています。

新たなスレッドを一時的な計算のために走らせて、その結果をthread-join!で 受け取るという使い方なら、この動作は便利です。「親スレッド」でまとめてエラーを 捕捉できるからです。けれどもスレッドをずっとループさせてジョブを次々と処理する、といった 用法ではthread-join!は呼ばれるとは限らず、この仕様は落とし穴になり得ます。 スレッドがエラーで死んだのに気づかないかもしれないからです。 (エラーで終了したスレッドがthread-join!されることなくGC対象になった場合は 警告が出力されます。しかし、GCがどのタイミングで起きるかはわかりません。)

そういう使い方をするスレッドについては、本体を常にguardで包んで エラーを明示的に処理すべきです。例えばエラーメッセージとスタックトレースを出したければ report-errorを呼びます。

 
(thread-start!
 (make-thread (^[] (guard (e [else (report-error e) #f])
                     ... thread body ...))))

スレッドでの例外の処理について詳しくはスレッド例外を参照してください。

註: 0.9.5の時点では、guardのエラーハンドラ節が 末尾コンテクストで呼ばれないというバグがあります。 例えば次のコードはSchemeでは本来スタックを消費せずにループするはずですが、 現在のGaucheではスタックを徐々に食いつぶしてしまいます。

 
(thread-start!
 (make-thread (^[] (let loop ()
                     (guard (e [else (report-error e) (loop)])
                       ... thread body ...)))))

当面は回避策として、loopの呼び出しをguardの外側に出してください。

 
(thread-start!
 (make-thread (^[] (let loop ()
                     (guard (e [else (report-error e)])
                       ... thread body ...)
                     (loop)))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.32.2 スレッド手続き

Builtin Class: <thread>

スレッドを表すクラスです。それぞれのスレッドは、POSIXスレッドにより 評価される関連付けられた手続きを持ちます。手続きが正常に戻ると、 その結果は内部的な“結果”スロットに格納され、thread-join!により 取得することができます。例外が投げられるか thread-terminate!により終了されるかで、手続きが異常終了すると、 例外条件が内部的な“結果としての例外”スロットに格納され、 その例外条件は終了したスレッドに対してthread-join!を 呼ぶスレッドへと渡されます。

それぞれのスレッドは独自の動的環境と動的なハンドラスタックを持っています。 あるスレッドが生成されると、その動的な環境は生成者の動的な環境によって 初期化されます。そのスレッドの動的なハンドラスタックは初期においては空です。

スレッドは以下の4つの状態のうちのひとつを取ります。thread-state手続きで スレッドの状態を調べることができます。

new

まだ作られたばかりで起動されてない状態です。make-threadが返すスレッドは この状態です。スレッドがひとたび起動されると、そのスレッドがこの状態に戻ることは 決してありません。 この時点ではPOSIXスレッドはまだ作られません。thread-start!によって POSIXスレッドが作られ、Gaucheのスレッドを実行します。

runnable

thread-start!によって起動されたスレッドはこの状態になります。 スレッドがシステムコールによるブロックされている時もその状態はrunnable であることに注意してください。

stopped

スレッドがthread-stop!によって止められるとこの状態になります。 この状態のスレッドはthread-cont!によって再びrunnableになり、 止められた時点から実行を再開することができます。

terminated

割り当てられたコードの実行が終了したり、thread-terminate!によって 強制的に終了させられた時に、スレッドはこの状態になります。 一度この状態になると他の状態に遷移することはありません。

複数のスレッドで共有されるリソースへのアクセスは、同期化プリミティブにより 明示的に保護されなければなりません。同期プリミティブ参照。

ポートへのアクセスはGaucheによりシリアライズされます。 複数のスレッドが1つのポートへの書き込みを試みた場合、それらの出力は 混じることもありますが、失われる出力はなく、そのポートのステータスは 一貫性が保たれます。複数のスレッドが1つのポートからの読み込みを試みた 場合、1つの読み込みプリミティブ(例えば、readread-charread-lineなど)がアトミックに実行されます。

シグナルハンドラは全てのスレッドで共有されますが、それぞれのスレッドは 独自のシグナルマスクを持ちます。詳細は、シグナルとスレッドを参照。

スレッドオブジェクトは以下の外部スロットを持ちます。

Instance Variable of <thread>: name

スレッドに関連付けられる名前。 これは単にアプリケーションにとっての便宜を図るためのものです。 原始となるスレッドは“root”という名前を持ちます。

Instance Variable of <thread>: specific

アプリケーションが使うスレッドローカルなスロット。

Function: current-thread

[SRFI-18]、[SRFI-21] 現在のスレッドを返します。

Function: thread? obj

[SRFI-18]、[SRFI-21] objがスレッドなら#t、そうでなければ#fを返します。

Function: make-thread thunk :optional name

[SRFI-18]、[SRFI-21] thunkを実行するための新しいスレッドを生成して返します。 そのスレッドの実行を開始するには、thread-start!を呼ぶ必要があります。 thunkの実行結果は、thread-join!を呼ぶことで回収できます。

オプション引数nameを与えることで、そのスレッドに名前を与えることができます。

作成されたスレッドは、呼び出したスレッドのシグナルマスクを継承し (シグナルとスレッド参照)、また呼び出したスレッドの持つ その時点でのパラメータのコピーを受けとります。

これらの初期化操作以外に、作られるスレッドと呼び出したスレッド間の関係は ありません。Unixのプロセスのような親子関係があるわけではないのです。 どのスレッドも、他のスレッドに対してthread-join!を発行 して結果を受け取ることができます。 もし誰もthread-join!を発行せず、また作られたスレッドに対する 参照を保持していなかった場合、スレッドは実行が終了した後にガベージコレクトされます。

もしスレッドが捕捉されない例外のために実行を終了し、その結果がthread-join!で 回収されなかった場合、標準エラーポートに“thread dies a lonely death” という 警告メッセージが出力されます。そのようなケースは通常何らかのコーディングエラーで あるからです。スレッドの結果を回収しない場合は、 thunk中ですべての例外を捕捉し処理しなければなりません。

内部的に、この手続きは単にSchemeスレッドオブジェクトを割り当て初期化している だけです。POSIXスレッドはthread-start!が呼ばれるまで生成されません。

Function: thread-state thread

threadの状態を示す、newrunnablestoppedterminatedのいずれかのシンボルを返します。

Function: thread-name thread

[SRFI-18]、[SRFI-21] threadのスロットnameの値を返します。

Function: thread-specific thread
Function: thread-specific-set! thread value

[SRFI-18]、[SRFI-21] threadの指定したスレッドの値を取得/設定します。

Function: thread-start! thread

[SRFI-18]、[SRFI-21] threadを開始します。threadがすでに開始されていればエラーになります。 threadを返します。

Function: thread-yield!

[SRFI-18]、[SRFI-21] 呼び出しているスレッドの実行を中断し、他に待機中の実行可能なスレッドがあれば、 CPUにそれを処理させます。

Function: thread-sleep! timeout

[SRFI-18]、[SRFI-21] 呼び出しているスレッドをtimeoutに指定した時間だけ中断します。 timeoutは絶対的な時間を表す<time>オブジェクト(時間参照)か、 この手続きが呼ばれた時刻からの相対的な秒数を表す実数でなければなりません。

指定された時間が経過すると、thread-sleep!は未定義値を返します。

timeoutが過去の時間を指していたら、thread-sleep!はすぐに戻ります。

Function: thread-stop! thread :optional timeout timeout-val

対象となるスレッドthreadの実行を一時的に停止します。 停止されたスレッドはthread-cont!により実行を再開させられます。

停止リクエストは同期的に処理されます。すなわち、GaucheのVMは実行ループ中の 「安全な」場所で停止リクエストをチェックし、それがあれば自分自身を停止するように なっています。このことは、threadがシステムコールによりブロックされている場合、 それが戻ってくるまでスレッドがstopped状態にならないことを意味します。

デフォルトでは、thread-stop!は対象スレッドが停止状態になってから 戻ってきます。しかしそうなるまでにどれだけ時間がかかるかわからないので、 省略可能引数timeoutを与えて、タイムアウトすることができます。 timeout引数には#f(タイムアウトせず停止するまで待ち続ける)、 <time>オブジェクト(絶対的な時刻を指定)、もしくは 実数(現在からの秒数を指定)を与えられます。

thread-stop!の戻り値は、対象スレッドを停止させられたなら そのthreadオブジェクト、タイムアウトしたならtiemout-valです。 timeout-valが省略された場合は#fとみなされます。

対象スレッドが既に呼び出しスレッドの要求によって停止していた場合は、 直ちにthreadが返されます。

thread-stop!がタイムアウトしてthread-stop!から帰ってきた 場合でも、リクエストは有効なままになっており、未来のいつかthreadを 停止させます。呼び出し側は、いずれthread-stop!を再び呼び出して 停止を確認する必要があります。

対象スレッドが既に別のスレッドによって停止させられていた場合 (別のスレッドによって停止要求が出されて停止待ちに なっている状態も含みます)や、 対象スレッドがrunnableでもstoppedでもない状態の場合は エラーが報告されます。

Function: thread-cont! thread

thread-stop!で停止したthreadの実行を再開します。 threadが停止状態でなかったり、thread-cont!を呼び出したのとは 別のスレッドによって停止させられていた場合はエラーが報告されます。

停止要求を既に出していて、タイムアウトしていた場合、thread-cont!は その停止要求をキャンセルする役割を持ちます。

Function: thread-terminate! thread

[SRFI-18]、[SRFI-21] 指定されたスレッドthreadを終了します。 threadは終了され、<terminated-thread-exception>のインスタンスが threadの結果例外のフィールドに格納されます。

threadが呼び出しているスレッドと同じ場合、この手続きは戻りません。 そうでなければ、この手続きは未定義値を返します。

threadには(dynamic-windでの’after’手続きのような)クリーンアップ手続きを 呼ぶチャンスがないので、この手続きは注意して使って下さい。 threadがクリティカルセクションにあるならば、一貫性のない状態を残すことに なります。 しかし、あるスレッドが一旦終了すると、そのスレッドが保持していたmutexは ’abandoned’(放棄された)状態になり、そのようなmutexをロックしようとするスレッドは ’abandoned mutex exception’を投げるので、その状況を知ることができます。 同期プリミティブ参照。

Function: thread-join! thread :optional timeout timeout-val

[SRFI-18]、[SRFI-21] threadの終了、あるいはtimeoutが与えられていればtimeoutが それに達するのを待ちます。

Timeoutは絶対的な時間を表す<time>オブジェクト(時間参照)か、 この手続きが呼ばれた時刻からの相対的な時間を秒数で表した実数でなければなりません。 タイムアウトが指定されていない(デフォルト)は#fです。

threadが正常に終了したら、thread-join!threadの 結果フィールドに格納されている値を返します。 threadが異常終了したら、thread-join!threadの結果例外 フィールドに格納されている例外を投げます。それは <terminated-thread-exception><uncaught-exception>のどちらかです。

タイムアウトに達すると、timeout-valが与えられていればtimeout-valを返し、 与えられていなければ<join-timeout-exception>を投げます。

これらの例外の詳細についてはスレッド例外を参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.32.3 同期プリミティブ

Mutex

Builtin Class: <mutex>

同期のための基本的デバイスです。次の4つの状態のいずれかを持ちます: locked/owned、locked/not-owned、unlocked/abandoned、unlocked/not-abandoned。 Mutexは、それがunlocked(ロックされていない状態)であるときのみ、 (mutex-lock!により)ロックされます。 所有されている(owned) mutexは、そのmutexを所有しているスレッドを記憶しています。 通常、所有者となるスレッドはmutexをロックしたスレッドですが、 ロックしたのとは別のスレッドがmutexを所有するようにすることもできます。 ロックはmutex-unlock!によるか、所有するスレッドが終了すると解放されます。 前者の場合、mutexはunlocked/not-abandoned(ロックされておらず、放棄されていない状態) になります。 後者の場合、mutexはunlocked/abandoned(ロックされておらず、放棄された状態)になります。

Mutexは、以下の外部スロットを持ちます。

Instance Variable of <mutex>: name

Mutexの名前。

Instance Variable of <mutex>: state

Mutexの状態。これは読み取りのみ可能なスロットです。 下記のmutex-stateの説明を参照して下さい。

Instance Variable of <mutex>: specific

アプリケーションが任意のデータを保持することのできるスロットです。 例えば、アプリケーションはこの固有フィールドで’再帰的な’ mutexを 実装することができます。

Function: mutex? obj

[SRFI-18]、[SRFI-21] objがmutexであれば#t、そうでなければ#fを返します。

Function: make-mutex :optional name

[SRFI-18]、[SRFI-21] 新しいmutexオブジェクトを生成して返します。 生成時には、mutexの状態は、unlocked/not-abandoned(ロックされておらず、 放棄されていない状態)です。オプションで、このmutexに名前を付けることができます。

Function: mutex-name mutex

[SRFI-18]、[SRFI-21] Mutexの名前を返します。

Function: mutex-specific mutex
Function: mutex-specific-set! mutex value

[SRFI-18]、[SRFI-21] Mutexの固有の値を取得/セットできます。

Function: mutex-state mutex

[SRFI-18]、[SRFI-21] mutexの状態を返します。状態は以下のうちの1つです。

あるスレッド

Mutexはlocked/owned(ロックされ所有されている)で、所有者は返されたスレッド。

シンボル not-owned

Mutexはlocked/not-owned(ロックされているが所有されていない)。

シンボル abandoned

Mutexはunlocked/abandoned(ロックされておらず、放棄されている)。

シンボル not-abandoned

Mutexはunlocked/not-abandoned(ロックされておらず、放棄されていない)。

Function: mutex-lock! mutex :optional timeout thread

[SRFI-18]、[SRFI-21] mutexをロックします。mutexがunlocked/not-abandoned( ロックされておらず放棄されていない状態)なら、 この手続きはその状態を排他的なlocked(ロックされた状態)に変更します。 デフォルトでは、mutexはlocked/owned(ロックされ、所有された状態)になり、 所有者は呼び出したスレッドです。 他の所有しているスレッドを、引数threadを与えることもできます。 引数thread#fが与えられると、mutexはlocked/not-owned (ロックされ所有されていない状態)になります。

mutexがunlocked/abandoned(ロックされておらず放棄された状態)ならば、それはつまり、 他の何らかのスレッドがそのロックを解放せずに終了した場合、 この手続きはmutexの状態を変更した後に、’abandoned mutex exception’ (スレッド例外参照)を通知します。

mutexがlocked(ロックされた状態)で、timeoutが省略されるか#fならば、 この手続きはmutexのロックが解放されるまでブロックします。 timeoutが指定されている場合は、ロックが獲得できなかったケースでは 指定された時間に達した時にmutex-lock!は戻ります。 timeoutには、絶対的な時間(<time>オブジェクト、時間参照)か、 相対的な時間を(実数で)指定できます。

mutexのロックが成功するとmutex-lock!#tを返し、 タイムアウトに達すると#fが返ります。

mutexそれ自身は’再帰的なロック’の機能は実装していません。 つまり、mutexをロックしたスレッドが再度mutexをロックしようと すると、そのスレッドはブロックします。しかし、このmutexに 基づいて再帰的なロックのセマンティクスを実装することは難しくありません。 次の例は、SRFI-18のドキュメントから引用したものです。

 
(define (mutex-lock-recursively! mutex)
  (if (eq? (mutex-state mutex) (current-thread))
      (let ((n (mutex-specific mutex)))
        (mutex-specific-set! mutex (+ n 1)))
      (begin
        (mutex-lock! mutex)
        (mutex-specific-set! mutex 0))))

(define (mutex-unlock-recursively! mutex)
  (let ((n (mutex-specific mutex)))
    (if (= n 0)
        (mutex-unlock! mutex)
        (mutex-specific-set! mutex (- n 1)))))
Function: mutex-unlock! mutex :optional condition-variable timeout

[SRFI-18]、[SRFI-21] mutexをアンロックします。mutexの状態は、unlocked/not-abandoned (ロックされておらず、放棄されていない状態)となります。 呼び出しているスレッドにより所有されていないmutexをアンロックすることは 許されています。

オプショナル引数のconditional-variableが与えられている場合、 mutex-unlock!は“条件変数待機”の動作も行います(例えば、POSIXスレッドの pthread_cond_wait)。 現在のスレッドはmutexをアンロックし、 condition-variableの待ち状態に入る動作をアトミックに行います。 スレッドは、他のスレッドがcondition-variableにシグナルを通知するか (下記のcondition-variable-signal!condition-variable-broadcast!を 見て下さい)、 timeoutが与えられていてそれに達すると、ブロックが解除されます。 引数timeoutは、絶対的な時間を表す<time>オブジェクト(時間参照)、 相対的な時間を秒数で表す実数、タイムアウトしないことを表す#fのいずれかです。 ブロックが解除された時に、必ずしも条件が満たされているとは限らないので、 次に挙げる例(SRFI-18のドキュメントより引用)のように、 呼び出したスレッドはmutexのロックを再獲得して条件を検査するべきです。

 
(let loop ()
  (mutex-lock! m)
  (if (condition-is-true?)
      (begin
        (do-something-when-condition-is-true)
        (mutex-unlock! m))
      (begin
        (mutex-unlock! m cv)
        (loop))))

mutex-unlock!の戻り値は、タイムアウトした場合に#f、 それ以外の場合は#tとなります。

Function: mutex-locker mutex
Function: mutex-unlocker mutex

それぞれ(lambda () (mutex-lock! mutex))(lambda () (mutex-unlock! mutex)) を返します。 これらのクロージャは、各mutexにつきひとつづつしか作られないため、 タイトなループの中ではこれらの形のリテラルなラムダ式を使うよりも軽量です。

Function: with-locking-mutex mutex thunk

mutexをロックしてthunkを呼びます。次のように実装されています。

 
(define (with-locking-mutex mutex thunk)
  (dynamic-wind
   (mutex-locker mutex)
   thunk
   (mutex-unlocker mutex)))

条件変数

Builtin Class: <condition-variable>

条件変数は、ある条件が真になるのを待っているスレッドの集合を保持します。 あるスレッドがその条件を変更する時、condition-variable-signal!あるいは condition-variable-broadcast!が呼ばれ、それは1つ以上の待機中の スレッドのブロックを解除するため、それらのスレッドは条件が満足するかどうか 検査できます。

条件変数オブジェクトは以下のスロットを持ちます。

Instance Variable of <condition-variable>: name

条件変数の名前。

Instance Variable of <condition-variable>: specific

アプリケーションが任意のデータを保持できるスロット。

SRFI-18は、pthreadのpthread_cond_waitに相当する手続きを 持たないことに注意してください。条件変数を待つのは、 mutex-unlock!の省略可能引数に条件変数を渡し、 その後mutexを再びmutex-lock!で得ることで行います。 この設計は柔軟性のためです。詳しくはSRFI-18を参照して下さい。

このような、pthreadで条件変数を使う定石は:

 
while (some_condition != TRUE) {
  pthread_cond_wait(condition_variable, mutex);
}

SRFI-18では次のようなコードになります。

 
(let loop ()
  (unless some-condition
    (mutex-unlock! mutex condition-variable)
    (mutex-lock! mutex)
    (loop)))
Function: condition-variable? obj

[SRFI-18]、[SRFI-21] objが条件変数なら#t、そうでなければ#fを返します。

Function: make-condition-variable :optional name

[SRFI-18]、[SRFI-21] 新しい条件変数を返します。オプショナル引数nameで その名前を与えることができます。

Function: condition-variable-name cv

[SRFI-18]、[SRFI-21] 条件変数の名前を返します。

Function: condition-variable-specific cv
Function: condition-variable-specific-set! cv value

[SRFI-18]、[SRFI-21] 条件変数の固有の値を取得/セットします。

Function: condition-variable-signal! cv

[SRFI-18]、[SRFI-21] cvで待機しているスレッドがある場合は、それらのうちの1つがスケジューラに より選択され、実行可能にされます。

Function: condition-variable-broadcast! cv

[SRFI-18]、[SRFI-21] cvで待機している全てのスレッドのブロックを解除します。

アトム

An atom is a convenient wrapper to make operations on a given set of objects thread-safe. Instead of defining thread-safe counterparts of every structure, you can easily wrap an existing data structures to make it thread-safe.

Function: atom val …

Creates and returns an atom object with val … as the initial values.

Function: atom? obj

Returns #t iff obj is an atom.

The following procedures can be used to atomically access and update the content of an atom. They commonly takes optional timeout and timeout-val arguments, both are defaulted to #f, which causes those procedures to block until they acquire a lock.

Those arguments can be used to modify the behavior when the lock cannot be acquired in timely manner. timeout may be a <time> object (see section 時間) to specify an absolute point of time, or a real number to specify the relative time in seconds. If timeout is expired, those procedures give up acquiring the lock, and the value given to timeout-val is returned.

Function: atom-ref atom :optional index timeout timeout-val

Returns index-th value of atom. See above for timeout and timeout-val arguments.

 
(define a (atom 'a 'b))

(atom-ref a 0) ⇒ a
(atom-ref a 1) ⇒ b
Function: atomic atom proc :optional timeout timeout-val

Calls proc with the current values in atom, while locking atom. proc must take as many arguments as the number of values atom has.

The returned value(s) of proc is the result of atomic, unless timeout occurs. See above for timeout and timeout-val arguments.

For example, the ref/count procedure in the following example counts the number of times the hashtable is referenced in thread-safe way.

 
(define a (atom (make-hash-table 'eq?) (list 0)))

(define (ref/count a key)
  (atomic a
   (lambda (ht count-cell)
     (inc! (car count-cell))
     (hash-table-get h key))))
Function: atomic-update! atom proc :optional timeout timeout-val

Calls proc with the current values in atom while locking atom, and updates the values in atom by the returned values from proc. proc must take as many arguments as the number of values atom has, and must return the same number of values.

The returned value(s) of proc is the result of atomic, unless timeout occurs. See above for timeout and timeout-val arguments.

The following example shows a thread-safe counter.

 
(define a (atom 0))

(atomic-update! a (cut + 1 <>))

Note: The term atom in historical Lisps meant objects that are not a cons cell (pair). Back then cons cells were the only aggregated datatype and there were few other datatypes (numbers and symbols), so having a complementary term to cells made sense.

Although it still appears in introductory Lisp tutorials, modern Lisps, including Scheme, has so many datatypes and it makes little sense to have a specific term for non-aggregate types.

Clojure adopted the term atom for thread-safe (atomic) primitive data, and we followed it.

Note: The constructor of atom is not make-atom but atom, following the convention of list/make-list, vector/make-vector, and string/make-string; that is, the name without make- takes its elements as variable number of arguments.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.32.4 スレッド例外

例外のいくつかのタイプは、スレッド関連の手続きから投げられます。 これらの例外は、Gaucheの例外メカニズム(例外参照)により 扱われます。

Builtin Class: <thread-exception>

スレッド関連の例外の基底クラスです。<exception>クラスを継承しています。 スロットを1つ持っています。

Instance Variable of <thread-exception>: thread

この例外を投げたスレッド。

Builtin Class: <join-timeout-exception>

待機していたスレッドが戻る前にタイムアウトに達した時にthread-join!によって 投げられる例外。<thread-exception>を継承しています。

Builtin Class: <abandoned-mutex-exception>

ロックされるmutexが、unlocked/abandoned(ロックされておらず、放棄された状態) であるときにmutex-lock!により投げられる例外。 <thread-exception>を継承しています。スロットを1つ持ちます。

Instance Variable of <abandoned-mutex-exception>: mutex

この例外の原因となったmutex。

Builtin Class: <terminated-thread-exception>

待機していたスレッドが(thread-terminate!により)異常終了した 場合に(thread-join!により)投げられる例外。 <thread-exception>を継承し、スロットを1つ持ちます。

Instance Variable of <terminated-thread-exception>: terminator

この例外の原因となったスレッドを終了したスレッド。

Builtin Class: <uncaught-exception>

待機していたスレッドが捕捉されない例外により終了された場合に thread-join!により投げられる例外。 <thread-exception>を継承し、スロットを1つ持ちます。

Instance Variable of <uncaught-exception>: reason

そのスレッドの終了の原因となった例外。

Function: join-timeout-exception? obj
Function: abandoned-mutex-exception? obj
Function: terminated-thread-exception? obj
Function: uncaught-exception? obj

[SRFI-18]、[SRFI-21] これらの手続きは、objが特定のタイプの例外かどうかを検査します。 SRFI-18との互換性のために提供されています。

Function: uncaught-exception-reason exc

[SRFI-18]、[SRFI-21] <uncaught-exception>オブジェクトのreasonスロットの値を 返します。 SRFI-18との互換性のために提供されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.33 gauche.time - 時間の計測

Module: gauche.time

Schemeコードの実行時間を測る3つの方法を提供します。 インタラクティブな使用に便利なtimeマクロ、 ベンチマークのための手続きのセット、 プログラム中に埋め込んで使える<time-counter>オブジェクトです。

インタラクティブな実行時間の計測

註: timeマクロはgauche.timeモジュールをオートロードする ようにあらかじめ定義されているので、timeマクロを使うだけなら (use gauche.time)としておく必要はありません。

Macro: time expr expr2 …

expr expr2 … を順に評価し、最後の式の結果を返します。 結果が返される前に、全ての式の評価にかかった実(経過)時間および ユーザースペース、カーネルスペースで費されたCPU時間がカレントエラーポートに 報告されます。

現在の実装は、経過時間に対してはsys-gettimeofday (時間参照)を、CPU時間に対してはsys-times (システムへの問い合わせ参照)を用いています。従って、 それぞれの数値の分解能はこれらの手続きが用いているシステムコールに依存します。 CPU時間は10ms単位で、経過時間はそれより細かいことが多いです。 但しgettimeofday(2)コールをサポートしていないOSでは経過時間が最悪の場合 秒単位になります。

 
gosh> (time (length (sort (call-with-input-file "/usr/share/dict/words"
                                                port->string-list))))
;(time (length (sort (call-with-input-file "/usr/share/dict/words" port- ...
; real   0.357
; user   0.350
; sys    0.000
45427

ベンチマーク

計測したいルーチンの一回の実行が一瞬で終わるために、 何度も繰り返して実行してより正確に時間を測りたいということはよくあります。 また、複数の実装を計測して比べてみたいということもよくあります。 ここに挙げるのは、そのために便利な手続きです。

これらベンチマーク手続きの名前と振る舞いは、PerlのBenchmarkモジュールを 参考にしています。

Function: time-this how thunk

thunkを何度も呼び出し、実行時間を測ります。 howには次の形が指定できます。

integer

指定された回数だけ、thunkを呼びます。

(cpu real)

CPU時間の合計が指定された数値(秒)を越えるまでthunkを繰り返し呼びます。

このルーチンはまた、thunkを実行するのと同じ回数だけ 空のループを実行して、その時間を結果から引くので、 結果にはthunkの実行時間のみが反映されます。

戻り値は後述する<time-result>レコードです。先に例を示しておきます。

 
;; Run the thunk 1,000,000 times
(time-this 1000000 (lambda () (expt 100 30)))
  ⇒ #<time-result 1000000 times/  1.030 real/  1.040 user/  0.000 sys>

;; Run the thunk at least 5.0 cpu seconds
(time-this '(cpu 5.0) (lambda () (expt 100 30)))
  ⇒ #<time-result 4903854 times/  5.090 real/  5.050 user/  0.010 sys>
Record: <time-result>

ベンチマーク結果を保持するレコードです。以下のスロットがあります。

Instance Variable of <time-result>: count

thunkが実行された回数です。 このスロットはアクセサ手続きtime-result-countでもアクセスできます。

Instance Variable of <time-result>: real

thunkの実行にかかった実経過時間の合計です。 このスロットはアクセサ手続きtime-result-realでもアクセスできます。

Instance Variable of <time-result>: user

thunkの実行にかかったユーザCPU時間の合計です。 このスロットはアクセサ手続きtime-result-userでもアクセスできます。

Instance Variable of <time-result>: sys

thunkの実行にかかったシステムCPU時間の合計です。 このスロットはアクセサ手続きtime-result-sysでもアクセスできます。

Function: make-time-result count real user sys

<time-result>レコードのコンストラクタです。

Function: time-result? obj

<time-result>レコードかどうかを判定します。

Function: time-result+ t1 t2 :key (with-count #f)
Function: time-result- t1 t2 :key (with-count #f)

ふたつの<time-result>レコードの和と差を求め、新しいレコードを返します。

with-countが偽なら、 real, user, sysの各スロットのみが計算対象となり、 結果のcountスロットはt1のcountスロットの値がそのまま引き継がれます。 これは、部分ごとに別々に計測された結果を対象に計算する場合を意図しています。

with-countが真なら、 countスロットも同様に計算対象となります。 これは、何度か走らせたベンチマーク結果を対象に計算する場合を意図しています。

Function: time-these how alist
Function: time-these/report how alist

These procedures benchmarks multiple chunks of code to compare.

The alist argument must be the form of ((key . thunk) …), where key is a symbol and thunk is a procedure taking no arguments.

The how argument is the same as time-this; that is, either an integer for number of iterations, or a list (cpu x) to indicate x seconds of cpu time.

time-these runs benchmarks for each thunk in alist using time-this, and returns the result in a list of the form (how (key1 . result1) (key2 . result2) …), where each result is a <time-result> object.

time-these/report outputs the benchmark results and comparison matrix in human readable way to the current output port.

 
gosh> (time-these/report '(cpu 3.0)
        `((real1 . ,(cut expt 100 20))
          (real2 . ,(cut %expt 100 20))
          (imag  . ,(cut expt +100i 20))))
Benchmark: ran real1, real2, imag, each for at least 3.0 cpu seconds.
  real1: 3.312 real, 3.320 cpu (3.320 user + 0.000 sys)@ 1694277.11/s n=5625000
  real2: 2.996 real, 3.010 cpu (3.010 user + 0.000 sys)@35595634.55/s n=107142860
   imag: 3.213 real, 3.190 cpu (3.190 user + 0.000 sys)@  862068.97/s n=2750000

              Rate  real1 real2   imag
  real1  1694277/s     -- 0.048  1.965
  real2 35595635/s 21.009    -- 41.291
   imag   862069/s  0.509 0.024     --

The first part of the report shows, for each thunks, the real (elapsed) time, the cpu time used (and its breakdown of user and system time), the rate of iteration per second, and the total number of iterations.

The second part compares the speed between each pair of the benchmarks. For example, its first row tells that the benchmark real1 is 0.048 times faster than real2 and 1.965 times faster than imag.

Function: report-time-results result

This is a utility procedure to create a report from the result of time-these. Actually, time-these/report is just a combination of time-these and this procedure:

 
(define (time-these/report how samples)
  (report-time-results (time-these how samples)))

より細かい計測

Class: <time-counter>

時間カウンタの抽象クラスです。時間カウンタは 時間の経過と共にその値が増加してゆく一種のタイマーです。 何度でもカウントを止めたり開始したりできます。 カウンタの値はカウントが止まっている時に読み出すことができます。 複数の時間カウンタを使えば、 例えばループ中の二つの部分について費される時間を別々に計測することもできます。

具体的なサブクラスが、どの時間をカウントするかを決定します。 時間カウンタを使うには、下に挙げるサブクラスのいずれかを インスタンシエイトしなければなりません。

Class: <real-time-counter>
Class: <user-time-counter>
Class: <system-time-counter>
Class: <process-time-counter>

それぞれ、実経過時間、ユーザースペースCPU時間、カーネルスペースCPU時間、 総CPU時間 (ユーザー+カーネル)を計測する時間カウンタのクラスです。

Method: time-counter-start! (counter <time-counter>)
Method: time-counter-stop! (counter <time-counter>)

時間カウンタcounterを開始/停止します。カウンタが走っている間の時間が、 カウンタが停止した時点でカウンタの値に加算されます。

開始/停止の対はネストすることができます。その場合は、一番外側の対のみが 有効です。 つまり、既に走っているカウンタに対しtime-counter-start!を呼んでも 何も起こりませんが、一度余分にtime-counter-stop!を呼ばないと カウンタは止まりません。 これは、内部に既に開始/停止の対を含んでいるかもしれない大きなコードブロックの 全体の時間を計測したいというような場合に便利です。

既に停止しているカウンタに対してtime-counter-stop!を呼んでも 何も起こりません。

Method: time-counter-reset! (counter <time-counter>)

カウンタcounterの値をリセットします。既にcounterが走っている 場合は、リセットの前にカウンタは停止させられます。

Method: time-counter-value (counter <time-counter>)

カウンタcounterの現在の値(秒数)を実数で返します。 分解能はそれぞれのカウンタが用いているシステムコールに依存します。

Macro: with-time-counter counter expr …

expr …が評価される間だけcounterを走らせる、 便利なマクロです。最後の式の結果を返します。このマクロは次のように 定義されます。

 
(define-syntax with-time-counter
  (syntax-rules ()
    ((_ counter . exprs)
     (dynamic-wind
      (lambda () (time-counter-start! counter))
      (lambda () . exprs)
      (lambda () (time-counter-stop! counter))))
    ))

下の例では、ループ内でのprocess-Aとprocess-Bにて費された 概略の時間をそれぞれ計測します。

 
(let ((ta (make <real-time-counter>))
      (tb (make <real-time-counter>)))
  (dotimes (i 100000)
    (with-time-counter ta
      (process-A))
    (with-time-counter tb
      (process-B)))
  (format #t "Time spent in process-A: ~s\n" (time-counter-value ta))
  (format #t "Time spent in process-B: ~s\n" (time-counter-value tb))
  )

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.34 gauche.unicode - Unicodeユーティリティ

Module: gauche.unicode

このモジュールは、Unicodeのコードポイントの列に対する様々な操作を提供します。

Gaucheはコンパイル時に、内部文字エンコーディングとしてUnicode以外も選べます。 その場合、文字列に対して完全にUnicode互換の動作を提供できない場合があります。 そこで、本モジュールの多くの操作は、文字列を対象にするものと、 コードポイントの数値のシーケンスを対象にするものの両方で提供されます。

Gaucheの内部エンコーディングがnoneeuc-jpsjisの場合は、 文字および文字列に対する操作はUnicode標準で定義されたものと完全には一致しないでしょう。 操作の結果が、内部エンコーディングでは定義されていない文字になった場合、 それは代替文字に置き換えられます。各関数のエントリで詳しく説明してあります。

コードポイントの列に対する操作はGaucheの内部文字エンコーディングとは 無関係であり、Unicode標準の定義が完全にサポートされます。 Gaucheがutf-8でコンパイルされている場合、コードポイント列に 対する操作は、文字列に対する操作と (各要素をchar->integerおよび integer->charで変換すること以外は)一致します。 コードポイント列に対する操作はGaucheの内部文字エンコーディングにかかわらず ポータブルなアルゴリズムを必要とする場合に便利です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.34.1 Unicode transfer encodings

The procedures in this group operate on codepoints represented as integers. In the following descriptions, ‘octets’ refers to an integer between 0 to 255, inclusive.

They take optional strictness argument. It specifies what to do when the procedure encounters a datum outside of the defined domain. Its value can be either one of the following symbols:

strict

Raises an error when the procedure encounters such input. This is the default behavior.

permissive

Whenever possible, treat the date as if it is a valid value. For example, codepoint value beyond #x10ffff is invalid in Unicode standard, but it may be useful for some other purpose that just want to use UTF-8 as an encoding scheme of binary data.

ignore

Whenver possible, treat the invalid input as if they do not exist.

The procedure may still raise an error in permissive or ignore strictness mode, if there can’t be a sensible way to handle the input data.

Function: ucs4->utf8 codepoint :optional strictness

Takes an integer codepoint and returns a list of octets that encodes the input in UTF-8.

 
(ucs4->utf8 #x3bb)  ⇒ (206 187)
(ucs4->utf8 #x3042) ⇒ (227 129 130)

If strictness is strict (default), input codepoint between #xd800 to #xdfff, and beyond #x110000, are rejected. If strictness is permissive, it accepts input between 0 and #x7fffffff, inclusive; it may produce 5 or 6 octets if the input is large (as the original UTF-8 definition). If strictness is ignore, it returns an empty list for invalid codepoints.

Function: utf8-length octet :optional strictness

Takes octet as the first octet of UTF-8 sequence, and returns the number of total octets requried to decode the codepoint.

If strictness is strict (default), this procedure returns either 1, 2, 3 or 4. An error is thrown if octet cannot be a leading octet of a proper UTF-8 encoded Unicode codepoint.

If strictness is permissive, this procedure may return an integer between 0 and 6, inclusive. It allows the codepoint range #x110000 to #x7fffffff as the original utf-8 spec, so the maximum number of octets can be up to 6. If the input is in the range between #xc0 and #xdf, inclusive, this procedure returns 1–it’s up to the application how to treat these illegal octets. For other values, it returns 0.

If strictness is ignore, this procedure returns 0 when it would raise an error if strictness is strict. Other than that, it works the same as the default case.

Function: utf8->ucs4 octet-list :optional strictness

Takes a list of octets, and decodes it as a utf-8 sequence. Returns two values: The decoded ucs4 codepoint, and the rest of the input list.

An invalid utf8 sequence causes an error if strictness is strict, or skipped if it is ignore. If strictness is permissive, the procedure accepts the original utf-8 sequence which can produce surrogated pair range (between #xd800 and #dfff) and the range between #x110000 to #x7fffffff. The invalid octet sequence is still an error with permissive mode.

Function: utf8->string u8vector :optional start end

[R7RS] Converts a sequence of utf8 octets in u8vector to a string. Optional start and/or end argument(s) will limit the range of the input.

If Gauche’s native encoding is utf8, u8vector->string (see section ユニフォームベクタの変換) will do the job faster; but this routine can be used regardless of Gauche’s native encoding, and it raises an error if u8vector contains octet sequences illegal as utf8.

Function: string->utf8 string :optional start end

[R7RS] Converts a string to a u8vector of utf8 octets. Optional start and/or end argument(s) will limit the range of the input.

If Gauche’s native encoding is utf8, string->u8vector (see section ユニフォームベクタの変換) will do the job faster; but this routine can be used regardless of Gauche’s native encoding.

Function: ucs4->utf16 codepoint :optional strictness

Takes an integer codepont and returns a list of integers that encodes the input in UTF-16. The output is either one integer or two integers, and each integer is in the range between 0 and 65535 (inclusive).

If strictness is strict (default), input codepoint between #xd800 to #xdfff, and beyond #x110000, are rejected. If strictness is permissive, it accepts high surrogates and low surrogates, in which case the result is single element list of input. If strictness is ignore, an empty list is returned for an invalid codepoint (including surrogates).

Function: utf16-length code :optional strictness

Code must be an integer between 0 and 65535, inclusive. Returns 1 if code is BMP character codepoint, or 2 if code is high surrogate codepoint.

If strictness is strict (default), an error is signalled if code is a low surrogate, or it is out of range. If strictness is permissive, 1 is returned for low surrogates, but an error is signalled for out of range arguments. If strictness is ignore, 0 is returned for low surrogates and out of range arguments.

Function: utf16->ucs4 code-list :optional strictness

Takes a list of integers and decodes it as a utf-16 sequence. Returns two values: The decoded ucs4 codepoint, and the rest of input list.

If strictness is strict (default), an invalid utf-16 sequence and out-of-range integer raise an error. If strictness is permissive, an out-of-range integer causes an error, but a lone surrogate is allowed and returned as is. If strictness is ignore, lone surrogates and out-of-range integers are just ignored.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.34.2 Unicode text segmentation

These procedures implements grapheme-cluster and word breaking algorithms defined in UAX #29: Unicode Text Segmentation.

Function: string->words string
Function: codepoints->words sequence

From given string or codepoint sequence (a <sequence> object containing list of codepoints), returns a list of words. Each cluster is represented as a string, or a sequence of the same type as input, respectively.

 
(string->words "That's it.")
 ⇒ ("That's" " " "it" ".")
(codepoints->words '(84 104 97 116 39 115 32 105 116 46)
 ⇒ ((84 104 97 116 39 115) (32) (105 116) (46))

In the second example, the list is a list of codepoints of characters in "That’s it."

Function: string->grapheme-clusters string
Function: codepoints->grapheme-clusters sequence

From given string or codepoint sequence (a <sequence> object containing list of codepoints), returns a list of grapheme clusters. Each cluster is represented as a string, or a sequence of the same type as input, respectively.

The following procedures are low-level building blocks to build the above string->words etc. A generator argument is a procedure with no arguments, and returns a value (or some values) at at time for every call, until it returns EOF.

Function: make-word-breaker generator
Function: make-grapheme-cluster-breaker generator

From given generator is a generator of characters or codepoints, returns a generator that returns two values: The first value is the character or codepoint generated from the original generator, and the second value is a boolean flag, which is #t if a word or a grapheme cluster breaks before the character/codepoint, and #f otherwise.

Suppose a generator g returns characters in a string That's it., one at a time. Then the created generator will work as follows:

 
(define brk (make-word-breaker g))
(brk)  ⇒  #\T     and #t
(brk)  ⇒  #\h     and #f
(brk)  ⇒  #\a     and #f
(brk)  ⇒  #\t     and #f
(brk)  ⇒  #\'     and #f
(brk)  ⇒  #\s     and #f
(brk)  ⇒  #\space and #t
(brk)  ⇒  #\i     and #t
(brk)  ⇒  #\t     and #f
(brk)  ⇒  #\.     and #t
(brk)  ⇒  #<eof>  and #t

It shows the word breaks at those character boundaries shown by the caret ^ below (for clearity, I use _ to indicate the space).

 
  T h a t ' s _ i t .
 ^           ^ ^   ^ ^
Function: make-word-reader generator return
Function: make-grapheme-cluster-reader generator return

The input generator is a generator of characters or codepoints, and return is a procedure that takes a list of characters or codepoints, and returns an object. These procedures creates a generator that returns an object at at time, each consists of a word or a grapheme cluster, respectively.

Suppose a generator g returns characters in a string That's it., one at a time, again. Then the created generator works as follows:

 
(define brk (make-word-reader g list->string))
(brk)  ⇒  "That's"
(brk)  ⇒  " "
(brk)  ⇒  "it"
(brk)  ⇒  "."
(brk)  ⇒  #<eof>

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.34.3 Full string case conversion

Function: string-upcase string
Function: string-downcase string
Function: string-titlecase string
Function: string-foldcase string

[R6RS][R7RS] Converts given string to upper case, using language-independent full case folding defined by Unicode standard. They differ from srfi-13’s procedures with the same names (see section 文字列のケース(大文字小文字)マッピング), which simply uses character-by-character case mapping. Notably, the length of resulting string may differ from the source string, and some conversions are sensitive to whether the character is at the word boundary or not. The word boundaries are determined according to UAX #29 text segmentation rules.

 
(string-upcase "straße")
 ⇒ "STRASSE"
(string-downcase "ΧΑΟΣΧΑΟΣ.ΧΑΟΣ. Σ.")
 ⇒ "χαοσχαοσ.χαος. σ."
(string-titlecase "You're talking about R6RS, right?")
 ⇒ "You're Talking About R6rs, Right?"
(string-foldcase "straße")
 ⇒ "strasse"
(string-foldcase "ΧΑΟΣΣ")
 ⇒ "χαοσσ"
Function: codepoints-upcase sequence
Function: codepoints-downcase sequence
Function: codepoints-titlecase sequence
Function: codepoints-foldcase sequence

Like string-upcase etc, but these work on a sequence of codepoints instead. Returns a sequence of the same type of the input.

 
(codepoints-upcase '#(115 116 114 97 223 101))
 ⇒ #(83 84 82 65 83 83 69)
Function: string-ci=? string1 string2 string3 …
Function: string-ci<? string1 string2 string3 …
Function: string-ci<=? string1 string2 string3 …
Function: string-ci>? string1 string2 string3 …
Function: string-ci>=? string1 string2 string3 …

[R7RS] Case-insensitive string comparison, using full-string case conversion.

Note that Gauche has builtin string-ci=? etc., which use character-wise case folding (see section 文字列の比較). These are different procedures.

 
(string-ci=? "\u00df" "SS") ⇒ #t

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.35 gauche.uvector - ユニフォームベクタ

Module: gauche.uvector

SRFI-4(SRFI-4)に定義されている、 要素が同一の数値型であるようなベクタを提供します。

Gaucheの実装は、SRFI-4の仕様を次のように拡張しています。

通常のベクタではなくSRFI-4ベクタを使うことにより得られる利点がいくつかあります。 まず、SRFI-4ベクタは通常のベクタよりもコンパクトです。 いくつかのオペレーション(特に、Gaucheの拡張仕様であるベクタ上の数値演算)では、 型検査と型変換を個々の要素に対して行わなくても良いため、 極めて効率の良い演算が可能です。さらに、 数値の配列を扱う外部のライブラリとのインタフェースが容易です。 例えば、GaucheのOpenGLバインディングではSRFI-4ベクタを多用しています。

以下の11タイプのベクタが定義されます。

s8vector

要素が -2^7 から 2^7-1 の間の正確な整数であるベクタ

u8vector

要素が 0 から 2^8-1 の間の正確な整数であるベクタ

s16vector

要素が -2^15 から 2^15-1 の間の正確な整数であるベクタ

u16vector

要素が 0 から 2^16-1 の間の正確な整数であるベクタ

s32vector

要素が -2^31 から 2^31-1 の間の正確な整数であるベクタ

u32vector

要素が 0 から 2^32-1 の間の正確な整数であるベクタ

s64vector

要素が -2^63 から 2^63-1 の間の正確な整数であるベクタ

u64vector

要素が 0 から 2^64-1 の間の正確な整数であるベクタ

f16vector

要素が、16ビット浮動小数点数(half floatとしても知られている)で表現 し得るような不正確な実数であるベクタ

f32vector

要素が、GaucheをコンパイルしたCコンパイラのfloatで表現し得るような 不正確な実数(通常IEEE単精度浮動少数点数)であるベクタ

f64vector

要素が、GaucheをコンパイルしたCコンパイラのdoubleで表現し得るような 不正確な実数(通常IEEE倍精度浮動少数点数)であるベクタ

ベクタの型が許す範囲外の値を格納しようとした場合、通常はエラーとなります。 いくつかの手続きは省略可能な引数clampによって、 そのような場合に別のふるまいを指定することができます。 clampには以下のいずれかの値を与えることが出来ます。

#f

デフォルト (エラーを通知)

high

高い方の値をクランプ、すなわち、格納しようとする値が許される値の最大値より大きかった 場合は、可能な最大値を代わりに格納します。

low

低い方の値をクランプ、すなわち、格納しようとする値が許される値の最小値より大きかった 場合は、可能な最小値を代わりに格納します。

both

高いほうと低いほうの両方の値をクランプします。

 
(list->u8vector '(-1))         ⇒ error
(list->u8vector '(-1) 'low)    ⇒ #u8(0)
(list->u8vector '(-1) 'high)   ⇒ error
(list->u8vector '(3000) 'high) ⇒ #u8(255)
(list->u8vector '(-100 20 300) 'both) ⇒ #u8(0 20 255)

以下の記述では、TAGs8, u8, s16, u16, s32, u32, s64, u64, f16, f32, f64 のいずれにも置き換えて読むことができるものとします。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.35.1 ユニフォームベクタの基本操作

Builtin Class: <TAGvector>

TAGvectorのクラス。<sequence>を継承します。

Reader Syntax: #TAG(n …)

リテラルの単一型のベクタを記述します。

 
#s8(3 -2 4)
#u32(4154 88357 2 323)
#f32(3.14 0.554525 -3.342)
Function: TAGvector? obj

[SRFI-4] objTAGvectorなら#tを、そうでなければ#fを返します。

Function: uvector? obj

objがいずれかの型のユニフォームベクタなら#tを、 そうでなければ#fを返します。

Function: TAGvector x

[SRFI-4] 数値x … を要素に持つTAGvectorを作成して返します。 正確な整数のベクタに対しては、数値は正確な整数でなければならず、 また有効な範囲内の値でなければなりません。

 
(s8vector 1 2 3) ⇒ #s8(1 2 3)
Function: make-TAGvector len :optional fill

[SRFI-4] 長さlenTAGvectorを作成して返します。各要素はfillで 初期化されます。正確な整数のベクタに対しては、fillは正確な整数でなければならず、 また有効な範囲内の値でなければなりません。 fillが省略された場合、各要素の初期値は不定です。

 
(make-u8vector 4 0) ⇒ #u8(0 0 0 0)
Function: make-uvector class len :optional fill

これはGaucheの拡張です。各ユニフォームベクタの型に対応する関数を呼ぶかわりに、 欲しいユニフォームベクタのクラスを渡してベクタを作ることができます。

 
(make-uvector <u8vector> 3)    ⇒ #u8(0 0 0)
(make-uvector <s8vector> 5 -1) ⇒ #s8(-1 -1 -1 -1 -1)
Function: TAGvector-length vec

[SRFI-4] TAGvector vecの長さを返します。

モジュールgauche.collectionをインポートしていれば、 vecの長さを知るのに、総称関数size-ofを使うこともできます (gauche.collection - コレクションフレームワーク参照)。

 
(s16vector-length '#s16(111 222 333)) ⇒ 3

(use gauche.collection)
(size-of '#s16(111 222 333)) ⇒ 3
Function: uvector-length uvector

これはTAGvector-lengthの汎用バージョンです。 どんな型のユニフォームベクタでも渡すことができ、 その要素数が返されます。

Function: uvector-size uvector :optional start end

この手続きは全てのユニフォームベクタに適用することができます。 uvectorのバイナリデータとしてのサイズをオクテット数で返します。

startおよび/またはendが与えられた場合は、 これらのインデックスの間のデータについてのみサイズが計算されます。 endには、ベクタ終端を示すために-1を与えることもできます。 返される値は (write-uvector uvector port start end) によって 出力されるオクテット数と一致します。

(要素数を返すuvector-lengthと混同しないようにしてください。)

 
(uvector-size '#u8(1 2 3))        ⇒ 3
(uvector-size '#u64(1 2 3))       ⇒ 24

(uvector-size '#u32(0 1 2 3) 2)   ⇒ 8
(uvector-size '#u32(0 1 2 3) 0 1) ⇒ 4
Function: uvector-class-element-size class

与えられたクラスのユニフォームベクタの1要素が占める大きさをバイト数で返します。 classがユニフォームベクタのクラスでない場合はエラーが投げられます。

 
(uvector-class-element-size <u8vector>)  ⇒ 1
(uvector-class-element-size <s64vector>) ⇒ 8
Function: TAGvector-ref vec k :optional fallback

[SRFI-4+] TAGvector veck番目の要素を返します。

kが有効な範囲外であった場合、通常はエラーが通知されますが、 省略可能な引数fallbackが与えられている場合はそれが返されます。

モジュールgauche.collectionをインポートしていれば、 総称関数refを使うこともできます。

 
(u16vector-ref '#u16(111 222 333) 1) ⇒ 222

(use gauche.collection)
(ref '#u16(111 222 333) 1) ⇒ 222
Function: uvector-ref vec k :optional fallback

TAGvector-refの汎用バージョンです。 vecにどんな種類のユニフォームベクタを取ることができ、 そのk番めの要素を返します。 kが有効な範囲外であった場合、通常はエラーが通知されますが、 省略可能な引数fallbackが与えられている場合はそれが返されます。

この手続きは様々な種類のユニフォームベクタに対して動作するような一般的なコードを 書く際にとても便利ですが、それぞれの種類のユニフォームベクタ専用の アクセサに比べると遅いです。GaucheのコンパイラはTAGvector-refの 呼び出しを認識して非常に効率の良いコードを出すのに対し、 この手続きは通常の手続き呼び出しになるからです。内部のループ内で呼び出す場合、 これは大きな差になるかもしれません。

なお、(setter uvector-ref)uvector-set!です。

Function: TAGvector-set! vec k n :optional clamp

[SRFI-4+] TAGvector veck番目の要素に数値nをセットします。 省略可能な引数clampが、nが正しい範囲外の数であった場合の動作を指定します。 デフォルトではエラーが通知されます。

モジュールgauche.collectionをインポートしていれば、 総称関数refのsetter手続きを使うこともできます。

 
(let ((v (s32vector -439 852 8933)))
  (s32vector-set! v 1 4)
  v)
 ⇒ #s32vector(-439 4 8933)

(use gauche.collection)
(let ((v (s32vector -439 852 8933)))
  (set! (ref v 1) 4)
  v)
 ⇒ #s32vector(-439 4 8933)
Function: uvector-set! vec k val

TAGvector-set!の汎用バージョンです。全ての種類の ユニフォームベクタを扱えますが、特定のユニフォームベクタ用のセッターを 使うよりやや遅いです。

Function: TAGvector-fill! vec fill :optional start end

vecのすべて要素にfillをセットします。 startendで要素の範囲を指定することも出来ます。

Function: TAGvector-copy vec :optional start end

ベクタvecをコピーします。 省略可能な引数startendが与えられた場合、 それらは取り出される要素の範囲を制限します。

 
(u8vector-copy '#u8(1 2 3 4))     ⇒ #u8(1 2 3 4)
(u8vector-copy '#u8(1 2 3 4) 2)   ⇒ #u8(3 4)
(u8vector-copy '#u8(1 2 3 4) 1 3) ⇒ #u8(2 3)
Function: uvector-copy vec :optional start end

これはTAGvector-copyの汎用バージョンです。 どんな型のuvectorでもvecに渡すことができ、そのコピー (もしくは、start/endによっては一部のコピー)が返されます。

Function: TAGvector-copy! target tstart source :optional sstart send

target および source はともに TAGvector でなければ なりません。さらに、target は変更可能でなければなりません。 この手続きは、sourceの要素を、インデックスsstartから(これを含み) send までを、target へインデックス tstartからコピーします。 sstartおよびsendは省略可能で、その場合には、それぞれ、 0 および sourceの長さが仮定されます。

 
(let ((target (u8vector 0 1 2 3 4 5 6)))
  (u8vector-copy! target 2 '#u8(10 11 12 13 14) 1 4)
  target)
 ⇒ #u8(0 1 11 12 13 6)

もし、コピー元のベクタの sstartsend の間にある要素の 数がコピー先のベクタのtstart以降の部分よりも大きければ、超過分の ベクタはだまって捨てられます。

targetsourceに同一のベクタを渡しても構いません。 コピー先とコピー元の領域が重なっていても、コピーは常に正しく行われます。

注意事項: この手続きは以前はユニフォームベクタ target および source のみを引数としてとり、source の内容を target へ コピーするためだけに使われました。両方のベクタは同じ型で、同じ長さでなけ ればなりませんでした。この API は string-copy! (SRFI-13) および vector-copy! (SRFI-133)にあわせて現在の形式に改訂されています。 旧来のインタフェースもバックワードコンパチビリティのためにサポートされて いますが、これは廃止予定で、将来のリリースではサポートされなくなります。

Function: TAGvector-multi-copy! target tstart tstride source :optional sstart ssize sstride count

この手続きは、コピー元ベクタsourceの異なる部分を コピー先ベクタtargetの異なる部分へと一度にコピーするのに使えます。

ssizeが省略されるか0の場合、この手続きは次の動作をします。

 
;; For each i from 0 to count:
(TAGvector-copy! target (+ tstart (* i tstride))
                 source sstart)

つまり、sourceベクタの内容 (sstartが指定されてればそこから、 指定が無ければ最初から)をtargetベクタに、tstrideづつインデックスを 進めながらコピーします。コピー先のインデックスがtargetベクタの範囲を 越えるか、countが指定されていればその回数分だけコピーが済むかすれば 手続きは終了します。次の例を見てください。

 
(define t (make-u8vector 10 0))
(u8vector-multi-copy! t 0 4 '#u8(1 2 3))

t ⇒ #u8(1 2 3 0 1 2 3 0 1 2)

ssizeに正の整数が与えられた場合は、コピー元ベクタもssizeごとに 分割されます。

 
;; For each i from 0 to count:
(TAGvector-copy! target (+ tstart (* i tstride))
                 source (+ sstart (* i sstride))
                        (+ sstart (* i sstride) ssize))

つまり、コピー元インデックスをsstrideづつ、コピー先を dstrideづつ増やしながら、コピー元からそれぞれssize分のデータが切り出されて targetにコピーされます。この場合、sstrideは省略されると ssizeと同じになります。

 
(define t (make-u8vector 12 0))
(u8vector-multi-copy! t 0 4 '#u8(1 2 3 4 5 6 7 8 9) 0 3)

t ⇒ #u8(1 2 3 0 4 5 6 0 7 8 9 0)

操作はcount個のデータ片がコピーされるか、コピー元あるいはコピー先の インデックスがそれぞれのベクタの終端に達したら終わります。

ヒント: コピー元のベクタの中程だけ (最後までではなく) をコピーしたい場合は、 sstrideに0を与えればできます。

 
(define t (make-u8vector 12 0))
(u8vector-multi-copy! t 0 4 '#u8(1 2 3 4 5 6 7 8 9) 2 4 0)

t ⇒ #u8(3 4 5 6 3 4 5 6 3 4 5 6)

コレクションやシーケンスフレームワークを使うと、さらに様々な操作を 行うことができます。

 
(use gauche.collection)
(use gauche.sequence)

(fold + 0 '#s32(1 2 3 4)) ⇒ 10

(map-to <f32vector> * '#f32(3.2 1.1 4.3) '#f32(-4.3 2.2 9.4))
  ⇒ #f32(-13.760001 2.420000 40.420002)

(subseq #u32(1 4 3 4 5) 2 4) ⇒ #u32(3 4)
Function: uvector-copy! target tstart source :optional sstart send

これはTAGvector-copy!の汎用バージョンです。 コピー元sourceとコピー先targetはユニフォームベクタであれば どの型でも許され、また両者の型が異なっていても構いません。 ビット表現がそのままコピーされます。従って異なる型のユニフォームベクタ間で コピーした場合は、結果は数値の内部表現に依存します。 そのような用法は、バイナリデータを扱う時には便利でしょう。

tstarttargetの型によって解釈され、 sstartsendsourceの型によって解釈されます。

 
(rlet1 v (make-u8vector 6 0)
  (uvector-copy! v 1 '#u32(0 #x01020304 0) 1 2))
 ⇒ #u8(0 1 2 3 4 0) or #u8(0 4 3 2 1 0)
Function: TAGvector-append vec …

引数はすべてTAGvectorでなければなりません。 引数ベクタの内容を全てつなぎ合わせた新たなベクタを返します。

 
(u8vector-append '#u8(1 2 3) '#u8(4 5) '#u8() '#u8(6 7 8))
  ⇒ #u8(1 2 3 4 5 6 7 8)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.35.2 ユニフォームベクタの変換

Function: TAGvector->list vec :optional start end

[SRFI-4+] TAGvector vecをリストに変換します。 省略可能な引数startendが与えられた場合、 それらは取り出される要素の範囲を制限します。

モジュールgauche.collectionをインポートしていれば、 総称関数coerce-toを使うこともできます。

 
(u32vector->list '#u32(9 2 5)) ⇒ (9 2 5)

(use gauche.collection)
(coerce-to <list> '#u32(9 2 5)) ⇒ (9 2 5)
Function: TAGvector->vector vec :optional start end

TAGvector vecをベクタに変換します。 省略可能な引数startendが与えられた場合、 それらは取り出される要素の範囲を制限します。

モジュールgauche.collectionをインポートしていれば、 総称関数coerce-toを使うこともできます。

 
(f32vector->vector '#f32(9.3 2.2 5.5))   ⇒ #(9.3 2.2 5.5)
(f32vector->vector '#f32(9.3 2.2 5.5) 2) ⇒ #(5.5)

(use gauche.collection)
(coerce-to <vector> '#f32(9.3 2.2 5.5)) ⇒ #(9.3 2.2 5.5)
Function: list->TAGvector list :optional clamp

[SRFI-4+] リストlistTAGvectorに変換します。 省略可能な引数clampが、リスト内の要素が正しい範囲外の数であった場合の 動作を指定します。

モジュールgauche.collectionをインポートしていれば、 総称関数coerce-toを使うこともできます。

 
(list->s64vector '(9 2 5)) ⇒ #s64(9 2 5)

(use gauche.collection)
(coerce-to <s64vector> '(9 2 5)) ⇒ #s64(9 2 5)
Function: vector->TAGvector vec :optional start end clamp

ベクタvecTAGvectorに変換します。 省略可能な引数startendが与えられた場合、 それらは取り出される要素の範囲を制限します。 省略可能な引数clampが、ベクタ内の要素が正しい範囲外の数であった場合の 動作を指定します。

モジュールgauche.collectionをインポートしていれば、 総称関数coerce-toを使うこともできます。

 
(vector->f64vector '#(3.1 5.4 3.2)) ⇒ #f64(3.1 5.4 3.2)

(use gauche.collection)
(coerce-to <f64vector> '#(3.1 5.4 3.2)) ⇒ #f64(3.1 5.4 3.2)
Function: string->s8vector string :optional start end immutable?
Function: string->u8vector string :optional start end immutable?

与えられた文字列の内部表現のバイト列と同じバイト列を持つs8vectorもしくは u8vectorを返します。省略可能な範囲引数startendは、 変換される文字列中の文字位置を指定します (バイト位置ではないことに注意)。

デフォルトでは、元の文字列の内容が新たに作られる変更可能なユニフォームベクタへと コピーされ、そのベクタが返されます。しかし、省略可能引数immutable?に 真の値が渡された場合、返されるユニフォームベクタは変更不可能となり、 また文字列本体のコピーが避けられる可能性があります。 (Gaucheでは、文字列本体は変更不可能で、string-set!は新たな 文字列本体を作り出します。したがってstring->u8vectorimmutable?#tを渡してユニフォームベクタを作った後、 元の文字列を変更しても、作られたユニフォームベクタは変更されません。)

これらの手続きは、文字を構成するバイト列をランダムにアクセスしたい場合などに 有用です。

 
(string->u8vector "abc") ⇒ #u8(97 98 99)

;; 内部コードがEUCの場合
(string->u8vector "いろは") ⇒ #u8(164 164 164 237 164 207)
 
(string->u8vector "very large string .... " 0 -1 #t)
  ⇒ #u8(...)  ; 変更不可、元の文字列と内容を共有
Function: string->s8vector! target tstart string :optional start end
Function: string->u8vector! target tstart string :optional start end

target は、それぞれ s8vector あるいは u8vector でなければなりません。 target は、変更可能でなければなりません。 string の生バイト表現を target へインデックス tstart からコピーします。

target を返します。

 
(let ((target (make-u8vector 10 0)))
  (string->u8vector! target 3 "abcde"))
 ⇒ #u8(0 0 0 97 98 99 100 101 0 0)
Function: s8vector->string vec :optional start end terminator
Function: u8vector->string vec :optional start end terminator

与えられたs8vectorもしくはu8vector vecのバイト列と同じ内部バイト列を 持つ文字列を作成して返します。省略可能な範囲引数startendは、 vec中の変換されるべき範囲をバイト位置で指定します。

省略可能引数terminatorは正確な整数か#fでなければなりません (#fがデフォルト)。正確な整数が与えられた場合、vecの中に その数値が現れれば、その直前までが結果の文字列となります。 例えば0を与えることで、バッファからNUL終端された文字列を読むことができます。

 
(u8vector->string '#u8(65 66 0 67 68) 0 5)   ⇒ "AB\0CD"
(u8vector->string '#u8(65 66 0 67 68) 0 5 0) ⇒ "AB"

vec中のバイト列が文字列の内部表現として不正な値を持っていた場合は、 不完全な文字列が返されます。

Function: string->s32vector string :optional start end
Function: string->u32vector string :optional start end

与えられた文字列stringの各文字の内部コードを値として持つ s32vectorもしくはu32vectorを返します。 省略可能な範囲引数startendは、 変換される文字列中の文字位置を指定します。

これらの手続きは、文字列中の文字をランダムにアクセスする場合に便利です。

Function: string->s32vector! target tstart string :optional start end
Function: string->u32vector! target tstart string :optional start end

targetは変更可能なs32vectorもしくはu32vectorでなければなりません。 targetの場所tstartから、stringの各文字のコードポイントを 順に埋めてゆきます。targetベクタの終端に達するか、文字が無くなるまで繰り返します。

省略可能な引数startendstring内の文字位置のインデックスで、 考慮するstringの範囲を制限します。

Function: s32vector->string vec :optional start end terminator
Function: u32vector->string vec :optional start end terminator

startendを考えなければ、 これらの手続きは次のコードのような動作をします:

 
(lambda (vec) (map-to <string> integer->char vec)))

省略可能な範囲引数startendは、 vec中の変換されるべき範囲を指定します。

省略可能引数terminatorは、正確な整数か#fでなければ なりません (#fがデフォルト)。正確な整数が与えられ、 入力にその数値が見つかった場合は、出力の文字列はその直前で 打ちきられます。

 
(u32vector->string '#u32(65 66 0 67 68) 0 5 0) ⇒ "AB"
Function: uvector-alias uvector-class vec :optional start end

この手続きは、クラスがuvector-classであり、 ユニフォームベクタvecのメモリ領域を共有するような 新しいユニフォームベクタを作成して返します。 省略可能な引数startendが与えられた場合は vecの内容のうちそれらのインデックスで指定される範囲のみが使われます。 メモリ領域が共有されているので、vecに加える変更は 新しいベクタから見えますし、その逆も可能です。

クラスuvector-classはユニフォームベクタのクラスでなければなりませんが、 vecのクラスと一致している必要はありません。 そのような場合は、新しいベクタはvecと同じメモリ領域の値を 異なる値と解釈するかもしれません。 例えば、次のコードはGaucheが走っているマシンが リトルエンディアンであるかビッグエンディアンであるかを判定します。

 
(let ((u8v (uvector-alias <u8vector> #u32(1))))
  (if (zero? (u8vector-ref u8v 0))
      'big-endian
      'little-endian))

uvector-classがs8vectorかu8vector以外の場合、 新しいベクタが指す領域は、そのベクタの要素のアラインメントの要請に したがっていなければなりません。 ユニフォームベクタの開始点は常に全てのユニフォームベクタのアラインメント要請を 満たします。したがって、例えばu8vectorからu32vectorを作成する場合、 startおよびendは4の倍数でなければなりません (あるいは、start/endが省略された場合、vecの長さが 4の倍数でなければなりません)。 与えられたパラメータでアラインメントの要請が満たせない場合はエラーが通知されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.35.3 ユニフォームベクタの数値演算

Function: TAGvector-add vec val :optional clamp
Function: TAGvector-add! vec val :optional clamp
Function: TAGvector-sub vec val :optional clamp
Function: TAGvector-sub! vec val :optional clamp
Function: TAGvector-mul vec val :optional clamp
Function: TAGvector-mul! vec val :optional clamp

要素毎の計算手続きです。vecTAGvectorでなければなりません。 また、valvecと同じ長さのTAGvectorかベクタかリスト、 あるいは数値(整数ベクタに対しては正確な整数、実数ベクタに対しては実数) でなければなりません。

valTAGvectorの場合、 vecと対応する要素毎に加算、減算、乗算が行われ、 結果がTAGvectorとして返されます。 破壊的なバージョン(名前に‘!’がついているもの)では、vecが 結果を格納するために再利用されます。 演算の結果がTAGvectorの要素の値域外になった場合の動作は 省略可能な引数clampによって指定されます。 (f32vectorとf64vectorでは、値域外になった要素にはinfinityが格納され、 clampの値は無視されます)。

valが数値である場合、vecの各要素とその数値の間で演算が行われます。

 
(s8vector-add '#s8(1 2 3 4) '#s8(5 6 7 8)) ⇒ #s8(6 8 10 12)
(u8vector-sub '#u8(1 2 3 4) '#u8(2 2 2 2)) ⇒ error
(u8vector-sub '#u8(1 2 3 4) '#u8(2 2 2 2) 'both) ⇒ #u8(0 0 1 2)

(f32vector-mul '#f32(3.0 2.0 1.0) 1.5) ⇒ #f32(4.5 3.0 1.5)
Function: TAGvector-div vec val
Function: TAGvector-div! vec val

要素毎の除算です。これらはf32vectorとf64vectorのみに対して定義されます。 valvecと同じ大きさのTAGvectorかベクタかリスト、 あるいは実数でなければなりません。

 
(f32vector-div '#f32(1.0 2.0 3.0) 2.0) ⇒ #f32(0.5 1.0 1.5)
Function: TAGvector-and vec val
Function: TAGvector-and! vec val
Function: TAGvector-ior vec val
Function: TAGvector-ior! vec val
Function: TAGvector-xor vec val
Function: TAGvector-xor! vec val

要素毎の論理(ビット)演算です。 これらの手続きは整数ベクタに対してのみ定義されています。 valvecと同じ大きさのTAGvectorかベクタかリスト、 あるいは正確な整数でなければなりません。vecの各要素と、対応するvalの要素 (valが非スカラー値の場合)もしくはval自身 (valが整数の場合)とのビット毎のand, inclusive orまたはexclusive or が計算され、結果がTAGvectorで返されます。 破壊的なバージョン(名前に‘!’がついているもの)では、vecが 結果を格納するために再利用されます。

Function: TAGvector-dot vec0 vec1

ふたつのTAGvectorの内積を計算します。 vec0vec1の長さは等しくなければなりません。

Function: TAGvector-range-check vec min max

vecTAGvectorでなければなりません。 minmaxはそれぞれ、vecと同じ長さのTAGvector、 ベクタ、リストのいずれかか、実数もしくは#fでなければなりません。

vecの各要素に対して、この手続きはそれが対応するminvalmaxval の間にあるかどうかを検査します。minvalmaxvalも範囲に含みます。 ここで、minvalmaxvalは、min/maxが非スカラー値 であればvecの要素に対応するそれぞれの要素、 min/maxが数値であればその数値そのものです。 min#fの場合、最小値はマイナス無限大と考えられます。 max#fの場合、最大値はプラス無限大と考えられます。

vecの全ての要素が範囲内であった場合は#fが返されます。 そうでなければ、範囲を外れた要素のうちもっとも左のもののvec内での インデックスが返されます。

 
(u8vector-range-check '#u8(3 1 0 2) 0 3)  ⇒ #f
(u8vector-range-check '#u8(3 1 0 2) 1 3)  ⇒ 2

(u8vector-range-check '#u8(4 32 64 98) 0 '#u8(10 40 70 90))
  ⇒ 3

;; Range check in a program
(cond
 ((u8vector-range-check u8v 1 31)
  => (lambda (i)
      (errorf "~sth vector element is out of range: ~s"
              i (u8vector-ref u8v i))))
 (else (do-something u8v)))
Function: TAGvector-clamp vec min max
Function: TAGvector-clamp! vec min max

vecTAGvectorでなければなりません。 minmaxはそれぞれ、vecと同じ長さのTAGvector、 ベクタ、リストのいずれかか、実数もしくは#fでなければなりません。

TAGvector-range-checkと同じように、この手続きはvecの各要素が minおよびmaxで指定される最小値と最大値の間にあるかどうかを 検査します。要素が最小値より小さかった場合はそれが最小値に置き換えられます。 要素が最大値より大きかった場合はそれが最大値に置き換えられます。

TAGvector-clampはvecのコピーを作ってそれに対して クランプ操作を行います。TAGvector-clamp!はvecを直接 変更します。どちらもクランプ操作が行われた後のTAGvectorを返します。

 
(s8vector-clamp '#s8(8 14 -3 -22 0) -10 10) ⇒ #s8(8 10 -3 -10 0)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.35.4 ユニフォームベクタのブロック入出力

ユニフォームベクタは、メモリの固まりを抽象化しているものと考えることも できます。それなら、それをバイナリI/Oに使えないでしょうか。もちろんできます。

Function: read-uvector class size :optional iport endian

クラスclassのユニフォームベクタの要素をsize個、iportから 読み込み、それを新たに作ったユニフォームベクタとして返します。 iportが省略された場合は、現在の入力ポートが使われます。

例えば次のようにして、入力をオクテットストリームとして読み込めます:

 
(with-input-from-string "abcde"
  (^[] (read-uvector <u8vector> 5)))
 ⇒ #u8(97 98 99 100 101)

入力ポートが既にEOFに達していた場合はEOFオブジェクトが返されます。 size要素を読む前に入力がEOFに達した場合、返されるuvectorの長さは sizeより短いものとなります。

iportがバッファードポートであり、そのバッファリングモードが ‘modest’ か ‘none’ であった場合、read-uvectoriportが EOFに達していなくても、size要素を読む前に戻ることがあります (バッファリングモードについてはファイルポートを参照して下さい)。 パイプやネットワークに接続されたポートはデフォルトでそのように振舞います。

データはバイトストリームとして読まれるので、 s8vectorとu8vector以外のユニフォームベクタを与えた場合は 結果がエンディアンに影響を受けることに注意して下さい。 省略可能引数endianによって、入力データのエンディアンを指定 することができます。省略した場合はパラメータdefault-endianの 値が使われます。エンディアンの扱いについてより詳しくはエンディアンを参照して ください。

入力データの大きさが不明で、EOFまで全てを読み込みたい場合は、下の port->uvectorを使ってください。

R7RSにはread-bytevectorがあります。これはread-uvector<u8vector>を渡すのと等価です。

Function: read-uvector! vec :optional iport start end endian

与えられた入力ポートiportからデータの固まりを読みだし、それを ユニフォームベクタvecに格納します。 どんな種類のユニフォームベクタでも与えることができます。 省略可能な引数startendが与えられた場合は、 それらがvec中でデータが格納されるべき領域のインデックスの範囲を 示します。endについては-1でvecの最後を示すこともできます。 その範囲外のvecの値は変更されません。 startendが与えられなかった場合はvec全体が使われます。 iportが省略された場合はカレント入力ポートが使われます。

要求された領域を埋め終る前に入力がEOFに達した場合は、ベクタの残りの部分は 変更されません。

read-uvector!が呼ばれた時既にiportがEOFに達していた場合は EOFが、そうでなければ読まれた要素の数 (バイト数ではありません) が返されます。

iportがバッファードポートであり、そのバッファリングモードが ‘modest’ か ‘none’ であった場合、read-uvector!iportが EOFに達していなくても、vecを埋める前に戻ることがあります (バッファリングモードについてはファイルポートを参照して下さい)。 パイプやネットワークに接続されたポートはデフォルトでそのように振舞います。 もし、十分なデータが到着することがわかっており、vecを確実に埋めたい場合は ポートのバッファリングモードを‘full’に変更して下さい。

データはバイトストリームとして読まれるので、 s8vectorとu8vector以外のユニフォームベクタを与えた場合は 結果がエンディアンに影響を受けることに注意して下さい。 省略可能引数endianによって、入力データのエンディアンを指定 することができます。省略した場合はパラメータdefault-endianの 値が使われます。エンディアンの扱いについてより詳しくはエンディアンを参照して ください。

Function: read-block! vec :optional iport start end endian

read-uvector!の古い名前です。互換性のためサポートされていますが、 新しいコードではread-uvector!を使ってください。

Function: port->uvector iport :optional class

入力ポートiportからEOFまでデータを読み込み、classクラスの ユニフォームベクタに格納して返します。classが省略された場合は <u8vector>が使われます。

要素が1オクテット以上のユニフォームベクタのクラスを指定した場合、 各要素はプラットフォームのネイティブバイトオーダーで埋められます。

この手続きは組み込みのport->string等と対応しています (入力ユーティリティ手続き参照)。

Function: write-uvector vec :optional oport start end endian

ユニフォームベクタvecの内容を「そのまま」oportに書き出します。 oportが省略された場合はカレント出力ポートが使われます。 省略可能な引数startendが与えられた場合は、 それらのインデックスの範囲が示すvecの内容のみが出力されます。 endに-1を渡してvecの最後を示すこともできます。 この手続きの返す値は未定義です。

s8vectoru8vector以外のユニフォームベクタを与えた場合、 read-uvectorと同じようにエンディアンの影響を受けることに注意して 下さい。省略可能引数endianによって出力のエンディアンを 指定できます。省略した場合はパラメータdefault-endianの値が 使われます (エンディアン参照)。

Function: write-block vec :optional iport start end endian

write-uvectorの古い名前です。互換性のためサポートされていますが、 新しいコードではwrite-uvectorを使ってください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.36 gauche.version - バージョン番号の比較

Module: gauche.version

このモジュールは、バージョン番号(リリース番号)を比較する便利な手続きを提供します。 ここでのバージョン番号とはソフトウェアのリリースにつけられる "0.5.1"、"3.2-3"、"8.2pl1"といった文字列で、 通常これらの番号間には順序関係が定義できます。 例えば "1.2.3" は "1.2" より新しいが "2.1" より古い、といった具合です。 以下のようにこれらの文字列の順序を比較することができるようになります。

 
(version<? "2.2.3" "2.2.11")     ⇒ #t
(version<? "2.3.1" "2.3")        ⇒ #f
(version<? "2.3.1-1" "2.3.1-10") ⇒ #t
(version<? "13a" "5b")           ⇒ #f

バージョン番号の付け方には特に標準というものはありませんが、 よく使われている方法を採用しました。全ての場合に使えるとはいきませんが、 大抵の場合はカバーできるのではないかと思います。

厳密に言えば、分岐などの場合があるのでバージョン番号間の関係は半順序関係にしかなりません。 このモジュールでは単純な定義を使って、全順序関係があるものとして扱います。

まず、ここでのバージョン番号は以下の構文に従うものとします。

 
 <version> : <principal-release>
           | <version> <post-subrelease>
           | <version> <pre-subrelease>
 <principal-release> : <relnum>
 <post-subrelease>   : [.-] <relnum>
 <pre-subrelease>    : _ <relnum>?
 <relnum>            : [0-9A-Za-z]+

通常<relnum>は数字部分と、それに続く省略可能な拡張部分とからなります。 例えば "23a" は数字部分23と拡張部分"a"からなります。 もし<relnum>が数字で始まらない場合、その数字部分は-1であると考えることにします。

<relnum>の順序関係を次のように定義します。

  1. relnum Aとrelnum Bが異なる数字部分を持っている場合、拡張部分を無視して 数字部分を数値として比較します。例:"3b" < "4a"。
  2. relnum Aとrelnum Bの数字部分が等しい場合、拡張部分を辞書順に比較します。 例:"4c" < "4d"、"5" < "5a"。

<relnum>間の順序が定義できたところで、バージョン番号の順序を次のように定義します。

  1. 各々のバージョン番号を分解して <principal-release> とそれに続く サブリリース部品のリストに入れる。このリストの各要素のことを「リリース部品」 と呼びます。
  2. もし両方のリストの最初のリリース部品が同じなら、それを両方のリストから 取り除きます。これをリストの先頭が異るまでくりかえします。
  3. そうすると次のような場合に分られます。
    1. 両方のリストが空: バージョンは同じ。
    2. 一方のリスト (A) が空で、他方のリスト (B) の先頭がポストサブリリース: A が B より前のバージョン。
    3. 一方のリスト (A) が空で、他方のリスト (B) の先頭がプリサブリリース: B が A より前のバージョン
    4. リスト A の先頭がポストサブリリースで、リスト B の先頭がプリサブリリース: B が A より前のバージョン
    5. 両方のリストとも先頭がポストサブリリースであるかプリサブリリース: relnum を比較する。

以下はいくつかの例です。

 
"1" < "1.0" < "1.1" < "1.1.1" < "1.1.2" < "1.2" < "1.11"
"1.2.3" < "1.2.3-1" < "1.2.4"
"1.2.3" < "1.2.3a" < "1.2.3b"
"1.2_" < "1.2_rc0" < "1.2_rc1" < "1.2" < "1.2-pl1" < "1.2-pl2"
"1.1-patch112" < "1.2_alpha"

<pre-subrelease> があるのは、「リリース候補」あるいは 「プリリリース」のバージョンをつかえるようにするためです。

ヒント: 「1.2正式リリースもしくはそれ以降」をチェックした場合は (version<=? "1.2" v) とします。これは、 1.2_pre3といったプレリリース版を除外します。一方、 「プレリリース版も含めて1.2もしくはそれ以降」としたい場合は (version<=? "1.2_" v)と書けます。これは、 1.2_pre1等は含み、それ以前の例えば1.1.99999等は除外します。

許容できるバージョンを条件の組み合わせで指定したいことはよくあります。 例えば「バージョン1.3以降、ただし1.4.1を除く」とか、 「バージョン1.1以上で、1.5より前」等。 バージョン指定 (version spec) は、S式でバージョンに関する条件を表現する手段です。 version-satisfy?手続きで、バージョンがバージョン指定を満たしている かどうかチェックできます。

バージョン指定の構文は以下の通りです。

 
<version-spec> : <version>
               | (<op> <version>)
               | (and <version-spec> ...)
               | (or <version-spec> ...)
               | (not <version-spec>)

<version> : version string
<op>      : = | < | <= | > | >=
Function: version=? ver1 ver2
Function: version<? ver1 ver2
Function: version<=? ver1 ver2
Function: version>? ver1 ver2
Function: version>=? ver1 ver2

ふたつのバージョン番号文字列 ver1ver2 の順序関係によって 真偽値を返します。もし引数が、バージョン番号を定義するのに不正な文字列を 含んでいた場合、エラーが上ります。

Function: version-compare ver1 ver2

ふたつのバージョン番号文字列 ver1ver2 を比較し、 ver1 のほうが ver2 より前、ver1ver2 は同じ、 ver1ver2 の後、の3つの場合によって、それぞれ、 -1、0、1 を返します。

Function: relnum-compare rel1 rel2

これは version-compareの下位レベルの手続きです。 ふたつのリリース番号 (relnum) rel1rel2 を比較し、 rel1rel2 より前、rel1rel2 が同じ、 rel1rel2 より後、の3つの場合によって、それぞれ、 -1、0、1 を返します。

以下の手続きは、与えられたバージョンがバージョン指定を満たすかどうかを 検査するためのものです。

Function: valid-version-spec? spec

構文チェッカです。specがバージョン指定として正当なフォームであれば #tを、そうでなければ#fを返します。 バージョン指定フォームについてはモジュールgauche.versionの記述を参照してください。

Function: version-satisfy? spec version

バージョン番号versionがバージョン指定specを満たせば#tを、 そうでなければ#fを返します。 バージョン指定フォームについてはモジュールgauche.versionの記述を参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

9.37 gauche.vport - 仮想ポート

Module: gauche.vport

仮想ポートあるいは手続き的ポートとは、その振舞いを Scheme でプログラム可能なポートです。

このモジュールは 2 種類の仮想ポートを提供します。ひとつは、 完全仮想ポートで、すべての I/O 操作でユーザが提供する手続きが 呼出されるものです。もうひとつは、仮想バッファポートで、 I/O 操作は内部バッファ上で行われ、ユーザが提供する手続きは バッファを一杯にするかフラッシュする必要がある場合にのみ 呼出されます。

このモジュールはさらに、ユニフォームベクタにより バックアップされる仮想バッファポートも提供します。 これは仮想ポートの使用例でもあります。

完全仮想ポート

このタイプの仮想ポートは、<virtual-input-port> クラス および <virtual-output-port> クラスで実現されています。 適切なスロットに手続きをセットすることでポートの振舞いをカスタマイズ 可能です。

Class: <virtual-input-port>

このクラスのインスタンスは入力ポートとして使えます。 このポートの振舞いはインスタンスのスロットに設定された値に依存します。

まともな入力ポートとして動かすためには、少くとも、getb スロット あるいは getc スロットのどちらか一方は設定しなければなりません。 さもなければ、このポートはすべての入力要求に対して EOF を返します。

Instance Variable of <virtual-input-port>: getb

設定されているのなら、その値は、引数を取らない手続きでなければなりません。 バイナリ入力の要求のたびに、この手続きが呼ばれます。

この手続きは、0 から 255 までの正確な整数を返すか、#fあるいは EOF オブジェクトを返さなければなりません。整数を返す場合には、 それがこのポートから読みとられる値となります。それ以外の値を返す 場合は、このポートは EOF を返します。

このポートが文字入力を要求され、かつ、getc 手続きを持たない場合、 このポートはこの手続きを呼び(複数回の可能性もある)文字全体を構築します。

Instance Variable of <virtual-input-port>: getc

設定されているのなら、その値は、引数を取らない手続きでなければなりません。 文字入力の要求のたびに、この手続きが呼ばれます。

この手続きは文字を返すか、または#fあるいはEOFオブジェクトを返さなければ なりません。文字を返した場合には、それがこのポートから読みとられる値と なります。それ以外の値を返す場合は、このポートは EOF を返します。

このポートがバイナリ入力を要求され、かつ、getb 手続きを持たない場合、 このポートはこの手続きを呼び、文字をバイト列に変換し、それをこのポートから 読みだされる値として使います。

Instance Variable of <virtual-input-port>: gets

設定されているなら、その値は、正の正確な整数の引数をひとつだけとる手続き でなければなりません。ブロックバイナリ入力、たとえば、 read-uvectorなどが要求された場合に、呼出されます。

そしてそれは文字列、 不完全文字列、 #f、 EOF オブジェクトを 返さなくてはなりません. 文字列を返す場合は上記正の正確な整数によって 指定された大きさを越えてはなりません。空文字列、 #f、EOF を 返した場合はポートの終端にたどり着いたものとみなされます。 (注意: 文字列大きさは文字数でなく文字列が占めるバイト数で指定されます。)

この手続きは効率のためにあります。もし、この手続きが用意されて いなければ、このポートは、データブロックを準備するのに getbを 繰り返し呼びます。場合によっては、ブロック入力を用意するほうが はるかに効率的です。(たとえば、メモリチャンクのブロックからの読みだし を行なうような場合です。)

こうした利点を必要としないのなら、このスロットは未設定のままにして おいてもかまいません。

Instance Variable of <virtual-input-port>: ready

設定されているなら、その値は、真偽値をひとつだけとる手続きでなければ なりません。この手続きは、このポートに対して、char-ready? あるいは byte-ready? が呼ばれたときに呼出されます。設定した手続き が返す値が、これらの手続きの結果になります。

char-ready? が呼ばれたのなら、真偽値引数は #t です。 byte-ready? が呼ばれたのなら、真偽値引数は #f になります。

未設定なら、char-ready? および byte-ready? は このポートに対して常に、#t を返します。

Instance Variable of <virtual-input-port>: close

設定されているのなら、その値は引数を取らない手続きでなければなりません。 その手続きはこのポートがクローズされるときに呼びだされ、返り値は 捨てられます。このポートをクローズするときになんらかのアクションが 必要でないのなら、未設定にしておけます。

この手続きはファイナライザから呼ばれることもあり得ます。したがって、 この手続きは慎重に書く必要があります。後述のファイナライズに 関する注意を参照してください。

Instance Variable of <virtual-input-port>: seek

設定されているのなら、その値は、オフセット引数とそれがどこからかを示す引数 の 2つの引数をとる手続きでなければなりません。これらの引数の意味は、 port-seek (ポート共通の操作 参照) のそれと同じです。 この手続きは、次の読み出しが新しい位置から始められるように 内部のリードポインタを調整しなければなりません。そして 更新されたポインタ(ポート先頭からのバイトオフセット) を返さなければなりません。

未設定なら、このポートに対する port-seek および port-tell の呼び出しは #f になります。

この手続きは単に現在の位置を問合せるために、 offset として 0 を、whence として SEEK_CUR を与えて 呼び出すこともあることに注意してください。リードポインタの位置は知って いるが、動かすことができないという場合にも、この手続きを提供することが できます。上のような問合せには、現在位置を返し、そうでない場合には、 #f を返します。

Class: <virtual-output-port>

このクラスのインスタンスは出力ポートとして使えます。 このポートの振舞いはインスタンスのスロットに設定された値に依存します。

まともな出力ポートとして動かすためには、少くとも、putb スロット あるいは putc スロットのどちらか一方は設定しなければなりません。

Instance Variable of <virtual-output-port>: putb

設定されているなら、その値は、バイト値(0 から 255までの正確な整数)を ひとつだけ引数としてとる手続きでなければなりません。バイナリ出力 の要求のたびに、この手続きが呼ばれます。この手続きの返り値は無視 されます。

このスロットが未設定の場合、バイナリ出力を要求されると、このポートは <io-unit-error> エラーを発生させることがあります。

Instance Variable of <virtual-output-port>: putc

設定されているなら、それは文字をひとつ引数にとる手続きでなければなりません。 文字出力の要求があるたびにこの手続きがよばれます。この手続きの返り値は 無視されます。

このスロットが未設定であっても、putb スロットが設定されていれば、 この仮想ポートは文字をバイト列に展開してから、putb を呼びだします。

Instance Variable of <virtual-output-port>: puts

設定されていれば、その値は、文字列(不完全なものである可能性もある)を ひとつ引数としてとる手続きでなければなりません。この手続きの返り値は 無視されます。

これは、効率のためにあります。このスロットが未設定であれば、この 仮想ポートは、データのかたまりを出力するために、putb あるいは putc を繰り返し呼びます。もし、ひとかたまりの 出力を効率的に実行可能なコードであれば、この手続きを提供できます。

Instance Variable of <virtual-output-port>: flush

設定されていれば、その値は引数をとらない手続きでなければなりません。 ポートのフラッシュを要求されたときに呼ばれます(たとえば、flush がこのポートに対して呼ばれたとき、あるいは、このポートがクローズ されるとき)。

この手続きは、ポートが何らかのバッファリングをおこなうか、なんらかの 状態を持つような場合に便利です。ポートが状態をともなう操作を行わない のなら、これは未設定にしておけます。

この手続きはファイナライザから呼ばれることがあります。したがって、 特別な注意が必要です。後述のファイナライザに関する注意を 参照してください。

Instance Variable of <virtual-output-port>: close

<virtual-input-port>close スロットと同様です。

Instance Variable of <virtual-output-port>: seek

<virtual-input-port>seek スロットと同様です。

仮想バッファポート

このタイプの仮想ポートは <buffered-input-port> クラス および <buffered-output-port> クラスで実現されています。 適切なスロットに手続きを設定するこでポートの振舞いをカスタマイズ することができます。

これらのポートは、内部バッファを持っており、そのバッファを満たすか フラッシュするかの必要があるときにのみ、Scheme の手続きが呼ばれます。 通常、I/O毎に Scheme の手続きを呼ぶよりも遥かに効率がいいものです。 実際の内部バッファリング機構は、GaucheのファイルI/Oポートと同じです。

これらのポートはバッファとして u8vector を使います。詳細は、 gauche.uvector - ユニフォームベクタ を参照してください。

Class: <buffered-input-port>

このクラスのインスタンスは、入力ポートとして振舞います。 これは以下のようなインスタンススロットを持ちます。 意味のある入力ポートとして使うには少くとも、fill スロットを 設定しなければなりません。

Instance Variable of <buffered-input-port>: fill

設定されているなら、u8vector の引数を一つとる手続きでなければ なりません。そのベクタの最初からデータを満たさなければなりません。 要求されたものよりデータの残りが少ない場合には、 ベクタ全体を満たす必要はありません。しかしながら、 データが残っている場合には少くとも1バイトは満たさなければなりません。 もしデータがまだ利用可能になっていなければ、なにがしかのデータが利用可能に なるまで待たなければなりません。

この手続きは実際に満されたバイト数を返さなければなりません。ポートが EOFに達したことを示すために、0 または EOFオブジェクトを返すこともできます。

Instance Variable of <buffered-input-port>: ready

設定されているなら、引数をとらない手続きでなければなりません。 この手続きは、なにがしかの読み込み用データが利用可能になっていれば 真の値を返し、そうでなければ、#fを返します。完全仮想ポートとはちがい、 バイナリI/Oと文字I/Oを区別する必要はありません。

このスロットが未定義の場合、このポートは常にデータが利用可能になっている とみなされます。

Instance Variable of <buffered-input-port>: close

設定されていれば、引数をとらない手続きでなければなりません。この手続きは 仮想バッファポートがクローズされるときに呼出されます。ポートがクローズ されるときに、なにがしかのクリーンアップを行うのでなければ、設定する必要 はありません。

この手続きは、ファイナライザから呼ばれる可能性がありますので、特別な 注意が必要です。後述のファイナライズに関する注意書きを参照してください。

Instance Variable of <buffered-input-port>: filenum

設定されているなら、このポートの使っているファイルディスクリプタ番号(正確な非負整数) を返す手続きでなければなりません。この手続きは port-file-number をこのポートに対して呼出したときに呼ばれます。

そのようなファイルディスクリプタが存在していなければ、 #f を返すことができます。 あるいは、このスロットを未設定にしておくこともできます。

Instance Variable of <buffered-input-port>: seek

設定されているなら、オフセット引数とどこからかを示す引数の2つをとる 手続きでなければなりません。これは、前述の <virtual-input-port>のseek 手続きと同じように動作します。

この手続きはファイナライザから呼ばれる可能性があり、特別な注意が 必要です。後述のファイナライズに関する注意を参照してください。

これらのスロットの値以外に、当該ポートの内部バッファのサイズを設定する のにはmakeメソッドに:buffer-sizeというキーワード引数で非 負の正確な整数を渡します。:buffer-sizeが省略されるか、0 が渡さ れた場合にはシステムのデフォルトのバッファサイズ(たとえば、8kとか)が使 われます。:buffer-sizeはインスタンススロットではないので、バッ ファポートのインスタンスを生成したあとで設定することはできません。以下 は 64K のバッファを使うバッファの作り方の例です。

 
(make <buffered-input-port> :buffer-size 65536 :fill my-filler)
Class: <buffered-output-port>

このクラスのインスタンスは出力ポートとして振舞います。これは、以下のような インスタンススロットを持ちます。少くとも flush は設定しなければ なりません。

Instance Variable of <buffered-output-port>: flush

設定されているなら、u8vectorのバッファとフラグという2つの 引数をとる手続きでなければなりません。手続きはバッファ内のデータを どこかへ出力しなければならず、実際に出力したバイト数を返します。

フラグが偽なら、この手続きはバッファ全体よりも少い(ただし、最低でも 1バイト)の出力を行ってもかまいません。もしフラグが真ならば、この手続きは、 バッファの内容をすべて出力しなければなりません。

Instance Variable of <buffered-output-port>: close

<buffered-input-port>close スロットと同様です。

Instance Variable of <buffered-output-port>: filenum

<buffered-input-port>filenum スロットと同様です。

Instance Variable of <buffered-output-port>: seek

<buffered-input-port>seek スロットと同様です。

これらのスロットの値以外に、当該ポートの内部バッファのサイズを設定する のにはmakeメソッドに:buffer-sizeというキーワード引数で非 負の正確な整数を渡します。詳しくは前述 <buffered-input-port> の項を見てください。

ユニフォームベクタポート

以下の 2つの手続きは、ユニフォームベクタでバックアップされた、 バッファ入/出力ポートを返します。読み込み元のベクタあるいは 書き出し相手のベクタはどのようなユニフォームベクタであってもかまいませんが、 実際の入出力時にはu8vector にaliasされます (ユニフォームベクタの変換uvector-alias 参照)。

pack/unpack(binary.pack - バイナリデータのパック参照) と一緒に使うと バイナリのデータ構造をパースしたり、構築したりするのに便利です。 また、仮想ポートの使い方の例でもあります。実装法に興味があれば、 ソースツリーの ‘gauche/vport.scm’(あるいは ‘ext/vport/vport.scm’) を読んでください。

Function: open-input-uvector uvector

与えられたユニフォームベクタ uvectorの最初からその内容を読む 入力ポートを返します。読み込み動作が、uvectorの終端に到達したら EOFが返されます。シーク操作も実装されています。

Function: open-output-uvector :optional uvector :key extendable

与えられた uvector をデータの格納に使うような出力ポートを返します。

uvectorが全て埋まった後、何が起きるかは、extendable引数によって 変わります。もし偽の値(デフォルト)では、更に送られてきたデータは黙って捨てられます。 extendableが真の値であった場合、追加のデータを保持するため 格納領域は自動的に拡張されます。

extendableに真の値を与えた場合、貯められたデータを取り出すには、 下で説明するget-output-uvectorを使ってください。渡したuvector 引数には溢れたデータは格納されないので。

特別な場合として、uvector引数を省略することができます。 この場合、データストレージとしてu8vectorが使われます。 uvector引数を省略した場合、extendable引数を 与えることはできませんが、これは真の値を与えたものとみなされます。 でないと意味がないからです。get-output-uvectorで 貯められたデータを取り出すことができます。

ポートに対してシーク操作も使うことができます。 ただし、whenceにSEEK_ENDを与えた場合の解釈が、extendableかどうかで 変わることに注意してください。extendableなポートでは、 末尾は「それまでに書き込まれたデータのうちもっとも大きなオフセット」の次に 設定されます。uvectorポートを開いてすぐに1バイト書き込んだ状態では、 uvector引数にどんなに大きなベクタを渡していたとしても、SEEK_END の基準点は2バイト目です。一方、extendableでないポートでは、 末尾はそれまでに書き込んだデータによらず、常に与えたuvectorの 末尾の次になります。その場合は、末尾やそれ以降にシークすることはできません (port-seekSEEK_ENDを渡す時、オフセットは必ず負数でなければ なりません)。

Function: get-output-uvector port :key shared

portopen-output-uvectorで作られたポートであるなら、 これまでに蓄えられたデータをuvectorとして返します。 portがそうでない場合は#fが返されます。

返り値のベクタは、open-output-uvectorに渡したuvector と同じ型で、実際に書き込まれたデータの最後尾までの範囲になります。 それはopen-output-uvectorに渡されたuvectorより小さいかも しれませんし、ポート作成時にextendableに真の値が渡されていた場合は 元のuvectorよりも大きいかもしれません。

uvectorの型がs8vectoru8vector以外である場合、 書き込まれたデータのうち完全な要素に足りない部分は結果に含まれません。 例えば、s32vectorでポートを作り7バイト書き込んでから get-output-uvectorを呼ぶと、返されるs32vectorは 1要素になります。最後の3バイトは完全な32bit整数を構成しないからです。

デフォルトでは、返り値のベクタは新たにアロケートされたものです。 しかしsharedキーワード引数に真の値を渡すと、可能ならば、 portが使っている格納領域をそのまま参照するベクタが返されます。 このオプションによってコピーを減らすことができますが、 後でportをシークして前に戻って再書き込みすると、 返されたベクタの内容が書き換わってしまう可能性があることに注意してください。

リストポート

以下の手続きは、文字またはオクテットのリストを入力ポートのソースとして利用するものです。 ある意味、port->list系の手続き(入力ユーティリティ手続き参照)や port->char-lseq系の手続き(遅延シーケンス参照)の逆であると 言えます。

Function: open-input-char-list char-list
Function: open-input-byte-list byte-list

与えられた文字もしくはオクテットのリストをデータ源とする 入力ポートを作って返します。

 
(read (open-input-char-list '(#\a #\b)))
 ⇒ ab
Function: get-remaining-input-list port

portopen-input-char-listopen-input-byte-listで 作られたものであった場合、ポート中のまだ読まれていない部分のリストを返します。 既に全てのデータが読まれていたり、ポートが open-input-char-listopen-input-byte-listで作られたもので なかった場合は、空リストが返されます。

注意: Gaucheは一つのポートからのバイナリ読み出しと文字読み出しを混ぜて使うことを 許しています。文字のリストで作られたリストポートからバイトを読み出したり、 あるいはpeek-byteした場合、ひとつ文字がデータ源から取り出され、 バイト列に分解されてポート内部の一時バッファに置かれます。 その状態でこの手続きを呼び出した場合、バッファ内のバイト列は戻り値には含まれません。

ジェネレータポート

以下の手続きは、文字あるいはオクテットのジェネレータ 入力ポートのソースとして利用するものです。 ある意味、port->char-generator系の手続き (ジェネレータの生成参照)の逆であると言えます。

Function: open-input-char-generator cgen
Function: open-input-byte-generator bgen

与えられたジェネレータをデータ源とする入力ポートを作って 返します。cgenは文字を生成するジェネレータ、 bgenはバイト(オクテット、0から255の間の正確な整数)を 生成するジェネレータでなけばなりません。 ジェネレータが想定される値以外のものを返した場合はエラーが通知されます。

 
(read (open-input-char-generator (string->generator "foo")))
 ⇒ foo

ジェネレータは本質的に副作用に依存しているオブジェクトなので、 この手続きにcgenbgenを渡したら、その後で それらのジェネレータに触ってはなりません。 もしそれらのジェネレータを使ってしまった場合、以降の動作は未定義となります。

Function: get-remaining-input-generator port

portopen-input-char-generatoropen-input-byte-generatorで作られたものであった場合、 残りのデータを生成するジェネレータを返します。 ポートが既にデータを全て消費していた場合は、空のジェネレータが返されます。

残りのデータを生成するジェネレータを取り出したら、 ジェネレータポートからはも読み出してはなりません。 内部状態を共有しているため、予期せぬ結果がもたらされることになるでしょう。 副作用に頼らず安全な振る舞いが必要な場合は、遅延シーケンスと リストポートを使うのが良いでしょう。

ファイナライズに関する注意

クローズされていない仮想ポートがガベージ・コレクションされると、 クローズ手続きが呼ばれます(仮想バッファポートの場合は、 フラッシュ手続きがクローズ手続きの前に呼ばれます)。これは、 そのポートのファイナライザによって行われます。これはガベージ・コレクション 処理の一部ですから(Scheme 手続きそのものはガーベッジ・コレクタの メインパートの外側で呼ばれているのですが、それでも)特別な注意が必要です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10. ライブラリモジュール - R7RS統合

GaucheはR7RS以前からあり、使い勝手のために非常に多くの組み込み手続きを 持っています(組み込みライブラリ参照)。そのほとんどはR7RSのスーパーセットと なっていますが、いくつかの手続きや構文は違う名前であったり、 違うインタフェースを取っていたりします。

R7RSと完全に互換な構文と関数は、本章で説明されるモジュール群によって提供されます。 R7RSプログラムとライブラリは特定の形式で書かれるので (import宣言で始まるか、もしくはdefine-libraryフォーム)、 通常はR7RSのコードを見ているのかGauche特有のコードを見ているのかははっきり区別できます。 また、Gauche特有のコードからR7RSライブラリをロードすることも、その逆も、 全く透過的にできます。 但し、コードを書く時には、どちらの「世界」にいるのかを意識する必要があります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.1 二つの世界を往き来する

Gaucheを起動した直後は、それがREPLであってもスクリプトファイルの実行であっても、 トップレベルはuserモジュールになっています。userモジュールは gaucheモジュールを継承しています。 また、ライブラリを読み込む際にも、(select-moduleが呼ばれるまでは) トップレベルはgaucheを継承したモジュールになっています。 これが、(use something)等といちいち書かないでも Gaucheの組み込み手続きを使える理由です。 (モジュールの継承についてはモジュールの継承を参照してください)。

一方、R7RSではimportフォームによってどの名前空間を利用するか明示することが 求められています(例: (import (scheme base)))。 また、R7RSライブラリはそれぞれdefine-libraryフォームで囲う必要があります。 プログラムの最初のimportより前、あるいはライブラリのdefine-libraryの 外側は、R7RSの世界の外であり、標準では何も規定していません。

Gaucheはこれを利用して、適切な「世界」を設定し、R7RSコードと既存のGaucheコードを 透過的に混ぜるられるようにしています。

註:3つのimport形式で説明しますが、 R7RSのimportはGaucheのimportと異なります。この節では 区別が必要な時は前者をr7rs#import、後者をgauche#importと 表記しますが、実際のコードではr7rs#gauche#といったプレフィクスを つけずに、単にimportと書いてください。

R7RSライブラリをロードする

define-libraryフォーム自身は、gaucheモジュール内のマクロとして 定義されていて、R7RS環境を設定してその中身を評価するコードに展開されます。 (Gaucheコードからuseを使うか、 R7RSコードからr7rs#importフォームを使うかして)R7RSライブラリをロードすると、 Gaucheはファイルの評価をgaucheモジュール内で始めますが、すぐに define-libraryフォームに当たるので、その中身はR7RS環境で処理されることになります。

以下のコードを持つ、R7RSライブラリ(mylib foo)があるとしましょう。

 
(define-library (mylib foo)
  (import (scheme base))
  (export snoc)
  (begin
    (define (snoc x y) (cons y x))))

このファイルは*load-path*にあるディレクトリのどれかの下に、 ‘mylib/foo.scm’という名前で保存されなければなりません。

R7RSコードからは、このライブラリはr7rs#importフォームでロードできます。

 
(import (mylib foo))

(snoc 1 2) ⇒ (2 . 1)

Gaucheコードからこのライブラリを使うには、ライブラリ名の各要素を.で つなげたものをモジュール名として、useします。

 
(use mylib.foo)

(snoc 1 2) ⇒ (2 . 1)

Gaucheライブラリのロード

Gaucheライブラリのfoo.barをR7RSコードから使うには、 モジュール名を.で区切った名前のリストをライブラリ名として使います。 例えばgauche.lazyモジュールは次のとおりR7RSから使えます。

 
(import (gauche lazy))

SRFIモジュールについては、R7RSでは慣習的に(srfi n)という 名前を使うので、Gaucheもそれに合わせています。R7RSコードから srfi-1とsrfi-13を使うには、次のようにします。

 
(import (srfi 1) (srfi 13))

(これが動くのは、Gaucheがsrfiという名前を特別扱いしているのではなく、 Gaucheをインストールした時にsrfi/1.scm等のアダプタライブラリが作られているからです)

ヒント: Gaucheの組み込みの機能 (Gaucheコードでは最初から使える束縛) を R7RSコードから使いたい場合は、(gauche base)ライブラリをimportしてください (gauche.base - Gauche組み込み関数のインポート参照)。

 
(import (gauche base))

filter ⇒ #<closure filter>

R7RSスクリプトの実行

R7RSスクリプトは必ずimportフォームで始まります。 しかし、r7rs#importgauche#importと文法や意味が異なります。 そこで、ちょっとしたトリックを使っています。

goshがスクリプトファイルを読み込むのはuserモジュールです。 そこにはuser#importというマクロが用意してあり、 そのマクロは引数を調べて、それがR7RSのimportの構文であれば r7rs.userモジュールに切り替えてr7rs#importを実行し、 そうでなければgauche#importを実行します。 詳しくは3つのimport形式を参照してください。

R7RSスクリプトの例です:

 
(import (scheme base) (scheme write))
(display "Hello, world!\n")

Gaucheスクリプトを見慣れている人は、R7RSプログラムがmain手続きを 特別扱いしないことに気をつけてください。R7RSプログラムは、トップレベルフォームを 順に評価してゆくだけです。したがって次のスクリプトは何も表示しません:

 
(import (scheme base) (scheme write))
(define (main args)
  (display "Hello, world!\n")
  0)

R7RSスクリプトからコマンドライン引数を利用するには、 (scheme process-context)ライブラリのcommand-lineを使います (scheme.process-context - R7RSプロセスコンテキスト および コマンドライン引数参照)。

R7RS REPLを使う

gosh-r7オプションつきで起動され、スクリプトファイルが 与えられていなかった場合は、R7RS REPLモードになります。 使いやすいように、以下のモジュール (R7RS用語では「ライブラリ」)は 自動的にロードされます。

 
(scheme base) (scheme case-lambda) (scheme char)
(scheme complex) (scheme cxr) (scheme eval)
(scheme file) (scheme inexact) (scheme lazy)
(scheme load) (scheme process-context) (scheme read)
(scheme repl) (scheme time) (scheme write)

さらに、ヒストリ変数*1*2*3*1+*2+*3+*e および *historyも使えます (ヒストリ変数の詳細についてはREPLでの開発参照)。

R7RS REPLにいることはプロンプトを見ればわかります。 現在のモジュールがr7rs.userとなっているからです。

 
gosh[r7rs.user]> 

R7RS REPLからGauche REPLにスイッチするには、(gauche base)を インポートしてselect-moduleを使ってuserモジュールを選びます。

 
gosh[r7rs.user]> (import (gauche base))
#<undef>
gosh[r7rs.user]> (select-module user)
#<undef>
gosh> 

((select-module gauche)とする手もありますが、おすすめはしません。 gaucheモジュールの中を変えると思いがけない副作用が生じるかもしれませんから。)

R7RSコードをファイルに書きだし、R7RS REPLにそのファイルをロードしてインタラクティブに 開発を進めている場合 (例えばEmacsのSchemeモードなら、C-c C-lでファイルがロードできます)、 そのファイルがR7RSとして正しい形式になっていることを確認してください。 つまり、ファイルはimport宣言で始まるか、define-library形式 から構成されるものでなければなりません。それ以外の、単にSchemeコードが並べて 書いてあるだけのファイルをロードした場合、 それはREPLでの現在のモジュールが何であるかにかかわらずGaucheのuserモジュールに 読み込まれるので、r7rs.userモジュールからはデフォルトでは見えません。

Gauche REPLからのスイッチ

デフォルトでは、goshはスクリプトファイルが与えられなければGaucheのREPLに 入ります。REPLの説明はREPLでの開発を参照してください。

Gauche REPLからR7RS REPLにスイッチするには、 単にr7rsスタイルのimportを使うだけです。user#importは あなたが欲しいのがR7RSであることを察知して環境を切り替えます。

 
gosh> (import (scheme base))
#<undef>
gosh[r7rs.user]> 

ただし、gosh-r7オプションつきで起動していなければ、 この時点でロードされているのはuser#importで指定したライブラリだけです。

もし、「すっぴん」のR7RS環境、つまり(scheme base)でさえロードされていない 環境へと切り替えたければ、r7rsモジュールをuseして 直接r7rs.userモジュールを選択します。

 
gosh> (use r7rs)
gosh> (select-module r7rs.user)
gosh[r7rs.user]> 

こうして切り替えた場合、使えるフォームはimportdefine-library だけです (defineでさえ定義されていません!) この環境でSchemeを書くには、まず (import (scheme base)) 等として 束縛をインポートする必要があります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.2 3つのimport形式

歴史的な事情から、Gaucheにはimport形式が3つあります。 もともとのGaucheのimport、R7RSのimport、 そしてハイブリッドのimportです。

通常、コードを読み書きしている時は、それが伝統的なGaucheコードかR7RSコードかは 明確ですし、またimportの使われ方はだいたい決まっているので、 現場で混乱が起きることはありません。ただ、コードの外でimportについて 話す場合は、どのimportのことかをはっきりさせるのが良いでしょう。

ハイブリッドのimportは前節で説明したuser#importです (二つの世界を往き来する参照)。 user#importは GaucheのimportとR7RSのimportの両方の文法を理解できるので、 実際に知る必要のあるのは最初の2つの形式です。

Gaucheのモジュールシステム設計はSTkを継承していて、 importは純粋に名前空間の操作として使われています。 つまり、既にメモリ上に存在するモジュールをインポートするということです。 そのモジュールが定義されているファイルを、(必要なら)ロードするという 操作は別のプリミティブrequireにより実現されます。 典型的には、ひとつのファイルがひとつのモジュールを定義していて、 そのモジュールを使うにはまずファイルをrequireし、 次にモジュールをimportするということになります (これはGaucheでは頻繁に出てくるので、useというマクロが定義されているくらいです)。 ファイルのロードとモジュールのインポートが分かれていることは、 たまに変わったハックが必要とされる時に役に立ちます。 Gaucheのimportについてはモジュールの使用を参照してください。

R7RSは実装に選択の余地を与えるため、モジュール(ライブラリ)とファイルの関係は 敢えて規定していません。R7RSのimportは、 必要ならば暗黙のうちに、透過的にファイルをロードすることが期待されています。 つまり、R7RSのimportはGaucheのuseと意味的に同じと考えられます。

ハイブリッドimportはSchemeスクリプトの最初にのみ現れて、 実行されるスクリプトが伝統的なGaucheコードであるかR7RSコードであるかを 判断します。詳細は二つの世界を往き来するを参照してください。

ではR7RSのimportを説明します:

Special Form: import import-spec …

[R7RS] import-specで指定されるライブラリをインポートします。 R7RSでいうライブラリは、Gaucheがモジュールと呼んできたものです。両者は同じものです。

R7RSライブラリの名前はシンボルか整数のリストで表されます。 例: (scheme base)(srfi 1)。 この名前は、ピリオドを挟んでシンボル名をつなげることで、Gaucheのモジュール名へと 変換されます。つまり、R7RSの(scheme base)はGaucheからはscheme.base モジュールとして扱えます。逆にGaucheのdata.queueモジュールは R7RSからは(data queue)ライブラリとして扱えます。 R7RSプログラムからこれら二つのライブラリを使うには、プログラムの最初に 次のように記します。

 
(import (scheme base) 
        (data queue))

これはGaucheでuseフォームを使うのと同じです。つまり、もし その名前を持つモジュールが現在のプロセス内に無ければまずファイルをロードし、 それからモジュールがexportしている束縛を現在のモジュールから見えるようにします。

 
(use scheme.base)
(use data.queue)

(勘の良い読者は、R7RSのライブラリがピリオドを中に含むシンボルを名前に使っていたら どうなるのか疑問に思ったことでしょう。正直なところ、まだどうするか決めていません。 多分、何らかのエスケープ機構を用意することになると思いますが、 今のところ、できる限りモジュール名にはアルファベット、数字、ハイフンだけを 使うようにしておいて下さい。)

R7RSのimportフォームは、 Gaucheのuseと同様、どのシンボルをimportするか/しないかを指定したり、 名前を変えてimportしたりプレフィクスを付加することができます。 R7RSのimportの正式な構文は次の通りです。

 
<import declaration> : (import <import-set> <import-set> ...)

<import-set> : <library-name>
  | (only <import-set> <identifier> <identifier> ...)
  | (except <import-set> <identifier> <identifier> ...)
  | (prefix <import-set> <identifier>)
  | (rename <import-set>
            (<identifier> <identifier>)
            (<identifier> <identifier>) ...)

<library-name> : (<identifier-or-base-10-integer>
                  <identifier-or-base-10-integer> ...)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.3 R7RSライブラリ形式

R7RSのライブラリはdefine-libraryフォームで定義されます。

R7RSの観点ではdefine-libraryフォーム自体はSchemeコードではありません。 それ自身はScheme世界の外にあります。define-libraryフォームが、 R7RS Schemeの境界となるのです。その内側はR7RSの世界であり、 その外側に関してR7RSは関知しません。例えば、R7RSの仕様の範囲内では、 define-libraryフォーム自体をマクロで生成する、ということはできません。

Gaucheでは、R7RSの世界をGaucheの世界の中に作っています。 define-library自体はGauche世界で解釈されます。実際、 define-libraryはGaucheのマクロとして実装されています。 けれどもR7RSのコードを書く際には、define-libraryがどう実装されているかは 気にする必要はありません。また、define-libraryの外側には 何も書かないようにしてください。

Macro: define-library library-name library-decl …

[R7RS] library-nameという名前を持つライブラリを定義します。 library-nameはシンボルもしくは10進整数のリストです。

 
<library-name> : (<identifier-or-base-10-integer>
                  <identifier-or-base-10-integer> ...)

ライブラリ宣言library-declは、エクスポート宣言、 インポート宣言、beginに囲まれたSchemeコード、 インクルードフォーム、あるいはcond-expandフォームです。

 
<library-decl> : (export <export-spec> …)
               | <import declaration>
               | (begin <command-or-definition> …)
               | (include <string> <string2> …)
               | (include-ci <string> <string2> …)
               | (include-library-declarations 
                        <string> <string2> …)
               | (cond-expand <cond-expand-clause>
                              <cond-expand-clause2> …)
               | (cond-expand <cond-expand-clause>
                              <cond-expand-clause2> …
                              (else <library-decl> …))

export宣言はGaucheのexportフォームと同じです。 モジュールの使用参照。

import宣言はR7RSのimportフォームで、 3つのimport形式で説明しています。

includeinclude-ciフォームはGaucheのそれと同じです。 インクルード参照。Gaucheはインクルードされるコードの中身については 感知せず、単にその中身をbeginで囲んで元のフォームと 置き換えるだけです。けれどもR7RSでは、これらのフォームが読み込むファイルには Schemeコードのみが許されます(例えばdefine-libraryフォームや 他のライブラリ宣言フォームはだめです)。

include-library-declarations宣言はinclude宣言と 同じように動作しますが、読み込まれるファイルの中身はSchemeコードではなく ライブラリ宣言として解釈されます。

cond-expand宣言もGaucheのそれと同じです。 機能条件式参照。 但し、define-libraryの直下で使われた場合は、 展開結果もライブラリ宣言になっている必要があります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.4 scheme.base - R7RS基本ライブラリ

Module: scheme.base

R7RSの(scheme base)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は(import (scheme base))とすることで 使えます。

Gaucheの組み込み束縛と共通なもの

以下の構文の手続きはGaucheの組み込みのものと同じです。

Primitive expression types
 
quote if include include-ci lambda
Derived expression types
 
cond case and or when unless cond-expand let let* letrec letrec*
let-values let*-values begin do make-parameter parameterize
guard quasiquote unquote unquote-splicing case-lambda
Macros
 
let-synatx letrec-syntax syntax-rules syntax-error define-syntax
Variable definitions
 
define define-values
Record type definitions
 
define-record-type
Equivalence predicates
 
eqv? eq? equal?
Numbers
 
number? complex? real? rational? integer? exact? exact-integer?
= < > <= >= zero? positive? negative? odd? even? max min + * - / abs
floor/ floor-quotient floor-remainder
truncate/ truncate-quotient truncate-remainder
quotient modulo remainder gcd lcm numerator denominator
floor ceiling truncate round rationalize square exact-integer-sqrt
expt inexact exact number->string string->number
Booleans
 
not boolean? boolean=?
Pairs and lists
 
pair? cons car cdr set-car! set-cdr! caar cadr cdar cddr null? list?
make-list list length append reverse list-tail list-ref list-set!
memq memv member assq assv assoc list-copy
Symbols
 
symbol? symbol=? symbol->string string->symbol
Characters
 
char? char=? char<? char>? char<=? char>=? char->integer integer->char
Strings
 
string? make-string string string-length string-ref string-set!
string=? string<? string>? string<=? string>=? substring string-append
string->list list->string string-copy string-copy! string-fill!
Vectors
 
vector? make-vector vector vector-length vector-ref vector-set!
vector->list list->vector vector->string string->vector
vector-copy vector-copy! vector-append vector-fill!
Control features
 
procedure? apply map call-with-current-continuation call/cc 
values call-with-values dynamic-wind
Exception
 
error
Environments and evaluation
 
scheme-report-environment null-environment
Input and output
 
input-port? output-port? port? current-input-port current-output-port
current-error-port close-port close-input-port close-output-port
open-input-string open-output-string get-output-string
read-char peek-char read-line eof-object? eof-object char-ready?
newline write-char

バイトベクタユーティリティ

Function: bytevector n …
Function: bytevector? obj
Function: make-bytevector size :optional byte
Function: bytevector-length bv
Function: bytevector-u8-ref bv index
Function: bytevector-u8-set! bv index byte
Function: bytevector-copy bv :optional start end
Function: bytevector-copy! to at from :optional start end
Function: bytevector-append bv …

[R7RS] R7RSのbytevectorはGaucheのu8vectorです。

これらの手続きはgauche.uvectoru8vectoru8vector?make-u8vectoru8vector-lengthu8vector-refu8vector-set!u8vector-copyu8vector-copy!u8vector-appendとそれぞれ同じです。 (gauche.uvector - ユニフォームベクタ参照)。

以下の二つの手続きはgauche.unicodeモジュールの同名の手続きと同じです。 (Unicode transfer encodings参照):

 
utf8->string string->utf8

制御機能

Function: string-map proc str …
Function: string-for-each proc str …

[R7RS] これらの手続きはSRFI-13のstring-map及びstring-for-each (文字列のマッピング参照)とは異なる引数を取るので、 混乱を避けるためscheme.baseでのみ提供されます。

但し、文字列引数をひとつだけ渡す場合は、 srfi-13でもscheme.baseでも全く同様に動作します。

Function: raise obj
Function: raise-continuable obj

[R7RS] Gaucheのraiseは、obj<serious-condition>でなければ、 例外ハンドラから戻って来ることを許しています。 継続可能な例外報告と継続不可能な例外報告を手続きで区別するのは、 例外ハンドラが渡されたコンディションを再び投げる場合に問題となります (元のコンディションがraiseで投げられたのかraise-continuableで 投げられたのか知る術がない!)。 しかしR7RSではそのモデルを採用したため、何とか合わせようとしています。

R7RS版のraiseはGaucheのraiseのラッパーで、 Gaucheのraiseから戻って来たらエラーを投げるようにしています。

R7RSのraise-continuableは今のところGaucheのraiseの 別名になっています。<serious-condition>を渡さなければ、 戻ってくることが可能です。R7RS準拠でないのは、 <serious-condition>かそのサブクラスのオブジェクト(例えば<error>)を raise-continuableに渡した場合ですが、 <error>を投げて戻って来ることを期待するというのはちょっとおかしいですよね。

Function: error-object? exc

[R7RS] (condition-has-type? exc <error>))として定義されています。

Function: error-object-message exc

[R7RS] exc<message-condition>であれば message-prefixの内容を、持っていなければ空文字列を返します。

Function: error-object-irritants exc

[R7RS] exc<message-condition>であれば message-argsの内容を、持っていなければ空文字列を返します。

Function: read-error? exc

[R7RS] (condition-has-type? e <read-error>))として定義されています。

Function: file-error? exc

[R7RS] 今のところ、Gaucheは独立した<file-error>コンディションを持っていませんが、 ファイルエラーのほとんどは<system-error>として投げられます。 この手続きは<system-error>のエラーコードを調べ、ファイルに起因するエラー の場合に#tを返すようにしています。

入出力

Function: textual-port? port
Function: binary-port? port

[R7RS] Gaucheのポートはどちらも扱えるので、これらの手続きはport?と等価です。

Function: input-port-open? iport
Function: output-port-open? oport

[R7RS] iport/oportがそれぞれ入力/出力ポートであり、かつクローズされていなければ #tを返します。

Function: open-input-bytevector u8vector
Function: open-output-bytevector
Function: get-output-bytevector port

[R7RS] これらは基本的にgauche.vportopen-input-uvectoropen-output-uvectorget-output-uvector とそれぞれ同じです(gauche.vport - 仮想ポート参照)。 但しR7RS版は<u8vector>しか受け付けません。

Function: read-u8 :optional iport
Function: peek-u8 :optional iport
Function: u8-ready? :optional iport

[R7RS] それぞれread-bytepeek-bytebyte-ready?と 同じです。

Function: read-bytevector size :optional iport

[R7RS] (read-uvector <u8vector> size iport)と等価です。 See section ユニフォームベクタのブロック入出力.

Function: read-bytevector! bv :optional iport start end

[R7RS] read-uvector!の別名です。ユニフォームベクタのブロック入出力参照。

Function: write-string string :optional oport start end

[R7RS] startend引数が省略された場合は(display string oport)と 同じです。それらが与えられた場合は、string中の指定範囲のみが出力されます。

Function: write-u8

[R7RS] write-byteの別名です。

Function: write-bytevector bv :optional oport start end

[R7RS] write-uvectorと同じです。ユニフォームベクタのブロック入出力参照。

Function: flush-output-port :optional oport

[R7RS] flushの別名です。

Function: features

[R7RS] サポートされているfeature-identifierのリストを返します。 feature-identifierについては、cond-expandを参照してください (機能条件式参照)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.5 scheme.case-lambda - R7RS case-lambda

Module: scheme.case-lambda

R7RSの(scheme case-lambda)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme case-lambda))とすることで使えます。

このモジュールからエクスポートされるのはcase-lambdaだけで、 Gauche組み込みのcase-lambdaそのものです。 詳しくは手続きを作るを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.6 scheme.char - R7RS文字ライブラリ

Module: scheme.char

R7RSの(scheme char)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme char))とすることで使えます。

以下の手続きはGaucheの組み込みのものと同じです。文字参照。

 
char-alphabetic? char-ci<=? char-ci<?  char-ci=? char-ci>=? char-ci>?
char-downcase char-foldcase char-lower-case? char-numeric?
char-upcase char-upper-case?  char-whitespace?

以下の手続きはgauche.unicodeで提供されているものと同じです (Full string case conversion参照)。Unicodeで定義されている フルセットの大文字小文字変換を使います(例えばドイツ語のエスツェットが考慮されます)。

 
string-ci<=? string-ci<?  string-ci=? string-ci>=?  string-ci>?
string-downcase string-foldcase string-upcase
Function: digit-value c

[R7RS] cNdカテゴリの文字、つまり十進数の数字を表す文字であれば、 その文字が表す値を返します。それ以外の場合は#fを返します。

 
(digit-value #\3) ⇒ 3

(digit-value #\z) ⇒ #f

Unicodeには20種類以上の数字が定義されています。

 
(digit-value #\x11068) ⇒ 2

Gauche組み込みのdigit->integerはより汎用的なインタフェースを 持っています(文字参照)。

 
(digit-value c) ≡ (digit->integer c 10 #t)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.7 scheme.compex - R7RS複素数

Module: scheme.complex

R7RSの(scheme complex)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme complex))とすることで使えます。

このモジュールは以下の束縛を提供します。 全てGaucheの組み込みです(数値の変換参照)。

 
angle imag-part magnitude make-polar make-rectangular real-part

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.8 scheme.cxr - R7RS cxrアクセサ

Module: scheme.cxr

R7RSの(scheme complex)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme complex))とすることで使えます。

このモジュールは以下の束縛を提供します。 全てGaucheの組み込みです(リストへのアクセスと変更参照)。

 
caaar caadr cadar caddr cdaar cdadr cddar cdddr caaaar caaadr caadar
caaddr cadaar cadadr caddar cadddr cdaaar cdaadr cdadar cdaddr cddaar
cddadr cdddar cddddr

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.9 scheme.eval - R7RS eval

Module: scheme.eval

R7RSの(scheme eval)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme eval))とすることで使えます。

Function: eval expr environment

[R7RS] Gauche組み込みのevalと同じです (eval と repl参照)。

Function: environment import-list …

[R7RS] これはR7RSでevalに渡せる環境指定子を作る手段です。Gaucheでは、 環境指定子は単なるモジュールオブジェクトです。

引数はr7rs#importが取るものと同じです。 この手続きはまず空の環境(=無名のモジュールです。 詳しくはモジュールイントロスペクションmake-module参照)を作り、 それからimport-listに指定される束縛をインポートします。

以下の例は、scheme.base及びGauche組み込みのselect-moduleを インポートした環境を作ります。

 
(environment
 '(scheme base)
 '(only (gauche base) select-module))
 ⇒ #<module #f>  ; an anonymous module

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.10 scheme.file - R7RSファイルライブラリ

Module: scheme.file

R7RSの(scheme file)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme file))とすることで使えます。

次に挙げる束縛はGaucheの組み込みと同じです (ファイルポート及びファイルの状態参照)。

 
call-with-input-file call-with-output-file
file-exists?
open-input-file open-output-file
with-input-from-file with-output-to-file
Function: delete-file filename

[R7RS] これはfile.utildelete-fileと同じです (ファイル操作参照)。

Function: open-binary-input-file filename
Function: open-binary-output-file filename

[R7RS] Gaucheでは、ポートは同時にテキストポートでもバイナリポートでもあるので、 これらのR7RS手続きは単に open-input-fileopen-output-fileの別名になっています。 ファイルポート参照。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.11 scheme.inexact - R7RS不正確数

Module: scheme.inexact

R7RSの(scheme inexact)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme inexact))とすることで使えます。

このモジュールは以下の束縛を提供します。全てGaucheの組み込みです (数値の演算及び数値に関する述語参照)。

 
acos asin atan cos exp finite? infinite? log nan? sin sqrt tan

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.12 scheme.lazy - R7RS遅延評価

Module: scheme.lazy

R7RSの(scheme inexact)ライブラリの束縛をエクスポートします。 R7RSプログラムからは、エクスポートされた束縛は (import (scheme inexact))とすることで使えます。

以下の束縛は、Gauche組み込みです (Delayとforceとlazy参照)。

 
delay force promise?
Special Form: delay-force promise

[R7RS] これはGauche組み込みのlazyと同じです。 使い方についてはDelayとforceとlazyの議論を参照してください。

Function: make-promise obj

[R7RS] objがプロミスならそのまま返されます。そうでなければ、 forceされた時にobjが返されるようなプロミスが返されます。 これは手続きなので、objmake-promiseを呼び出す前に 評価されてしまいます。従って遅延評価には使えませんが、 確実にプロミスを得たい場合に使えます。

forceがプロミスのみを取るような実装ではこの手続きは重要です。 ポータブルなコードは、forceに渡すものがプロミスであることを 確実にしたい場所でこの手続きを使うようにしてください。

Gaucheのforceはプロミスでない値も取れるので、 Gauche専用のコードを書いている時はこの手続きは不要です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.13 scheme.load - R7RS load

Module: scheme.load

Exports bindings of R7RS (scheme load) library. From R7RS programs, those bindings are available by (import (scheme load)).

Function: load file :optional env

[R7RS] R7RS load takes environment as an optional argument, while Gauche load takes it as a keyword argument (among other keyword arguments). See section Schemeファイルのロード.

In Gauche, env is just a module. In portable code, you can create a module with desired bindings with R7RS environment procedure; see section scheme.eval - R7RS eval.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.14 scheme.process-context - R7RSプロセスコンテキスト

Module: scheme.process-context

Exports bindings of R7RS (scheme process-context) library. From R7RS programs, those bindings are available by (import (scheme process-context)).

The following bindings are the same as Gauche built-ins (see section コマンドライン引数, and プログラムの終了):

 
command-line exit

The following bindings are the same as SRFI-98 (see section srfi-98 - 環境変数へのアクセス):

 
get-environment-variable get-environment-variables
Function: emergency-exit :optional (obj 0)

[R7RS] Terminate the program without running any clean-up procedures (after thunks of dynamic-wind). Internally, it calls the _exit(2) system call directly. The optional argument is used for the process exit code.

This is almost the same as Gauche’s sys-exit, except that sys-exit requires the exit code object (see section プログラムの終了).


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.15 scheme.read - R7RS read

Module: scheme.read

Exports bindings of R7RS (scheme read) library. From R7RS programs, those bindings are available by (import (scheme read)).

The only binding exported from this module is read, which is the same as Gauche’s built-in. See section データの読み込み.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.16 scheme.repl - R7RS repl

Module: scheme.repl

Exports bindings of R7RS (scheme repl) library. From R7RS programs, those bindings are available by (import (scheme repl)).

The only binding exported from this module is interaction-environment, which is the same as Gauche’s built-in. See section eval と repl.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.17 scheme.time - R7RS time

Module: scheme.time

Exports bindings of R7RS (scheme time) library. From R7RS programs, those bindings are available by (import (scheme time)).

Function: current-second

[R7RS] Returns a real number represents the number of seconds since the midnight of Jan. 1, 1970 TAI (which is 23:59:52, Dec 31, 1969 UTC, that is, -8 seconds before Unix Epoch.) Number of leap seconds were inserted since then, and as of 2014, UTC is 35 seconds behind TAI. That means the number returned is 27 seconds larger than the unix time, which is returned from sys-time or sys-gettimeofday.

The reason that R7RS adopts TAI is that it is monotonic and suitable to take difference of two timepoints. The unix time returned by sys-time and sys-gettimeofday are defined in terms of UTC date and time, so if the interval spans across leap seconds, it won’t reflect the actual number of seconds in the interval. (The precise definition is given in section 4.15 of IEEE Std 1003.1, 2013 Edition, a.k.a Single Unix Specification 4.)

However, since we don’t know yet when the next leap second happen, the current implementation just uses a fixed amount of offset from the unix time.

Just be aware the difference, or you’ll be surprised if you pass the return value of current-second to the UTC time formatter such as sys-strftime, or compare it with the file timestamps which uses the unix time. You can convert between TAI and UTC using srfi-19 (see section 日付).

Function: current-jiffy

[R7RS] Returns an exact integer measuring a real (wallclock) time elapsed since some point in the past, which does not change while a process is running. The time unit is (/ jiffies-per-second)-th second.

The absolute value of current jiffies doesn’t matter, but the difference can be used to measure the time interval.

Function: jiffies-per-second

Returns a constant to tell how many time units used in current-jiffy consists of a second. Currently this is 10^9 on 64bit architectures (that is, nanosecond resolution) and 10^4 on 32bit architectures (100 microseconds resolution).

The resolution for 32bit architectures is unfortunately rather coarse, but if we make it finer the current jiffy value easily becomes bignums, taking time to allocate and operate, beating the purpose of benchmarking. With the current choice, we have 53,867 seconds before we spill into bignum. On 64bit architectures we have enough bits not to worry about bignums, with nanosecond resolution.

If you want to do more finer benchmarks on 32bit machines, you need to roll your own with sys-clock-gettime-monotonic or sys-gettimeofday.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.18 scheme.write - R7RS write

Module: scheme.write

Exports bindings of R7RS (scheme write) library. From R7RS programs, those bindings are available by (import (scheme write)).

This module provides the following bindings, all of which are Gauche built-in (see section オブジェクトの出力).

 
display write write-shared write-simple

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.19 scheme.r5rs - R5RS互換性

Module: scheme.r5rs

This module is to provide R5RS environment in R7RS programs. The following bindings are exported. Note that lambda is scheme#lambda, without the support of extended formals (:optional etc.) See section 手続きを作る, for the details of extended formals.

 
* + - / < <= = > >= abs acos and angle append apply asin assoc assq
assv atan begin boolean? caaaar caaadr caaar caadar caaddr caadr
caar cadaar cadadr cadar caddar cadddr caddr cadr
call-with-current-continuation call-with-input-file
call-with-output-file call-with-values car case cdaaar cdaadr cdaar
cdadar cdaddr cdadr cdar cddaar cddadr cddar cdddar cddddr cdddr cddr
cdr ceiling char->integer char-alphabetic? char-ci<=? char-ci<?
char-ci=? char-ci>=? char-ci>? char-downcase char-lower-case?
char-numeric? char-ready? char-upcase char-upper-case? char-whitespace?
char<=? char<? char=? char>=? char>? char? close-input-port
close-output-port complex? cond cons cos current-input-port
current-output-port define define-syntax delay denominator display
do dynamic-wind eof-object? eq? equal? eqv? eval even? exact->inexact
exact? exp expt floor for-each force gcd if imag-part inexact->exact
inexact? input-port? integer->char integer? interaction-environment
lambda lcm length let let* let-syntax letrec letrec-syntax list
list->string list->vector list-ref list-tail list? load log magnitude
make-polar make-rectangular make-string make-vector map max member
memq memv min modulo negative? newline not null-environment null?
number->string number? numerator odd? open-input-file open-output-file
or output-port? pair? peek-char positive? procedure? quasiquote quote
quotient rational? rationalize read read-char real-part real? remainder
reverse round scheme-report-environment set! set-car! set-cdr! sin
sqrt string string->list string->number string->symbol string-append
string-ci<=? string-ci<? string-ci=? string-ci>=? string-ci>?
string-copy string-fill! string-length string-ref string-set!
string<=? string<? string=? string>=? string>? string? substring
symbol->string symbol? tan truncate values vector vector->list
vector-fill! vector-length vector-ref vector-set! vector?
with-input-from-file with-output-to-file write write-char zero?

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11. ライブラリモジュール - SRFI

この章ではSRFIの機能を提供するモジュールをリストします。 一部のSRFIはGaucheのコアに組み込まれており、 この章には挙げられていないことに注意して下さい。 サポートされている全てのSRFIのリストは標準への準拠にあります。

(ここに挙げられていないSRFIでも、サポートされているものについては (use srfi-N)(import (srfi N))と書くことができます。)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.1 srfi-1 - リストライブラリ

Module: srfi-1

SRFI-1 は、リスト操作ライブラリの豊富なコレクションです (SRFI-1)。 このライブラリを使うには、(use srfi-1) として下さい。 Olin Shivers氏のリファレンス実装に基づいて実装されています。

GaucheはSRFI-1の手続きの多くを組み込みで提供しています。 以下に挙げるSRFI-1手続きはsrfi-1モジュールをロードしなくても使えます。 これらの手続きの説明は、ペアとリストを参照してください。

 
null-list? cons* last member
take drop take-right drop-right take! drop-right!
delete delete! delete-duplicates delete-duplicates!
assoc alist-copy alist-delete alist-delete!
any every filter filter! fold fold-right find find-tail
split-at split-at! iota

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.1.1 SRFI-1 リスト操作関数

List constructors

Function: xcons cd ca

[SRFI-1] (cons ca cd) と同等です。高階手続きへ渡すのに便利です。

Function: list-tabulate n init-proc

[SRFI-1] n個の要素をもつリストを構築し、それぞれの要素を (init-proc i) で生成します。

 
(list-tabulate 4 values) ⇒ (0 1 2 3)
Function: circular-list elt1 elt2 …

[SRFI-1] 指定した要素をもつ循環リストを構築します。

 
(circular-list 'z 'q) ⇒ (z q z q z q …)

List predicates

Function: not-pair? x

[SRFI-1] (lambda (x) (not (pair? x)))と同じです。

SRFI-1 では、「真性リストおよびドットリストの両方で、すべての有限リストを 扱う手続き用の終端条件として便利なように用意した」とあります。

Function: list= elt= list …

[SRFI-1] elt= を用いて、n番目の要素をそれぞれ比較することで、 与えられたリストの同値性を決定します。

list= を真性リスト以外に適用するとエラーになります。

同値性判定の手続きは eq? と整合性がなければなりません。すなわち

 
(eq? x y) ⇒ (elt= x y).

List selectors

Function: first pair
Function: second pair
Function: third pair
Function: fourth pair
Function: fifth pair
Function: sixth pair
Function: seventh pair
Function: eighth pair
Function: ninth pair
Function: tenth pair

[SRFI-1] リスト(非真性でも可)のn番目の要素を返します。

Function: car+cdr pair

[SRFI-1] (car pair) および (cdr pair) の二つの値を返します。

List miscellaneous routines

Function: zip clist1 clist2 …

[SRFI-1] (map list clist1 clist2 …) と同等です。 n 本のリストが zip に渡された場合には、そのなかで一番短いものと 同じ長さのリストを返します。返されたリストは、要素が n 要素のリストで、 そのそれぞれが、引数として渡ってリストの対応する要素になっています。

 
(zip '(one two three)
     '(1 2 3)
     '(odd even odd even odd even odd even))
     ⇒ ((one 1 odd) (two 2 even) (three 3 odd))

(zip '(1 2 3)) ⇒ ((1) (2) (3))

引数のリストのうち、少くともひとつは有限のリストでなければなりません。

 
(zip '(3 1 4 1) (circular-list #f #t))
     ⇒ ((3 #f) (1 #t) (4 #f) (1 #t))
Function: unzip1 list
Function: unzip2 list
Function: unzip3 list
Function: unzip4 list
Function: unzip5 list

[SRFI-1] unzip1 はリストのリストを引数としてとります。それぞれの リストは少くとも一つの要素を含むものでなくてはなりません。結果として それぞれのリストの最初の要素のリストを返します。 unzip2 はリストのリストを引数としてとります。それぞれのリストは 少くとも二つの要素を含むものでなくてはなりません。結果として二つの値を 返します。最初の要素のリストと二番目の要素のリストです。unzip3 は 3番目までの要素について同様です。以下も同様です。

 
(unzip2 '((1 one) (2 two) (3 three))) ⇒
   (1 2 3) and
   (one two three)

List fold, unfold & map

Function: pair-fold kons knil clist1 clist2 …
Function: pair-fold-right kons knil clist1 clist2 …

[SRFI-1] fold および fold-right と同様ですが、kons 手続き は与えられた clistcar ではなく、cdr をとります。

 
(pair-fold cons '() '(a b c d e))
  ⇒ ((e) (d e) (c d e) (b c d e) (a b c d e))

(pair-fold-right cons '() '(a b c d e))
  ⇒ ((a b c d e) (b c d e) (c d e) (d e) (e))
Function: unfold p f g seed :optional tail-gen

[SRFI-1] 基本リスト再帰構築子です。 以下のように再帰的に定義されています。

 
(unfold p f g seed tail-gen) ≡
   (if (p seed)
       (tail-gen seed)
       (cons (f seed)
             (unfold p f g (g seed))))

ここでは、p は終了位置の判定、g は現在の「種」から次の「種」 を生成するのに用い、f はそれぞれの「種」をリストの要素に変換する のに用いられます。

 
(unfold (pa$ = 53) integer->char (pa$ + 1) 48)
  ⇒ (#\0 #\1 #\2 #\3 #\4)
Function: unfold-right p f g seed :optional tail

[SRFI-1] 基本リスト反復構築子です。 以下のように再帰的に定義されています。

 
(unfold-right p f g seed tail) ≡
  (let lp ((seed seed) (lis tail))
    (if (p seed)
        lis
        (lp (g seed) (cons (f seed) lis))))
 
(unfold-right (pa$ = 53) integer->char (pa$ + 1) 48)
 ⇒ (#\4 #\3 #\2 #\1 #\0)
Function: map! f clist1 clist2 …

[SRFI-1] 手続き fclist1 の各要素と clist2 の対応する要素 に適用され、結果はひとつのリストになります。clist1 のセルは 結果のリストを構築するのに再利用されます。

Function: map-in-order f clist1 clist2 …

[SRFI-1] map の変形バージョンですが、f の適用順序が、引数として 与えられたリストの要素の左から右への順であることを保証します。 Gauche では map の実装はこの順になっているので、map と 同意です。

Function: pair-for-each f clist1 clist2 …

for-each と似ていますが、手続き f はまず clist自体に 適用され、次ににそれらのcdr に適用され、となります。

 
(pair-for-each write '(a b c))
 ⇒ prints (a b c)(b c)(c)

List partitioning

Function: partition pred list
Function: partition! pred list

[SRFI-1] filterremove を同時に行い、 2つのリストを返します。一つ目は pred により list の要素をフィルタリング した結果で、二つ目は pred により list の要素を削除した結果です。

 
(partition odd? '(3 1 4 5 9 2 6))
  ⇒ (3 1 5 9) (4 2 6)

partition! はその場で更新されるバージョンです。結果を生成するために list を破壊的に更新するかもしれません。

List searching

Function: take-while pred clist
Function: take-while! pred list

[SRFI-1] clist の最初から、pred を満足する限りの最長部分要素を返します。

Function: drop-while pred clist

[SRFI-1] clist の最初から、pred を満足する限りの最長部分要素を削除し、 残りを返します。

Function: span pred clist
Function: span! pred list
Function: break pred clist
Function: break! pred list

[SRFI-1] span(values (take-while pred clist) (drop-while pred clist)) と等価です。breakpred の意味を反転します。

Function: list-index pred clist1 clist2 …

[SRFI-1] pred を満足する最も左の要素のインデックスを返します。 predを満たす要素が無い場合は#fを返します。

Association lists

Function: alist-cons key datum alist

[SRFI-1] (cons (cons key datum) alist) を返します。 これは、Gauche の組み込み手続き acons の別名です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.1.2 集合としてのリスト

これらの手続きはリストを集合としてあつかいます。すなわち、どのような 要素があるかは重要ですが、その順序は重要ではありません。

この範疇にあるすべての手続きは、比較手続き elt= を最初の引数として とります。この比較手続きは与えられた二つの集合の要素が等しいかどうかを 判定します。

リストは検索に線形時間を必要とするため、ここに挙げた手続きは 大きな集合を扱うには向いていません。もし対象となる集合が 二桁以上の要素を持つことが分かっているなら、srfi-113 - setとbagを 参照してください。

集合の要素の組み合せについては util.combinations - 組み合わせ も参照してください。

Function: lset<= elt= list1 …

[SRFI-1] list1 のすべての要素が list2 (以降の集合)に含まれている ときに限り #t を返します。リストが与えられなかった場合 および一つだけしか与えられなかった場合には、#t を返します。

Function: lset= elt= list1 list2 …

[SRFI-1] list1 のすべての要素が list2 に含まれており、かつ、 list2 のすべての要素が list1 に含まれていれば、#t を返します。

 
(lset= eq? '(b e a) '(a e b) '(e e b a)) ⇒ #t
Function: lset-adjoin elt= list elt …

[SRFI-1] elt … を集合 list にまだなければ、追加します。 (順序はとくに決っていません。)

 
(lset-adjoin eq? '(a b c) 'a 'e) ⇒ '(e a b c)
Function: lset-union elt= list1 …

[SRFI-1] list1 … の和集合を返します。

Function: lset-intersection elt= list1 list2 …

[SRFI-1] すべての list に含まれる要素の集合を返します。

Function: lset-difference elt= list1 list2 …

[SRFI-1] list1 には含まれていて、list2 には含まれていない要素の集合を 返します。引数が n 個与えられた場合には、差分をとる二項演算が 畳み込まれます。

Function: lset-xor elt= list1 …

[SRFI-1] 与えられた集合の排他的論理和を返します。すなわち、list1 および list2 のどちらか一方にのみ属する要素からなる集合を返します。 引数が n 個の場合には、xor の二項演算が畳み込まれます。

Function: lset-diff+intersection elt= list1 list2 …

[SRFI-1] 与えられた集合の差集合と積集合のふたつの集合を返します。

Function: lset-union! elt= list …
Function: lset-intersection! elt= list1 list2 …
Function: lset-difference! elt= list1 list2 …
Function: lset-xor! elt= list1 …
Function: lset-diff+intersection! elt= list1 list2 …

[SRFI-1] それぞれ対応する手続きのその場で更新するバージョンです。 最初の引数のリストのセルが結果を構築するのに再利用されるかもしれません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.2 srfi-4 - 単一型のベクタ

Module: srfi-4

SRFI-4は、gauche.uvectorモジュールとして実装されています。 gauche.uvector - ユニフォームベクタ参照。 このモジュールは後方互換性のために単にgauche.uvectorを継承しています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.3 srfi-5 - シグネチャとrest引数に互換性のあるlet形式

Module: srfi-5

このモジュールは SRFI-5 の拡張 let 構文を提供します。

Macro: let ((var val) … [. (rest val …)]) body …
Macro: let name ((var val) … [. (rest val …)]) body …
Macro: let (name (var val) … [. (rest val …)]) body …

[SRFI-5] let 構文は 2通りの拡張がされています。

この拡張に関する理論的根拠については SRFI-5 のドキュメントを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.4 srfi-7 - 機能ベースプログラム設定言語

Module: srfi-7

このモジュールは、SRFI-7 で定義されている プログラム設定メタ言語(program フォーム)を提供します。

Gauche は srfi-7 モジュールを autoload しますので、(use srfi-7) と 明示的に書く必要はありません。

program フォームは Scheme の式である必要がないことに注意してください。 SRFI-7 では、この programフォームを処理して Scheme プログラムを 生成してから、それを別の方法で実行するような実装を許しています。

Gauche program フォームをマクロとして実装しています。それゆえ、 そのフォームを直接、評価することができます。とはいえ、program フォームと他のフォームを一つのファイル中で混在させたり、program フォームの返り値を期待するのは、よいことではありません。

programフォームの典型的な使い方は、program フォームのみを 含む単一のファイルを用意することです。(programフォーム中で files 節(後述)を用いて他のファイルをロードするとが可能です。) このようなプログラムファイルを Gauche で実行するには、単にそれを ロードするだけです。

Configuration Language: program program-clause program-clause2 …

[SRFI-7] これは Scheme プログラムを構成する設定言語で、機能の利用可能性に基づいて います。

Scheme のプログラムは、この programフォームから構築されます。 Gauche は構築された Scheme のプログラムをそのまま実行します。

program-clause は以下の "Program Clause" のどれか一つである 必要があります。

Program Clause: requires feature-id feature-id2 …

[SRFI-7] feature-idsrfi-0 のものと同じです (see section 機能条件式)。 あとに続くコードが feature-id を必要とすることを教えます。

Gauche ではサポートされていない feature-id が与えられた場合には、 エラーシグナルが発生します。

Program Clause: files filename …

[SRFI-7] プログラムに filenameの内容を挿入します。Gauche では 単に、現在のモジュール中への filename のロードをひきおこします。

Program Clause: code scheme-expression …

[SRFI-7] scheme-expression がプログラムに挿入されます。

Program Clause: feature-cond clause clause2 …

[SRFI-7] clause は以下のようなフォームです。

 
(requirement program-clause program-clause2 …)

ここでは、requirement は以下のもののどれかひとつでなければなりません。

最後の clauserequirementelse になるでしょう。

Gauche はそれぞれの requirement をひとつずつチェックし、 満された requirement が見つかると、そのclause中の program-clause をプログラムに挿入します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.5 srfi-11 - Let-values

Module: srfi-11

二つのマクロ、let-valueslet*-values を定義しています。 これらは、多値とともに利用すると便利です。([SRFI-11])

Macro: let-values ((vars expr) …) body …

[SRFI-11] vars は変数のリストです。expr は評価され、最初の返り値は、 vars の最初の変数に束縛されます。二番目の返り値は、vars の 二番目の変数に束縛され、以下同様です。そのあと、body が評価されます。 expr のスコープは let と同様に、let-values の 外側になります。

 
(let-values (((a b) (values 1 2))
             ((c d) (values 3 4)))
  (list a b c d)) ⇒ (1 2 3 4)

(let ((a 1) (b 2) (c 3) (d 4))
  (let-values (((a b) (values c d))
               ((c d) (values a b)))
    (list a b c d))) ⇒ (3 4 1 2)

vars lambda パラメータと同様に、ドット付リストでも、 単一のシンボルでもかまいません。

 
(let-values (((x . y) (values 1 2 3 4)))
  y) ⇒ (2 3 4)

(let-values ((x (values 1 2 3 4)))
  x) ⇒ (1 2 3 4)

expr によって返された値の数と vars が期待する数とが一致しない 場合にはエラーになります。

Macro: let*-values ((vars expr) …) body …

[SRFI-11] let-valuesと同じですが、各 expr のスコープが先行する vars を含みます。

 
(let ((a 1) (b 2) (c 3) (d 4))
  (let*-values (((a b) (values c d))
                ((c d) (values a b)))
    (list a b c d))) ⇒ (3 4 3 4)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6 srfi-13 - 文字列ライブラリ

Module: srfi-13

文字列に関連する関数の大きなセットを定義しています。 Gaucheでは、それらの関数はいくつかのファイルに分割されており、 (use srfi-13)というフォームは単にそれらのファイルの オートロードのセットアップをするだけです。 したがって、スクリプトのスタートアップを遅くするようなことは ありません。 詳細な仕様とデザインに関する議論については、SRFI-13 (SRFI-13) を参照して下さい。 このマニュアルは、関数のAPIのリファレンスとして提供されます。 いくつかのSRFI-13の関数は、Gaucheのビルトインになっており、 ここにはリストされていないものもあります。 注意: SRFI-13のドキュメントは、これらの関数を実装するモジュールの 名前を“string-lib”と“string-lib-internals”とすることを推奨しています。 Gaucheでは、一貫性のために“srfi-13”と名付けています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.1 一般規約

文字列ライブラリのAPIにはいくつか共通するものがありますが、 それぞれの関数の説明において繰り返しません。

引数の仕様

以下の引数名はその型を暗に表しています。

s, s1, s2

これらの引数は文字列でなければなりません。

char/char-set/pred

この引数は、文字、文字セットオブジェクト、あるいは1つの文字を引数に取り 真偽値を返す述語のいずれかです。“文字にchar/char-set/predを適用する” の意味はそれぞれ、char/char-set/predが文字ならば与えられた文字と比較される、 char/char-set/predが文字セットならばその文字セットに与えられた文字が 含まれるかを検査する、char/char-set/predが述語ならばそれを与えられた 文字に適用する、となります。“ある文字がchar/char-set/predを満足する” とは、その文字への前述のような適用が真値を返すという意味になります。

start, end

SRFI-13の関数の多くは、その操作が実行される対象入力文字列の 範囲を限定する開始インデックスと終了インデックスをオプショナルな 引数として取ります。開始(start番目)の文字は含まれ、 終了(end番目)の文字は含まれません。 これらが指定されるとき、0 <= start <= end <= length of the stringが 満たされなければなりません。startendのデフォルト値は、 それぞれ0と文字列の長さです。

sharedバージョン

いくつかの関数は、その名前に“/shared”が付きます。 SRFI-13では、それらの関数はより良いパフォーマンスのために 入力文字列の一部を共有しても良いと定義しています。 Gaucheは、共有文字列という概念を持っていませんし、 それらの関数は単に共有でないバージョンの変名に過ぎません。 しかし、Gaucheは内部的には文字列の保存場所を共有しているので、 一般的には部分文字列をコピーするオーバヘッドについて心配する 必要はありません。

rightバージョン

ほとんどの関数は、入力文字列を左から右へと扱います。 いくつかの関数は、その名前に“-right”が付き、右から左へと 扱うものがあります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.2 文字列についての述語

Function: string-null? s

[SRFI-13] sが空文字列、""なら、#tを返します。

Function: string-every char/char-set/pred s :optional start end

[SRFI-13] sに含まれる全ての文字がchar/char-set/predを 満足するかを検査します。満足するならば、string-everyは 最後に適用されたchar/char-set/predが返した値を戻り値とします。 どの適用も#fを返した場合は、string-everyはすぐに #fを返します。

Function: string-any char/char-set/pred s :optional start end

[SRFI-13] sに含まれるいずれかの文字がchar/char-set/pred を満足するかを検査します。いずれかの文字が満足するならば、 string-anyはその適用が返した値を戻り値とします。 どの文字もchar/char-set/predを満たさなければ、#fが返ります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.3 文字列の構築子

Function: string-tabulate proc len

[SRFI-13] procは整数を1つ引数として取り文字を返す手続きで なければなりません。string-tabulateは、そのi番目の 文字が(proc i)で計算されるような文字列を返します。

 
(string-tabulate
  (lambda (i) (integer->char (+ i #x30))) 10)
 ⇒ "0123456789"
Function: reverse-list->string char-list

[SRFI-13] ≡ (list->string (reverse char-list)).


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.4 文字列の選択

Function: substring/shared s start :optional end

[SRFI-13] Gaucheでは、引数endがオプショナルであることを除いて、 substringと同じです。

 
(substring/shared "abcde" 2) ⇒ "cde"
Function: string-copy! target tstart s :optional start end

[SRFI-13] 文字列sを、文字列targettstart番目以降へコピーします。 target文字列は変更可能でなければなりません。 オプションの引数startendは、sの範囲を制限します。 コピーされた文字列がtargetの終端を越えたらエラーが通知されます。

 
(define s (string-copy "abcde"))
(string-copy! s 2 "ZZ")
s ⇒ "abZZe"

targetsに同じ文字列を渡しても構いません。コピー元とコピー先の 領域が重なっていても、コピーは常に正しく行われます。

Function: string-take s nchars
Function: string-drop s nchars
Function: string-take-right s nchars
Function: string-drop-right s nchars

[SRFI-13] string-takeは、sの最初のnchars文字からなる文字列を返します。 string-dropは、sから最初のnchars文字からなる文字列を 除いた残りを返します。*-rightバージョンは、文字列の最後から数えます。 返される文字列はいつもsのコピーであり、どの文字も削除されないことが 保証されています。

 
(string-take "abcde" 2) ⇒ "ab"
(string-drop "abcde" 2) ⇒ "cde"

(string-take-right "abcde" 2) ⇒ "de"
(string-drop-right "abcde" 2) ⇒ "abc"
Function: string-pad s len :optional char start end
Function: string-pad-right s len :optional char start end

[SRFI-13] 文字列slenより短い場合は、charがそれぞれ左か 右にパディングされた長さlenの文字列を返します。 slenよりも長い場合は、len文字が右端か左端から 取り除かれます。Charのデフォルト値は#\spaceです。 startendが与えられると、sの部分文字列がソース 文字列として使われます。

 
(string-pad "abc" 10)    ⇒ "       abc"
(string-pad "abcdefg" 3) ⇒ "efg"

(string-pad-right "abc" 10) ⇒ "abc       "

(string-pad "abcdefg" 10 #\+ 2 5)
  ⇒ "+++++++cde"
Function: string-trim s :optional char/char-set/pred start end
Function: string-trim-right s :optional char/char-set/pred start end
Function: string-trim-both s :optional char/char-set/pred start end

[SRFI-13] sからchar/char-set/predにマッチする文字を削除します。 String-trimsの左から文字を削除し、 string-trim-rightは右から、string-trim-bothは 両端から削除します。 Char/char-set/predのデフォルト値は#[\s]、つまり空白文字の 文字セットです。startendが与えられると、sの部分文字列が ソース文字列として使われます。

 
(string-trim "   abc  ")       ⇒ "abc  "
(string-trim-right "   abc  ") ⇒ "   abc"
(string-trim-both "   abc  ")  ⇒ "abc"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.5 文字列の色々な比較

Function: string-compare s1 s2 proc< proc= proc> :optional start1 end1 start2 end2
Function: string-compare-ci s1 s2 proc< proc= proc> :optional start1 end1 start2 end2

[SRFI-13] 文字列s1s2をコードポイント毎に左から比較します。 不一致がs1のインデックスkで見つかった場合、対応するs2の コードポイントよりs1のコードポイントの方が小さければ proc<kを引数にして呼び出し、逆に大きければ proc>kを引数にして呼び出します。 二つの文字列が等しければ、proc=が、s1の比較された最後のインデックスを 引数にして呼び出されます。

 
(string-compare "abcd" "abzd"
                (^i `(< ,i)) (^i `(= ,i)) (^i `(> ,i)))
  ⇒ (< 2)

(string-compare "abcd" "abcd"
                (^i `(< ,i)) (^i `(= ,i)) (^i `(> ,i)))
  ⇒ (= 3)

省略可能引数は入力の文字列の比較すべき範囲を制限します。 但し、proc<proc=proc>に渡されるインデックスは 常にs1の最初から数えたものになります。

 
(string-compare "zzabcdyy" "abcz"
   (^i `(< ,i)) (^i `(= ,i)) (^i `(> ,i)) 2 6 0 4)
 ⇒ (< 5)

(string-compare "zzabcdyy" "abcz"
   (^i `(< ,i)) (^i `(= ,i)) (^i `(> ,i)) 2 5 0 3)

 ⇒ (= 4)

string-compare-ciは大文字小文字を区別しない比較です。 文字毎の大文字小文字変換を行って比較するので、文字列の長さが変わる 大文字小文字変換のケース (ドイツ語のエスツェット等) は考慮されません。

Function: string= s1 s2 :optional start1 end1 start2 end2
Function: string<> s1 s2 :optional start1 end1 start2 end2
Function: string< s1 s2 :optional start1 end1 start2 end2
Function: string<= s1 s2 :optional start1 end1 start2 end2
Function: string> s1 s2 :optional start1 end1 start2 end2
Function: string>= s1 s2 :optional start1 end1 start2 end2

[SRFI-13] 二つの文字列s1s2を比較します。省略可能引数で それぞれの文字列の一部のみを比較するように指定できます。 比較は文字ごとに行われます。

註: 組み込み手続きのstring=?等も文字毎の比較に使えますが、 引数の取り方が違います。文字列の比較参照。

Function: string-ci= s1 s2 :optional start1 end1 start2 end2
Function: string-ci<> s1 s2 :optional start1 end1 start2 end2
Function: string-ci< s1 s2 :optional start1 end1 start2 end2
Function: string-ci<= s1 s2 :optional start1 end1 start2 end2
Function: string-ci> s1 s2 :optional start1 end1 start2 end2
Function: string-ci>= s1 s2 :optional start1 end1 start2 end2

[SRFI-13] 二つの文字列s1s2を、大文字小文字を区別せずに比較します。 省略可能引数でそれぞれの文字列の一部のみを比較するように指定できます。 大文字小文字の畳み込みと比較は文字ごとに行われます。複数の文字に 影響を与えるような大文字小文字の畳み込みは考慮しません。

註: Gaucheには他に2種類の大文字小文字を区別しない文字列比較手続き群があります。 どちらもstring-ci=?等のようにクエスチョンマーク付きの名前を持っています。 Gauche組み込みのものは文字毎に大文字小文字を畳み込みます (文字列の比較参照)。一方、gauche.unicodeにあるものは 文字列としての畳み込みを行います(Full string case conversion参照)。 R7RS版は後者です。

Function: string-hash s :optional bound start end
Function: string-hash-ci s :optional bound start end

[SRFI-13] (註: Gaucheは、SRFI-128に準拠したstring-hashstring-ci-hashを 組み込みで持っています。詳しくはハッシュ参照。 SRFI-13のAPIはSRFI-128の上位互換になっています。内部で使っているハッシュ アルゴリズムは一緒なので、省略可能引数を全て省略したSRFI-13のstring-hashは、 組み込みのstring-hashと同じ値を返します。 一方、組み込みのstring-ci-hashは文字列として大文字小文字の畳み込みを 行います(ドイツ語のエスツェットとSSは等しく扱われます)が、SRFI-13の string-hash-ciは文字毎に大文字小文字を畳み込むように定められているので、 結果が異なる可能性があります。特に強い理由がなければ、新しいコードは 組み込みのSRFI-128版を使うことをおすすめします。)

文字列sのハッシュ値を計算して返します。string-hash-ciでは ハッシュ値を計算する前に各文字について大文字小文字を畳み込みを行います。

省略可能引数boundは、与えられたなら正の正確な整数でなければならず、 返される値は0から(- bound 1)までの値に制限されます。 startendは、与えられればs中の対象となる部分文字列を 指定します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.6 文字列のプリフィックスとサフィックス

Function: string-prefix-length s1 s2 :optional start1 end1 start2 end2
Function: string-suffix-length s1 s2 :optional start1 end1 start2 end2
Function: string-prefix-length-ci s1 s2 :optional start1 end1 start2 end2
Function: string-suffix-length-ci s1 s2 :optional start1 end1 start2 end2

[SRFI-13] 二つの文字列s1s2の、共通するプレフィクスもしくはサフィックスの 長さを返します。省略可能引数はそれぞれの文字列の探索範囲を限定します。 *-ci版は文字ごとに、大文字小文字を区別しない比較を行います。

 
(string-prefix-length "abacus" "abalone")   ⇒ 3
(string-prefix-length "machine" "umbrella") ⇒ 0
(string-suffix-length "peeking" "poking")   ⇒ 4

(string-prefix-length "obvious" "oblivious" 2 7 4 9)
  ⇒ 5
Function: string-prefix? s1 s2 :optional start1 end1 start2 end2
Function: string-suffix? s1 s2 :optional start1 end1 start2 end2
Function: string-prefix-ci? s1 s2 :optional start1 end1 start2 end2
Function: string-suffix-ci? s1 s2 :optional start1 end1 start2 end2

[SRFI-13] それぞれ、s2s1のプレフィクスまたはサフィックスになっていた場合に #tを、それ以外の場合に#fを返します。 省略可能引数はそれぞれの文字列の探索範囲を限定します。 *-ci版は文字ごとに、大文字小文字を区別しない比較を行います。

 
(string-prefix? "scheme" "sch")   ⇒ #t
(string-prefix? "scheme" "lisp")  ⇒ #f

(string-prefix? "mit-scheme" "scheme" 4) ⇒ #t

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.7 文字列の探索

Function: string-index s char/char-set/pred :optional start end
Function: string-index-right s char/char-set/pred :optional start end

[SRFI-13] 文字列sの中で、char/char-set/predにマッチする 最初の要素を探し、そのインデックスを返します。 sの中にchar/char-set/predが見つからない場合は、#fを 返します。オプションのstartendは、sの中で検索対象と なる範囲を制限します。

 
(string-index "Aloha oe" #\a) ⇒ 4
(string-index "Aloha oe" #[Aa]) ⇒ 0
(string-index "Aloha oe" #[\s]) ⇒ 5
(string-index "Aloha oe" char-lower-case?) ⇒ 1
(string-index "Aloha oe" #\o 3) ⇒ 6

ポータビリティよりも速度を重視する場合は、Gaucheのビルトイン手続き string-scan(文字列を扱うその他の手続き)を参照して下さい。

Function: string-skip s char/char-set/pred :optional start end
Function: string-skip-right s char/char-set/pred :optional start end

[SRFI-13] char/char-set/predにマッチしない最初の要素を探し、 そのインデックスを返します。そのような要素が見つからない場合、#fを 返します。オプションのstartendは、sの中で検索対象と なる範囲を制限します。

Function: string-count s char/char-set/pred :optional start end

[SRFI-13] sの中で、char/char-set/predにマッチする要素の数を カウントします。オプションのstartendは、sの中で検索対象と なる範囲を制限します。

Function: string-contains s1 s2 :optional start1 end1 start2 end2
Function: string-contains-ci s1 s2 :optional start1 end1 start2 end2

[SRFI-13] s1の中で、文字列s2を探します。見つかった場合は、 s1でマッチした文字列が始まるインデックスを返します。そうでなければ、 #fを返します。 オプションのstart1end1start2end2は、 s1s2の範囲を制限します。

ポータビリティよりも速度を重視する場合は、Gaucheのビルトイン手続き string-scan(文字列を扱うその他の手続き)を参照して下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.8 文字列のケース(大文字小文字)マッピング

Function: string-titlecase s :optional start end
Function: string-titlecase! s :optional start end
Function: string-upcase s :optional start end
Function: string-upcase! s :optional start end
Function: string-downcase s :optional start end
Function: string-downcase! s :optional start end

[SRFI-13] 文字列sをタイトルケース、大文字、小文字にそれぞれ変換します。 これらの手続きは、文字ごとにchar-upcase等を使った変換を行います。 すなわち、string-upcasestring-downcaseは以下の操作と 考えることができます。

 
(string-upcase s)
  ≡ (string-map char-upcase s)
(string-downcase s)
  ≡ (string-map char-downcase s)

一文字がケース変換によって複数文字になるような場合も考慮したい場合は、 gauche.unicodeモジュールに定義されている同名の手続きを使ってください (Full string case conversion参照)。

string-titlecase!string-upcase!string-downcase!はその場で更新するバージョンで、sを破壊的に 変更します。ただし、Gaucheでは文字列の変更は新たな文字列を作るのと効率的に 何ら変わりがありません。これらの手続きは互換性のためだけに用意されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.9 文字列の反転と追加

Function: string-reverse s :optional start end
Function: string-reverse! s :optional start end

[SRFI-13] sの文字の位置を逆順にした文字列を返します。 string-reverse!sそのものを変更します。

 
(string-reverse "mahalo") ⇒ "olaham"
(string-reverse "mahalo" 3) ⇒ "ola"
(string-reverse "mahalo" 1 4) ⇒ "aha"

(let ((s (string-copy "mahalo")))
  (string-reverse! s 1 5)
  s)
  ⇒ "mlahao"
Function: string-concatenate string-list

[SRFI-13] 文字列のリストを連結します。

 
(string-concatenate '("humuhumu" "nukunuku" "apua" "`a"))
  ⇒ "humuhumunukunukuapua`a"
Function: string-concatenate/shared string-list
Function: string-append/shared s …

[SRFI-13] string-concatenatestring-appendの“共有” バージョンです。Gaucheでは、これらは単に別名です。

Function: string-concatenate-reverse string-list
Function: string-concatenate-reverse/shared string-list

[SRFI-13] string-listを連結する前に逆順にします。 Gaucheでは、“共有”バージョンは全く同じ動作をします。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.10 文字列のマッピング

Function: string-map proc s :optional start end
Function: string-map! proc s :optional start end

[SRFI-13] string-mapは、sの全ての文字に対してprocを 適用し、その結果を文字列に集めそれを返します。 一方、string-map!sそのものを変更します。

 
(string-map char-upcase "wikiwiki") ⇒ "WIKIWIKI"
(string-map char-upcase "wikiwiki" 4) ⇒ "WIKI"

(let ((s (string-copy "wikiwiki")))
  (string-map! char-upcase s 4)
  s)
  ⇒ "wikiWIKI"
Function: string-fold kons knil s :optional start end
Function: string-fold-right kons knil s :optional start end

[SRFI-13] 文字列に対して動作するfoldfold-right (リストをたどる手続き参照) です。

 
(string-fold cons '() "abcde")
  ⇒ (#\e #\d #\c #\b #\a)
(string-fold-right cons '() "abcde")
  ⇒ (#\a #\b #\c #\d #\e)
Function: string-unfold p f g seed :optional base make-final

[SRFI-13] A fundamental string builder. The p, f and g are procedures, taking the current seed value. The stop predicate p determines when to stop: If it returns a true value, string building stops. The mapping function f returns a character from the current seed value. The next seed function g returns a next seed value from the current seed value. The seed argument gives the initial seed value.

 
(string-unfold (^n (= n 10))
               (^n (integer->char (+ n 48)))
               (^n (+ n 1))
               0)
  ⇒ "0123456789"

The optional argument base is, when given, prepended to the result string. Another optional argument make-final is a procedure that takes the last return value of g and returns a string that becomes the suffix of the result string.

 
(string-unfold (^n (= n 10))
               (^n (integer->char (+ n 48)))
               (^n (+ n 1))
               0 "foo" x->string)
  ⇒ "foo012345678910"
Function: string-unfold-right p f g seed :optional base make-final

[SRFI-13] Another fundamental string builder. The meanings of arguments are the same as ‘string-unfold’. The only difference is that the string is build right-to-left. The optional base, if given, becomes the suffix of result, and the result of make-final becomes the prefix.

 
(string-unfold-right (^n (= n 10))
                     (^n (integer->char (+ n 48)))
                     (^n (+ n 1))
                     0 "foo" x->string)
  ⇒ "109876543210foo"
Function: string-for-each proc s :optional start end

[SRFI-13] Apply proc on each character of string s, from left to right. Optional start and end arguments limit the range of the input string.

Function: string-for-each-index proc s :optional start end

[SRFI-13] Call proc on each index of the string s.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.11 文字列のローテーション

Function: xsubstring s from :optional to start end

[SRFI-13] Takes a substring of inifinite repetition of string s between index from (inclusive) and index to (exclusive).

For example, if s is "abcde", we repeat it infinitely to both sides. So 5n-th character for integer n is always #\a, which extends negative n as well.

 
(xsubstring "abcde" 2 10)
  ⇒ "cdeabcde"
(xsubstring "abcde" -9 -2)
  ⇒ "bcdeabc"
Function: string-xcopy! target tstart s sfrom :optional sto start end

[SRFI-13]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.12 他の文字列操作

Function: string-replace s1 s2 start1 end1 :optional start2 end2

[SRFI-13] 文字列s1start1文字目(inclusive)からend1文字目(exclusive) までを文字列s2に置き換えた文字列を新たに作って返します。s1, s2は 変更されません。 オプショナルな引数start2end2が与えられた場合は、 s2がまずそれらによって切り取られて置換文字列として使われます。 置き換える隙間の大きさ、つまり(- end1 start1)s2と同じ長さである必要はありません。 実質的に、この手続きは次のコードと等価です。

 
(string-append (substring s1 0 start1)
               (substring s2 start2 end2)
               (substring s1 end1 (string-length s1)))
Function: string-tokenize s :optional token-set start end

[SRFI-13] 文字列 s を、token-set で指定される文字セットで 構成される、空でない最大限連続した文字のシーケンスのそれぞれを 要素とするリストを返します。 token-set のデフォルト値は char-set:graphic (定義済みの文字セット参照)。

同様の機能を提供する、しかし異なる基準を持つ、Gauche の組み込み手続き string-split (文字列を扱うその他の手続き 参照) も見て下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.13 文字列のフィルタリング

Function: string-filter char/char-set/pred s :optional start end
Function: string-delete char/char-set/pred s :optional start end

[SRFI-13] それぞれ、文字列s中の文字のうちchar/char-set/predで示される テストを通る、または通らない文字からなる文字列を返します。

 
(string-filter char-upper-case? "Hello, World!")
  ⇒ "HW"

(string-delete char-upper-case? "Hello, World!")
  ⇒ "ello, orld!"

(string-delete #\l "Hello, World!")
  ⇒ "Heo, Word!"

(string-filter #[\w] "Hello, World!")
  ⇒ "HelloWorld"

註: srfi-13は制定後に改訂され、引数char/char-set/predsの 順序が逆になりました。制定時点では引数順は (string-filter s pred)で、Gaucheもそれに従って実装していました。 しかし既存の実装のほとんどは(string-filter pred s)という順序に なっています。srfi-13の参照実装がそうだったからです。

0.9.4からGaucheも現在のsrfi-13の実装に合わせましたが、 以前の仕様で書かれたコードとの互換性を保つため、 引数順を逆にしても動作するようになっています。 新しく書かれるコードは、現在のsrfi-13の引数順を使うのが良いでしょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.6.14 低レベルな文字列に関する手続き

Function: string-parse-start+end proc s args
Function: string-parse-final-start+end proc s args

[SRFI-13]

Macro: let-string-start+end (start end [rest]) proc-exp s-exp args-exp body …

[SRFI-13]

Function: check-substring-spec proc s start end
Function: substring-spec-ok? s start end

[SRFI-13]

Function: make-kmp-restart-vector s :optional c= start end

[SRFI-13]

Function: kmp-step pat rv c i c= p-start

[SRFI-13]

Function: string-kmp-partial-search pat rv s i :optional c= p-start s-start s-end

[SRFI-13]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.7 srfi-14 - 文字集合ライブラリ

Module: srfi-14

SRFI-14で定義された文字集合ライブラリを実装します (SRFI-14)。 以下の文字集合手続きはGauche組み込みになっているので、文字集合を参照してください。

 
char-set char-set? char-set-contains? char-set-copy
char-set-complement char-set-complement!

Gaucheでは<char-set>クラスは<collection>を継承し、 コレクションプロトコルを実装しているので、gauche.collectionモジュールで 提供される操作も使えます (gauche.collection - コレクションフレームワーク参照)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.7.1 文字セットの構築子

Function: list->char-set char-list :optional base-cs
Function: list->char-set! char-list base-cs

[SRFI-14] 与えられた文字のリストchar-listから文字セットを構築して返します。 文字セットbase-csが与えられていた場合、返される文字セットは その文字セットにchar-list中の文字を追加したものとなります。 list->char-set!は、結果を格納するためにbase-csを 変更するかもしれません。

Function: string->char-set s :optional base-cs
Function: string->char-set! s base-cs

[SRFI-14] 追加する文字をリストでなく文字列sで与えることを除けば list->char-setlist->char-set!と同じです。

Function: char-set-filter pred char-set :optional base-cs
Function: char-set-filter! pred char-set base-cs

[SRFI-14] char-set内の文字のうち、(pred c) が真の値を 返すようなcからなる文字セットを作成して返します。 文字セットbase-csが与えられた場合は、その内容が結果に追加されます。 char-set-filter!は、結果を格納するためにbase-csを変更するかも しれません。

Function: ucs-range->char-set lower upper :optional error? base-cs
Function: ucs-range->char-set! lower upper error? base-cs

[SRFI-14]

Function: integer-range->char-set lower upper :optional error? base-cs
Function: integer-range->char-set! lower upper error? base-cs
Function: ->char-set x

[SRFI-14] 様々な種類のオブジェクトを文字セットに変換する関数です。xに 文字のコレクション、文字セット、あるいは文字を渡すことができます。 引数が文字セットの場合はそれがそのまま返されます。 引数が文字の場合は、その文字のみを含む文字セットが返されます。

註: SRFI-14の->char-setは文字列、文字セット、文字のいずれかのみを 引数に取ります。Gaucheでは文字の任意のコレクションを取るように拡張しました。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.7.2 文字セットの比較

Function: char-set= char-set1 …

[SRFI-14] 与えられた文字セットが全て同じ要素を持っている場合に限り#tを返します。

 
(char-set=)  ⇒ #t
(char-set= (char-set)) ⇒ #t
(char-set= (string->char-set "cba")
           (list->char-set #\a #\b #\c))
  ⇒ #t
Function: char-set<= char-set1 …

[SRFI-14]

Function: char-set-hash char-set :optional bound

[SRFI-14]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.7.3 文字セットのイテレーション

Function: char-set-cursor char-set

[SRFI-14]

Function: char-set-ref char-set cursor

[SRFI-14]

Function: char-set-cursor-next char-set cursor

[SRFI-14]

Function: end-of-char-set? ccursor

[SRFI-14]

Function: char-set-fold kons knil char-set

[SRFI-14]

Function: char-set-unfold pred fun gen seed :optional base-char-set
Function: char-set-unfold! pred fun gen seed base-char-set

[SRFI-14]

Function: char-set-for-each proc char-set

[SRFI-14]

Function: char-set-map proc char-set

[SRFI-14]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.7.4 文字セットへの問い合わせ

Function: char-set-every pred char-set
Function: char-set-any pred char-set
Function: char-set-count pred char-set

[SRFI-14] これらの手続きは、predchar-setに含まれる各文字に適用します。

char-set-everypred#fを返したら直ちに#fを返します。 predが最後まで#fを返さなかった場合は、最後のpredの返り値が char-set-everyの返り値となります。

char-set-anypredが真の値を返した場合に直ちにその値を返します。 predが最後まで#fを返した場合には#fを返します。

char-set-countpredが真の値を返した回数を返します。

char-setは非常に大きくなる可能性があり (例えばごく小さな文字セットの補集合)、 その場合、これらの手続きは非常に長くかかり得ることに注意してください。

Function: char-set->list char-set
Function: char-set->string char-set

[SRFI-14] char-setに含まれる文字をリストあるいは文字列にして返します。 大きな文字セットに適用する場合は注意してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.7.5 文字セットに適用できる代数的関数

Function: char-set-adjoin char-set char1 …
Function: char-set-adjoin! char-set char1 …

[SRFI-14] char-setchar1 …を加えた文字集合を返します。

Function: char-set-delete char-set char1 …
Function: char-set-delete! char-set char1 …

[SRFI-14]

Function: char-set-union char-set …
Function: char-set-union! char-set1 char-set2 …

[SRFI-14]

Function: char-set-intersection char-set …
Function: char-set-intersection! char-set1 char-set2 …

[SRFI-14]

Function: char-set-difference char-set1 char-set2 …
Function: char-set-difference! char-set1 char-set2 …

[SRFI-14]

Function: char-set-xor char-set …
Function: char-set-xor! char-set1 char-set2 …

[SRFI-14]

Function: char-set-diff+intersection char-set1 char-set2 …
Function: char-set-diff+intersection! char-set1 char-set2 char-set3 …

[SRFI-14]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.7.6 定義済みの文字セット

Variable: char-set:letter

[SRFI-14]

Variable: char-set:blank

[SRFI-14]

Variable: char-set:iso-control

[SRFI-14]

Variable: char-set:digit
Variable: char-set:hex-digit

[SRFI-14]

Variable: char-set:graphic

[SRFI-14]

Variable: char-set:lower-case
Variable: char-set:upper-case
Variable: char-set:title-case

[SRFI-14]

Variable: char-set:printing

[SRFI-14]

Variable: char-set:punctuation

[SRFI-14]

Variable: char-set:whitespace

[SRFI-14]

Variable: char-set:symbol

[SRFI-14]

Variable: char-set:ascii

[SRFI-14]

Variable: char-set:empty

[SRFI-14]

Variable: char-set:full

[SRFI-14]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.8 srfi-19 - 時間のデータ型と手続き

Module: srfi-19

このSRFIは、時間と日付に関する様々な表現と、それらの間の変換メソッドを提供します。

Gaucheでは、timeオブジェクトは<time>クラスとして組み込みで サポートされています(時間参照)。dateオブジェクトは以下で説明する <date>クラスとしてサポートされます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.8.1 時間のタイプ

時間のタイプはシンボルで表現されます。 このモジュールでは以下の定数が自分の名前に束縛された形で提供されています。

Constant: time-utc

[SRFI-19] UTC timeです。Gaucheの組み込みのcurrent-timeは常にこのタイプの時間を 返します (時間参照)。

Constant: time-tai

[SRFI-19] International Atomic Time。この時間は閏秒による調整を受けないため、 UTCより若干大きな値を取ります。

Constant: time-monotonic

[SRFI-19] 実装依存の、単調増加する時間。Gaucheの実装ではこれはtime-taiと同じです。

Constant: time-duration

[SRFI-19] 二つの絶対的な時点の間の期間。

Constant: time-process

[SRFI-19] 現在のプロセスのCPU時間。 Gaucheは、POSIX time(3)コールが返すユーザ時間とシステム時間の合計からこの値を 計算します。

Constant: time-thread

[SRFI-19] 現在のスレッドのCPU時間。 現在の実装ではこれはtime-processと同じです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.8.2 時間に関する問い合わせ

Function: current-time :optional time-type

[SRFI-19] Gauche組み込みのcurrent-time (時間参照)を拡張して、 必要な時間のタイプを省略可能な引数time-typeで指定できるようにしています。 可能なtime-typeの値は時間のタイプで説明されているものです。

Function: current-date :optional tz-offset

[SRFI-19] 現在の日付を<date>クラスのインスタンスとして返します(日付参照)。 tz-offsetが与えられた場合、それがUTCからのオフセットを表す秒数として 使われます。tz-offsetが与えられなかった場合はローカルタイムゾーンで 表現された日付が返されます。

Function: current-julian-day

[SRFI-19] 現在のユリウス日(Julian day)を返します。Julian dayは -4714-11-24T12:00:00Z (November 24, -4714 at noon, UTC) からの日数を 実数で表現したものです。

Function: current-modified-julian-day

[SRFI-19] 現在の修正ユリウス日(Modified julian day)を返します。Modified julian dayは 1858-11-17T00:00:00Z (November 17, 1858 at midnight, UTC) からの日数を実数で表現したものです。

Function: time-resolution

[SRFI-19]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.8.3 時間に関する手続き

Function: make-time type nanoseconds seconds

[SRFI-19] 与えられた初期値を持つ<time>クラスのインスタンスを返します。 (make <time> :type type :second seconds :nanosecond nanoseconds) と等価です。

(この関数は0.6.8より前のリリースでは間違って定義されていました。 secondsnanosecondsの引数が逆になっていたのです。 make-timeを使っているコードは注意して下さい)。

Function: time-type time
Function: time-second time
Function: time-nanosecond time
Function: set-time-type! time type
Function: set-time-second! time second
Function: set-time-nanosecond! time nanosecond

[SRFI-19] <time>オブジェクトのスロットのアクセサです。

Function: copy-time time

[SRFI-19] 与えられたtimeと同じ内容の新しい<time>オブジェクトを返します。

Function: time=? time0 time1
Function: time<? time0 time1
Function: time<=? time0 time1
Function: time>? time0 time1
Function: time>=? time0 time1

[SRFI-19] 二つの時間を比較します。時間のタイプは一致していなければなりません。

Function: time-difference time0 time1
Function: time-difference! time0 time1

[SRFI-19] 二つの時間の差をtime-durationタイプの時間として返します。 二つの時間のタイプは一致していなければなりません。 Time-difference!は結果を格納するためにtime0を変更します。

Function: add-duration time0 time-duration
Function: add-duration! time0 time-duration
Function: subtract-duration time0 time-duration
Function: subtract-duration! time0 time-duration

[SRFI-19] time-durationtime0に加算、またはtime0から減算します。 返される時間のタイプはtime0と同じです。time-durationのタイプは time-durationでなければなりません。 add-duration!subtract-duration!は結果を格納するのに time0を再利用します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.8.4 日付

Class: <date>

日付を表現します。

Instance Variable of <date>: nanosecond

日付のナノ秒部分、0 から 999,999,999 までの整数です。

Instance Variable of <date>: second

日付の秒部分、0 から 60 までの整数です。(60 は閏秒)

Instance Variable of <date>: minute

日付の分部分、0 から 59 までの整数です。

Instance Variable of <date>: hour

日付の時部分、0 から 23 までの整数です。

Instance Variable of <date>: day

日付の日部分、0 から 31 日までの整数です。実際の上限は年と月によって決定 されます。(註: 1 は朔日(ついたち)、0 は仕様では認められていますが、 なぜなのかはわかりません。)

Instance Variable of <date>: month

日付の月部分、1 から 12 までの整数です。1 は一月、2 は二月、以下同様です。 (註: これは POSIX の <sys-tm> の命名法とは異ります。)

Instance Variable of <date>: year

日付の年部分です。

Instance Variable of <date>: zone-offset

このタイムゾーンが GMT より何秒東にあるかを示す整数です。

Function: make-date nanosecond second minute hour day month year zone-offset

[SRFI-19] 与えられた値から <date> オブジェクトを作成します。 註: この手続きは与えられた値が正しい範囲であるかどうかをチェックしません。

Function: date? obj

[SRFI-19] obj<date> オブジェクトである場合にかぎり真を返します。

Function: date-nanosecond date
Function: date-second date
Function: date-minute date
Function: date-hour date
Function: date-day date
Function: date-month date
Function: date-year date
Function: date-zone-offset date

[SRFI-19] アクセサです。

Function: date-year-day date
Function: date-week-day date
Function: date-week-number date day-of-week-starting-week

[SRFI-19] それぞれ、一年の何日目(一月一日が 1)か、何曜日(日曜日が 0、月曜日が 1 など)か、一年のうち何週目(一年の最初の週が一週間に満たない場合はその 週は無視)かを計算します。

Function: date->julian-day date
Function: date->modified-julian-day date
Function: date->time-monotonic date
Function: date->time-tai date
Function: date->time-utc date

[SRFI-19] 日付から様々な様式の日付/時間に変換します。

Function: julian-day->date jd :optional tz-offset
Function: julian-day->time-monotonic jd
Function: julian-day->time-tai jd
Function: julian-day->time-utc jd

[SRFI-19] ユリウス日からから様々な様式の日付/時間に変換します。

Function: modified-julian-day->date jd :optional tz-offset
Function: modified-julian-day->time-monotonic jd
Function: modified-julian-day->time-tai jd
Function: modified-julian-day->time-utc jd

[SRFI-19] 修正ユリウス日から様々な様式の日付/時間に変換します。

Function: time-monotonic->date time :optional tz-offset
Function: time-monotonic->julian-day time
Function: time-monotonic->modified-julian-day time
Function: time-monotonic->time-tai time
Function: time-monotonic->time-tai! time
Function: time-monotonic->time-utc time
Function: time-monotonic->time-utc! time

[SRFI-19] time-monotonic形式から様々な様式の日付/時間に変換します。

Function: time-tai->date time :optional tz-offset
Function: time-tai->julian-day time
Function: time-tai->modified-julian-day time
Function: time-tai->time-monotonic time
Function: time-tai->time-monotonic! time
Function: time-tai->time-utc time
Function: time-tai->time-utc! time

[SRFI-19] time-tai形式から様々な様式の日付/時間に変換します。

Function: time-utc->date time :optional tz-offset
Function: time-utc->julian-day time
Function: time-utc->modified-julian-day time
Function: time-utc->time-monotonic time
Function: time-utc->time-monotonic! time
Function: time-utc->time-tai time
Function: time-utc->time-tai! time

[SRFI-19] UTC 形式から様々な様式の日付/時間に変換します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.8.5 日付の読み書き

Function: date->string date :optional format-string

[SRFI-19+] <date>オブジェクトを文字列に変換します。書式は format-stringで指定します。format-stringが省略された場合に は"~c"が指定されたものとします。

書式文字列は出力にコピーされます。ただし、~で始まる並びの部分は 以下の規則にしたがって書き換えられます。

~~

文字~そのもの。

~a

ロケールによる曜日省略形(Sun...Sat)

~A

ロケールによる曜日完全形(Sunday...Saturday)

~b

ロケールによる月省略形(Jan...Dec)

~B

ロケールによる月完全形(January...December)

~c

ロケールによる日付時刻(たとえば、"Fri Jul 14 20:28:42-0400 2000")

~d

月日にち、0を補填(01...31)

~D

日付(mm/dd/yy)

~e

月日にち、空白補填( 1...31)

~f

秒 + 小数秒、小数点はロケールによるセパレータ(たとえば、5.2)

~h

~b に同じ

~H

時、0を補填、24時制(00...23)

~I

時、0を補填、12時制(01...12)

~j

年日にち、0補填(001...366)

~k

時、空白補填、24時制( 0...23)

~l

時、空白補填、12時制( 1...12)

~m

月、0補填、(01...12)

~M

分、0補填、(00...59)

~n

改行

~N

ナノ秒、0補填

~p

ロケールによるAM、PM

~r

時刻、12時制、"~I:~M:~S ~p"と同じ

~s

エポックからの経過秒(UTC)

~S

秒、0補填(00...60)

~t

水平タブ

~T

時刻、24時制、"~H:~M:~S"と同じ

~U

年経過週、週は日曜はじまり(01...53)

~V

年経過週、週は月曜はじまり(00...52)

~w

曜日を表す数字、日曜が0 (0...6)

~W

年経過週、週は月曜日はじまり(00...52)

~x

ロケールによる日付、例えば、"07/31/00"

~X

ロケールによる時刻、24時制、例えば、"06:51:44"

~y

西暦の下2桁(00...99)

~Y

西暦

~z

RFC-2822スタイルのタイムゾーン

~1

ISO-8601 の「年-月-日」形式

~2

ISO-8601 の「時-分-秒タイムゾーン」形式

~3

ISO-8601 の「時:分:秒」形式

~4

ISO-8601 の「西暦-月-日T時:分:秒タイムゾーン」形式

~5

ISO-8601 の「西暦-月-日T時:分:秒」形式

注意:現時点でGaucheはロケールの設定には従っておらず、常にロケールが "C" に設定されているものとしてフォーマットします。これについては将来変 更する可能性がありますので、現在の振舞いを前提として、たとえば、 ~aが常に"Sun".."Sat"のようにフォーマットされることを前提にプロ グラムしてはいけません。

いまのところプロセスのロケールを設定する標準的な方法がないので、 "C"ロケール形式を取得できるかどうかを確かめるポータブルな方法がありま せん。とはいえ、Gaucheでは SRFI-19の拡張として"C"ロケールであることを 確認する方法を提供しています。@~ とディレクティブ文 字の間に挿入し、~@a のようにします。

Function: string->date string template-string

[SRFI-19]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.9 srfi-27 - ランダムビットのソース

Module: srfi-27

このモジュールはメルセンヌツイスタアルゴリズム (math.mt-random - Mersenne Twister乱数発生器 参照) を基礎に用いたSRFI-27疑似乱数発生器インタフェースを提供します。

Function: random-integer n

[SRFI-27] デフォルトの乱数発生源を用いて、0 から n-1 までの正確な整数の乱数を 返します。この手続きの乱数のシードを設定するには default-random-source に対して random-source-randomize! あるいは、 random-source-pseudo-randomize! を用います。

Function: random-real

[SRFI-27] デフォルトの乱数発生源を用いて、0 より大きく、1 未満の実数乱数を返します。 この手続きの乱数のシードを設定するには default-random-source に対して random-source-randomize! あるいは、 random-source-pseudo-randomize! を用います。

Variable: default-random-source

[SRFI-27] random-integer および random-real が利用するデフォルトの 乱数発生源を保持しています。

Function: make-random-source

[SRFI-27] 乱数の発生源を生成し返します。 現在の Gauche の実装では、これは <mersenne-twister> のオブジェクトです。 将来の実装では変更される可能性があります。

Function: random-source? obj

[SRFI-27] obj が乱数発生源オブジェクトであれば、#t を返します。

Function: random-source-state-ref s
Function: random-source-state-set! s state

[SRFI-27] 乱数発生源 s の状態の「スナップショット」を獲得あるいは設定します。 state は背後にある発生器に依存する内容をもつ不透明なオブジェクトです。

Function: random-source-randomize! s

[SRFI-27] 乱数発生源 s の状態を完全にランダムな状態にしようとします。 現在の実装では乱数のシードを設定するのに、現在の時刻とプロセス ID を 使います。

Function: random-source-pseudo-randomize! s i j

[SRFI-27] 乱数発生源 s の状態を発生源とは独立した (i, j) 番の 初期状態に設定します。i および j は非負の整数です。 この手続きは乱数発生源 s を二つの非負整数でインデックス付した、 大規模の独立の乱数発生源として再利用するときに利用できます。 この手続きは、完全な決定性があることに注意してください。

Function: random-source-make-integers s

[SRFI-27] 呼出しのたびに乱数発生源 s より、 0 から n-1 までの整数の 乱数を返す、一つの整数 n を引数としてとる手続きを、返します。

Function: random-source-make-reals s :optional unit

[SRFI-27] 呼出しのたびに乱数発生源 s から 0 より大きく 1 未満の実数乱数を 返す、無引数の手続きを返します。 unit が与えられた場合、返された手続きが生成する実数乱数は unit 単位に量子化されます。unit は、 0 より大きく 1 未満で なければなりません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.10 srfi-29 - 地域化

Module: srfi-29

このモジュールは SRFI-29 で定義されたメッセージの地域化機構を実装したものです。

実際には、このモジュールは2つのサブモジュール、srfi-29.bundle および srfi-29.format で構成されています。srfi-29モジュールは このふたつのモジュールをextendしています。このようにしているのは、 SRFI-29 での format 手続きの定義が Gauche ネイティヴの(すなわち Common Lisp の) format とでは ~@* 指令の扱い において互換性がないからです。

そんなわけで、SRFI-29 の format を含む srfi-29.format と それ以外(「バンドル」API)を含む srfi-29.bundle に分離しました。 プログラムを SRFI-29 と完全に互換性のあるものにしたければ、srfi-29 モジュールを使って下さい。こうすると Gauche ネイティヴの format が上書きされます。SRFI-29 の「バンドル」API を使いたいだけで、 Gauche の format をそのままにしておきたいなら、srfi-29.bundle を使って下さい。

地域化機能は text.gettext モジュールによっても提供され (see section text.gettext - 地域化メッセージ)、Gauche でメッセージの地域化をするのなら text.gettext を使うのが好ましい方法です。srfi-29 モジュールは 主として SRFI-29 の機能を使っているコードをポーティングするためにあります。

バンドル指定子

バンドル指定子はシンボルを要素とする任意のリストです。しかし 典型的には以下のようなフォームになります。

 
(package language country details …)

ここで、package はソフトウェアパッケージを指定し、 language および country は言語と国コードを指定、そして、 details はエンコーディングのようなそのほかの情報を指定します。

デフォルトのバンドル指定子の値は以下のようなパラメータで取得可能です。

Parameter: current-language
Parameter: current-country
Parameter: current-locale-details

[SRFI-29] current-language および current-country パラメータは ISO 639-1 の言語コードと ISO 3166-1 の国コードをそれぞれシンボルで 保持しています。current-locale-details はエンコーディングのような 補助的な地域情報のリストを保持しています。

これらのパラメータは、環境変数 LANGlang_country.encoding フォーマットの形式で設定されていれば、初期化されます。たとえば、 LANGja_JP.eucJP に設定されていれば、それらの パラメータはそれぞれ jajp(eucjp) に設定されます。 LANGC であるかあるいは定義されていなければ、 デフォルト値は、それぞれ enus() です。

バンドル準備

Function: declare-bundle! bundle-specifier association-list

[SRFI-29] テンプレートキー(シンボル)と特定ロケールのメッセージ(文字列)の連想リストを bundle-specifier をキーとして、バンドルデータベースに入れます。

Gauche は現在のところメモリ上のバンドルデータベースのみサポートしています。 つまり、地域化されてメッセージを探しあてるには、そのアプリケーション内で declare-bundle! を呼ぶ必要があるということです。

Function: save-bundle! bundle-specifier
Function: load-bundle! bundle-specifier

[SRFI-29] Gauche は今のところ永続バンドルデータベースをサポートしていないので、 これらの手続きはなにもせず #f を返します (これはいまのところ srfi-29 の振舞いと適合しています)。

地域化されたメッセージの検索

Function: localized-template package-name message-template-name

[SRFI-29] 地域化されたメッセージを検索します。当該メッセージは package-name パッケージの中で、message-template-name シンボルと関連つけられています。

拡張された format 手続き

Function: format format-string args

[SRFI-29] SRFI-29 は SRFI-28 の format 手続きの仕様(これは ~a~s~% および ~~ 指令をサポートしている)を 引数再配置をサポートするために拡張しています。

~N@* 指令は N-番目のオプション引数から値を引き出す次の 指令の契機となります。参照された値が消費されることはありません。また、 参照された値がその後につづく指令の処理に影響を与えることはありません。

SRFI-28 の仕様は Gauche ネイティヴの format (see section 出力) と 互換性がありますが、この SRFI-29 の拡張には互換性がありません。 具体的には、Gauche ネイティヴの format~N@* 指定は 引数ポインタを N-番目のオプション引数を指すように変更します。 それゆえ、そのあとに続くすべての引数に影響をあたえます。

この非互換性があるために、この関数は、分離したモジュール srfi-29.format で定義されています。srfi-29.bundlesrfi-29.format を拡張する srfi-29 を使うと、format 手続きは SRFI-29 の format でそのモジュール内で上書きされます。 Gauche ネイティヴの format を使いたければ、srfi-29.bundle だけを使うようにしてください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.11 srfi-37 - args-fold プログラム引数処理

Module: srfi-37

このモジュールは、args-fold を実装しています。 これは、SRFI-37 (SRFI-37) で定義されている コマンドライン引数の処理をするもうひとつの手続きです。

gauche.parseopt (gauche.parseopt - コマンドライン引数の解析 参照) とは違って、args-fold は関数的なインタフェースです。すなわち、 利用者側の状態は、パーザの引数を通じて明示的に渡され、値が返ります。 また、ロングオプションを含む、POSIX および GNU の getopt のガイドラインに 準拠しています。

Function: args-fold args options unrecognized-proc operand-proc :rest seeds

与えられたオプション仕様 options および、ふたつの手続き unrecognized-procoperand-proc とにしたがって、プログラム 引数 args を左から右へ処理します。

options はオプションオブジェクトのリストで以下のようなものです。 各オプションオブジェクトはオプションの名前、そのオプションが引数を とるかどうかを指定するフラグ、そのオプションを処理する手続き (今後これを オプション手続きとよびます)を保持しています。

args-fold は単一文字のオプション(ショートオプション)と ロングオプションの両方を認識します。ショートオプションは単一の ハイフンで始まって(たとえば、-a)いなければなりません。一方、 ロングオプションは二つのハイフンで始まって(たとえば、--help) いなければなりません。ショートオプションは連結することが可能で、 たとえば、-abc あるいは -a -b -c と書くことができます。 ショートオプション、ロングオプションはともに、必須引数あるいはオプショナル 引数を取ることができます。ショートオプションの必須引数は当該オプション あとに空白なしあるいは空白をいれて指定することができます。たとえば、 -afoo あるいは -a foo が可能です。ロングオプションの 引数は’=’文字あるいは空白をはさみます。たとえば、 --long=foo または --long foo です。

args-fold はオプションの引数ではなく、ハイフンで始まっていない コマンドライン引数をオペランドとしてとりあつかいます。 args-fold はオペランドとオプションが交互に出現するのを許します。 しかし、’--’ に出逢うと、のこりは、ハイフンで始まっていようと なかろうと、オペランドとして扱われます。

与えられたオプションが options 内のオプションオブジェクトのひとつ にマッチすれば、オプション手続きが以下のように呼出されます。

 
(option-proc option name arg seed …)

ここで、option はマッチしたオプションオブジェクトで、name は実際にオプションを指定するのに使われた文字列、arg はそのオプション の引数(あるいは存在しなければ、#f)、そして seed … は 利用者側の状態情報です。option-procseed と同じ数だけ 値を返さなければなりません。

args-fold がオペランドを見つけたら、operand-proc が 以下のように呼出されます。

 
(operand-proc operand seed …)

operand-procseed と同じ数だけ 値を返さなければなりません。

呼出し側の状態は明示的にシード引数で渡され、戻されます。初期のシード値は args-fold に与えた seed です。この値は、オプション手続き unrecognized-procoperand-proc によって戻され、 次にこれらのオプション手続きの呼出し時にシード引数として使われます。 これらの手続きの最後の呼出しから返されたこの値は args-fold によって 返されます。

Function: option names require-arg? optional-arg? processor

渡されたプロパティでオプションオブジェクトを作成します。

names は文字と(あるいは)文字列のリストです。文字はショートオプションに 使い、文字列はロングオプションに使います。

ふたつのフラグ require-arg?optional-arg? はオプションが 必須引数をとるか、あるいは、オプショナル引数をとるかを示します。

processor はそのオプションの処理をする手続きです。

オプションの引数が ’=’ つきで渡された場合には、 たとえ require-arg?optional-arg? がともに #f であっても、そのオプションの引数はオプション処理手続きに渡されることに 注意してください。オプション手続きにその引数を処理させようとします。

ショートオプションに対するオプショナルなオプションの引数は、オプションに つづいてスペースなしで指定されたときにだけ認識されることにも 注意してください。たとえば、ショートオプション d がオプショナル 引数をもつことになっているとき、’-dfoo’ は ’-d’ が引数 ’foo’ をもつと解釈されますが、’-d foo’ は引数なしの ’-d’ とオペランド ’foo’ と解釈されてしまいます。もし、 ’-d’ が必須引数をもつと設定されているなら、両方ともに ’-dfoo’ は ’-d’ が引数 ’foo’ をもつと解釈されます。

Function: option? obj

obj がオプションオブジェクトなら #t を返し、 そうでなければ、#f を返します。

Function: option-names option
Function: option-required-arg? option
Function: option-optional-arg? option
Function: option-processor

オプションオブジェクト option のプロパティを返します。

簡単な例:

 
(use srfi-37)

(define options
 (list (option '(#\d "debug") #f #t
               (lambda (option name arg debug batch paths files)
                 (values (or arg "2") batch paths files)))
       (option '(#\b "batch") #f #f
               (lambda (option name arg debug batch paths files)
                 (values debug #t paths files)))
       (option '(#\I "include") #t #f
               (lambda (option name arg debug batch paths files)
                 (values debug batch (cons arg paths) files)))))

(define (main args)
  (receive (debug-level batch-mode include-paths files)
    (args-fold (cdr args)
               options
               (lambda (option name arg . seeds)         ; unrecognized
                 (error "Unrecognized option:" name))
               (lambda (operand debug batch paths files) ; operand
                 (values debug batch paths (cons operand files)))
               0      ; default value of debug level
               #f     ; default value of batch mode
               '()    ; initial value of include paths
               '()    ; initial value of files
               )
     (print "debug level = " debug-level)
     (print "batch mode = " batch-mode)
     (print "include paths = " (reverse include-paths))
     (print "files = " (reverse files))
     0))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.12 srfi-42 - 先行評価的内包表記

Module: srfi-42

このモジュールはジェネリックな内包表記(comprehension)機構を提供します。 この機構は他の言語(Haskell、Pythonなど)では組み込みの機構になっていま す。この機構は豊富な操作手続を提供しているので、リストジェネレータとい うだけではなく、ジェネリックなループ構文(Common Lisp の loop マ クロ並みに強力/邪悪だという人もいます)を提供しています。

この機構は名前の通り、先行評価的に走ります。すなわち、リストを生成する場合、評価時 にすべてのリストを生成します。要素を要求駆動的に生成するわけで はありません。それゆえ、無限列を表現することはできません。それが自然に できる Haskell とは違います。Gaucheは、遅延生成される有限/無限列を 扱う方法を他にいくつか提供しています。 遅延シーケンスgauche.generator - ジェネレータutil.stream - ストリームライブラリを 参照してください。

先行評価的内包表記の例

いくつかの例からはじめましょう。

5番目までの整数の自乗のリストを生成しましょう。

 
(list-ec (: i 5) (* i i)) ⇒ (0 1 4 9 16)

list-ecはリストを生成する内包表記マクロです。 最初のフォーム(: i 5)qualifierと呼ばれ、 繰り返しを行う値の集合を指定します (この例では0以上5未満の整数)。 最後のフォーム(* i i)bodyと呼ばれ、 qualifierが指定する値それぞれにつき評価される通常のScheme式です。

内包表記は複数のqualifierを持つことができます。 次の例は数の対(x y)の集合を生成します。ここでxは 2以上 5未満、yは1以上 x 未満です。

 
(list-ec (: x 2 5) (: y 1 x) (list x y))
  ⇒ ((2 1) (3 1) (3 2) (4 1) (4 2) (4 3))

複数のqualifierはネストするように動作します。つまり、(: x 2 5)は 残りの節—(: y 1 x) および (list x y) を繰り返すように 指定しているということです。

上の2つの例はHaskellで書くと以下のようになります。

 
[ i*i   | i <- [0..4] ]
[ (x,y) | x <- [2..4], y <- [1..x-1] ]

違いに注意:(1) Haskellでは要素になる本体部が先にきて、そのあとに修飾 部(セレクタ)がきます。SRFI-42では本体部は最後になります。(2) SRFI-42で は範囲指定の下限はそれを含み、上限はそれを含みません。

a^3+b^3 = c^3+d^3を満すような数字の集合(a b c d)を列挙し ましょう。

 
(define (taxi-number n)
  (list-ec (: a 1 n)
           (: b (+ a 1) n)
           (: c (+ a 1) b)
           (: d (+ c 1) b)
           (if (= (+ (expt a 3) (expt b 3))
                  (+ (expt c 3) (expt d 3))))
           (list a b c d)))

複数の変数を(ネストするのではなく)同時に変化させたい場合は、 複数のqualifierを次のようにまとめることができます。

 
(list-ec (:parallel (: x '(a b c d)) (: y '(1 2 3 4)))
         (list x y))
  ⇒ ((a 1) (b 2) (c 3) (d 4))

リストだけではなく、他のシーケンスも生成できます。

 
(vector-ec (: i 5) i) ⇒ #(0 1 2 3 4)
(string-ec (: i 5) (integer->char (+ i 65))) ⇒ "ABCDE"

畳み込み演算も適用できます。

 
(sum-ec (: i 1 100) i)
  ⇒ 4950    ;; 1以上100未満の整数の和
(product-ec (: i 1 10) i)
  ⇒ 362880 ;;  1以上10未満の整数の積

内包表記マクロ

それぞれの内包表記は以下のような形式になります。

 
(comprehension-macro qualifierbody)

qualifier …の指定に従ってbodyをくりかえし評価します。内包表記の種類 によって、bodyの結果は(リスト、ベクタ、文字列などに)集約されるか、 (sum、product、min、maxなどによって)畳み込まれるか、あるいは、単に捨て られます。

それぞれのqualifierは、それ以降のqualifierbody をどのように繰り返すかを指定します。qualifierには、繰り返しに 使う値を生成する生成的qualifierと、条件によって値を繰り返しから 省く制御的qualifierがあります。以下のQualifiersの節を参照してください。

いくつかの内包表記では、追加の値がqualifiersの前か、body の後に置かれます。

Macro: do-ec qualifier … body

[SRFI-42] bodyを繰り返します。bodyの返り値は捨てられます。 この形式は副作用目的で使います。

Macro: list-ec qualifier … body

[SRFI-42] bodyを繰り返し、結果をリストに集めて返します。

Macro: append-ec qualifier … body

[SRFI-42] bodyを繰り返し、その結果のリストを結合して返します。 bodyは必ずリストを返さなければなりません。

Macro: string-ec qualifier … body
Macro: string-append-ec qualifier … body

[SRFI-42] bodyを繰り返します。bodystring-ecでは文字へ、 string-append-ecでは文字列へと評価されなければなりません。 結果を集め、または結合した文字列が買えされます。

Macro: vector-ec qualifier … body

[SRFI-42] Repeats body and collects the results into a vector.

Macro: vector-of-length-ec k qualifier … body

[SRFI-42] This is like vector-ec, except that the length of the result vector is known to be k. It can be more efficient than vector-ec. Unless the comprehension repeats exactly k times, an error is signaled.

Macro: sum-ec qualifier … body
Macro: product-ec qualifier … body

[SRFI-42] body must yield a numeric value. Returns sum of and product of the results, respectively.

Macro: min-ec qualifier … body
Macro: max-ec qualifier … body

[SRFI-42] body must yield a numeric value. Returns maximum and minimum value of the results, respectively. body must be evaluated at least once, or an error is signaled.

Macro: any?-ec qualifier … test
Macro: every?-ec qualifier … test

[SRFI-42] Evaluates test for each iteration, and returns #t as soon as it yields non-#f (for any-ec?), or returns #f as soon as it yields #f (for every?-ec). Unlink the comprehensions introduced above, these stop evaluating test as soon as the condition meets. If the qualifiers makes no iteration, #f and #t are returned, respectively.

Macro: first-ec default qualifier … body
Macro: last-ec default qualifier … body

[SRFI-42] First initializes the result by the value of the expression default, then start iteration, and returns the value of the first and last evaluation of body, respectively. In fact, first-ec only evaluates body at most once.

These procedures are most useful when used with control qualifiers. For example, the following first-ec returns the first set of distinct integers (x, y, z), where x*x+y*y+z*z becomes a square of another integer w.

 
(first-ec #f (:integers w) (: z 1 w) (: y 1 z) (: x 1 y)
          (if (= (* w w) (+ (* x x) (* y y) (* z z))))
          (list x y z w))

Note that the first qualifier, (:integers w), generates infinite number of integers; if you use list-ec instead of first-ec it won’t stop.

Macro: fold-ec seed qualifier … expr proc
Macro: fold3-ec seed qualifier … expr init proc

[SRFI-42] Reduces the values produced by expr.

Suppose expr produces a sequence of values x0, x1, …, xN. Fold-ec calculates the following value:

 
(proc xN (…(proc x1 (proc x0 seed))…))

It’s similar to fold, except that proc is evaluated within the scope of qualifier … so you can refer to the variables introduced by them. On the other hand, seed is outside of the scope of qualifiers.

Fold-ec3 is almost the same but the initial value calculation. In fold-ec3, seed is only used when qualifiers makes no iteration. Otherwise it calculates the following value:

 
(proc xN (…(proc x1 (init x0))…))

Qualifiers

生成的qualifier

このタイプのqualifierは、いくつかの値(無限個のこともあります)を次々に 生成し、各値について残りの節を繰り返します。

以下の説明において、varsというのはひとつの識別子か、 識別子とフォーム(index identifier2)の並びです。 最初の形式での識別子、あるいは二番目の形式での最初の識別子は、 生成された値が束縛される変数です。二番目の形式のidentifier2は、 0から始まり値が生成されるたびにインクリメントされる整数値が束縛されます。 次の例を見てください。

 
(list-ec (: x '(a b c)) x)
  ⇒ (a b c)
(list-ec (: x (index y) '(a b c)) (cons x y))
  ⇒ ((a . 0) (b . 1) (c . 2))
EC Qualifier: : vars arg1 args …

arg1 args … の型に基づいて、以下の生成的qualifierの いずれかにディスパッチされる、汎用的なqualifierです。

EC Qualifier: :list vars arg1 args …
EC Qualifier: :vector vars arg1 args …
EC Qualifier: :string vars arg1 args …

それぞれのフォームにおいて、 arg1 args …は全てリスト、ベクタ、文字列でなければなりません。 各要素をvarsに束縛して続く節を繰り返します。

 
(list-ec (:string c "ab" "cd") c) ⇒ (#\a #\b #\c #\d)

汎用qualifier:に渡された引数が全てリスト、ベクタ、あるいは文字列 であった場合は、これらのqualifierが使われます。

EC Qualifier: :integers vars

0から始まり1づつ増加しつづける無限正確整数列を生成します。

EC Qualifier: :range vars stop
EC Qualifier: :range vars start stop
EC Qualifier: :range vars start stop step

startから始まり、stepづつ増加し、stopを越えないような 正確な整数列を生成します。startが省略された場合は0、 stepが省略された場合は1が使われます。 stepに負数を与えれば減少列も作れます。

 
(list-ec (:range v 5) v)      ⇒ (0 1 2 3 4)
(list-ec (:range v 3 8) v)    ⇒ (3 4 5 6 7)
(list-ec (:range v 1 8 2) v)  ⇒ (1 3 5 7)
(list-ec (:range v 8 1 -2) v) ⇒ (8 6 4 2)

汎用qualifier:に1個から3個の正確な整数が与えられた場合は、 このqualifierが使われます。

EC Qualifier: :real-range vars stop
EC Qualifier: :real-range vars start stop
EC Qualifier: :real-range vars start stop step

startから始まり、stepづつ増加し、stopを越えないような 実数列を生成します。startが省略された場合は0、 stepが省略された場合は1が使われます。 全ての引数が正確数であれば正確な数列が、ひとつでも非正確数が混じって入れば 非正確な数列が生成されます。

 
(list-ec (:real-range v 5.0) v)
  ⇒ (0.0 1.0 2.0 3.0 4.0)
(list-ec (:real-range v 1 4 1/3) v)
  ⇒ (1 4/3 5/3 2 7/3 8/3 3 10/3 11/3)
(list-ec (:real-range v 1 5.0 1/2) v)
  ⇒ (1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5)

汎用qualifier:に1個から3個の実数が与えられ、そのうちどれかひとつでも 正確な整数でないものがあれば、このqualifierが使われます。

EC Qualifier: :char-range vars min max

文字minからmaxまで(両端含む)の文字を順に生成します。 文字順はchar<=?で比べられるのと同じ順になります (文字参照)。

 
(list-ec (:char-range v #\a #\e) v)
  ⇒ (#\a #\b #\c #\d #\e)

汎用qualifier:に2つの文字が与えられた場合は、 このqualifierが使われます。

EC Qualifier: :port vars port
EC Qualifier: :port vars port read-proc

入力ポートportから、read-procを使って読まれる値を、 EOFに出会うまで次々に生成します。read-procが省略された場合はread が使われます。

 
(call-with-input-string "a \"b\" :c"
  (^p (list-ec (:port v p) v)))
  ⇒ (a "b" :c)

汎用qualifier:に2つの引数が与えられ、最初の引数が入力ポートであれば、 このqualifierが使われます。

EC Qualifier: :generator vars gen

これはSRFI-42には定義さていない、Gauche独自の拡張です。 genは引数と取らない手続きです。このqualifierは genがEOFを返すまで繰り返します。

Gaucheは、そのような手続きを作ったり操作したりする便利な関数群を 提供しています。gauche.generator - ジェネレータを参照してください。

 
(use gauche.generator)
(list-ec (:generator v (grange 1 8)) v)
  ⇒ (1 2 3 4 5 6 7)

汎用qualifier:に1つの引数が与えられ、それが引数無しで呼び出し可能なものであれば、 このqualifierが使われます。

EC Qualifier: :parallel generator …

複数のジェネレータ節を並列に走査するのに使います。 generatorのどれかが値を使い切った時点で止まります。

 
(list-ec (:parallel (: x '(a b c))
                    (: y "defg"))
  (cons x y))
 ⇒ ((a . #\d) (b . #\e) (c . #\f))

;; Compare with this:
(list-ec (: x '(a b c))
         (: y "defg")
  (cons x y))
 ⇒ ((a . #\d) (a . #\e) (a . #\f) (a . #\g)
     (b . #\d) (b . #\e) (b . #\f) (b . #\g)
     (c . #\d) (c . #\e) (c . #\f) (c . #\g))
EC Qualifier: :let vars expr
EC Qualifier: :while generator expr
EC Qualifier: :until generator expr
EC Qualifier: :dispatched vars dispatch arg1 args …
EC Qualifier: :do (lb …) ne1? (ls …)
EC Qualifier: :do (let (ob …) oc …) (lb …) ne1? (let (ib …) ic …) ne2? (ls …)

Control qualifiers

EC Qualifier: if test
EC Qualifier: not test
EC Qualifier: and test …
EC Qualifier: or test …
EC Qualifier: begin command … expr
EC Qualifier: nested qualifier …

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.13 srfi-43 - ベクタライブラリ(旧式)

Module: srfi-43

このモジュールは実効的に、R7RSとsrfi-133に取って代わられています。 いくつかの手続きはR7RSやsrfi-133の同名の手続きとは非互換で、 このモジュールはsrfi-43に依存している既存のコードのためだけに 残されています。

「現代的な」ベクタライブラリは、ベクタsrfi-133 - ベクタライブラリ で説明されています。新たなコードはそちらを使うべきです。

以下のsrfi-43の手続きは組み込みになっています。 説明はベクタを参照してください。

 
make-vector     vector          vector?         vector-ref
vector-set!     vector-length   vector-fill!    vector-copy
vector-copy!    vector-append   vector->list    list->vector
reverse-list->vector

以下のsrfi-43の手続きはsrfi-113でも同様にサポートされています。 説明はsrfi-133 - ベクタライブラリを参照してください。

 
vector-unfold         vector-unfold-right   vector-reverse-copy
vector-reverse-copy!  vector-concatenate    vector-empty?
vector=               vector-index          vector-index-right
vector-skip           vector-skip-right     vector-binary-search
vector-any            vector-every          vector-swap!    
reverse-vector->list

ここでは上に挙げられていない手続きを説明します。

Function: vector-fold kons knil vec1 vec2 …
Function: vector-fold-right kons knil vec1 vec2 …

[SRFI-43] srfi-113vector-foldvector-fold-rightと似ていますが、 kons手続きは追加で、現在のインデックスを第一引数として取ります。 従って、nを引数で与えられたベクタの数とすると、 konsn+2個の引数を取らなければなりません。 kons(kons <index> <cumulated-value> <elt1> <elt2> ...) のように呼ばれます。

Gaucheにはfold-with-indexがあって、ベクタをインデックスつきで foldするのに使えます(シーケンス上のマップ)が、 konsが受け取る引数の順序が違います: インデックスの次に 各ベクタの要素、そして最後に累積された値が来ます。

 
(use srfi-43)
(vector-fold list '() '#(a b c) '#(d e f))
  ⇒ (2 (1 (0 () a d) b e) c f)

(use gauche.sequence)
(fold-with-index list '() '#(a b c) '#(d e f))
  ⇒ (2 c f (1 b e (0 a d ())))
Function: vector-map f vec1 vec2 …
Function: vector-map! f vec1 vec2 …
Function: vector-for-each f vec1 vec2 …
Function: vector-count f vec1 vec2 …

[SRFI-43] R7RSのvector-mapおよびvector-for-each、 そしてsrfi-133vector-map!およびvector-coutと ほぼ同じですが、fが現在のインデックスを最初の引数として(他の引数より前に) 取ります。

Gaucheはsrfi-43vector-mapvector-map!vector-for-each互換な手続きをそれぞれ vector-map-with-indexvector-map-with-index!vector-for-each-with-indexという 名前で提供しています。ベクタ参照。

 
(vector-map list '#(a b c))
 ⇒ #((0 a) (1 b) (2 c))
(vector-map list '#(a b c) '#(d e f g))
 ⇒ #((0 a d) (1 b e) (2 c f))
(vector-count = '#(0 2 2 4 4))
 ⇒ 3

(註: vector-count=を二つの引数、現在のインデックスと ベクタの要素で呼び出します。従ってこの例は、ベクタの要素がインデックスと一致する 場合を数えています。)

gauche.collectionのジェネリックなmapfor-eachは ベクタに対しても使えますが、マップされる手続きはインデックスを取らず、 また結果はリストになります。 (vector-map f vec1 vec2 …)は機能的には (map-to-with-index <vector> f vec1 vec2 …)と同じになります。 詳しくはgauche.collection - コレクションフレームワークgauche.sequence - シーケンスフレームワークを参照してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.14 srfi-55 - require の拡張

Module: srfi-55

このモジュールでは require-extension マクロが定義されています。 これは、ポータブルなスクリプトを書くためのもうひとつ別の方法です。 指定した特性を確認する他の方法については、機能条件式 およびsrfi-7 - 機能ベースプログラム設定言語を見てください。

このモジュールは、require-extensionを使った際に自動的にロードさ れます。それゆえ明示的に(use srfi-55)する必要はありませんし、ポー タビリティのためにはするべきではありません。

Macro: require-extension clause …

clauseで指定した拡張を以降のプログラムで利用可能にします。

clauseは以下の形式になります。

 
(extension-id extension-arg …)

現時点では、srfiのみextension-idとして使えます。その引数 はSRFIの番号です。

たとえば、以下の

 
(require-extension (srfi 1 13 14))

は以下の Gauche の use 形式にほぼ対応します。

 
(use srfi-1)
(use srfi-13)
(use srfi-14)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.15 srfi-60 - 整数に対するビット操作

Module: srfi-60

This srfi provides bit operations on integers, regarding them as 2’s complement representation.

Most of the functionalities in this module are already provided as builtin procedures (see section ビット演算). This srfi defines popular aliases for many of those procedures, thus has better portability. There are also several additional procedures than Gauche’s builtin bitwise operations.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.15.1 Bitwise operators

Function: lognot n
Function: bitwise-not n

Same as builtin lognot (see section ビット演算).

Function: logand n …
Function: bitwise-and n …

Same as builtin logand (see section ビット演算).

Function: logior n …
Function: bitwise-ior n …

Same as builtin logior (see section ビット演算).

Function: logxor n …
Function: bitwise-xor n …

Same as builtin logxor (see section ビット演算).

Function: bitwise-if mask n0 n1

Returns integer, whose n-th bit is taken as follows: If the n-th bit of mask is 1, the n-th bit of n0; otherwise, the n-th bit of n1.

 
(bitwise-if #b10101100 #b00110101 #b11001010)
 ⇒ #b01100110
Function: logtest mask n
Function: any-bits-set? mask n

Same as builtin logtest (see section ビット演算).


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.15.2 Integer properties

Function: logcount n
Function: bit-count n

Same as builtin logcount (see section ビット演算).

Function: integer-length n

Same as builtin integer-length (see section ビット演算).

Function: log2-binary-factors n
Function: first-set-bit n

Returns the number of factors of two of integer n; that is, returns a maximum k such that (expt 2 k) divides n without a remainder. It is the same as the index of the least significant 1 in n, hence the alias first-set-bit.

 
(log2-binary-factors 0) ⇒ -1   ; edge case
(log2-binary-factors 1) ⇒ 0
(log2-binary-factors 2) ⇒ 1
(log2-binary-factors 15) ⇒ 0
(log2-binary-factors 16) ⇒ 4

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.15.3 Bit within word

Function: logbit? index n
Function: bit-set? index n

Same as builtin logbit? (see section ビット演算).

Function: copy-bit index n bit

Same as builtin copy-bit (see section ビット演算).


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.15.4 Field of bits

Function: bit-field n start end

Same as builtin bit-field (see section ビット演算).

Function: copy-bit-field to from start end

Same as builtin copy-bit-field (see section ビット演算).

Function: ash n count
Function: arithmetic-shift n count

Same as builtin ash (see section ビット演算).

Function: rotate-bit-field n count start end

Rotate the region of n between start-th bit (inclusive) and end-th bit (exclusive) by count bits.

 
(rotate-bit-field #b110100100010000 -1 5 9)
 ⇒ 26768 ; #b110100010010000

(rotate-bit-field #b110100100010000 1 5 9)
 ⇒ 26672 ; #b110100000110000
Function: reverse-bit-field n start end

Reverse the order of bits of n between start-th bit (inclusive) and end-th bit (exclusive).

 
(reverse-bit-field #b10100111 0 8)
 ⇒ 229 ; #b11100101

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.15.5 Bit as booleans

Function: integer->list n :optional len

Breaks n to each bits, representing 1 as #t and 0 as #f, LSB last, and returns a list of them. If a nonnegative integer len is given, it specifies the length of the result. If it is omitted, (integer-length n) is used.

 
(integer->list 9)   ⇒ (#t #f #f #t)
(integer->list 9 6) ⇒ (#f #f #t #f #f #t)
Function: list->integer lis

Takes a list of boolean values, replaces the true value for 1 and the false value for 0, and compose an integer regarding each value as a binary digit. If n is nonnegative integer, (eqv? (list->integer (integer->list n)) n) is true.

 
(list->integer '(#f #t #f #f #t)) ⇒ 9
Function: booleans->integer bool …

(list->integer (list bool …))


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.16 srfi-69 - 基本的なハッシュテーブル

Module: srfi-69

This module is a thin adaptor on Gauche’s built-in hashtables (see section ハッシュテーブル). This is provided for the compatibility to the portable libraries; the hashtable object created by this module’s make-hash-table is the same as the one created by Gauche’s built-in, and you can pass the table to both APIs.

Here’s a summary of difference between srfi-69 and Gauche’s built-in hash table API:

The following procedures are the same as Gauche’s built-in ones. See section ハッシュテーブル, for the details.

 
hash-table?       hash-table-delete!   hash-table-exists?
hash-table-keys   hash-table-values    hash-table-fold
hash-table->alist hash-table-copy 
Function: make-hash-table :optional eq-pred hash-proc :rest args

[SRFI-69] Creates a new hashtable and returns it. This is the same name as Gauche’s built-in procedure, but the arguments are different.

The eq-pred argument is an equality predicate; it takes two arguments and returns #t if two are the same, and #f if not. When omitted, equal? is used.

The hash-proc argument is a hash function. It takes two arguments: an object to hash, and a positive integer to limit the range of the hash value. (Note that Gauche’s native hash functions takes only one argument.) When omitted, Gauche tries to choose appropriate hash function if eq-pred is known one (eq?, eqv?, equal?, string=? or string-ci=?). Otherwise we use Gauche’s hash procedure, but there’s no guarantee that it works appropriately; you should give suitable hash-proc if you pass custom eq-pred.

The returned hash table is an instance of Gauche’s native hash table. You can pass it to Gauche’s builtin procedures.

Srfi-69 allows implementation-specific arguments args to be passed to make-hash-table. At this moment, Gauche ignores them.

Function: alist->hash-table alist :optional eq-pred hash-fn :rest args

[SRFI-69] Like Gauche’s builtin alist->hash-table, but takes eq-pred and hash-fn separately, instead of a single comparator.

The alist argument is a list of pairs. The car of each pair is used for a key, and the cdr for its value.

See make-hash-table above for the description of eq-pred, hash-fn and args.

Function: hash-table-equivalence-function ht
Function: hash-table-hash-function ht

[SRFI-69] Returns equivalence function and hash function of the hashtable ht.

The hash function returned from hash-table-hash-function takes two arguments, an object to hash and bound, a positive exact integer. Note that the function returned by hash-table-hash-function may not be eq? to the one you gave to make-hash-table.

Function: hash-table-ref ht key :optional thunk

[SRFI-69] Looks up the value corresponding to key in a hash table ht. If there’s no entry for key, thunk is called without arguments. The default of thunk is to signal an error.

Function: hash-table-ref/default ht key default

[SRFI-69] Looks up the value corresponding to key in a hash table ht. This is like Gauche’s hash-table-get, but default can’t be omitted.

Function: hash-table-set! ht key val

[SRFI-69] This is the same as Gauche’s hash-table-put!.

Function: hash-table-update! ht key proc :optional thunk
Function: hash-table-update!/default ht key proc default

[SRFI-69]

Function: hash-table-size ht

[SRFI-69] Returns the number of entries in a hash table ht. The same as Gauche’s hash-table-num-entries.

Function: hash-table-walk ht proc

[SRFI-69] For each entry in a hash table ht, calls proc with two arguments, a key and its value. It’s the same as Gauche’s hash-table-for-each.

Function: hash-table-merge! ht1 ht2

[SRFI-69] Add all entries in a hash table ht2 into a hash table ht1, and returns ht1.

Function: hash obj :optional bound

[SRFI-69] Like Gauche’s hash, except this one can take bound argument; if provided, it must be a positive integer, and the return value is limited between 0 and (- bound 1), inclusive.

Function: string-hash obj :optional bound
Function: string-ci-hash obj :optional bound

[SRFI-69] These are like srfi-13’s (see section srfi-13 - 文字列ライブラリ), except these don’t take start and end argument.

Function: hash-by-identity obj :optional bound

[SRFI-69] This is Gauche’s eq-hash, except this one can take bound argument.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.17 srfi-98 - 環境変数へのアクセス

Module: srfi-98

このsrfiはシステムの環境変数にアクセスするポータブルな方法を定義しています。 Gaucheは組み込みで環境変数にアクセスする手続きを持っていますが (環境の問い合わせ参照)、 ポータブルなコードを書くにはこちらのAPIを使うのが良いでしょう。

Function: get-environment-variable name

[SRFI-98] 文字列nameで指定される名前を持つ環境変数の値を文字列で返します。 そのような環境変数が存在しなければ、#fが返されます。

この手続きはsys-getenvと等価です。

 
(get-environment-variable "PATH")
  ⇒ "/bin:/usr/sbin:/usr/bin"
Function: get-environment-variables

[SRFI-98] 全ての環境変数の名前と値の連想リストを返します。

この手続きは、省略引数を取らない時のsys-environ->alistと等価です。

 
(get-environment-variables)
  ⇒ (("PATH" . "/bin:/usr/sbin:/usr/bin")
        …)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.18 srfi-106 - 基本的なソケットインタフェース

Module: srfi-106

ポータブルなソケットインタフェースです。

Gaucheはgauche.netモジュールで包括的なネットワークAPIを提供しています (gauche.net - ネットワーキング参照)。しかしそれはGauche特有のものです。 このsrfiはソケット操作の小さなサブセットを提供し、 簡単なネットワーキングを必要とするアプリケーションをポータブルに書けるようにします。

gauche.netと同じ名前を持ち、インタフェースが異なる手続きがあることに注意してください。

このsrfiのAPIで作られるソケットオブジェクトはGaucheの<socket>オブジェクト そのものなので、gauche.netのAPIに渡したり、またその逆も可能です。

ソケットオブジェクト

Function: make-client-socket node service :optional ai-family ai-socktype ai-flags ai-protocol

[SRFI-106] Returns a socket to communicate with the node node and service. If the socket type is connection-oriented (that is, ai-socktype is *sock-stream*, which is the default), the returned socket is already connected.

Both node and service must be strings. A service name solely consists of decimal digits is interpreted as a port number.

The default value of optional arguments are as follows: *af-net* for ai-family, *sock-stream for ai-socktype, (socket-merge-flags *ai-v4mapped* *ai-addrconfig*) for ai-flags, and *ipproto-ip* for ai-protocol. See below for valid flag values.

This API differs from make-client-socket in gauche.net.

 
(make-client-socket "127.0.0.1" "80")
 ⇒ a <socket> connected to port 80 of localhost
Function: make-server-socket service :optional ai-family ai-socktype ai-protocol

[SRFI-106] Creates and resturns a server socket that binds and listens at the port specified by service, which must be a string. A service name solely consists of decimal digits is interpreted as a port number.

The default value of optional arguments are as follows: *af-net* for ai-family, *sock-stream for ai-socktype, and *ipproto-ip* for ai-protocol. See below for valid flag values.

This API differs from make-server-socket in gauche.net.

Function: socket? obj

[SRFI-106] Equivalent to (is-a? obj <socket>).

Communication

Function: socket-accept socket
Function: socket-shutdown socket how
Function: socket-input-port socket
Function: socket-output-port socket
Function: socket-close socket

[SRFI-106] Same as the procedures provided in gauche.net. See section gauche.net - ネットワーキング.

Function: socket-send socket u8vector :optional flags

[SRFI-106] Almost same as socket-send in gauche.net, except that this procedure only accepts a u8vector as the message. (The one in gauche.net can take a string as well.)

Returns the number of octets that are actually sent.

Function: socket-recv socket size :optional flags

[SRFI-106] This is like socket-recv in gauche.net, except that this procedure returns the received data in u8vector, instead of a string. If the peer has shut down the connection, this procedure returns an empty u8vector, #u8().

The size argument specifies the maximum size of the receiving data. The returned vector may be shorter if that much data is received.

Flags

The srfi provides common names for constants of typical socket flags, as well as macros that map symbolic name(s) to the flags.

Function: socket-merge-flags flag …

[SRFI-106] Merge bitwise flags. This is simply logior in Gauche.

Function: socket-purge-flags base-flag flag …

[SRFI-106] Drop the bitwise flags in base-flag that are set in flag ….

Address family

*af-inet*AF_INET
*af-inet6*AF_INET6
*af-unspec*AF_UNSPEC
Macro: address-family name

Name can be either one of symbols inet, inet6, or unspec, and the macro expands into the value of *af-inet*, *af-inet6* or *af-unspec*, respectively.

If name is other object, an error is signaled.

Socket domain

*sock-stream*SOCK_STREAM
*sock-dgram*SOCK_DGRAM
Macro: socket-domain name

Name can be either one of symbols stream or datagram, and the macro expands into the value of *sock-stream* and *sock-dgram*, respectively.

If name is other object, an error is signaled.

Address info

*ai-canonname*AI_CANONNAME
*ai-numerichost*AI_NUMERICHOST
*ai-v4mapped*AI_V4MAPPED
*ai-all*AI_ALL
*ai-addrconfig*AI_ADDRCONFIG
Macro: address-info name …

Maps combination of names canoname, numerichost, v4mapped, all and addrconfig to the combination of corresponding flags.

An error is signaled if other symbols are passed. (Note: canoname for *ai-canonname*).

Protocol

*ipproto-ip*IPPROTO_IP
*ipproto-tcp*IPPROTO_TCP
*ipproto-udp*IPPROTO_UDP
Macro: ip-protocol name

Maps one of names ip, tcp, and udp to the corresponding flag value. An error is signaled if other symbol is passed.

Message type

*msg-none*0
*msg-peek*MSG_PEEK
*msg-oob*MSG_OOB
*msg-waitall*MSG_WAITALL
Macro: message-type name …

Maps combination of names none, peek, oob and wait-all to the combination of corresponding flags.

An error is signaled if other symbols are passed. (Note: wait-all for *msg-waitall*).

Shutdown method

*shut-rd*SHUT_RD
*shut-wr*SHUT_WR
*shut-rdwr*SHUT_RDWR
Macro: shutdown-method name …

Maps combination of names read and write to the combination of corresponding flags.

An error is signaled if other symbols are passed.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.19 srfi-111 - ボックス

Module: srfi-111

This module defines the box datatype, which is a simple container that can hold one Scheme object. It can be used as a minimal data storage, or a sort of mutable indirect “pointer”.

Traditionally a pair (with ignoring its cdr) or a single-element vector has been used for this purpose; in modern Scheme you can also define a record type with one mutable field. Nevertheless, a box is very common abstraction to describe various algorithms, and having common interface to it is useful.

The srfi leaves some details to implementations. Here are our choices:

Function: box val

Returns a fresh box object that contains the value val.

Function: box? obj

Returns #t iff obj is a box object.

Function: unbox box

Returns box’s content.

Function: set-box! box val

Mutate box’s content with val. Returns unspecified value.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.20 srfi-112 - 実行環境の問い合わせ

Module: srfi-112

This srfi provides a portable way to obtain runtime information.

Function: implementation-name

[SRFI-112] Returns a string "Gauche".

Function: implementation-version

[SRFI-112] Returns a string of Gauche’s version. The same as gauche-version (see section 環境の問い合わせ).

Function: cpu-architecture

[SRFI-112] Returns a string of CPU architecture info, such as "x86_64". Same as the machine field of the return value of sys-uname (see section システムへの問い合わせ).

Function: machine-name

[SRFI-112] Returns the host name. Same as the nodename field of the return value of sys-uname. (see section システムへの問い合わせ).

Function: os-name

[SRFI-112] Returns the OS name. Same as the sysname field of the return value of sys-uname.

Function: os-version

[SRFI-112] Returns the OS version. Same as the release field of the return value of sys-uname.

Here’s an example of output:

 
gosh> (implementation-name)
"Gauche"
gosh> (implementation-version)
"0.9.5"
gosh> (cpu-architecture)
"x86_64"
gosh> (machine-name)
"scherzo"
gosh> (os-name)
"Linux"
gosh> (os-version)
"3.2.0-89-generic"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.21 srfi-113 - setとbag

Module: srfi-113

セットとバッグは、Scheme値の順序づけのないコレクションです。 セットは重複を考慮しません。既にセット中にある要素をさらに追加しても、 その要素は依然として一つだけセット中にあると認識されます。 一方、バッグは重複を数えます。既にバッグ中にひとつだけある要素と同じものを 追加すると、バッグ中のその要素は二つと数えられます。

要素が「同じ」であるかどうかの判定のため、セットとバッグは構築時に 比較器を取ります。比較器は順序手続きを持っていなくても構いません (要素の順序づけは必要ありません)が、ハッシュ手続きは持っている必要があります。 比較器について詳しくは基本的な比較器を参照してください。

Gauche独自拡張として、セットやバッグはコレクションプロトコルを実装しており、 汎用のコレクション操作が適用できます (gauche.collection - コレクションフレームワーク参照)。

 
(coerce-to <list> (set eq-comparator 'a 'b 'a 'b))
  ⇒ (a b)      ; order may differ

(coerce-to <list> (bag eq-comparator 'a 'b 'a 'b))
  ⇒ (a a b b)  ; order may differ

Constructors

Function: set comparator elt …
Function: bag comparator elt …

Creates a new set and bag from given elements elt …. Given comparator will be used to compare equality of elements.

 
(set->list (set eq-comparator 'a 'b 'a 'b))
  ⇒ (a b)

(bag->list (bag eq-comparator 'a 'b 'a 'b))
  ⇒ (a a b b)
Function: set-unfold stop? mapper successor seed comparator
Function: bag-unfold stop? mapper successor seed comparator

Procedurally creates a set or a bag. The first three arguments, stop?, mapper and successor, are all procedures that takes one argument, the current seed value. It may be easier to know their types:

 
seed      :: Seed
stop?     :: Seed -> Boolean
mapper    :: Seed -> ElementType
successor :: Seed -> Seed

The stop? procedure takes the current seed value and returns a boolean value - if it is true, iteration stops.

The mapper procedure takes the current seed value and returns an item, which is to be included in the resulting set or bag.

The successor procedure takes the current seed value and returns the next seed value.

And the seed argument gives the initial seed value.

 
(set->list (set-unfold (^s (= s 75))
                       integer->char
                       (^s (+ s 1))
                       65
                       eqv-comparator))
 ⇒ (#\D #\H #\A #\E #\I #\J #\B #\F #\C #\G)

Predicates

Function: set-contains? set obj
Function: bag-contains? bag obj

Check if obj is in the set or the bag.

Function: set-empty? set
Function: bag-empty? bag

Returns #t iff the given set or bag is empty.

Function: set-disjoint? set1 set2
Function: bag-disjoint? bag1 bag2

Returns #t iff the given arguments (sets or bags) don’t have common items. Both arguments must have the same comparator—otherwise an error is signaled.

Accessors

Function: set-member set obj default
Function: bag-member bag obj default

Returns an element in the given set or bag which is equal to obj in terms of the set’s or the bag’s comparator. If no such element is found, default will be returned.

Note that the returned object doesn’t need to be “the same” as obj in a usual sense. See the following example:

 
(let s (set string-ci-comparator "abc" def")
  (set-member s "ABC" #f))
  ⇒ "abc"
Function: set-element-comparator set
Function: bag-element-comparator bag

Returns the comparator used to compare the elements for the set or the bag.

Updaters

Function: set-adjoin set elt …
Function: bag-adjoin bag elt …

Returns a newly created set or bag that contains all the elements in the original set/bag, plus given elements. The new set/bag’s comparator is the same as the original set/bag’s one.

Function: set-replace set elt
Function: bag-replace bag elt

Returns a newly created set/bag with the same comparator with the original set/bag, and the same elements, except that the elements equal to elt (in terms of set/bag’s comparator) is replaced by elt. If the original set/bag doesn’t contain an element equal to elt, the original one is returned.

 
(let ((s (set string-ci-comparator "ABC" "def")))
  (set->list (set-replace s "abc")))
  ⇒ ("abc" "def")
Function: set-delete set elt …
Function: bag-delete bag elt …

Returns a newly created set or bag that has the same comparator and the same elements in the original set/bag, except that the item which is equal to elt.

Function: set-delete-all set elt-list
Function: bag-delete-all bag elt-list

Returns a newly created set or bag with the same comparator of the original set/bag, with the elements of the original set/bag except the ones listed in elt-list.

Function: set-adjoin! set elt …
Function: bag-adjoin! bag elt …
Function: set-replace! set elt
Function: bag-replace! bag elt
Function: set-delete! set elt …
Function: bag-delete! bag elt …
Function: set-delete-all! set elt-list
Function: bag-delete-all! bag elt-list

These are the linear update versions of their counterparts. It works just like the ones without !, except that the original set/bag may be reused to produce the result, instead of new one being allocated.

Note that it’s not guaranteed that the original set/bag is modified, so you should use the return value of them, instead of relying on the side effects.

Function: set-search! set elt failure success
Function: bag-search! bag elt failure success

Lookup-and-modify procedures. The failure and success arguments are procedures.

First, they search elt in the given set/bag. If an item that matches elt is found, the success procedure is called with three arguments, as follows:

 
(success item update remove)

The update argument is a procedure that takes two arguments, as (update new-item retval). It replaces the matching item in the set/bag with new-item, and returns retval. The remove argument is a procedure that takes one argument, as (remove retval). It removes the mathing item in the set/bag, and returns retval.

If an item that matches elt is not found, the failure procedure is called with two arguments, as follows:

 
(failure insert ignore)

The insert argument is a procedure that takes one argument, as (insert retval). It inserts elt into the set/bag, and returns retval. The ignore argument is a procedure that takes one argument, as (ignore retval). It just returns retval.

The return values of set-search! and bag-search! is the modified set/bag (which may or may not be eq? to the passed one), and the value returned by success or failure procedures.

Note that retval isn’t used in this process; it is just to provide one of the return values of set-search!/bag-search!, for the procedures passed to success or failure are expected to be tail-called.

If there are more than one item that matches elt in a bag, bag-search! only invokes success for the first item it finds. You can recurse into bag-search! in the failure procedure to visit all matching items. It is guaranteed that success and failure procedures are tail-called.

The whole set

Function: set-size set
Function: bag-size bag

Returns the number of items in the set/bag.

Function: set-find pred set failure
Function: bag-find pred bag failure

Apply pred on each item in the set/bag, and returns the first item on which pred returns true. Since sets and bags are unordered, if there are more than one items that satisfy pred, you won’t know which one will be returned.

If there’re no items that satisfy pred, a thunk failure is called and its result is returned.

Function: set-count pred set
Function: bag-count pred bag

Returns the number of items that satisfy pred in the set/bag.

Function: set-any? pred set
Function: bag-any? pred bag

Returns true iff any item in the set/bag satisfy pred.

Function: set-every? pred set
Function: bag-every? pred bag

Returns true iff every item in the set/bag satisfy pred.

Mapping and folding

Function: set-map comparator proc set
Function: bag-map comparator proc bag

Create and return a new set/bag with the comparator comparator, whose items are calculated by applying proc to each element in the original set/bag.

Function: set-for-each proc set
Function: bag-for-each proc bag

Apply proc to each element in the set/bag. The result of proc is ignored. Return value is undefined.

Function: set-fold proc seed set
Function: bag-fold proc seed bag

For each item in the set/bag, call proc with two arguments, an item and a seed value. What proc returns becomes the next seed value. The seed argument gives the initial seed value, and the last return value of proc will be the result of set-fold/bag-fold.

 
(bag-fold + 0 (bag eqv-comparator 1 1 2 2 3 3 4 4))
  ⇒ 20
Function: set-filter pred set
Function: bag-filter pred bag

Returns a newly created set/bag with the same comparator of the original set/bag, and its content consists of items from the original set/bag that satisfy pred.

 
(set->list (set-filter odd? (set eqv-comparator 1 2 3 4 5)))
  ⇒ (1 3 5)
Function: set-remove pred set
Function: bag-remove pred bag

Returns a newly created set/bag with the same comparator of the original set/bag, and its content consists of items from the original set/bag that does not satisfy pred.

 
(set->list (set-remove odd? (set eqv-comparator 1 2 3 4 5)))
  ⇒ (2 4)
Function: set-partition pred set
Function: bag-partition pred bag

Returns two sets or bags, both have the same comparator of the original set or bag. The first one consists of the items from the original set/bag that satisfy pred, and the second one consists of the items that don’t.

 
(receive (in out) (set-remove odd? (set eqv-comparator 1 2 3 4 5))
  (values (set->list in)
          (set->list out)))
  ⇒ (1 3 5) and (2 4)
Function: set-filter! pred set
Function: bag-filter! pred bag
Function: set-remove! pred set
Function: bag-remove! pred bag
Function: set-partition! pred set
Function: bag-partition! pred bag

Linear update versions of their counterparts (the procedures without !). They work like their respective counterpart, but they are allowed (but not required) to reuse the original set/bag to produce the result(s).

Note that it is not guaranteed that the original set/bag is modified, so you have to use the return value(s) instead of relying on the side effects.

Copying and conversion

Function: set-copy set
Function: bag-copy bag

Returns a copy of the set/bag.

Function: set->list set
Function: bag->list bag

Returns a list of all items in the set/bag. Since sets and bags are unordered, there’s no guarantee on the order of items.

Function: list->set comparator elt-list
Function: list->bag comparator elt-list

Creates a set or a bag with the given comparator, and the list of element. Functionally equivalent to the followings:

 
(apply set comparator elt-list)
(apply bag comparator elt-list)
Function: list->set! set elt-list
Function: list->bag! bag elt-list

Add items in elt-list to the existing set/bag, and returns the updated set/bag. The original set/bag is also modified. Functionally equivalent to the followings:

 
(apply set-adjoin! set elt-list)
(apply bag-adjoin! bag elt-list)
Function: bag->set bag
Function: set->bag set

Conversions between a bag and a set. Returns a newly created bag or set, respectively.

If bag has duplicated items, bag->set coerces them to one item.

Function: set->bag! bag set

Adds all items in set to bag, and returns bag. Both bag and set must have the same comparator.

Function: bag->alist bag

Returns a list of (item . count), where item is an item in bag, and count is the number of that item in the bag.

Function: alist->bag comparator alist

Creates a new bag with comparator, and fills it according to alist, which must be a list of (item . count).

If there’s duplicate items in alist, only fist one counts.

Subsets

Function: set=? set1 set2 …
Function: bag=? bag1 bag2 …

Returns true iff all sets/bags have exactly same items.

The comparators of the argument sets/bags are not checked, but assumed to be the same, in terms of the equality of items.

Function: set<? set1 set2 …
Function: bag<? bag1 bag2 …
Function: set>? set1 set2 …
Function: bag>? bag1 bag2 …
Function: set<=? set1 set2 …
Function: bag<=? bag1 bag2 …
Function: set>=? set1 set2 …
Function: bag>=? bag1 bag2 …

Returs true iff each preceding set/bag is a proper subset of, a proper superset of, a subset of, or a superset of the following set/bags, respectively.

Again, the comparators of the argument sets/bags are not checked, but assumed to be the same, in terms of the equality of items.

Set theory operations

Function: set-union set1 set2 …
Function: bag-union bag1 bag2 …

Returns a newly allocated set or bag which is a union of all the sets/bags.

Function: set-intersection set1 set2 …
Function: bag-intersection bag1 bag2 …

Returns a newly allocated set or bag which is an intersection of all the sets/bags.

Function: set-difference set1 set2 …
Function: bag-difference bag1 bag2 …

Returns a newly created set or bag that contains items in set1/bag1 except those are also in set2/bag2 ….

 
(sort (set->list (set-difference (set eqv-comparator 1 2 3 4 5 6 7)
                                 (set eqv-comparator 3 5 7 9 11 13)
                                 (set eqv-comparator 4 8 16 32))))
  ⇒ (1 2 6)
Function: set-xor set1 set2
Function: bag-xor bag1 bag2

Returns a newly created set or bag that consists of items that are either in set1/bag1 or set2/bag2, but not in both.

 
(sort (set->list (set-xor (set eqv-comparator 2 3 5 7 11 13 17)
                          (set eqv-comparator 3 5 7 9 11 13 15))))
  ⇒ (2 9 15 17)
Function: set-union! set1 set2 …
Function: bag-union! bag1 bag2 …
Function: set-intersection! set1 set2 …
Function: bag-intersection! bag1 bag2 …
Function: set-difference! set1 set2 …
Function: bag-difference! bag1 bag2 …
Function: set-xor! set1 set2
Function: bag-xor! bag1 bag2

Linear update versions of their corresponding procedures. Those procedures works like their !-less counterparts, except that they are allowed to, but not required to, reuse set1/bag1 to produce the result.

The caller should always use the returned set/bag instead of relying on the side effects.

Bag-specific procedures

Function: bag-sum bag1 bag2 …
Function: bag-sum! bag1 bag2 …

Returns a bag that gathers all the items in given bags, counting duplicates. The functional version bag-sum always creates new bag to return. The linear update version bag-sum! is allowed to, but not required to, modify bag1 to produce the result.

 
(sort (bag->list (bag-sum (bag eqv-comparator 1 1 2 4 5 5 6)
                          (bag eqv-comparator 3 3 5 9))))
  ⇒ (1 1 2 3 3 4 5 5 5 6 9)

Note the difference from bag-union:

 
(sort (bag->list (bag-union (bag eqv-comparator 1 1 2 4 5 5 6)
                            (bag eqv-comparator 3 3 5 9))))
  ⇒ (1 1 2 3 3 4 5 5 6 9)
Function: bag-product n bag
Function: bag-product! n bag

Returns a bag that contains every item as n-times many as the original bag. A fresh bag is created and returned by bag-product, while bag-product! may reuse bag to produce the result.

 
(sort (bag->list (bag-product 2 (bag eq-comparator 'a 'b 'r 'a))))
  ⇒ (a a a a b b r r)
Function: bag-unique-size bag

Returns the number of unique elements in bag.

 
(bag-unique-size (bag eqv-comparator 1 1 2 2 3 3 4))
 ⇒ 4
Function: bag-element-count bag elt

Returns the number of specified element elt in bag.

 
(bag-element-count (bag eqv-comparator 1 1 2 2 2 3 3) 2)
 ⇒ 3
Function: bag-for-each-unique proc bag

For each unique item in bag, calls proc with two arguments: The item, and the count of the item in the bag.

Function: bag-fold-unique proc seed bag

For each unique item in bag, calls proc with three arguments: The item, the count of the item, and the previous seed value. The seed argument provides the initial seed value; the result of proc is used for the next seed value, and the last result of proc is returned from bag-fold-unique.

 
(sort (bag-fold-unique acons '()
        (bag equal-comparator "a" "a" "b" "b" "b" "c" "d"))
      string<? car)
 ⇒ (("a" . 2) ("b" . 3) ("c" . 1) ("d" . 1))
Function: bag-increment! bag elt count
Function: bag-decrement! bag elt count

Linear update bag to increase or decrease the count of elt in it by count, which must be an exact integer. Note that the element count won’t get below zero; if a bag has two a’s, and you call (bag-decrement! bag 'a 100), you get a bag with zero a’s.

Comparators

Comparator: set-comparator
Comparator: bag-comparator

Comparators to be used to compare sets or bags. They don’t provide comparison procedure, for you cannot define a total order among sets or bags. They do provide hash functions.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.22 srfi-114 - 比較器

Module: srfi-114

This module is provided for the compatibility of code using srfi-114. The new code should use srfi-128, which is fully built-in.

The following procedures are built-in. See section 基本的な比較器, for the detailed documentation. Those are also exported from srfi-114 for the compatibility.

Predicates

comparator?,

Standard comparators

boolean-comparator, char-comparator, char-ci-comparator, string-comparator, string-ci-comparator, symbol-comparator, exact-integer-comparator, integer-comparator, rational-comparator, real-comparator, complex-comparator, number-comparator, pair-comparator, list-comparator, vector-comparator, bytevector-comparator, uvector-comparator

The default comparator

default-comparator

Wrapped equality predicates

eq-comparator, eqv-comparator, equal-comparator

Accessors

comparator-equality-predicate, comparator-comparison-procedure, comparator-hash-function

Primitive applicators

comparator-test-type, comparator-check-type, comparator-compare, comparator-hash

Comparison predicates

=?, <?, <=?, >?, >=?

Basic comparator interface

Function: make-comparator type-test equal compare hash :optional name

[SRFI-114+] This is SRFI-114 style comparator constructor. The optional name argument is Gauche’s extension.

This is the same as built-in make-comparator/compare. See section 基本的な比較器, for the details.

Do not confuse this with built-in (SRFI-128) make-comparator; if you (use srfi-114), this one shadows the built-in one.

Note that a comparator works for both SRFI-114 and SRFI-128 procedures, regardless of how it is constructed.

Function: comparator-comparison-procedure? c
Function: comparator-hash-function? c

[SRFI-114] Returns true iff a comparator c can be used to order objects or to hash them, respectively. These are aliases of built-in comparator-ordered? and comparator-hashable?.

Function: comparator-type-test-procedure c

[SRFI-114] Returns type test predicate of a comparator c. This is an alias of bulit-in comparator-type-test-predicate.

Function: comparator-equal? c a b

[SRFI-114] Checks equality of a and b using the equality predicate of a comparator c. This can be also written in =?, which is bulit-in (see section 比較器にまつわる述語とアクセサ).

 
(=? c a b)

Auxiliary comparator constructors

Function: make-inexact-real-comparator epsilon rounding nan-handling

[SRFI-114] Returns a comparator for inexact real numbers, taking into account of errors and NaNs.

The basic idea is that we compare two finite real numbers after rounding them to epsilon interval, which must be a nonnegative real number. (Note that it’s not to compare two numbers “close enough”, as often being done to compare inexact numbers. “Close enough” scheme won’t be transitive.)

The rounding mode is specified by the rounding argument. It can be either one of the symbols round, ceiling, floor or truncate, or a procedure that takes two arguments, a real number and an epsilon, and returns the rounded result of the first argument according to the given epsilon.

The nan-handling argument determines how to handle the case to compare NaN and non-NaN numbers. (If both are NaNs, this comparator regards them as equal). It can be either one of the followings:

min

If it’s a symbol min, NaN is compared as smaller than all other real numbers, even than -inf.0.

max

If it’s a symbol min, NaN is compared as greater than all other real numbers, even than +inf.0.

error

If it’s a symbol error, an error is signaled.

a procedure taking one argument

The procedure is invoked with the real number which is not NaN. If it ever returns, it must return eithr 1, 0 or -1, for it’s used as the result of the comparison procedure of the comparator. However, since the procedure doesn’t know which argument is non-NaN, it’s hard to have consistent semantics; the best bet is to throw a custom error.

 
(define c (make-inexact-real-comparator 0.1 'round 'error))

(comparator-compare c 0.112 0.098) ⇒ 0
(comparator-compare c 0.131 0.172) ⇒ -1

Note: Rounding to the nearest epsilon interval would involve scaling inexact numbers, and that may reveal small difference between the actual number and its notation. For example, an inexact real number denoted as 0.15 is actually slightly smaller than 15/100, and rounding with epsilon 0.1 would result 0.1, not 0.2.

Function: make-car-comparator cmpr
Function: make-cdr-comparator cmpr

[SRFI-114] ペアを受け取り、そのcarのみまたはcdrのみをcmprで 比較するような比較器を返します。

make-key-comparatorを使えばこれらは以下の通りに書けます。 (make-key-comparatorについては比較器を組み合わせるを参照)。

 
(define (make-car-comparator cmpr)
  (make-key-comparator cmpr pair? car))

(define (make-cdr-comparator cmpr)
  (make-key-comparator cmpr pair? cdr))
Function: make-list-comparator element-comparator
Function: make-vector-comparator element-comparator
Function: make-bytevector-comparator element-comparator

[SRFI-114] Returns a new comparator that compares lists, vectors and bytevectors element-wise using element-comparator, respectively. These are more general versions of list-comparator, vector-comparator and bytevector-comparator, which use default-comparator as element-comparator.

For a list comparator, it is an error to pass improper lists.

Note that comparing sequences of different lenghts is slightly different between lists and vector/bytevectors. List comparator uses “dictionary” order, so (1 3) comes after (1 2 3), assuming elements are compared numerically. For vectors and bytevectors, shorter one always precedes the other, so #(1 3) comes before #(1 2 3).

Function: make-listwise-comparator type-test element-comparator empty? head tail

[SRFI-114] More general version of make-list-comparator. Returns a comparator that compares structures which can be traversed using three procedures, empty?, head and tail. Each of those procedure receives a structure to be compared, and empty? must return #t iff the structure is empty, head must return the first element in the structure, and tail must return the same type of structure containing all the elements but the head. The type-test predicate checks if the arguments passed to the comparator to be a suitable structure.

That is, make-list-comparator can be written in make-listwise-comparator as follows.

 
(make-list-compartator element-comparator)
  ≡
  (make-listwise-comparator list? element-compartor null? car cdr)

This can be used to compare list-like structures. For example, the following call returns a comparator that compares elements of two lazy streams (see section util.stream - ストリームライブラリ).

 
(make-listwise-comparator stream?
                          element-comparator
                          stream-null?
                          stream-car
                          stream-cdr)
Function: make-vectorwise-comparator type-test element-comparator length ref

[SRFI-114] More general version of make-vector-comparator. Returns a comparator that compares structures which can be traversed using two procedures, length and ref. The length procedure must return the number of elements in the structure. The ref procedure receives a structure and a nonnegative exact integer index k, and must return k-th element of the structure.

That is, the following equivalence holds:

 
(make-vector-comparator element-comparator)
  ≡
  (make-vectorwise-comparator vector? element-comparator
                              vector-length vector-ref)

(make-bytevector-comparator element-comparator)
  ≡
  (make-vectorwise-comparator u8vector? element-comparator
                              u8vector-length u8vector-ref)
Function: make-pair-comparator car-comparator cdr-comparator

[SRFI-114] Creates a comparator that compares pairs, with their cars by car-comparator and their cdrs by cdr-comparator.

Function: make-improper-list-comparator element-comparator

[SRFI-114] This may be understood as recursive pair comparator; if objects to be compared are pairs, we recurse their cars then their cdrs. If objects to be compared are not pairs, we use element-comparator to compare them.

Function: make-selecting-comparator comparator1 comparator2 …

[SRFI-114] This creates a comparator that works any one of the given comparators; the objects to be compared are type-tested with each of the comparators in order, and the first comparator that accepts all objects will be used.

Function: make-refining-comparator comparator1 comparator2 …

[SRFI-114] This is similar to make-selecting-comparator, except that if the first comparator that accepts given objects to compare finds they are equal (or 0 by the comparison procedure), it tries other comparators down the list, if any.

Function: make-reverse-comparator comparator

[SRFI-114] Returns a comparator that just reverses the comparison order of comparator.

Function: make-debug-comparator comparator

[SRFI-114]

Comparison procedure constructors

Function: make-comparison< lt-pred
Function: make-comparison> gt-pred
Function: make-comparison<= le-pred
Function: make-comparison>= ge-pred
Function: make-comparison=/< eq-pred lt-pred
Function: make-comparison=/> eq-pred gt-pred

[SRFI-114] Utility procedures to create a comparison procedure (the one returns -1, 0, or 1) from the given predicate. For example, make-comparison< can be defined as follows:

 
(define (make-comparison< pred)
  (^[a b] (cond [(pred a b) -1]
                [(pred b a) 1]
                [else 0])))

Comparison syntax

Macro: if3 expr less equal greater

[SRFI-114] Three-way if: Evaluates expr, and then evaluates either one of less, equal, or greater, depending on the value of expr is either less than zero, equal to zero, or greater than zero, respectively.

Macro: if=? expr consequent :optional alternate
Macro: if<? expr consequent :optional alternate
Macro: if>? expr consequent :optional alternate
Macro: if<=? expr consequent :optional alternate
Macro: if>=? expr consequent :optional alternate
Macro: if-not=? expr consequent :optional alternate

[SRFI-114] Conditional evaluation according to comparison expression expr; that is, ifOP? evaluates consequent if (OP expr 0) is true, otherwise it evaluates alternate when provided.

 
(if<? (compare 10 20) 'yes)      ⇒ yes
(if>=? (compare 10 20) 'yes 'no) ⇒ no

Comparison predicate constructors

Function: make=? comparator
Function: make<? comparator
Function: make>? comparator
Function: make<=? comparator
Function: make>=? comparator

[SRFI-114]

 
((make=? comparator) obj1 obj2 obj3 …)
  ≡ (=? comparator obj1 obj2 obj3 …)

Interval comparison predicates

Function: in-open-interval? [comparator] obj1 obj2 obj3
Function: in-closed-interval? [comparator] obj1 obj2 obj3
Function: in-open-closed-interval? [comparator] obj1 obj2 obj3
Function: in-closed-open-interval? [comparator] obj1 obj2 obj3

[SRFI-114] Check if obj1, obj2 and obj3 has the following relationships:

 
(and (op1 obj1 obj2) (op2 obj2 obj3))

Where each of op1 and op2 can be (make<? comparator) (if that end is open), or (make<=? comparator) (if that end is closed).

When comparator is omitted, the default comparator is used.

 
(use srfi-42)
(list-ec (: x 0 5) (list x (in-closed-open-interval? 1 x 3)))
  ⇒ ((0 #f) (1 #t) (2 #t) (3 #f) (4 #f))

Min/max comparison procedures

Function: comparator-min comparator obj1 obj2 …
Function: comparator-max comparator obj1 obj2 …

[SRFI-114] Find the object in obj1 obj2 … that is minimum or maximum compared by comparator.

 
(comparator-min list-comparator '(a c b) '(a d) '(a c))
  ⇒ (a c)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.23 srfi-117 - リストを元にしたキュー

Module: srfi-117

リストを元にしたキューのライブラリです。 Gaucheはdata.queueモジュールでキューをサポートしています (スレッドセーフなキューも含まれています。詳しくはdata.queue - キューを参照。) このモジュールは、主としてポータブルなコードのために、 data.queue<queue>の上に実装されています。

このモジュールでいうリストキューは<queue>のインスタンスそのものなので、 make-queueで作ったキューをsrfi-117のAPIに渡したり、 make-list-queueで作ったキューをGaucheのキューAPIに渡すこともできます。

註: このsrfiのAPIには、性能のために、キューが使う内部のペアをそのまま返すことを要求されている ものがあります。これらの内部ペアはキューの操作によって破壊的に変更される可能性があります。 ユーザ側でも変更した場合、その後のキューの動作は保証されません。

Function: make-list-queue lis :optional last

[SRFI-117] リストlisの内容を初期値として持つリストキューを作って返します。 Gaucheでは、リストキューは<queue>のインスタンスです(data.queue - キュー参照)。

lisの実体は作成されたキューの所有物となります。 呼び出し側は、この関数を呼んだ後でlisを変更してはいけません。 また、作成されたキューの操作によってlisの内容は変更されるでしょう。

省略可能なlast引数は、もし与えられる場合は、lisの最後のペアで なければなりません。この引数が渡された場合、make-list-queueは自分で リストの末尾のペアを見つけるかわりに、呼び出し側を信頼してlastを最後の ペアとして保持します。

Function: list-queue elt …

[SRFI-117] elt …を初期内容とするリストキューを作成して返します。 Gaucheでは、リストキューは<queue>のインスタンスです(data.queue - キュー参照)。

Function: list-queue-copy queue

[SRFI-117] リストキューqueueのコピーを返します。

Function: list-queue-unfold p f g seed :optional queue

[SRFI-117] リストキューqueueの前に、(unfold p f g seed)で生成される 要素を付け足して、queueを返します。queueが省略された場合は 新たに作成したキューを使います。

 
(list-queue-unfold (pa$ = 5) ; p
                   (pa$ * 2) ; f
                   (pa$ + 1) ; g
                   0         ; seed
                   (list-queue 'x 'y 'z))
 ⇒ a queue containing (0 2 4 6 8 x y z)
Function: list-queue-unfold-right p f g seed :optional queue

[SRFI-117] queueの後ろに、(unfold-right p f g seed)で生成される 要素を付け足して、queueを返します。queueが省略された場合は 新たに作成したキューを使います。

 
(list-queue-unfold-right (pa$ = 5) ; p
                         (pa$ * 2) ; f
                         (pa$ + 1) ; g
                         0         ; seed
                         (list-queue 'x 'y 'z))
 ⇒ a queue containing (x y z 8 6 4 2 0)
Function: list-queue? obj

[SRFI-117] queueがリストキューなら#tを、そうでなければ#fを返します。 Gaucheではdata.queueモジュールのqueue?と同じです。

Function: list-queue-empty? queue

[SRFI-117] queueが空なら#tを、そうでなければ#fを返します。 data.queuequeue-empty?と同じです。

Function: list-queue-front queue

[SRFI-117] queueの先頭の要素を返します。queueが空の場合はエラーを報告します。 data.queuequeue-frontと同じです。

Function: list-queue-back queue

[SRFI-117] queueの末尾の要素を返します。queueが空の場合はエラーを報告します。 data.queuequeue-rearと同じです。

Function: list-queue-list queue

[SRFI-117] queueが内部的に保持している要素のリストを返します。 返されたリストは、queueが操作されれば破壊的に変更される可能性があり、 また返されたリストを破壊的に変更した場合はqueueの一貫性が失われます。 この手続きの主な目的は、他のキュー操作を効率よく実装することです。

単にキューの中身にオーバーヘッド無くアクセスしたい場合は、 list-queue-remove-all!が使えないかどうか検討してください。 そちらはキューの中身のリストを直接返すと同時にキュー自体を空にするので、安全です。

Function: list-queue-fist-last queue

queueの内部で要素を保持しているリストの先頭と末尾のペアを返します。 キューが空の場合は、二つの空リストを返します。

この手続きも、list-queue-listと同じく、内部のリストを直接返すため、 返されたリストは、queueが操作されれば破壊的に変更される可能性があり、 また返されたリストを破壊的に変更した場合はqueueの一貫性が失われます。 この手続きの主な目的は、他のキュー操作を効率よく実装することであり、 一般的な用途に使うべきではありません。

Function: list-queue-add-front! queue elt

[SRFI-117] eltqueueの先頭に追加します。 data.queue(queue-push! queue elt)と同じです。

Function: list-queue-add-back! queue elt

[SRFI-117] eltqueueの末尾に追加します。 data.queue(enqueue! queue elt)と同じです。

Function: list-queue-remove-front! queue

[SRFI-117] queueの先頭から要素をひとつ取り除き、その取り除かれた要素を返します。 queueが空ならエラーを報告します。 data.queue(dequeue! queue elt)と同じです。

Function: list-queue-remove-back! queue

[SRFI-117] queueの末尾から要素をひとつ取り除き、その取り除かれた要素を返します。 queueが空ならエラーを報告します。 この手続きは、キューの持つ要素数nに対してO(n)の時間がかかります。 もしこの操作を頻繁に必要とするなら、デック(deque, 両端キュー)を使うことを 検討すべきでしょう。(data.ideque - 変更不可な両端キューおよび data.ring-buffer - リングバッファ参照。)

Function: list-queue-remove-all! queue

[SRFI-117] queueを空にして、入っていた全ての要素をリストで返します。 リストはコピーされません。つまりO(1)の操作です。 可能なら、list-queue-listよりはこちらを使う方が安全です。 Gaucheでは、これはdata.queuedeque-all!と同じです。

Function: list-queue-set-list! queue lis :optional last

[SRFI-117] queueを変更して、lisの内容がキューの内容になるようにします。 queueの元の内容は捨てられます。省略可能なlast引数が渡される場合、 それはlisの最後のペアでなければなりません。手続きは呼び出し元を信頼して、 lisをスキャンせずにlastを最後のペアと考えることで O(1)操作を実現しています。

この手続きを呼び出した後では、lisqueueに所有され、破壊的変更を 受けます。この後でlisの内容をあてにしたり変更したりしてはいけません。

Function: list-queue-append queue …

[SRFI-117] 与えられたqueueを全部つないだ要素を持つリストキューを 新たに作成して返します。引数のキューは変更されません。 これは要素の総数nについてO(n)の操作となります。

Function: list-queue-append! queue …

[SRFI-117] 与えられたqueueの要素を全てつないだものを要素とするリストキューを 返します。操作によって、queueの内容は破壊されることがあり、 以降それらのキューを使ってはなりません (Gaucheでは、事故を避けるために、全てのqueueを空にしています)。 結果として返されるキューが、引数のどれともeq?になる必要はない、 ということに注意してください。 これはキューの総数mに対してO(m)の操作です(要素の総数ではなく)。

Function: list-queue-concatenate queues

[SRFI-117] (apply list-queue-append queues).

Function: list-queue-map proc queue

[SRFI-117] procqueueの各要素に適用して得られた結果を要素とする 新たなリストキューを返します。

Function: list-queue-map! proc queue

[SRFI-117] queueの各要素を、それにprocを適用して得られた結果に 置き替えます。

Function: list-queue-for-each proc queue

[SRFI-117] procqueueの各要素に適用します。結果は捨てられます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.24 srfi-118 - 簡単な可変長文字列

Module: srfi-118

This SRFI defines two string mutating operations that can change the length of the string: string-append! and string-replace!.

Note that, in Gauche, the body of strings is immutable; when you mutate a string, Gauche creates a fresh new string body and just switch a pointer in the original string to point the new string body. So it is not a problem to implement this SRFI in Gauche, but it also means you won’t get any performance benefit by using these operations. Using immutable counterparts (string-append and string-replace) gives you the same performance. (Be aware that the interface is slightly different from the immutable versions.)

We provide this module only for the compatibility. Gauche-specific programs should stay away from this module. Particulary, avoid code like the example in SRFI-118 document (build a string by append!-ing small chunks at a time)—they’re quadratic on Gauche.

Function: string-append! string values …

The string argument must be a mutable string. Modify string by appending values, each of which is either a character or a string.

 
(rlet1 a (string-copy "abc")
  (string-append! a #\X "YZ"))
 ⇒ "abcXYZ"
Function: string-replace! dst dst-start dst-end src :optional src-start src-end

The dst argument must be a mutable string. Replace dst between dst-start (inclusive) and dst-end (exclusive) with a string src. The optional arguments src-start and src-end limits the region of src to be used.

Be aware that the order of arguments differ from SRFI-13’s string-replace (see section 他の文字列操作); string-replace! resembles to string-copy! (also in SRFI-13), rather than string-replace.

 
(rlet1 a (string-copy "abc")
  (string-replace! a 1 2 "XYZ"))
 ⇒ "aXYZc"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

11.25 srfi-133 - ベクタライブラリ

Module: srfi-133

This module adds rich set of vector operations to the built-in / R7RS vector procedures.

The following procedures are built-in. See section ベクタ, for the description. We only explain the procedures that are not built-in.

 
make-vector          vector               vector?
vector-ref           vector-set!          vector-length
vector-fill!         vector-copy          vector-copy!
vector-append        vector->list         list->vector
reverse-list->vector vector->string       string->vector
vector-map           vector-map!          vector-for-each

This SRFI effectively supesedes srfi-43 (see section srfi-43 - ベクタライブラリ(旧式)). Note that the interface of following procedures in srfi-43 are changed for the consistency:

 
vector-map           vector-map!          vector-for-each
vector-fold          vector-fold-right    vector-count

Some of the functionalities of srfi-43 version is supported by built-in procedures (e.g. Built-in vector-map-with-index is the same as srfi-43’s vector-map). So there’s little point for new code to use srfi-43.

ベクタ構成子

Function: vector-unfold f length seed …

[SRFI-133] Creates a vector of length length, filling elements left to right by calling f repeatedly.

The procedure f must take as many arguments as one plus number of seed values, and must return the same number of values. The first argument is the index. The first return value is used for the element of the result vector, and the rest of return values are passed to the next call of f.

 
(vector-unfold (^[i x] (values (cons i x) (* x 2))) 8 1)
 ⇒ #((0 . 1) (1 . 2) (2 . 4) (3 . 8)
    (4 . 16) (5 . 32) (6 . 64) (7 . 128))
Function: vector-unfold-right f length seed …

[SRFI-133] Creates a vector of length length, filling elements right to left by calling f repeatedly.

The procedure f must take as many arguments as one plus number of seed values, and must return the same number of values. The first argument is the index. The first return value is used for the element of the result vector, and the rest of return values are passed to the next call of f.

 
(vector-unfold-right (^[i x] (values (cons i x) (* x 2))) 8 1)
 ⇒ #((0 . 128) (1 . 64) (2 . 32) (3 . 16)
    (4 . 8) (5 . 4) (6 . 2) (7 . 1))
Function: vector-reverse-copy vec :optional start end

[SRFI-133] Copies the vector vec with reversing its elements. Optional start and end arguments can limit the range of the input.

 
(vector-reverse-copy '#(a b c d e) 1 4)
 ⇒ #(d c b)
Function: vector-concatenate list-of-vectors

[SRFI-133] Same as (apply vector-append list-of-vectors).

Function: vector-append-subvectors spec …

[SRFI-133] The number of arguments must be multiple of 3. The argument list must be in the following format, where each vecN is a vector, and startN and endN are nonnegative integers:

 
vec1 start1 end1 vec2 start2 end2 …

This procedure creates a new vector by concatenating subvectors specified by each triplet. That is, it works as if it’s the following code, except it avoids copying each subvector:

 
(vector-append (vector-copy vec1 start1 end1)
               (vector-copy vec2 start2 end2)
               …)

Here’s an example:

 
(vector-append-subvectors '#(a b c d e) 0 3
                          '#(f g h i j) 2 5)
  ⇒ #(a b c h i j)

ベクタ上の述語

Function: vector-empty? vec

[SRFI-133] Returns #t if vec’s length is zero, and #f if vec’s length is more than zero. Signals an error if vec is not a vector.

Function: vector= elt= vec …

[SRFI-133] Compares vecs element-wise, using given predicate elt=. Returns #t iff lengths of all the vectors are the same, and every corresponding elements are equal by elt=. Elt= is always called with two arguments and must return #t iff two are the same.

ベクタ上の繰り返し

Function: vector-fold kons knil vec1 vec2 …

[SRFI-133] Kons is a procedure that takes n+1 arguments, where n is the number of given vectors. For each element of the given vectors, kons is called as (kons seed e_1i e_2i …), where and e_ni is the i-th element of the vector n. If the lengths of the vectors differ, iteration stops when the shortest vector is exhausted.

The initial value of seed is knil, and the return value from kons is used as the next seed value. The last return value of kons is returned from vector-fold.

The iteration is strictly left to right.

Note that the seed value precedes elements, which is opposite to fold (see section コレクションに対するマッピング). It’s an unfortunate historical glitch; vector-fold-left would be more consistent name.

 
(vector-fold (^[a b] (cons b a)) '() '#(a b c d))
  ⇒ (d c b a)
Function: vector-fold-right kons knil vec1 vec2 …

[SRFI-133] Like vector-fold, but elements in the vec1 vec2 … are visited from right to left.

Unlike fold-right (see section シーケンス上のマップ), the procedure kons takes the accumulated value in the first argument.

 
(vector-fold-right (^[a b] (cons b a)) '() '#(a b c d))
  ⇒ (a b c d)
Function: vector-count pred vec1 vec2 …

[SRFI-133] Applies pred on each elements in argument vectors (if N vectors are given, pred takes N arguments, the first being i-th element of vec1, the second being i-th element of vec2, etc.) Then returns the number of times pred returned true value. The order pred applied to each element is unspecified.

 
(vector-count odd? '#(0 1 2 3 4)
  ⇒ 2

(vector-count < '#(7 3 9 1 5) '#(6 8 2 3 8 8))
  ⇒ 3
Function: vector-cumulate f seed vec

[SRFI-133] Returns a fresh vector with the same size of vec, with the elements calculated as follows:

The first element of result vector is a result of procedure f called with seed and the first element of vec.

The i-th element of result vector is a result of procedure f called with i-1-th element of result vector and i-th element of vec.

 
(vector-cumulate string-append "z" '#("a" "b" "c"))
  ⇒ #("za" "zab" "zabc")

ベクタ上の検索

Function: vector-index pred vec1 vec2 …
Function: vector-index-right pred vec1 vec2 …

[SRFI-133] Returns the index of the first or the last elements in vec1 vec2 … that satisfy pred, respectively. Returns #f if no elements satisfy pred. In vector-index, comparison ends at the end of the shortest vector. For vector-index-right, all the vectors must have the same length.

Function: vector-skip pred vec1 vec2 …
Function: vector-skip-right pred vec1 vec2 …

[SRFI-133] Like vector-index and vector-index-right, except that the result of pred is negated. That is, returns the index of the first or the last elements that don’t satisfy pred.

Function: vector-binary-search vec value cmp :optional start end

[SRFI-133+] Look for value in a vector vec, and returns its index if it is found, or #f if it is not found. Comparison of value and an element in vec is done by a procedure cmp, which takes two arguments, and should return a negative integer if the first argument is less than the second, 0 if they are the same, and a positive integer if the first is greater than the second.

Elements in vec must be ordered from smaller to greater w.r.t. cmp. Using that fact, this procedure performs binary search instead of linear search.

The optional arguments start and end are an extention to SRFI-133, and can be used to limit the range of the search in start-th element (inclusive) to end-th element (exclusive).

Function: vector-any pred vec1 vec2 …

[SRFI-133] Applies pred on each corresponding elements of vec1 vec2 … left to right, and as soon as pred returns non-#f value, the procedure stops iteration and returns the value.

If no elements that satisfy pred are found, it returns #f.

Vectors can have different lengths. Iteration stops at the end of the shortest.

Function: vector-every pred vec1 vec2 …

[SRFI-133] Applies pred on each corresponding elements of vec1 vec2 … left to right. If all the elements (when the lengths of vectors differ, the first N elements where N is the length of the shortest) satisfy pred, returns the last result of pred. Otherwise returns #f.

Function: vector-partition pred vec

[SRFI-133]

ベクタの変更子

Function: vector-swap! vec i j

[SRFI-133] Swaps vector vec’s i-th and j-th elements.

Function: vector-reverse! vec :optional start end

[SRFI-133] Reverse the elements of vec. Returns an undefined value. Optional start and end arguments can limit the range of operation.

 
(rlet1 v (vector 'a 'b 'c 'd 'e)
  (vector-reverse! v 0 4))
  ⇒ #(d c b a e)
Function: vector-reverse-copy! target tstart source :optional sstart send

[SRFI-133] Like vector-copy!, but reverses the order of elements from start.

 
(rlet1 v (vector 'a 'b 'c 'd 'e)
  (vector-reverse-copy! v 2 '#(1 2)))
  ⇒ #(a b 2 1 e)

It is ok to pass the same vector to target and source; it always works even if the regions of source and destination are overlapping.

Function: vector-unfold! f rvec start end seeds …
Function: vector-unfold-right! f rvec start end seeds …

[SRFI-133]

ベクタの変換

Function: reverse-vector->list vec :optional start end

[SRFI-133] Same as (reverse (vector->list vec start end)), but more efficient.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12. ライブラリモジュール - ユーティリティ


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.1 binary.io - バイナリI/O

Module: binary.io

このモジュールは数値データを読み書きするバイナリ入出力用の基本手続きを提 供します。各データはポートあるいはユニフォームベクタ (gauche.uvector - ユニフォームベクタ参照)から読み込み、あるいはそれらへ書き出すことが できます。 構造をもつバイナリデータの入出力については、便利な packユーティリティがこのモジュールを使って実装されています (binary.pack - バイナリデータのパックを参照してください)。 スピードあるいはエンディアンの柔軟な制御などをやりたいときにこのモジュー ルが使えます。

バイナリのブロック入出力についてはgauche.uvector - ユニフォームベクタを参照してくだ さい。

エンディアン

このモジュールのほとんどの手続はオプション引数としてendian引数を とります。これはbig-endianlittle-endianarm-little-endianのいずれかのシンボルでなければなりません。 エンディアン引数が省略された場合には、組込みパラメータdefault-endian の現在値が使われます (エンディアン参照)。 (read-u8の ような 8ビット入出力手続ではエンディアン引数は意味がありませんが、 一貫性のため受け入れるようにしてあります。)

ポートを用いたI/O

Function: read-u8 :optional port endian
Function: read-u16 :optional port endian
Function: read-u32 :optional port endian
Function: read-u64 :optional port endian

それぞれ指定したエンディアンでportから8、16、32、64ビット符号無 整数を読み込みます。portが省略された場合は、現在の入力ポートが使 われます。portが整数の読み込みが完了する前にEOFに到達してしまっ た場合にはEOFが返ります。

Function: read-s8 :optional port endian
Function: read-s16 :optional port endian
Function: read-s32 :optional port endian
Function: read-s64 :optional port endian

それぞれ指定したエンディアンでportから8、16、32、64ビット、2の補 数符号付き整数を読み込みます。portが省略された場合は、現在の入力 ポートが使われます。portが整数の読み込みが完了する前にEOFに到 達してしまった場合にはEOFが返ります。

Function: read-uint size :optional port endian
Function: read-sint size :optional port endian

さらに柔軟性が高く、sizeオクテットの符号無しあるいは符号付き整数 をportから指定のエンディアンで読み込みます。 portが整数の読み込みが完了する前にEOFに到 達してしまった場合にはEOFが返ります。

Function: read-ber-integer :optional port

X.209のBER圧縮形式整数を読み込みます。BER圧縮形式整数は128進符号無し整 数です。最上位桁が最初にきます。最下位桁のバイトをのぞき、すべてのバイ トの最上位ビットはオンになっています。

Function: write-u8 val :optional port endian
Function: write-u16 val :optional port endian
Function: write-u32 val :optional port endian
Function: write-u64 val :optional port endian

それぞれ指定したエンディアンでportへ 8、16、32、64ビット、符号無 し整数として表現されている非負整数valを書き出します。 valはそれぞれ指定したビット数の範囲で表現できる値でなければなり ません。portが省略された場合は、現在の出力ポートが使われます。

Function: write-s8 val :optional port endian
Function: write-s16 val :optional port endian
Function: write-s32 val :optional port endian
Function: write-s64 val :optional port endian

それぞれ指定したエンディアンでportへ 8、16、32、64ビット、2の補 数符号付き整数として表現されている整数valを書き出します。 valはそれぞれ指定したビット数の範囲で表現できる値でなければなり ません。portが省略された場合は、現在の出力ポートが使われます。

Function: write-uint size val :optional port endian
Function: write-sint size val :optional port endian

さらに柔軟性が高く、sizeオクテットの符号無しあるいは符号付き整数 valportへ、指定のエンディアンで書き出します。 portが省略された場合は、現在の出力ポートが使われます。

Function: write-ber-integer val :optional port

BER圧縮形式の非負整数valportへ書き出します。 BER圧縮形式については前述のread-ber-integerを見てください。

Function: read-f16 :optional port endian
Function: read-f32 :optional port endian
Function: read-f64 :optional port endian

それぞれ、16、32、64ビットの浮動小数点数を読み込みます。 32ビットはIEEE754単精度、64ビットは倍精度の浮動小数点数です。 16ビット浮動小数点数は、1ビットの符号、5ビットの指数、10ビットの仮数からなる、 HDRイメージフォーマット等に使われている形式です。

portが省略された場合には、 現在の入力ポートが使われます。portが数値の読み込みが完了する前に EOFに到達してしまった場合にはEOFが返ります。

Function: write-f16 val :optional port endian
Function: write-f32 val :optional port endian
Function: write-f64 val :optional port endian

実数値valを、それぞれ16、32、64ビットの浮動小数点数として 書き出します。 portが省略された場合には、現在の出力ポートが使われます。

ユニフォームベクタを用いたI/O

以下のルーチンでは、引数 uv は任意の型のユニフォームベクタをとり えます。u8vector ではない場合には (uvector-alias <u8vector> uv) が呼ばれたのと同様の扱いになります。すなわち、 uvectorの内容を保持するメモリイメージから直接読み込まれます。pos 引数は当該のメモリ領域の最初からのバイト位置を指定するのに使います(こ れはユニフォームベクタの要素のサイズにかかわらず、つねにバイト位置です)。

Function: get-u8 uv pos :optional endian
Function: get-u16 uv pos :optional endian
Function: get-u32 uv pos :optional endian
Function: get-u64 uv pos :optional endian
Function: get-s8 uv pos :optional endian
Function: get-s16 uv pos :optional endian
Function: get-s32 uv pos :optional endian
Function: get-s64 uv pos :optional endian
Function: get-f16 uv pos :optional endian
Function: get-f32 uv pos :optional endian
Function: get-f64 uv pos :optional endian

ユニフォームベクタ uv のバイト位置 pos から指定したフォー マットで数値を読み込み、その数値を返します。指定した位置が当該ユニフォー ムベクタの内容のある範囲外を参照するものであった場合、エラーを示すシグ ナルがあがります。

Function: get-u16be uv pos
Function: get-u16le uv pos
Function: get-u32be uv pos
Function: get-u32le uv pos
Function: get-u64be uv pos
Function: get-u64le uv pos
Function: get-s16be uv pos
Function: get-s16le uv pos
Function: get-s32be uv pos
Function: get-s32le uv pos
Function: get-s64be uv pos
Function: get-s64le uv pos
Function: get-f16be uv pos
Function: get-f16le uv pos
Function: get-f32be uv pos
Function: get-f32le uv pos
Function: get-f64be uv pos
Function: get-f64le uv pos

これらは、get-* 手続のエンディアン(ビッグエンディアン be あるいはリトルエンディアン le)を指定した版です。スピードを要求 されるコードではオプション引数処理のオーバーヘッドを避けるためこちらを 使うのがいいでしょう。

Function: put-u8! uv pos val :optional endian
Function: put-u16! uv pos val :optional endian
Function: put-u32! uv pos val :optional endian
Function: put-u64! uv pos val :optional endian
Function: put-s8! uv pos val :optional endian
Function: put-s16! uv pos val :optional endian
Function: put-s32! uv pos val :optional endian
Function: put-s64! uv pos val :optional endian
Function: put-f16! uv pos val :optional endian
Function: put-f32! uv pos val :optional endian
Function: put-f64! uv pos val :optional endian

数値 val をユニフォームベクタ uv のバイト位置 pos か ら指定されたフォーマットで書き出します。指定した位置が当該ユニフォーム ベクタの内容のある範囲外を参照するものであった場合、エラーを示すシグナ ルがあがります。

Function: put-u16be! uv pos val
Function: put-u16le! uv pos val
Function: put-u32be! uv pos val
Function: put-u32le! uv pos val
Function: put-u64be! uv pos val
Function: put-u64le! uv pos val
Function: put-s16be! uv pos val
Function: put-s16le! uv pos val
Function: put-s32be! uv pos val
Function: put-s32le! uv pos val
Function: put-s64be! uv pos val
Function: put-s64le! uv pos val
Function: put-f16be! uv pos val
Function: put-f16le! uv pos val
Function: put-f32be! uv pos val
Function: put-f32le! uv pos val
Function: put-f64be! uv pos val
Function: put-f64le! uv pos val

これらは、put-* 手続のエンディアン(ビッグエンディアン be あるいはリトルエンディアン le)を指定した版です。スピードを要求 されるコードではオプション引数処理のオーバーヘッドを避けるためこちらを 使うのがいいでしょう。

互換性への注

read-u8 などは read-binary-uint8 と呼ばれていたもので、 read-f32read-f64 はそれぞれ read-binary-floatread-binary-double と呼ばれていたものです。 これらの古い名前は後方互換のためいまのところサポートされていますが、使 用については非推奨とします。この変更は名前の短縮とユニフォームベクタと の一貫性維持のために行われました。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.2 binary.pack - バイナリデータのパック

Module: binary.pack

このモジュールは、バイナリデータをテンプレートを使って パック/アンパック(読み書き)するためのインタフェースを提供します。 この機能は、その多くをPerlのpack/unpack関数から着想し、他の言語での 同じような機能と比較しながら、しかし、データベースライクな処理に 便利なように、より一般的でより効率が良くなるように実装しました。 これを受けて、最も大きな相違点は、全てのパック可能な値はアンパック 可能で(逆も同様)、パック/アンパックのデフォルトの振る舞いはポートI/Oを 使うので、巨大なファイルを読みながらそこからアンパックすることが できます。また、テンプレートはディスパッチクロージャとして格納でき、 パック、アンパック、あるいは値のスキップさえも、テンプレートを パーズし直すことなく行えます。

Function: pack template list :key :output :to-string?

listにある値を、文字列templateで指定されたフォーマットに したがって、現在の出力ポートに書き出します。テンプレート文字列は、 1文字のコードの連続で、オプションで(デフォルトが1である)カウント用の数字 が続きます。

フォーマット文字は一般的に、カウントを文字のバイト数と解釈する 文字列型と、カウントを繰り返し指示子と解釈するオブジェクト型に 分けられます。カウントは文字*で指定され、文字列型では 文字列の全体の長さを、オブジェクト型では残りの全ての値を使うことを 意味します。

カウントは大括弧に囲まれたテンプレートとしても指定でき、その場合 カウントはその大括弧を囲んでいるテンプレートのバイト数を意味します。 例えば、x[L]はlongをスキップします。

特別なフォーマット文字/は、パックされたデータが、値に続く 動的なカウント分を含むという構造を表すために使われます。 テンプレートは、<count-item>/<value-item>のように書かれ、 ここでは<count-item>は数値のカウントと解釈されるいかなる テンプレート文字、<value-item>はこのカウントを使ういかなる 他のテンプレート文字です。 <value-item>の後に通常のカウントが与えられても、無視されます。

フォーマット文字@は、カウントとともに使われ、テンプレートの 最初からの絶対位置までパディングします。

サブテンプレートは、括弧の中にグループ化されます。<>が使われると、 ネストされたリストに再帰的に適用されるグループ化オペレータとしても 振舞います。

文字列型:

a

任意の不完全文字列。NULLでパディングされます。

A

テキスト文字列。空白スペースでパディングされます。

Z

NULL終端(ASCIZ)文字列。NULLでパディングされます。

b

ビット文字列(それぞれのバイトにおけるビットオーダーは昇順)。

B

ビット文字列(それぞれのバイトにおけるビットオーダーは降順)。

h

16進文字列(低いニブルが先)。

H

16進文字列(高いニブルが先)。

オブジェクト型:

c

符号付き8ビット整数。

C

符号なし8ビット整数。

s

符号付き16ビット整数。

S

符号なし16ビット整数。

i

符号付き整数(>= 32ビット)。

I

符号なし整数(>= 32ビット)。

l

符号付きlong(32ビット)。

L

符号なしlong(32ビット)。

n, n!

ネットワークオーダー(ビッグエンディアン)での符号なし/符号つきshort(16ビット)。

N, N!

ネットワークオーダー(ビッグエンディアン)での符号なし/符号つきlong(32ビット)。

v, v!

VAXオーダー(リトルエンディアン)での符号なし/符号つきshort(16ビット)。

V, V!

VAXオーダー(リトルエンディアン)での符号なし/符号つきlong(32ビット)。

q

符号付きquad(64ビット)。

Q

符号なしquad(64ビット)。

f

ネイティブ形式の単精度float。

d

ネイティブ形式の倍精度float。

w

BER圧縮された整数。ベース128における符号なし整数で、最も大きな桁が 最初で、高いビットが最後の(一番小さな)バイト以外にセットされる。 したがって、どのような大きさの整数もエンコードできるが、 エンコーディングは効率的で、小さな整数は通常のchar/short/int エンコーディングの場合よりも占有する空間が少ない。

x

NULLバイト。

o

S式。readwriteで扱われる。

オプションのキーワード:outputが与えられると、 現在の出力ポートの代わりにそのポートが使われます。 :to-string?が与えられそれが真である場合は、 パックは蓄積され、その出力は文字列として返ります。

 
(pack "CCCC" '(65 66 67 68) :to-string? #t)
 ⇒ "ABCD"

(pack "C/a*" '("hello") :to-string? #t)
 ⇒ "\x05hello"
Function: unpack template :key :input :from-string

packの逆を行うもので、unpackは現在の入力ポートから、値が文字列の templateでパックされているものとして読み込み、その値をリストとして 返します。unpackはpackと同じフォーマット文字列を受け付けます。 また、いかなるリストxとフォーマット文字列fmtにおいても、 次のようなトートロジーが維持されます。

 
(equal? x (unpack fmt :from-string (pack fmt x :to-string? #t)))

ただ一つの例外は、テンプレートが*を含み、oテンプレートが 使われている場合です。これは、Schemeの数値リテラルは確実に区切られる ことができないからです(packの将来のバージョンでは、 新しいread構文を登録することによってこれは回避されるでしょう)。

オプションのキーワード:inputが与えられると、現在の入力ポートの 代わりにそのポートが使われます。:from-stringが与えられると、 packはその文字列を入力とします。

 
(unpack "CCCC" :from-string "ABCD")
 ⇒ '(65 66 67 68)

(unpack "C/a*" :from-string "\x05hello")
 ⇒ '("hello")

註: 現在のバージョンには、unpackのテンプレートの @が想定したように動かないというバグがあります。 将来のバージョンでfixされます。

Function: unpack-skip template :key :input

unpack-skipは、値を返さないことを除いてunpackと同じです。 いくつかのケースでは、特に固定サイズのテンプレートを使うときには、 単に値を読み飛ばしたいときにより効率的です。

Function: make-packer template

低レベルなインタフェースです。この関数は、pack、unpackやキャッシュ された同じテンプレートを読み飛ばす時に使われるディスパッチクロージャを 返します。ディスパッチクロージャは、以下のようにシンボルメソッドを 受け付けます。

'pack list

listにあるアイテムを、現在の出力ポートへpackします。

'unpack

現在の入力ポートからアイテムをunpackします。

'skip

現在の入力ポートからのアイテムをスキップします。

'packer

キャッシュされた’packクロージャを返します。

'unpacker

キャッシュされた’unpackクロージャを返します。

'skipper

キャッシュされた’skipクロージャを返します。

'length

テンプレートの知られている固定された長さを返します。

'variable-length?

テンプレートが可変長の要素を持っている場合に#tを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.3 compat.norational - 有理数のない算術演算

Module: compat.norational

リリース 0.8.7 までは、Gauche は正確な有理数をサポートしていませんでし た。2/3のような有理数リテラルを読み込むことはできていましたが、 約分して整数になる場合をのぞいては、その場で不正確な実数に変換されてい ました。正確な整数を正確な整数で割った場合も約分して整数になる場合をの ぞき、結果は不正確な実数になりました。

リリース 0.8.8 ではもうこのようなことはありません。正確な数同士の割り 算の結果は常に正確な数になります。ゼロで割ったときはもちろん例外です。

 
(/ 2 3)  ⇒ 2/3
(/ 5)    ⇒ 1/5
(/ 4 2)  ⇒ 2

計算は正確な値でできますが、ひとつ欠点もあります。正確有理数演算は整 数演算や不正確実数同士の演算にくらべてはるかに遅いのです。意図せず、計 算の初期段階で有理数を生成してしまうとその後の計算にはすべて正確な演算 が適用されてしまい性能は悲惨なことになります。

この問題を解決する適切は方法は適切な場所にexact->inexactを挿入 することです。しかし、この変換を簡単におこなうには、このモジュールをイ ンポートし、割り算に/を使うだけですみます。

 
(use compat.norational)

(/ 2 3)  ⇒ 0.6666666666666666
(/ 5)    ⇒ 0.2
(/ 4 2)  ⇒ 2

効果はグローバルには起こらず、明示的にcompat.norationalをインポー トしたモジュールでのみ有効です。

このモジュールは/を再定義しているだけです。したがって、コードに 正確な有理数のリテラルが含まれていても、それは不正確な実数に変換される ことはなく、正確な有理数としてあつかわれます。Gaucheに不正確な実数とし て有理数リテラルを扱わせるには、#iという接頭辞を追加しなければ なりません。

 
gosh> 1/3
1/3
gosh> #i1/3
0.3333333333333333

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.4 control.job - 制御モジュールのための汎用ジョブ記述子

Module: control.job

このモジュールは、制御フローサブシステム(control.*モジュール) の中で使う軽量な構造である job レコード型を提供します。 現在のところ、これを使っているのはcontrol.thread-poolだけですが、 他にもいくつかのモジュールがjobレコードを使う予定です。

control.*モジュールは、アプリケーションがジョブを追跡できるように jobレコードを返すことができます。ただし、 これは、アプリケーションが普通に使用するための ものではありません。アプリケーションが新しいジョブを 作成したり、その内容を変更したりすることは想定されていません; できるのは、そのジョブの属性について問い合わせることだけです。

この節では、アプリケーションが知るべき手続きだけを説明します。 制御サブシステムのためのインタフェースは未だ流動的であり、 さらなるサブシステムの開発が進むにつれて、変更されるかもしれません。

異なる制御フローサブシステムは、ジョブ構造の使い方が異なる かもしれません。この節では、共通する属性のみを説明します。 返されたジョブオブジェクトの扱い方については、個々の制御フロー モジュールを調べて下さい。

Record type: job

ジョブを表すレコード型です。アプリケーションはこれを、不透明(opaque)な 構造として扱うべきです。

Function: job? obj

objがジョブレコードだったら#tを、そうでなければ#fを返します。

Function: job-status job

ジョブの状態を返します。ジョブの状態は以下のいずれかになります。

#f

作成したてか、孤児のジョブです。通常、アプリケーションがジョブの この状態に遭遇することはありません。

acknowledged

ジョブは制御フローライブラリによって認識されていますが、まだ 実行されてはいません。

running

ジョブは実行中です。

done

ジョブは完了しています。アプリケーションはその処理の結果を、 job-resultを使って受け取ることができます。

error

ジョブはエラーで終了しています。アプリケーションはエラーの原因となった コンディションを、job-resultを使って受け取ることができます。

killed

ジョブは外部から殺されています。アプリケーションは殺された理由を、 job-resultを使って受け取ることができます。 どのような理由でジョブが殺されるかは制御フローサブシステムによります。

Function: job-result job

与えられたジョブがdone状態だったら、そのジョブの処理結果を返します。 与えられたジョブがerror状態だったら、そのエラーを説明する コンディションオブジェクトを返します。そのジョブがkilled状態だったら、 殺された理由を説明するオブジェクトを返します。このオブジェクトの詳細は、 実際の制御フローライブラリに依存することになります。これら以外の状態の ジョブに対してjob-resultを読んだ場合、何が返るかはわかりません; その結果を当てにすることはできません。

Function: job-wait job :optional timeout timeout-val

与えられたジョブがdoneerrorもしくはkilled 状態になるまで、呼び出したスレッドの実行をサスペンドします。 ジョブがすでにこれらの状態であった時は、ただちに制御を戻します。 ジョブの状態が返ります。

timeoutが与えられ、それが#fではない場合は、 タイムアウトのスペックとして有効なもの(時間の絶対値を表す <time>か、現時点からの相対的な秒数を表す実数)で なければなりません。timeoutの意味は、mutex-unlock! (see section 同期プリミティブ)のそれと同じです。 タイムアウトすると、そのジョブの状態が何であれ、 job-waitは引数timeout-valとして指定された 値を返します。デフォルト値は#fです。

そのジョブを作成する制御フローサブシステムによっては、 待ち受けできないかもしれません。それぞれのサブシステムの ドキュメントを調べて詳細を確認してください。

Function: job-acknowledge-time job
Function: job-start-time job
Function: job-finish-time job

制御フローサブシステムがジョブのタイムスタンプを記録する場合、 これらの手続きは、そのジョブがサブシステムによって認識された時刻、 開始された時刻、(正常に、あるいはエラーや殺されるなど異常に) 終了した時刻を(<time>オブジェクトとして)返します。 ジョブがそうした状態に達していない場合は#fを返します。

そのサブシステムがタイムスタンプを記録しない場合、これらの 手続きは常に#fを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.5 control.thread-pool - スレッドプール

Module: control.thread-pool

スレッドプールを提供します。Gaucheがpthreadサポート付きでコンパイルされている 場合にのみ利用可能です。

Class: <thread-pool>

スレッドプールオブジェクトのクラスです。ワーカースレッドのセットを保持し、 投入されたジョブを非同期に実行します。

現在の実装では、プールのサイズ (スレッド数) は固定で、プール作成時に 指定しなければなりません。いずれスレッド数を動的に増減させる機能を 追加する予定です。

また、ジョブのキューの最大長を指定することもできます。ジョブのキューが 一杯になると、空きができるまでは新たなジョブを投入することができなくなります (下記のadd-job!参照)。

Condition type: <thread-pool-shut-down>

スレッドプールがterminate-all!によって停止され、 新規のジョブを受け付けていないことを示すコンディションです。 <error>を継承します。次のスロットが提供されます。

Instance Variable of <thread-pool-shut-down>: pool

例外の原因となったスレッドプールオブジェクト

Function: make-thread-pool size :key (max-backlog 0)

大きさ(ワーカースレッド数)sizeのスレッドプールを作成して返します。 省略可能引数max-backlogによってジョブのバックログの最大値を 指定することもできます。0を与えた場合(デフォルト)は無制限です。

Function: thread-pool-results pool

ジョブをスレッドプールに投入する際に、ジョブの結果を知る必要があるかどうかを 指定することができます。結果を要求した場合は、終了したジョブレコードが プール中の結果キュー(<mt-queue>オブジェクト)にエンキューされます。 この手続きは、プールの結果キューを返します。 <mt-queue>の詳細についてはdata.queue - キューを参照してください。

Function: thread-pool-shut-down? pool

スレッドプールが停止され、新規のジョブを受け付けていない場合に#tを、 そうでなければ#fを返します。

Function: add-job! pool thunk :optional (need-result #f) (timeout #f)

thunkがスレッドプールpoolのスレッドにより実行されるように設定します。 jobレコードを返します。 (control.job - 制御モジュールのための汎用ジョブ記述子参照)。

返されるjobレコードはwaitableにはなっていません。結果を知る必要が ある場合は、省略可能引数need-resultに真の値を渡してください。 そうするとジョブが終了した時点 (正常終了でも異常終了でも) で、 jobレコードがスレッドプールのresult-queueに 入るので、そのキューから結果を受け取ることができます。 need-resultを省略したり偽値を渡した場合は、ジョブが終了しても jobレコードはresult-queueに入れられません。

返されるjobレコードにはタイムスタンプが付加され、 受付時間、実行開始時間、実行終了時間が記録されます。 (ジョブがまだ実行されていなかったり、終了されていない場合は、対応する タイムスタンプは#fになっています)。 ジョブがどのくらいキューの中で待たされ、どのくらい実行にかかったかを 知るのに便利でしょう。

スレッドプールが非負のmax-backlog値を持ち、 既にその数だけジョブが待ち行列に入っている場合は、 add-job!は待ち行列に空きができるまでブロックします。 timeout引数に、実数値の秒数、あるいは絶対時刻を指定する<time> オブジェクトを渡すことでタイムアウトを指定できます。タイムアウトに 達した場合は、add-job!はジョブを作らずに#fを返します。 timeout引数を省略するか、#fを渡した場合はタイムアウトが設定されません。

(註: この動作は0.9.1から変更されました。0.9.1では、add-job!は タイムアウト引数を取らず、常にタイムアウトに0秒が指定されたかのように 振る舞っていました。現在のバージョンで同じ動作をさせるには、timeout引数 に0を明示的に渡します。)

スレッドプールが停止していた場合、この手続きは <thread-pool-shut-down>コンディションを投げます。

Function: wait-all pool :optional (timeout #f) (check-interval #e5e8)

ジョブ待ち行列が空になり、すべての実行中のジョブも終了するまで待ちます。 終了待ちはcheck-intervalにナノ秒で指定される間隔でスレッドプールを ポールすることで行われます。すべてのジョブが終了したら#tを返します。

秒数を表す実数か、絶対時刻を表す<time>オブジェクトをtimeout 引数に渡すことで、タイムアウトを指定できます。タイムアウトに達した場合は、 wait-all#fを返します。

Function: terminate-all! pool :key (force-timeout #f) (cancel-queued-jobs #f)

投入されたジョブがすべて終了するのを待ち、すべてのスレッドを終了させます。 ひとたびこの手続きを呼ぶと、スレッドプールpoolは新規のジョブを受けつけ ません。この状態のスレッドプールに対してadd-job!を呼ぶと <thread-pool-shut-down>コンディションが投げられます。 この手続きはアプリケーションのシャットダウン時などに呼ばれることを意図しています。

デフォルトでは、この手続きはまずキューに既に投入されたジョブが全て処理されるのを 待ち、それからスレッドを穏やかに終了させます。

真の値をcancel-queued-jobs引数に与えると、キューに入っているが まだ開始されていないジョブは直ちにキャンセルされます。それらのジョブの ステータスにはkilledがセットされます。 ただし、既に開始されたジョブについてはキャンセルされません。

既に開始されたジョブも中断したい場合は、 タイムアウト値(秒数を表す実数か、絶対時刻を表す<time>オブジェクト)を force-timeout引数に渡します。 タイムアウトに達した時点で残っているスレッドは強制終了され、実行中のジョブも キャンセルされます。

スレッドの強制終了は極端な処置です。終了されるスレッドは、適切なクリーンアップを 行う機会も与えられないかもしれません。したがって通常は、 スレッドが処理中のジョブを終わらせるための適切な時間的猶予を与えるのが良いでしょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.6 crypt.bcrypt - パスワードハッシュ

Module: crypt.bcrypt

このモジュールは、blowfishを使ったパスワードハッシュアルゴリズムを実装します。 OpenBSDのbcryptアルゴリズム(version 2a, 2b)と互換です。

version 2a は脆弱性が発見されているので、新規のコードでは使用しないで ください。代わりに version 2b を使用して下さい。

典型的な使い方は非常にシンプルです。新しいパスワードハッシュ値を 得たい場合 (例えば新しいユーザのために、など) は、パスワード文字列を 唯一の引数としてbcrypt-hashpwを呼び出してください。

 
(bcrypt-hashpw password)
  ⇒ hashed-string

このルーチンは自動的にソルト値を付加します。戻り値の文字列はそのままユーザ データベースに格納できます。与えられたパスワードがハッシュ値に一致するか どうかを調べるには、ハッシュ値そのものを第二引数としてbcrypt-hashpwに 渡します。

 
(bcrypt-hashpw password hashed-string)
  ⇒ hashed-string

パスワードが正しければ、戻り値はhashed-stringと完全に一致するはずです。

Function: bcrypt-hashpw password :optional setting

passwordのハッシュ値を計算します。settingは ソルト値とパラメータを指定する文字列です。settingが 省略された場合は、適切なデフォルトのパラメータとランダムなソルト値が 自動的に選択されます。

戻り値のハッシュ値文字列にはソルト値とパラメータが含まれているので、 それを再びsettingに渡すことができます。したがって、パスワードを 既存のハッシュ値に一致するかチェックしたい場合は、ハッシュ値をsettingに渡し、 戻ってきた文字列が渡したハッシュ値と一致するかを見れば良いことになります。

bcryptアルゴリズムは最大72オクテットまでのパスワードを使えます。

新しいハッシュ値を計算する際にパラメータを指定したい場合は、次に説明する bcrypt-gensaltを使うとsettingに使える文字列を得ることができます。

Function: bcrypt-gensalt :key prefix count entropy-source

与えられたパラメータを折り込んだ、 bcrypt-hashpwsetting引数に使える文字列を返します。

prefix引数はハッシュ関数およびそのバージョンを指定する文字列です。 現在のところ、bcrypt互換である$2a$$2b$がサポート されています。 ただし、$2a$には脆弱性が発見されていますので、 新規のコードには$2b$を使用して下さい。 perfix引数を省略した場合は$2b$が用いられます。

count引数はハッシュの繰り返し回数に関係します。大きな値を指定すれば、 ハッシュ値の計算により長い時間がかかります。パスワードハッシュにおいては、 時間をかけた方が良いことに注意してください。一回のハッシュの時間が長くなれば 辞書攻撃への防御になります。一方で、通常のパスワードチェックでは ログインの度にたかだか一回しはハッシュ関数を呼ばないので、それがたとえコンマ数秒 かかったとしてもたいした負荷ではありません。 bcryptアルゴリズムでは、(expt 2 count)回ハッシュが繰り返されます。

entropy-source引数はランダムなバイト列を格納したu8vectorです。 bcryptアルゴリズムでは少なくとも16バイトの長さが必要です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.7 data.cache - キャッシュ

Module: data.cache

A cache is similar to a dictionary, associating keys to values, but its entries may dissapear according to the policy of the cache algorithm. This module defines a common protocol for cache datatypes, and also provides several typical cache implementations.

Examples

Let’s start from simple examples to get the idea.

Suppose you want to read files and you want to cache the frequently read ones. The following code defines a cached version of file->string:

 
(use data.cache)
(use file.util)

(define file->string/cached
  (let1 file-cache (make-lru-cache 10 :comparator string-comparator)
    (^[path] (cache-through! file-cache path file->string))))

The procedure closes a variable file-cache, which is an LRU (least recently used) cache that associates string pathnames to the file contents. The actual logic is in cache-through!, which first consults the cache if it has an entry for the path. If the cache has the entry, its value (the file content) is returned. If not, it calls file->string with the path to fetch the file content, register it to the cache, and return it. The capacity of cache is set to 10 (the first argument of make-lru-cache), so when the 11th file is read, the least recently used file will be purged from the cache.

The effect of cache isn’t very visible in the above example. You can insert some print stubs to see the cache is actually in action:

 
(define file->string/cached
  (let1 file-cache (make-lru-cache 10 :comparator string-comparator)
    (^[path]
      (print #"file->string/cached called on ~path")
      (cache-through! file-cache path
                      (^[path]
                        (print #"cache miss.  fetching ~path")
                        (file->string path))))))

Caveat: A cache itself isn’t MT-safe. If you are using it in multithreaded programs, you have to wrap it with an atom (see section 同期プリミティブ):

 
(use data.cache)
(use file.util)
(use gauche.threads)

(define file->string/cached
  (let1 file-cache (atom (make-lru-cache 10 :comparator string-comparator))
    (^[path]
      (atomic file-cache (cut cache-through! <> path file->string)))))

Common properties of caches

A cache of any kind has a comparator and a storage. The comparator is used to compare keys; in the example above, we use string-comparator to compare string pathnames (See section 基本的な比較器, for more about comparators).

The storage is a dictionary that maps keys to internal structures of the cache. By default, a hashtable is created automatically using the given comparator (or, if a comparator is omitted, using default-comparator). The comparator must have hash function.

Alternatively, you can give a pre-filled dictionary (copied from another instance of the same kind of cache) to start cache with some data already in it. Note that what the cache keeps in the dictionary totally depends on the cache algorithm, so you can’t just pass a random dictionary; it has to be created by the same kind of cache. If you pass in the storage, the comparator is taken from it.

Thus, the comparator constructors uniformly take keyword arguments comparator and storage; you can specify either one, or omit both to use the defaults.

Predefined caches

For storage and comparator keyword arguments, see above.

Function: make-fifo-cache capacity :key storage comparator

Creates and returns a FIFO (first-in, first-out) cache that can hold up to capacity entries. If the number of entries exceeds capacity, the oldest entry is removed.

Function: make-lru-cache capacity :key storage comparator

Creates and returns an LRU (least recently used) cache that can hold up to capacity entries. If the number of entries exceeds capacity, the least recently used entry is removed.

Function: make-ttl-cache timeout :key storage comparator timestamper

Creates and returns a TTL (time to live) cache with the timeout value timeout. Each entry is timestamped when it’s inserted, and it is removed when the current time passes timeout unit from the timestamp. The actual entry removal is done when the cache is accessed.

By default, the Unix system time (seconds from Epoch) is used as a timestamp, and timeout is in seconds. It may not be fine-grained enough if you add multiple entries in shorter intervals than seconds. You can customize it by giving a thunk to timestamper; the thunk is called to obtain a timestamp, which can be any monotonically increasing real number. If you give timestamper, the unit of timeout value should be the same as whatever timestamper returns.

Function: make-ttlr-cache timeout :key storage comparator timestamper

A variation of TTL cache, but the entry’s timestamp is updated (refreshed) whenever the entry is read. Hence we call it TTL with refresh (TTLR). But you can also think it as a variation of LRU cache with timeout.

The unit of timeout, and the role of timestamper argument, are the same as make-ttl-cache.

Common operations of caches

The following APIs are for the users of a cache.

Function: cache-lookup! cache key :optional default

Look for an entry with key in cache, and returns its value if it exists. If there’s no entry, the procedure returns default if it is provided, or throws an error otherwise.

Some types of cache algorithms update cache by this operation, hence the bang is in the name.

Function: cache-through! cache key value-fn

Look for an entry with key in cache, and returns its value if it exists. If there’s no entry, a procedure value-fn is called with key as the argument, and its return value is inserted into cache and also returned.

Generic function: cache-write! cache key value

This inserts association of key and value into cache. If there’s already an entry with key, it is overwritten. Otherwise a new entry is created.

The same effect can be achieved by calling cache-evict! then cache-through!, but cache algorithms may provide efficient way through this method.

Generic function: cache-evict! cache key

Removes an entry with key from cache, if it exists.

Generic function: cache-clear! cache

Removes all entries from cache.

Implementing a cache algorithm

Each cache algorithm must define a class inheriting <cache>, and implement the following two essential methods. The higher-level API calls them.

Generic function: cache-check! cache key

Looks for an entry with key in cache. If it exists, returns a pair of key and the associated value. Otherwise, returns #f. It may update the cache, for example, the timestamp of the entry for being read.

Generic function: cache-register! cache key value

Add an entry with key and associated value into cache. This is called after key is confirmed not being in cache.

Additionally, the implementation should consider the following points.

There are several procedures that help implementing cache subclasses:

Function: cache-comparator cache
Function: cache-storage cache

Returns the comparator and the storage of the cache, respectively.

Typical caches may be constructed with a storage (dictionary) and a queue, where the storage maps keys to (<n> . <value>), and queues holds (<key> . <n>), <n> being a number (timestamp, counter, etc.) Here are some common operations work on this queue-and-dictionary scheme:

Function: cache-populate-queue! queue storage

You can call this in the initialize method to set up the queue. This procedure walks storage to construct (<key> . <n>) pairs, sorts it in increasing order of <n>, and pushes them into the queue.

Function: cache-compact-queue! queue storage

The queue may contain multiple pairs with the same key. Sometimes the queue gets to have too many duplicated entries (e.g. the same entry is read repeatedly). This scans the queue and removes duplicated entries but the up-to-date one. After this operation, the length of the queue and the number of entries in the storage should match.

Function: cache-renumber-entries! queue storage

This procedure renumbers <n>s in the queue and the storage starting from 0, without changing their order, and returns the maximum <n>. The duplicated entries in the queue is removed as in cache-compact-queue!.

When you’re using monotonically increasing counter for <n> and you don’t want <n> to get too big (i.e. bignums), you can call this procedure occasionally to keep <n>’s in reasonable range.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.8 data.heap - ヒープ

Module: data.heap

ヒープは最小値また最大値を効率よく取り出せるデータコンテナです。 <tree-map>は全てのエントリの順序を常に保っていますが (ツリーマップ参照)、 ヒープは最小値/最大値以外については部分的にしか順序を保持していません。 最小値/最大値が取り除かれる時点で内部を再構成して、次の最小値/最大値を見つけます。 したがって、最小値/最大値のみが必要な場合、treemapより効率が良いです。 さらに、バイナリヒープでは値をメモリ効率の良い詰められた形で保持できます。

Class: <binary-heap>

バイナリヒープの実装です。内部的にmin-maxヒープを使っており、 最大値と最小値のどちらにもO(1)でアクセスできます。新たな値をpushしたり、 最大最小値をpopするのはO(log n)です。

バイナリヒープはまた、値をフラットなベクタに格納します。一般的なツリー構造が ノードあたりいくつかのポインタを必要とするのに比べずっとコンパクトです。 デフォルトでは疎なベクタ (疎なベクタ) がバッキングストレージとして使われ、 事実上上限なしでデータを格納できますが、通常のベクタやユニフォームベクタを バッキングストレージとして指定することもできます。

バイナリヒープはスレッドセーフではありません。複数のスレッドでアクセスする 可能性がある場合は、atomに入れるか、mutexを適切に使ってください(同期プリミティブ参照)。

Function: make-binary-heap :key comparator storage key

新しいバイナリヒープを作って返します。

comparatorキーワード引数は、エントリの比較方法を指定する比較器です。 比較手続きを持っていなければなりません。省略時はdefault-comparatorが 使われます。比較器について詳しくは基本的な比較器を参照してください。

storageキーワード引数には、データの格納に使うデータ構造を渡します。引数は ベクタ、ユニフォームベクタ、あるいは疎ベクタ(疎なベクタ参照)の インスタンスでなければなりません。省略時は<sparse-vector>の インスタンスが使われます。

ベクタまたはユニフォームベクタを渡した場合、そのベクタの大きさが ヒープが格納できる要素の最大数を決めます。 それが一杯になった時に自動的に拡張されることはありません。

keyキーワード引数は手続きでなければならず、要素の比較の前に各要素に適用されます。 この手続きを使って、実際に比較される値に付随するデータを格納しておくことができます。 次の例では、データのcarを使って比較しています。

 
(define *heap* (make-binary-heap :key car))
(binary-heap-push! *heap* (cons 1 'a))
(binary-heap-push! *heap* (cons 3 'b))
(binary-heap-push! *heap* (cons 1 'c))

(binary-heap-find-min *heap*) ⇒ (1 . c)
(binary-heap-find-max *heap*) ⇒ (3 . b)
Function: build-binary-heap storage :key comparator key num-entries

storageに入っているデータを使ってヒープを作り返します。 (この操作はしばしばヒープ化(heapify)と呼ばれます。) データ格納場所を新たにアロケートせずにヒープを作ることが可能です。 comparatorおよびkey引数はmake-binary-heapと同じです。

storageはベクタ、ユニフォームベクタ、疎ベクタのインスタンスでなければ なりません。このデータはヒープの属性を満たすために変更され、さらには 作られたヒープのデータ格納場所となります。ヒープの一部になるので、 storageを後から変更してはなりません。

storageの要素のうち、先頭からnum-entriesまでの要素が 有効であるとしてヒープ化されます。num-entriesが省略されるか #fであれば、ベクタおよびユニフォームベクタはその全てが、 疎ベクタはsparse-vector-num-entriesまでの要素がヒープ化されます。

Function: binary-heap-copy heap

ヒープをコピーして返します。データ格納場所も全てコピーされます。

Function: binary-heap-clear! heap

ヒープを空にします。

Function: binary-heap-num-entries heap

ヒープ中の要素数を返します。

Function: binary-heap-empty? heap

ヒープが空なら#tを、そうでなければ#fを返します。

Function: binary-heap-push! heap item

itemheapに挿入します。O(log n)の操作です。 ヒープが既に一杯であった場合はエラーが通知されます。

Function: binary-heap-find-min heap :optional fallback
Function: binary-heap-find-max heap :optional fallback

それぞれヒープ中の最小および最大の要素を返します。O(1)の操作です。 ヒープ自体は変更されません。

ヒープが空の場合、fallbackが与えられればそれが返され、 そうでなければエラーが通知されます。

Function: binary-heap-pop-min! heap
Function: binary-heap-pop-max! heap

それぞれヒープから最小および最大の要素を取り除き、取り除いた要素を返します。 O(log n)の操作です。 ヒープが空ならエラーが通知されます。

以下の手続きは一般的なデータ構造としてのヒープの操作には含まれませんが、 便利なので用意してあります。

Function: binary-heap-swap-min! heap item
Function: binary-heap-swap-max! heap item

これらはそれぞれ以下のコードと操作的には等価です。

 
(begin0 (binary-heap-pop-min! heap)
  (binary-heap-push! heap item))

(begin0 (binary-heap-pop-max! heap)
  (binary-heap-push! heap item))

ただしこれらの手続きは、 ヒープ特性を維持するための手続きを呼び出し毎に1回しか行わないので、 やや効率的です。

Function: binary-heap-find heap pred

ヒープの中で、述語predを満たす要素を返します。predを満たす要素が 複数ある場合にどれが返るかは不定です。predを満たす要素が無ければ #fが返ります。O(n)の操作です。

Function: binary-heap-remove! heap pred

predを満たす要素を全てヒープから取り除きます。O(n)の操作です。

Function: binary-heap-delete! heap item

itemに等しい要素を全てヒープから取り除きます。「等しい」の判定には ヒープの比較器およびキー手続きが使われます。O(n)の操作です。

キー手続きは、比較の前にitemにも適用されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.9 data.ideque - 変更不可な両端キュー

Module: data.ideque

This module provides a functional double-ended queue (deque, pronounced as “deck”), with amortized O(1) access of queue operations on either end.

It also serves as a convenient bidrectional list structures in a sense that operations from the end of the list is just as efficient as the ones from the front.

Note: If you don’t need immutability and wants space-efficient deque, you can also use data.ring-buffer as a deque (see section data.ring-buffer - リングバッファ).

Function: make-ideque n :optional init

Creates an ideque of length n with all the elements being init. If init is omitted, #f is used.

This is provided just for the symmetry with other container data structures; it’s not in srfi-134, and the portable code can use ideque-tabulate.

Function: ideque element …

[SRFI-134] Returns an ideque with the given elements.

Function: ideque-unfold p f g seed

[SRFI-134]

Function: ideque-unfold-right p f g seed

[SRFI-134]

Function: ideque-tabulate size init

[SRFI-134]

Function: ideque-empty? idq

[SRFI-134]

Function: ideque-add-front idq x
Function: ideque-add-back idq x

[SRFI-134]

Function: ideque-front idq
Function: ideque-bakc idq

[SRFI-134]

Function: ideque-remove-front idq
Function: ideque-remove-back idq

[SRFI-134]

Function: ideque-reverse idq

[SRFI-134]

Function: ideque= idq idq2 …

[SRFI-134]

Function: ideque-ref idq n

[SRFI-134]

Function: ideque-take idq n
Function: ideque-take-right idq n

[SRFI-134]

Function: ideque-drop idq n
Function: ideque-drop-right idq n

[SRFI-134]

Function: ideque-split-at idq n

[SRFI-134]

Function: ideque-length idq

[SRFI-134]

Function: ideque-append idq …

[SRFI-134]

Function: ideque-zip idq idq2 …

[SRFI-134]

Function: ideque-map proc idq …

[SRFI-134]

Function: ideque-filter-map proc idq …

[SRFI-134]

Function: ideque-for-each proc idq …
Function: ideque-for-each-right proc idq …

[SRFI-134]

Function: ideque-fold proc knil idq …
Function: ideque-fold-right proc knil idq …

[SRFI-134]

Function: ideque-append-map proc idq …

[SRFI-134]

Function: ideque-filter pred idq
Function: ideque-remove pred idq

[SRFI-134]

Function: ideque-partition pred idq

[SRFI-134]

Function: ideque-find pred idq :optional failure
Function: ideque-find-right pred idq :optional failure

[SRFI-134]

Function: ideque-take-while pred idq
Function: ideque-take-while-right pred idq

[SRFI-134]

Function: ideque-drop-while pred idq
Function: ideque-drop-while-right pred idq

[SRFI-134]

Function: ideque-span pred idq
Function: ideque-break pred idq

[SRFI-134]

Function: ideque-any pred idq …
Function: ideque-every pred idq …

[SRFI-134]

Function: ideque->list idq
Function: list->ideque list

[SRFI-134]

Function: ideque->generator idq
Function: generator->ideque gen

[SRFI-134]


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.10 data.imap - 変更不可なマップ

Module: data.imap

このモジュールは、O(log n)でアクセスと更新が可能な、変更不可のデータ構造を 提供します(ここでの「更新」とは、要求された変更を取り込んだ新たな構造を 作って返すことです)。 現在の実装は関数的な赤黒木に基づいています。

リストや連想リストは、スタック風の変更不可なデータ構造として使えます。 つまり既存のデータ構造自体に変更を加えることなく、その先頭に要素を追加したり、 先頭から要素を取り除いたりできるということです。一方で、リストや連想リストは 任意の要素にアクセスするにはO(n)の時間を必要としますが、もっと速いアクセスが必要な 場合もあります。<imap>オブジェクトはO(log n)のアクセスを可能にします。 そのかわり、要素の追加や削除にもO(log n)を必要とします。

Class: <imap-meta>

<imap>のメタクラスです。

Class: <imap>

変更不可なマップのクラスです。<imap-meta>をメタクラスとします。

<ordered-dictionary>を継承し、変更操作を除くディクショナリプロトコル を実装します(gauche.dictionary - ディクショナリフレームワーク参照)。 シーケンスとしてアクセスした場合は、キーと値のペアをキーの昇順に取り出すことができます。

Function: make-imap
Function: make-imap comparator
Function: make-imap key=? key<?

空の変更不可なマップを新たに作って返します。 引数無しの場合は、キーの比較にはdefault-comparatorが使われます。 キーの比較方法を指定したい場合は、比較手続きを持つ比較器comparatorを 引数に渡してください。比較器についてはsrfi-114 - 比較器を参照。 3番目の呼び出し形式は、キーの等価性を調べる述語key=?と 大小比較の述語key<?から比較器を作ります。どちらの述語も ふたつの引数をとらなければなりません。このインタフェースは tree-mapとの一貫性のためにサポートされています (ツリーマップ参照)。

Function: alist->imap alist
Function: alist->imap alist comparator
Function: alist->imap alist key=? key<?

Creates a new empty immutable map, populates it with key-value association list alist, and returns it. This may be a bit more efficient than creating an empty map with make-imap and populates it with imap-put one by one.

The comparator argument specifies how to compare the keys. It must have comparison procedure. If omitted, default-comparator is used. See section srfi-114 - 比較器, for the details.

The third form creates a key comparator from a equality predicate key=? and less-than predicate key<=?, both must accept two keys.

 
(define m (alist->imap '((a . 1) (b . 2))))

(imap-get m 'a) ⇒ 1
(imap-get m 'b) ⇒ 2
Function: tree-map->imap tree-map

Returns a new immutable map with the same content (and the same comparator) as tree-map.

Function: imap? obj

Returns #t if obj is an immutable map, #f otherwise.

Function: imap-empty? immap

Returns #t if an immutable map immap is empty, #f otherwise.

Function: imap-exists? immap key

Returns #t if key exists in an immutable map immap.

Function: imap-get immap key :optional default

Returns the value associated with key in an immutable map immap. If immap doesn’t have key, default is returned when provided, otherwise an error is signalled.

Function: imap-put immap key val

Returns a new immutable map where association of key to val is added to (or replaced in) an immutable map immap. This operation is O(log n).

 
(define m1 (alist->imap '((a . 1) (b . 2))))

(define m2 (imap-put m1 'a 3))

(imap-get m2 'a)  ⇒ 3
(imap-get m1 'a)  ⇒ 1  ; not affected
Function: imap-delete immap key

Returns a new immutable map where key is removed from immap. If immap doesn’t have key, returned map has the same content as immap.

 
(define m1 (alist->imap '((a . 1) (b . 2))))

(define m2 (imap-delete m1 'a))

(imap-get m2 'a #f)  ⇒ #f
(imap-get m1 'a)     ⇒ 1  ; not affected
Function: imap-min immap
Function: imap-max immap

Returns a pair of key and value with the minimum or maximum key in immap, respectively. If immap is empty, #f is returned.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.11 data.queue - キュー

Module: data.queue

キュー(FIFO)機能を提供します。 極めて軽量ですがスレッドセーフでないシンプルなキューと、 スレッドセーフなmtqueueがあります。基本的なキュー操作手続きは どちらのキューに対しても使うことができます。本節で説明されている 手続きをmtqueueに適用した場合、特に断りが無い限り、操作は アトミックに行われます。

また、スレッド間同期に使えるmtqueue用の手続きも用意されています。 例えばキューが空の時に読み出しスレッドをブロックさせたり キューの長さが指定値に達した場合に書き込みスレッドをブロックさせることができ、 いわゆる「チャネル」としてキューを使うことができます。

シンプルキューのAPIはSLIBのキュー実装の上位互換になっています。 要素を並びの末尾に追加するenqueue!と、要素を並びの先頭から取り除く dequeue!だけでなく、要素を並びの先頭に追加するqueue-push!も 提供されているので、スタックとしても使えます。

要素を並びの末尾からO(1)で取り除くAPIも欲しい場合は、 deque (double-ended queue)が必要です。data.ring-bufferモジュール は、ベクタの上に実装された(空間的にも時間的にも)効率の良いdequeを提供しています (data.ring-buffer - リングバッファ参照)。また、変更不可な両端キューdata.ideque もあります(data.ideque - 変更不可な両端キュー参照)。

また、SRFI-117はポータブルなリストベースのキューのAPIを提供しています (srfi-117 - リストを元にしたキュー)。

Class: <queue>

シンプルなキューのクラスです。

Instance Variable of <queue>: length

キュー中の要素の数を返す、読み取り専用のスロットです。

Class: <mtqueue>

mtqueueのクラスです。<queue>を継承しています。

Instance Variable of <mtqueue>: max-length

キュー中の要素数の上限を示します。

このスロットが0の場合、キューは要素を中に持つことはできませんが、同期デバイスとして動作します。 書き込みスレッドは、その要素を受け取る読み出しスレッドが現れるまでブロックし、 読み出しスレッドは、要素を渡してくれる書き込みスレッドが現れるまでブロックします。

Function: make-queue

空のシンプルなキューを作って返します。

Function: make-mtqueue :key max-length

空のmtqueueを作って返します。整数がmax-lengthに与えられた場合は、 それがmax-lengthスロットの値となります。

Function: queue? obj

objがキューであれば(シンプルなキューでもmtqueueでも)#tを返します。

Function: mtqueue? obj

objがmtqueueであれば#tを返します。

Function: queue-empty? queue

objが空のキューであれば#tを返します。

Function: queue-length queue

キューの中にある要素の数を返します。

Function: mtqueue-max-length mtqueue

キューが保持できる要素の最大数を返します。限度がない場合は#fが返ります。

Function: mtqueue-room mtqueue

保持できる最大容量に達するまであといくつ要素を受け入れることができるかを 示す整数を返します。例えば、既にキューがいっぱいであれば0が返ります。 キューに最大容量が設定されていなければ+inf.0が返ります。

この手続きが0以外有限値を返した場合でも、続くenqueue!の呼び出し時点で キューがいっぱいになっている可能性があることに注意してください。 この手続きの呼び出しとenqueue!の 呼び出しの間に、他のスレッドがキューに要素を入れるかもしれないからです。 有限長のmtqueueに確実に要素を挿入したい場合はenqueue/wait!を使ってください。

Function: mtqueue-num-waiting-readers mtqueue

mtqueueからの読み出しで待っているスレッドの数を返します。 返り値は常に非負の正確な整数です。

この手続きが値を返してからその値を使うまでの間に、別のスレッドが キューに要素を挿入したら、待っているスレッドの数は変わってしまうことに 注意してください。この手続きの返り値を安全に使うには、 キューに値を挿入する部分を別に排他制御する必要があります。

 
(define q (make-mtqueue))

(thread-start! (make-thread (^[] (dequeue/wait! q))))

(mtqueue-num-waiting-readers q) ⇒ 1

(enqueue! q 'a)

(mtqueue-num-waiting-readers q) ⇒ 0
Function: copy-queue queue

キューqueueのコピーを返します。

Function: enqueue! queue obj :optional more-objs …

objをキューqueueの末尾に追加します。 一つ以上のobjを与えることができ、その場合はそれらが順にenqueueされます。

queueがmtqueueの場合、渡されたオブジェクト全ての追加は アトミックに行われます。すなわち、途中に別スレッドが要素を挿入する ことはありません。さらに、max-lengthスロットが正の有限値を 持っており、このenqueue!の実行によってキューの要素数が max-lengthを越えることになる場合は、queueは 変更されずエラーとなります。 (max-lengthがゼロの場合、この手続きは常にエラーとなります。 下に説明するenqueue/wait!を使ってください。)

Function: queue-push! queue obj :optional more-objs …

objをキューqueueの先頭に追加します。 一つ以上のobjを与えることができ、その場合はそれらが順にpushされます。

enqueue!と同様に、queueがmtqueueの場合は 全てのオブジェクトはアトミックに追加され、またmax-lengthの値も チェックされます。詳しくは上のenqueue!の説明を参照してください。

Function: enqueue-unique! queue eq-proc obj :optional more-objs …
Function: queue-push-unique! queue eq-proc obj :optional more-objs …

objが既にqueueの中に含まれている場合にはqueueを 変更しないことを以外には、enqueue!およびqueue-push!と同じ 動作をします。objが含まれているかどうかの検査は 2引数の関数eq-procで行います。

queueがmtqueueの場合は 全てのオブジェクトはアトミックに追加され、またmax-lengthの値も チェックされます。詳しくは上のenqueue!の説明を参照してください。

Function: dequeue! queue :optional fallback
Function: queue-pop! queue :optional fallback

キューqueueの先頭からひとつ要素を取って返します。 二つの手続きは全く同じ動作をします。queue-pop!queue-push!と ペアで使われていることを強調したいときに使うと良いでしょう。

キューが空の場合は、fallbackが与えられていればそれが返され、 そうでなければエラーが報告されます。

キューがmtqueueでそのmax-lengthがゼロの場合、 キューは常に空であるとみなされます。ゼロ長のキューを同期デバイスとして 使う場合はdequeue/wait!を使ってください。

Function: dequeue-all! queue

キューの全ての内容をリストにして返します。キューそのものは空になります。 キューが既に空の場合は空リストが返されます。 下のqueue->listも参照してください。

Function: queue-front queue :optional fallback
Function: queue-rear queue :optional fallback

キューqueueの先頭もしくは末尾の要素を返します。キューそのものは変更されません。 キューが空の場合、fallbackが与えられていればその値を返し、 そうでなければエラーを報告します。

Function: list->queue list :optional class :rest initargs

与えられたリストlistの各要素をその順で持つようなキューを作成して返します。

デフォルトではシンプルキューが作られますが、class<class>の サブクラスを渡すことで他のキュークラスのインスタンスを作ることができます。 initargsclassのコンストラクタに渡されます。

Function: queue->list queue

キューqueueの内容をリストにして返します。 dequeue-all!と異なり、キューそのものの内容は変化しません。

Gaucheではqueue->listは新しいリストをアロケートしてキューの 内容をコピーします (dequeue-all!はコピーをせずにキューの内部の リストをそのまま返します)。組込みでqueue->listを持っているScheme 実装がいくつかありますが、その中にはqueue->listがキューの 内容をコピーすることを保証していないものがあるので、それらの処理系と 共有するコードではqueue->listがリストをコピーすることを あてにしない方が良いでしょう。

Function: queue-internal-list queue

queue->listと同じように、queueの内容をリストとして返しますが、 そのリストはqueueの内部構造と共有されているかもしれません。 つまり、返されたリストは続くqueueへの操作によって変更される可能性があり、 また返されたリストを変更するとqueueの一貫性は失われるでしょう。

この危険性のため、<mtqueue>をこの手続きに渡すことは禁じられています。 もしそうしたらエラーとなります。

queueに蓄積されたデータをコピーせずに取り出したいというだけなら、 dequeue-all!を使ってください。それは取り出すのとキューを空にする 操作をアトミックに行うので安全です。この手続きは、キューの中身を取り出すこと無く 内部にアクセスしたいという場合にだけ使って下さい。

Function: find-in-queue pred queue

キュー内の要素のうち述語predを満たす最初の要素を返します。 引数の順序はfindに揃えました (他のリスト手続き参照)。

Function: any-in-queue pred queue

SRFI-1のanyのように、queue中の各要素に 順にpredを適用し、それが真の値 (#tである必要はありません) に 評価されたらその値を返します。predを満たす要素が無ければ#fが 返ります。

Function: every-in-queue pred queue

SRFI-1のeveryのように、queue中の各要素に 順にpredを適用し、それが#fを返したらすぐ#fを返します。 predに対し#fを返す要素が無ければ、最後の要素にpredを 適用した結果が返ります。キューが空なら#fが返ります。

Function: remove-from-queue! pred queue

キューから、述語predを満たす要素を全て取り除きます。 要素が削除された場合は#tが、そうでなければ#fが返されます。 引数の順序はSRFI-1のremoveに揃えました (SRFI-1 リスト操作関数参照)。

移植性に関する註:Scheme48には、述語ではなく削除するオブジェクトそのものを取る delete-from-queue!がありますが、引数の順序が逆(キューが先)になっています。 まぎらわしい衝突を避けるため、敢えてdelete-from-queue!は 提供しませんでした。remove-from-queue!を使えば、Scheme48互換の方法でも、 あるいはSRFI-1と一貫性のある方法でもdelete-from-queue!を簡単に書けるでしょう。

Function: enqueue/wait! mtqueue obj :optional timeout timeout-val
Function: queue-push/wait! mtqueue obj :optional timeout timeout-val
Function: dequeue/wait! mtqueue :optional timeout timeout-val
Function: queue-pop/wait! mtqueue :optional timeout timeout-val

これらの手続きはmtqueueに対して使うことができ、 スレッド間同期を実現できます。 enqueue/wait!queue-push/wait!は、 キューの要素数がmax-lengthを越える場合に、キューに空きができるまで、 dequeue/wait!queue-pop/wait!は、 キューが空の場合に、キューに要素が追加されるまで、 呼び出しスレッドをブロックします。

timeout引数で、ブロックされたスレッドのタイムアウトを指定することができます。 #fは指定しなかった場合と同じで、キューの状態が変化するまで 無期限に待ちます。実数が渡された場合はそれが秒数と解釈され、最低限 その時間経過するまでは待ちます。渡されたのが<time>オブジェクト (時間参照)である場合は、そのオブジェクトが指定する絶対時刻を 経過するまで待ちます。

タイムアウトによりブロックが解かれた場合は、timeout-valで 指定した値が返されます。timeout-valの省略時値は#fです。

enqueue/wait!queue-push/wait!は、 タイムアウトせずに操作が成功した場合は#tを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.12 data.random - ランダムデータの生成

Module: data.random

このモジュールは、特定のデータ型や、特定の値の分布を持つランダムなデータを 生成するジェネレータ、およびそういったジェネレータを作り出すジェネレータ構築器を 提供します。

名前付けの規則:パラメータを受け取り、それにそったジェネレータを返す 手続きにはサフィックス$がついています (例: integer$)。 それ自体がジェネレータである手続きにはサフィックスがつきません (例: fixnums)。 コンビネータ、つまり複数のジェネレータを取ってジェネレータを返す手続きの名前は、 通常前置詞で終わります (例: list-of)。

グローバルな状態

このモジュールのジェネレータはすべて一つのグローバルな乱数状態を共有します。 モジュールがロードされた時点で、固定のシードによりこの状態は初期化されます。 次の手続きでグローバルな乱数状態のシードを読み出したり、新たな値に 設定することができます。

Function: random-data-seed
Function: (setter random-data-seed) seed-value

引数無しでrandom-data-seedを呼ぶと、現在の乱数状態を初期化した シード値が返されます。

ジェネリックなセッターと一緒に使えば、乱数状態をseed-valueをシードとして 再初期化することができます。

 
(random-data-seed)  ⇒ integer

; reinitialize the random state with a new random seed.
(set! (random-data-seed) 1) 

註: この手続きはパラメータインタフェース (引数として値を与えるとそれを新たな 値に設定し、以前の値を返す) にはなっていません。本質的に、パラメータとは 相容れないからです (gauche.parameter - パラメータ参照)。 得られるのは現在の乱数状態の出発点となるシード値であって、 現在の乱数状態そのものではありません。一旦別のシード値に切り替えて、 その後元のシード値に戻した場合、乱数状態は切り替えた時点に戻るのではなく、 あらためて初期化されます。

乱数状態を一時的に切り替えて、その後で切り替えた時点の状態を確実に回復したい場合は、 次に示すwith-random-data-seedを使ってください。

Function: with-random-data-seed seed thunk

現在のグローバルな乱数状態を保存し、乱数状態をseedで初期化して thunkを実行します。thunkから帰ってくるか、 制御がthunkを抜け出した場合、乱数状態はwith-random-data-seedが 呼び出された時点の状態に戻されます。

デフォルトのランダムシードが固定なのは、何もしなければ再現可能な振る舞いが得られる からです。

プリミティブデータ型のジェネレータ

以下のジェネレータは特に断りが無い限り、一様分布するデータを生成します。

例の中では、生成された値を具体的に示すために gauche.generatorモジュールのgenerator->listを 使っています。ジェネレータを扱うユーティリティについては gauche.generator - ジェネレータを参照してください。

Function: integers$ size :optional (start 0)
Function: integers-between$ lower-bound upper-bound

正確な整数のジェネレータを作成します。 integer$が返すジェネレータは、 start 以上 start + size 未満の整数を一様に発生させます。 integers-between$はが返すジェネレータは、lower-bound以上、 upper-bound以下の整数を一様に発生させます。

 
;; サイコロ
(define dice (integers$ 6 1))

;; サイコロを10回振ってみる
(generator->list dice 10)
 ⇒ (6 6 2 4 2 5 5 1 2 2)
Function: fixnums
Function: int8s
Function: uint8s
Function: int16s
Function: uint16s
Function: int32s
Function: uint32s
Function: int64s
Function: uint64s

固定範囲の一様整数ジェネレータです。 それぞれ、fixnumおよび8/16/32/64ビットの符号つき/符号無し整数を生成します。

 
(generator->list int8s 10)
 ⇒ (20 -101 50 -99 -111 -28 -19 -61 39 110)
Function: booleans

真偽値(#f#t)を等確率で生成します。

 
(generator->list booleans 10)
 ⇒ (#f #f #t #f #f #t #f #f #f #f)
Function: chars$ :optional char-set

char-setにある文字を一様分布で生成するジェネレータを作ります。 char-setが省略された場合は#[A-Za-z0-9]が使われます。

 
(define alphanumeric-chars (chars$))

(generator->list alphanumeric-chars 10)
 ⇒ (#\f #\m #\3 #\S #\z #\m #\x #\S #\l #\y)
Function: reals$ :optional size start
Function: reals-between$ lower-bound upper-bound

与えられた範囲の実数値を一様に生成するジェネレータを返します。 reals$の返すジェネレータは、 start 以上 start + size 以下の実数値を生成します。 sizeのデフォルト値は1.0startのデフォルト値は0.0です。 reals-between$の返すジェネレータは、 lower-bound以上upper-bound以下の実数値を生成します。

 
(define uniform-100 (reals$ 100))

(generator->list uniform-100 10)
 ⇒ (81.67965004942268 81.84927577572596 53.02443813660833)

reals$の返すジェネレータは、integer$と違って 上限の値を生成し得ることに注意してください。限界値を除外したい場合は、 gfilter等を使ってその値を棄却します。

 
(define generate-from-0-below-1
  (gfilter (^r (not (= r 1.0))) (reals$ 1.0 0.0)))
Function: samples$ collection

コレクションcollectionから毎回ランダムにひとつ要素を選んで返すジェネレータを 作成します。

下で説明するsamples-fromと混同しないようにしてください。samples-from は複数のジェネレータを組み合わせてサンプリングするものです。

 
(define coin-toss (samples$ '(head tail)))

(generator->list coin-toss 5)
 ⇒ (head tail tail head tail)

非一様分布

Function: reals-normal$ :optional mean deviation

期待値mean、標準偏差deviationの正規分布に従って実数値を生成する ジェネレータを作ります。省略時はmeanが0.0、deviationが 1.0になります。

Function: reals-exponential$ mean

期待値meanの指数分布に従って実数値を生成するジェネレータを作ります。

Function: integers-geometric$ p

成功確率p (0 ≦ p ≦ 1) である幾何分布に従って 非負整数値を生成するジェネレータを作ります。 期待値は1/p、分散は(1-p)/p^2になります。

Function: integers-poisson$ L

期待値Lのポアソン分布に従う非負整数値を生成するジェネレータを作ります。 分散もLになります。

複合データ型のジェネレータ

Function: samples-from generators

ジェネレータの有限シーケンス (ここではgauche.sequenceがシーケンスとして 扱うもの) を取り、新たなジェネレータを返します。 返されたジェネレータは、値を必要とする度に、 入力となるジェネレータのどれかを等確率で選んで、その入力ジェネレータから値を取ります。

 
(define g (samples-from (list uint8s (chars$ #[a-z]))))

(generator->list g 10)
 ⇒ (207 107 #\m #\f 199 #\o #\b 57 #\j #\e)

註: 固定した要素の集まりからサンプルするジェネレータを作るには、 上の方で説明したsamples$を使ってください。

Function: weighted-samples-from weight&gens

引数は、非負の実数値とジェネレータのペアのリストです。 実数値が「重み」、すなわちペアとなっているジェネレータが選ばれる相対確率を決定します。 重みの総和が1.0である必要はありません。

次の例では、uint8ジェネレータは文字ジェネレータより4倍頻繁に使われます。

 
(define g (weighted-samples-from
           `((4.0 . ,uint8s)
             (1.0 . ,(chars$)))))

(generator->list g 10)
 ⇒ (195 97 #\j #\W #\5 72 49 143 19 164)
Function: pairs-of car-gen cdr-gen

ペアを生成するジェネレータを作ります。 各ペアのcarはジェネレータcar-gen、 cdrはジェネレータcdr-genによって生成されます。

 
(define g (pairs-of int8s booleans))

(generator->list g 10)
 ⇒ ((113 . #t) (101 . #f) (12 . #t) (68 . #f) (-55 . #f))
Function: tuples-of gen …

リストを生成するジェネレータを作ります。 各リストのi番目の要素はi番目の引数のジェネレータによって生成されます。

 
(define g (tuples-of int8s booleans (char$)))

(generator->list g 3)
 ⇒ ((-43 #f #\8) (53 #f #\1) (-114 #f #\i))
Function: permutations-of seq

シーケンスseqの要素をランダムに並べ替えたシーケンスを生成する ジェネレータを作ります。

seqの型は、ビルダーを持つシーケンスである必要があります (gauche.sequence - シーケンスフレームワーク参照)。生成されるオブジェクトは seqと同じ型になります。

 
(generator->list (permutations-of '(1 2 3)) 3)
 ⇒ ((1 2 3) (2 3 1) (3 2 1))

(generator->list (permutations-of "abc") 3)
 ⇒ ("cba" "cba" "cab")
Function: combinations-of size seq

シーケンスseqからランダムにsize個の要素を取り出して 並べたシーケンスを生成するジェネレータを作ります。

seqの型は、ビルダーを持つシーケンスである必要があります (gauche.sequence - シーケンスフレームワーク参照)。生成されるオブジェクトは seqと同じ型になります。

 
(generator->list (combinations-of 2 '(a b c)) 5)
 ⇒ ((a c) (a b) (a c) (b a) (a c))

(generator->list (combinations-of 2 '#(a b c)) 5)
 ⇒ (#(a c) #(b c) #(c b) #(b a) #(b c))

以下の手続きは、省略可能なsizer引数を取ります。 sizer引数は非負整数か、非負整数を生成するジェネレータで、 その値(もしくは生成された値)が、最終的に生成されるデータの長さを決定します。

Gaucheの他のほとんどの手続きと違って、sizer引数は省略されない時は 最後の引数よりも前に来ます。これは統一性を損ないますが、 (lists-of 3 booleans) のように書ける、という誘惑に勝てませんでした。

sizer引数が省略された場合、パラメータdefault-sizerの値が 使われます。default-sizerのデフォルトは (integers-poisson$ 4)で作られるジェネレータです。

Function: lists-of item-gen
Function: lists-of sizer item-gen
Function: vectors-of item-gen
Function: vectors-of sizer item-gen
Function: strings-of
Function: strings-of item-gen
Function: strings-of sizer item-gen

それぞれ、リスト、ベクタ、文字列を生成するジェネレータを作ります。 作られるデータの各要素はジェネレータitem-genから取られます。 各データの長さはsizerにより決定されます。

strings-ofの場合はitem-genも省略することができます。 その場合は、(chars$)で作られるジェネレータが使われます。

 
(generator->list (lists-of 3 uint8s) 4)
 ⇒ ((254 46 0) (77 158 46) (1 134 156) (74 5 110))

;; デフォルトのsizerを使う
(generator->list (lists-of uint8s) 4)
 ⇒ ((93 249) (131 97) (98 206 144 247 241) (126 156 31))

;; sizerにジェネレータを使う
(generator->list (strings-of (integers$ 8) (chars$)) 5)
 ⇒ ("dTJYVhu" "F" "PXkC" "w" "")
Function: sequences-of class item-gen
Function: sequences-of class sizer item-gen

クラスclassのインスタンスであるシーケンスを生成するジェネレータを作ります。 シーケンスの要素はitem-genにより生成されます。 各シーケンスの長さはsizer引数 (省略時はdefault-sizerの値) によって決められます。sizerは非負整数か、非負整数を生成するジェネレータです。

class<sequence>のサブクラスであり、 ビルダーインタフェースを実装していなければなりません。

 
(generator->list (sequences-of <u8vector> 4 uint8s) 3)
 ⇒ (#u8(95 203 243 46) #u8(187 199 153 152) #u8(39 114 39 25))
Parameter: default-sizer

lists-ofvectors-ofstrings-ofsizer引数が省略された場合に使われるsizerです。

値は、非負整数か、非負整数を生成するジェネレータでなければなりません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.13 data.ring-buffer - リングバッファ

Module: data.ring-buffer

リングバッファは、両端を指す二つのポインタを持つ配列と考えられます。良くある使い方は、 生産者が配列の一方の端にデータを追加してゆき、消費者がもう一方の端から データを取り出してゆくというものです。もし端を示すポインタが配列の 終端に達したら、それはもう一方の端につながっているように振る舞います。 「リング」バッファと呼ばれるのはそのためです。

このモジュールの提供するリングバッファは、どちらの端にもデータを追加し、 どちらの端からもデータを取り出すことができます。したがって機能的には 両端キュー(double-ended queue, deque)の一種とも考えられます。 また、O(1)で要素にアクセス可能で、 さらにバッファが一杯になった時の振る舞いをカスタマイズできます。

リングバッファがデータを格納するバッキングストレージとして、 通常のベクタかユニフォームベクタを使えます。

Function: make-ring-buffer :optional initial-storage :key overflow-handler

リングバッファを作成します。デフォルトでは、データ格納のための領域として 新たなベクタがアロケートされます。かわりに、呼び出し側でベクタかユニフォームベクタ を用意してinitial-storageに渡すこともできます。その場合、 渡すベクタやユニフォームベクタは変更可能でなければなりません。以降、 リングバッファがそのベクタを変更するので、呼び出し側でベクタを変更 したりベクタの内容が保存されることを期待してはいけません。

overflow-handlerキーワード引数は、バッファがフルの状態で 新たな要素が追加されようとした時の振る舞いを指定します。 引数は手続きか、シンボルerroroverwriteのいずれかでなければなりません。

引数が手続きの場合、リングバッファと(一杯になった)バッキングストレージ が引数として渡されます。この手続きは以下の3つのアクションのうちひとつを 実行しなければなりません。(1)渡されたバッキングストレージ(ベクタか ユニフォームベクタ)と同じ型で、より大きなベクタまたはユニフォームベクタをアロケートして 返す。(2)シンボルerrorを返す。(3)シンボルoverwriteを返す。 手続きがベクタもしくはユニフォームベクタを返した場合は、それが 新たなバッキングストレージとして使われます。 返すベクタの内容を設定する必要はありません。リングバッファが適切に内容のコピーや 必要な初期化を行います。手続きがerrorを返した場合は、 “buffer is full”のエラーが投げられます。 手続きがoverwriteを返した場合は、 新たな要素で古い要素を上書きします(つまり、 あたかも別の端から1要素がポップされて捨てられ、その後で新たな要素が 追加されたかのように振る舞います)。

overflow-handler引数にシンボルerroroverwrite を渡した場合は、あたかもそれらのシンボルを無条件に返すoverflow handlerが 指定されたかのように振る舞います。

overflow-handlerが指定されない場合のデフォルトの動作は、 元のバッキングストレージの倍の容量をアロケートして返すことです。 下のmake-overflow-doublerを使うと、カスタマイズした オーバフローハンドラを簡単に作ることができます。

Function: make-overflow-doubler :key max-increase max-capacity

make-ring-bufferoverflow-handlerキーワード引数に 渡せる手続きを作って返します。

返される手続きは、リングバッファとバッキングストレージを引数に取り、 以下のとおり振る舞います。

max-increasemax-capacityのデフォルト値はどちらも +inf.0です。

Function: ring-buffer-empty? rb

リングバッファrbが空なら#tを、そうでなければ#fを返します。

Function: ring-buffer-full? rb

リングバッファrbがフルなら#tを、そうでなければ#fを返します。

Function: ring-buffer-num-elements rb

リングバッファrbが格納している要素数を返します。

Function: ring-buffer-capacity rb

リングバッファの現在のバッキングストレージの容量(格納可能な要素数)を返します。

Function: ring-buffer-front rb
Function: ring-buffer-back rb

リングバッファrbの先頭あるいは末尾の要素をそれぞれ返します。 バッファが空の場合はエラーが投げられます。

Function: ring-buffer-add-front! rb elt
Function: ring-buffer-add-back! rb elt

リングバッファrbの先頭もしくは末尾にそれぞれ要素を追加します。 rbがフルの場合の振る舞いは、バッファのオーバーフローハンドラによって 決定されます。詳しくはmake-ring-bufferのエントリを参照してください。

Function: ring-buffer-remove-front! rb
Function: ring-buffer-remove-back! rb

リングバッファrbの先頭もしくは末尾から要素をひとつ取り、取った要素を 返します。 バッファが空の場合はエラーが投げられます。

Function: ring-buffer-ref rb index :optional fallback

リングバッファrbindex番目の要素を返します。 要素は先頭から数えられます。したがって、新たな要素が先頭に付け加えられた場合、 既存の要素のインデックスはひとつづつずれます。

indexrbの持つ要素の範囲外の場合、fallbackが与えられていれば それを返し、そうでなければエラーが投げられます。

Function: ring-buffer-set! rb index value

リングバッファrbindex番目の要素にvalueをセットします。 要素は先頭から数えられます。したがって、新たな要素が先頭に付け加えられた場合、 既存の要素のインデックスはひとつづつずれます。

indexrbの持つ要素の範囲外の場合はエラーが投げられます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.14 data.sparse - 疎なデータコンテナ

Module: data.sparse

このモジュールは、非負整数でインデックスされる空間効率の良いデータ構造である 疎ベクタ (sparse vector)と 疎行列 (sparse matrix)、 および 疎ベクタを格納領域に用いるハッシュテーブルである疎テーブル (sparse table) を提供します。

疎ベクタは非負整数のインデックスと値を関連付けます。 整数でインデックスされるので名前にベクタがついていますが、 連続するメモリに置かれる配列ではなく、むしろ連想配列のようなものです。 (内部的には、現在の実装はコンパクトなトライを使っています)。 少なくとも2^32-1までのインデックスが使えることは保証されています。 実装が許す最大のインデックスのビット長は sparse-vector-max-index-bitsで得ることができます。 (将来的にはこの制限を無くす計画です)。

通常のベクタと違い、疎ベクタは作る時に大きさを指定する必要がありません。 サポートされている範囲内ならどんなインデックスにでも単に値を格納できます。

 
(define v (make-sparse-vector))

(sparse-vector-set! v 0 'a)
(sparse-vector-ref v 0) ⇒ a

(sparse-vector-set! v 100000000 'b)
(sparse-vector-ref v 100000000) ⇒ b

;; set! also work
(set! (sparse-vector-ref v 100) 'c)
(sparse-vector-ref v 100) ⇒ c

値がセットされていない要素にアクセスすると、デフォルトではエラーが通知されます。 ベクタごとに既定値を設定したり、 sparse-vector-refの省略可能引数にフォールバック値を 渡すことでエラーを回避できます。

 
(sparse-vector-ref v 1)        ⇒ error
(sparse-vector-ref v 1 'noval) ⇒ noval

(let1 w (make-sparse-vector #f :default 'x)
  (sparse-vector-ref w 1))     ⇒ x

疎行列は、二つの整数でインデックスされるという点以外は疎なベクタと同じです。

疎テーブルはハッシュテーブルと同じように動作しますが、キーのハッシュ値を インデックスとして値を疎ベクタに格納しています。

これらの疎なデータコンテナの主な目的は、メモリ効率です。 ベクタに値を入れておきたいけれどごく一部のインデックスしか使わないことが 分かっている、と言った場合に、巨大なベクタをアロケートしてその大部分を 使わないでおくのは明らかに無駄でしょう。ただそれだけではありません。 Gaucheのガベージコレクタは、一続きの巨大なメモリ領域を確保するのとあまり 相性が良くありません。大きなベクタを大量に使うとGCのオーバヘッドが急速に増えます。 Gaucheの組み込みハッシュテーブルは データストアに通常のベクタを使っているのですが、 大量のデータを詰め込むとその効果が目に見えてきます。ヒープサイズが急に増え、 GCはより頻繁に走り、しかも一回一回に要する時間は長くなるでしょう。 一方で、疎テーブルは大量のデータに対してもかなり安定に動作します。

疎なデータコンテナは、単純なデータコンテナに比べるとアクセスにオーバヘッドはあります。 通常のハッシュテーブルより若干遅いですし、通常のベクタと比べるとかなり遅いです。 けれども、テーブル内のデータ数が大きくなる領域では、通常のハッシュテーブルの アクセス時間の方が急速に悪化するため、いずれ二つのアクセス時間は そこそこ同じになります。

どちらを使うべきかはアプリケーションに依存します。良く分からないのであれば、 ベンチマークを取りましょう。簡単な基準としては、数万以上のエントリを持つ ハッシュテーブルを数個以上作るなら、疎テーブルの方が良くなるかもしれません。 実行中に“repeated allocation of large blocks”というGCからの 警告を目にしたなら、疎テーブルへの切り替えを考えてみましょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.14.1 疎なベクタ

Class: <sparse-vector-base>

疎なベクタの抽象ベースクラスです。<dictionary><collection>を 継承しています。<sequence>は継承していないことに注意してください。 疎なベクタは整数でインデクス可能ですが、インデクスの順番に要素にアクセスする手段を 持っていません。

疎なベクタは、<vector>と同様、任意のSchemeオブジェクトを格納可能である ものもあれば、<s8vector>等と同様に特定の範囲の数値のみ格納可能なものもあります。 全ての疎なベクタは同一セットのAPIで利用可能です。

疎なベクタはまた、コレクションAPIとディクショナリAPIを実装しています。これらについては gauche.collection - コレクションフレームワークおよびgauche.dictionary - ディクショナリフレームワークを 参照してください。

Class: <sparse-vector>
Class: <sparse-TAGvector>

疎なベクタの具体クラスです。それぞれ<sparse-vector-base>を継承します。

<sparse-vector>のインスタンスは任意のSchemeオブジェクトを格納できます。

TAGs8, u8, s16, u16, s32, u32, s64, u64, f16, f32, f64のいずれかで、 それぞれの疎なベクタの格納可能な値は、gauche.uvectorの 対応する<TAGvector>に準じます(gauche.uvector - ユニフォームベクタ参照)。 つまり、<sparse-u8vector>の要素には0以上255以下の正確な整数を 格納できます。

Function: make-sparse-vector :optional type :key default

空の疎なベクタを作成して返します。type引数は#f(デフォルト)か、 <sparse-vector-base>のサブクラスのどれか、 あるいはシンボルs8, u8, s16, u16, s32, u32, s64, u64, f16, f32, f64のいずれかを 指定できます。

typeが省略されるか#fの場合は<sparse-vector>のインスタンスが 作られます。typeがクラスであればそのインスタンスが、またシンボルであれば、 対応する<sparse-TAGvector>のインスタンスが作られます。

キーワード引数defaultによって、作成するベクタの要素既定値を指定できます。 この引数が与えられると、ベクタは全てあらかじめその値で埋められているかのように 振る舞います (ただしイテレータは陽にセットされた値のみ取り出します)。

キーワード引数を与える場合、それに先立って省略可能引数も与える必要が あることに注意してください。

 
(define v (make-sparse-vector 'u8 :default 128))

(sparse-vector-ref v 0) ⇒ 128
Function: sparse-vector-max-index-bits

実装が利用できる、疎なベクタのインデックスの最大のビット数を返します。 例えばこれが32を返したなら、インデックスとして(expt 2 32)まで 使えるということです。この値は最低でも32であることが保証されています。

以下のエントリにおいて、引数svは疎なベクタのインスタンスです。 他のオブジェクトが渡された場合はエラーが報告されます。

Function: sparse-vector-copy sv

疎なベクタsvのコピーを返します。

Function: sparse-vector-ref sv k :optional fallback

疎なベクタsvのインデックスkにある要素を返します。 kは正確な整数でなければなりません。

kに対応する要素がない場合は、次のように振る舞います。

Function: sparse-vector-set! sv k value

疎なベクタsvk番めの要素にvalueを設定します。 kは非負の正確な整数で、許される最大のインデックス以下でなければなりません。

Function: sparse-vector-num-entries sv

svの持つ要素数を返します。

Function: sparse-vector-exists? sv k

svk番目のエントリが値を持っていれば#tを、 そうでなければ#fを返します。 #t

Function: sparse-vector-delete! sv k

svk番目のエントリが値を持っていれば、それを消去して #tを返します。 そうでなければ何もせずに#fを返します。

Function: sparse-vector-clear! sv

疎なベクタを空にします。

Function: sparse-vector-inc! sv k delta :optional (fallback 0)

これは次のコードと同じ動作をしますが、数値を格納する疎ベクタでは特に 効率よく動作します。

 
(sparse-vector-set! sv k (+ (sparse-vector-ref sv k fallback) delta))

もし加算の結果がsvに許される数値の範囲を越えた場合は、 エラーが投げられます。将来はそういった場合に値をクランプするオプションも 用意する予定です。

Function: sparse-vector-update! sv k proc :optional fallback
Function: sparse-vector-push! sv k val
Function: sparse-vector-pop! sv k :optional fallback

疎なベクタのエントリの値を取り出してアップデートするパターンを 簡単に書くためのルーチンです。それぞれ、hash-table-update!hash-table-push!hash-table-pop!と 同じように動作します。(ハッシュテーブル参照)。

The following procedures traverses a sparse vector. Note that elements are not visited in the order of index; it’s just like hash table traversers.

At this moment, if you want to walk a sparse vector with increasing/decreasing index order, you have to get a list of keys by sparse-vector-keys, sort it, then use it to retrieve values. We may add an option in future to make-sparse-vector so that those walk operation will be more convenient.

Function: sparse-vector-fold sv proc seed

For each entry in sv, calls proc as (proc k_n v_n seed_n), where k_n is an index and v_n is a value for it, and seed_n is the returned value of the previous call to proc if n >= 1, and seed if n = 0. Returns the value of the last call of proc.

Function: sparse-vector-for-each sv proc
Function: sparse-vector-map sv proc

Calls proc with index and value, e.g. (proc k value), for each element of sv.

The results of proc are discarded by sparse-vector-for-each, and gathered to a list and returned by sparse-vector-map.

Function: sparse-vector-keys sv
Function: sparse-vector-values sv

Returns a list of all keys and all values in sv, respectively.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.14.2 疎行列

疎行列は要素が2つの非負整数でインデックスされること以外は疎なベクタと同じです。

註:この疎行列の実装は、事前に構造が分かっていない疎な行列をそれなりに空間効率良く 扱うことを目的としています(例えば、2Dのマップにランドマークを記録してゆく、 といったイメージです)。もし探しているものが、数値計算用に特定の構造を持つ疎行列の 実装でしたら、このモジュールは速度的に十分ではないでしょう。

現在の実装では、それぞれのインデックスの最大ビット数が sparse-vector-max-index-bitsの半分までという制限があります。 将来はこの制限をなくす予定です。

Class: <sparse-matrix-base>

An abstract base class of sparse matrixes. Inherits <collection>.

Like sparse vectors, a sparse matrix can be of type that can store any Scheme objects, or that can store only certain types of numbers.

All of these sparse matrix subtypes can be accessed by the same API.

Class: <sparse-matrix>
Class: <sparse-TAGmatrix>

The actual sparce matrix classes. Inherits <sparse-matrix-base>. An instance of <sparse-matrix> can contain any Scheme objects.

TAG either one of s8, u8, s16, u16, s32, u32, s64, u64, f16, f32, or f64. The range of values an instance of those classes can hold is the same as the corresponding <TAGvector> class in gauche.uvector (see section gauche.uvector - ユニフォームベクタ). That is, <sparse-u8matrix> can have exact integer values between 0 and 255.

Function: make-sparse-matrix :optional type :key default

Creates an empty sparse matrix. The type argument can be #f (default), one of subclasses of <sparse-matrix-base>, or a symbol of either one of s8, u8, s16, u16, s32, u32, s64, u64, f16, f32, or f64.

If type is omitted or #f, a <sparse-matrix> is created. If it is a class, an instance of the class is created (It is an error to pass a class that is not a subclass of <sparse-matrix-base>.) If it is a symbol, an instance of corresponding <sparse-TAGmatrix> is created.

You can specify the default value of the matrix by default keyword argument. If given, the vector behaves as if it is filled with the default value (but the matrix iterator only picks the values explicitly set).

Note that you have to give the optional argument as well to specify the keyword argument.

Function: sparse-matrix-num-entries mat

Returns the number of entries explicitly set in a sparse matrix mat.

Function: sparse-matrix-ref mat x y :optional fallback

Returns an element indexed by (x, y) in a sparse matrix mat. If the indexed element isn’t set, fallback is returned if provided; otherwise, if the matrix has the default value, it is returned; otherwise, an error is raised.

Function: sparse-matrix-set! mat x y value

Set value to the sparse matrix mat at the location (x, y).

Function: sparse-matrix-exists? mat x y

Returns #t iff the sparse matrix mat has a value at (x, y).

Function: sparse-matrix-clear! mat

Empties the sparse matrix mat.

Function: sparse-matrix-delete! mat x y

Remove the value at (x, y) from the sparse matrix mat.

Function: sparse-matrix-copy mat

Returns a fresh copy of mat.

Function: sparse-matrix-update! mat x y proc :optional fallback

Call proc with the value at (x, y) of the sparse matrix, and sets the result of proc as the new value of the location.

The optional fallback argument works just like sparse-matrix-ref; if provided, it is passed to proc in case the matrix doesn’t have a value at (x, y). If fallback isn’t provided and the matrix doesn’t have a value at the location, the default value of the matrix is used if it has one. Otherwise, an error is signalled.

Function: sparse-matrix-inc! mat x y delta :optional fallback
 
(sparse-matrix-update! mat x y (cut + <> delta) fallback)
Function: sparse-matrix-push! mat x y val
 
(sparse-matrix-update! mat x y (cut cons val <>) '())
Function: sparse-matrix-pop! mat x y
 
(rlet1 r #f
  (sparse-matrix-update! mat x y (^p (set! r (car p)) (cdr p))))
Function: sparse-matrix-fold mat proc seed

Loop over values in the sparse matrix mat. The procedure proc is called with four arguments, x, y, val and seed, for each index (x, y) which has the value val. The initial value of seed is the one given to sparse-matrix-fold, and the result of proc is passed as the next seed value. The last result of proc is returned from sparse-matrix-fold.

The procedure proc is only called on the entries that’s actually has a value, and the order of which the procedure is called is undefined.

Function: sparse-matrix-map mat proc
 
(sparse-matrix-fold sv (^[x y v s] (cons (proc x y v) s)) '()))
Function: sparse-matrix-for-each mat proc
 
(sparse-matrix-fold sv (^[x y v _] (proc x y v)) #f))
Function: sparse-matrix-keys mat
 
(sparse-matrix-fold sv (^[x y _ s] (cons (list x y) s)) '())
Function: sparse-matrix-values mat
 
(sparse-matrix-fold sv (^[x y v s] (cons v s)) '())

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.14.3 疎なテーブル

Class: <sparse-table>

A class for sparse table. Inherits <dictionary> and <collection>.

Operationally sparse tables are the same as hash tables, but the former consumes less memory in trade of slight slower access. (Roughly x1.5 to x2 access time when the table is small. As the table gets larger the difference becomes smaller.)

Function: make-sparse-table comparator

Creates and returns an empty sparse table. The comparator argument specifies how to compare and hash keys; it must be either a comparator (see section 基本的な比較器), or one of the symbols eq?, eqv?, equal? and string=?, like hash tables (see section ハッシュテーブル). If it is a symbol, eq-comparator, eqv-comparator, equal-comparator or string-comparator are used, respectively.

Function: sparse-table-comparator st

Returns the comparator used in the sparse table st.

Function: sparse-table-copy st

Returns a copy of a sparse table st.

Function: sparse-table-num-entries st

Returns the number of entries in a sparse table st.

Function: sparse-table-ref st key :optional fallback

Retrieves a value associated to the key in st. If no entry with key exists, fallback is returned when it is provided, or an error is signaled otherwise.

Function: sparse-table-set! st key value

Sets value with key in st.

Function: sparse-table-exists? st key

Returns #t if an entry with key exists in st, #f otherwise.

Function: sparse-table-delete! st key

Deletes an entry with key in st if it exists. Returns #t if an entry is actually deleted, or #f if there hasn’t been an entry with key.

Function: sparse-table-clear! st

Empties st.

Function: sparse-table-update! st key proc :optional fallback
Function: sparse-table-push! st key val
Function: sparse-table-pop! st key :optional fallback
Function: sparse-table-fold st proc seed
Function: sparse-table-for-each st proc
Function: sparse-table-map st proc
Function: sparse-table-keys st
Function: sparse-table-values st

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.15 data.trie - Trie

Module: data.trie

このモジュールはTrieを提供します。Trieはディレクトリに似 たデータ構造で、キーを値に写像します。また、キーは任意のシーケンスです。 内部的にはデータはツリーとして保持されます。このとき各ノードがキーシー ケンスの各要素に対応します。キーの検索は O(n) で、n はキーの長さです。 したがって、全体のエントリ数には余り影響を受けません。また、キーが共通 の接頭辞をもつような値の集合を簡単にみつけられます。

以下のサンプルを見れば考え方が理解できると思います。

 
(define t (make-trie))   ;; create a trie

(trie-put! t "pho" 3)    ;; populate the trie
(trie-put! t "phone" 5)
(trie-put! t "phrase" 6)

(trie-get t "phone")  ⇒ 5  ;; lookup

(trie-common-prefix t "pho")       ;; common prefix search
  ⇒ (("phone" . 5) ("pho" . 3))
(trie-common-prefix-keys t "ph")
  ⇒ ("phone" "pho" "phrase")

Trieでは文字列キーを使うことが多いですが、それに限定される必要はありま せん。あらゆるシーケンス(see section gauche.sequence - シーケンスフレームワーク)をキーにすることが できます。キーの型が違えば、別のキーとして扱われます。

 
(trie-put! t '(#\p #\h #\o) 8)  ;; different key from "pho"

Trieは<collection>を継承しており、コレクションフレームワークを ビルダも含めて実装しています。それゆえ、ジェネリックなコレクション操作 をTrieに適用することが可能です(gauche.collection - コレクションフレームワーク参照)。 反復するとTrieの各要素がキーと値の対として現れます。

Class: <trie>

Trieクラス。パブリックなスロットはありません。trieを操作するには以下の 手続きを使ってください。

このクラスはまた、ディクショナリインタフェースを実装しています (ディクショナリのためのジェネリック関数参照)。

Function: make-trie :optional tab-make tab-get tab-put! tab-fold tab-empty?

空のtrieを生成し返します。オプション引数は、 内部木のノードをどのようにマージするかをカスタマイズする手続きです。

それぞれのノードは子のノードを格納するテーブルを持つことができます。 キーシーケンスの要素でインデックスできます。(たとえば、trieがキーとし え文字列を使っているとすると、ノードのテーブルは文字でインデックスされ ています。)

tab-make

引数なしの手続き。呼ばれるとノード用の空テーブルを生成し返します。

tab-get tab elt

eltでインデックスされた子ノードを返すか、あるいはeltに対応 する子がテーブルにない場合には #fを返します。

tab-put! tab elt child-node

child-node#fでなければ、child-nodeeltとい うインデックスをつけて保存します。child-node#fなら eltのインデックスをもつエントリを削除します。どちらの場合にも この手続きは更新されたテーブルを返します。

tab-fold tab proc seed

tab内の各インデックスと要素ごとにprocを呼びます。シード値 が順に渡されていきます。シード値の初期値はseedです。すなわち、 procの型は(index, node, seed) -> seed のような型というこ とになります。返り値は最後のprocの適用結果です。

tab-empty? tab

tabが空なら#tを、そうでなければ#fを返す手続きです。 この手続きは省略するか#fを渡すことができます。その場合は空かどうかを チェックするのにtab-fold手続きが使われますが、少々重くなるかもしれません。

デフォルトではeqv?-ハッシュ可能であることが仮定されます。すなわ ち、以下の手続きが使われます。

 
tab-make: (lambda () (make-hash-table 'eqv?))

tab-get:  (lambda (tab k) (hash-table-get tab k #f))

tab-put!: (lambda (tab k v)
            (if v
              (hash-table-put! tab k v)
              (hash-table-delete! tab k))
            tab)

tab-fold: hash-table-fold

tab-empty?: (lambda (tab) (zero? (hash-table-num-entries tab)))

以下の例では子を管理するのに連想リストを用いるtrieを作成しています。 文字列キーの比較は大文字小文字を無視する方法で行っています。

 
(make-trie list
           (cut assoc-ref <> <> #f char-ci=?)
           (lambda (t k v)
             (if v
               (assoc-set! t k v char-ci=?)
               (alist-delete! k t char-ci=?)))
           (lambda (t f s) (fold f s t))
           null?)

tab-put!が更新されたテーブルを返すというのは重要で、これのおか げで、テーブル構造をおてがるに置き換えることができます。たとえば、 子の数が少い場合にはテーブルに連想リストを使い、いったん子の数がある閾 値を越えたら、(文字コードでインデックスされた)ベクタを使うように設計す ることができます。

Function: trie params kv …

初期の内容がkv … であるようなtrieを構成します。ここで、 kvはキーと値の対です。paramsはtrieを生成するときに make-trieに渡される引数のリストです。以下の例は2つのエントリ とデフォルトのテーブル手続をもつtrieを生成します。

 
(trie '() '("foo" . a) '("bar" . b))
Function: trie-with-keys params key …

キーにだけ関心がある場合には便利なtrie。各値はキーと同じ。以下 の例では2つのエントリとデフォルトのテーブル手続をもつtrieを生成します。

 
(trie-with-keys '() "foo" "bar")
Function: trie? obj

objがtrieなら#tを返し、さもなければ#fを返します。

Function: trie-num-entries trie

trie中のエントリの数を返します。

Function: trie-exists? trie key

triekeyというキーのエントリを含む場合には#tを返し、 さもなければ、#fを返します。

 
(let1 t (trie '() '("foo" . ok))
  (list (trie-exists? t "foo")
        (trie-exists? t "fo")
        (trie-exists? t "bar")))
  ⇒ '(#t #f #f)
Function: trie-partial-key? trie seq

trieの中に少なくともひとつ、seqと同じではないがseqを プリフィクスとするようなキーがあれば#tを返します。 seqと一致するキーが他にあるかないかは結果には影響を及ぼしません。 下の例を見てください。

 
(define t (trie '() '("foo" . ok) '("fo" . ok)))

(trie-partial-key? t "f")    ⇒ #t
(trie-partial-key? t "fo")   ⇒ #t
(trie-partial-key? t "foo")  ⇒ #f
(trie-partial-key? t "bar")  ⇒ #f
Function: trie-get trie key :optional fallback

trie中のkeyをもつエントリがあれば、それにむすびついている 値を返します。そのようなエントリがない場合、fallbackが与えられて いればそれを返し、さもなければ、エラーシグナルがあがります。

Function: trie-put! trie key value

keyに結びついたvaluetrieに挿入します。

Function: trie-update! trie key proc :optional fallback

trie中のエントリの検索が一度きりしか起らないことをのぞけば以下の コードのように動きます。

 
(let ((val (trie-get trie key fallback)))
  (trie-put! trie key (proc val)))
Function: trie-delete! trie key

trieからkeyに関連するエントリを削除します。 そのようなエントリがない場合にはこの手続きはなにもしません。

Function: trie->list trie

trieの各エントリを(key . value)という対にして すべてのエントリの対のリストを返します。エントリの順序は未定義です。

Function: trie-keys trie
Function: trie-values trie

それぞれ、trieのすべてのキーのリスト、すべての値のリストを返しま す。順序は未定義です。

Function: trie->hash-table trie ht-type

ht-typeタイプのハッシュテーブル(ハッシュテーブルのタイプについて はハッシュテーブルを参照)を作成し、trieのすべてのキーと値の対を セットします。

Function: trie-longest-match trie seq :optional fallback

seqのプレフィクスになっているキーのうちもっとも長いものを見つけて、 そのキーと値のペアを返します。そういったキーが見つからなかった場合は、 fallbackが与えられればそれを返し、なければエラーが通知されます。

この手続きと以下のtrie-common-prefix-*手続きを混同しないようにしてください。 この手続きでは、キーが引数のプレフィクスです。 trie-common-prefix-*手続きでは、引数がキーのプレフィクスです。

 
(let1 t (make-trie)
  (trie-put! t "a"  'a)
  (trie-put! t "ab" 'ab)

  (trie-longest-match t "abc")  ⇒ ("ab" . ab)
  (trie-longest-match t "acd")  ⇒ ("a"  . a)
  (trie-longest-match t "ab")   ⇒ ("ab" . ab)
  (trie-longest-match t "zy")   ⇒ error
  )
Function: trie-common-prefix trie prefix
Function: trie-common-prefix-keys trie prefix
Function: trie-common-prefix-values trie prefix

prefixではじまるキーをもつエントリをすべて集め、 trie-common-prefixはその各エントリを(key . value)の対にし たリストを返します。trie-common-prefix-keysは、キーのリストを trie-common-prefix-valuesは値のリストを返します。返されるリスト のエントリの順序は未定義です。 trieに指定したprefixをもつキーのエントリがなければ、 空リストが返されます。

接頭辞照合ではシーケンスの型を考慮しないことに注意してください。 trieのなかに"foo"(#\f #\o #\o)に対応するエントリ があれば、(trie-common-prefix trie "foo")はその両方を返します。

Function: trie-common-prefix-fold trie prefix proc seed

prefixではじまるキーをもつ各エントリに対して、procを3つの 引数、エントリのキー、値、現在のシード値で呼びます。seedは最初の シード値として使われ、procが返す値は次のprocの呼び出しのシー ド値として使われます。procが返した最後の値が trie-common-prefix-foldから返ります。 procが適用される順序は未定義です。trieprefixを持つ キーのエントリを含まない場合にはprocが呼ばれることはなく、 seedが返ります。

Function: trie-common-prefix-map trie prefix proc
Function: trie-common-prefix-for-each trie prefix proc

mapfor-eachfoldを合せたのと同じように、 trie-common-prefix-foldに合せたものです。 trie-common-prefix-mapprocをマッチするエントリのキーと 値に適用し結果をリストにあつめます。 trie-common-prefix-for-eachも同じくprocを適用しますが 結果は捨てます。

Function: trie-fold trie proc seed
Function: trie-map trie proc
Function: trie-for-each trie proc

これらの手続きはcommon-prefix版とおなじような働きをしますが、 trie全体をトラバースします。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.16 dbi - データベース非依存アクセス層

Module: dbi

このモジュールはさまざまなリレーショナルデータベースシステム(RDBMS)に アクセスするための統一されたインタフェースを提供します。個々のデータベー スシステムに特有の操作についてはデータベースドライバ(DBD)モジュールに パッケージされています。DBDのモジュールは通常暗黙裏にDBIの層からロード されます。

このモジュールは Perl の DBI/DBD アーキテクチャに強く影響を受けていま す。Perl DBIを使った経験があるなら、このモジュールを使うのはたやすいで しょう。

まず例を見るほうがよいでしょう。以下はdbiモジュールを使ったデー タベースアクセス例の概要です。

 
(use dbi)
(use gauche.collection) ; to make 'map' work on the query result

(guard (e ((<dbi-error> e)
           ;; handle error
           ))
  (let* ((conn   (dbi-connect "dbi:mysql:test;host=dbhost"))
         (query  (dbi-prepare conn
                   "SELECT id, name FROM users WHERE department = ?"))
         (result (dbi-execute query "R&D"))
         (getter (relation-accessor result)))
    (map (lambda (row)
           (list (getter row "id")
                 (getter row "name")))
         result)))

dbi-connectにわたす、"dbi:mysql:test;host=dbhost"引数以 外は使用するデータベースシステムに依存する部分はありません。この引数に より、dbiモジュールはこのアクセスがmysqlデータベースに対 するものであると判断します。そして、mysql-特有の手続を扱うようにします。 別のデータベースシステムwhateverを使いたいのであれば、単に "dbi:whatever:parameter"dbi-connectに渡せ ばよく、dbd.whateverがシステムにインストールされていれば同じよ うにできます。

データベースに対するクエリはdbi-prepareを使って作成します。 クエリの発行はdbi-executeで行います。このような2つのフェーズを 使うことで、パラメータ化されたSQL文の一種であるプリペアドクエリを 作ることができます。上の例ではクエリはSQL文の中で'?'で表現され ている部分に、ひとつの引数をわりあてます。実引数の値は dbi-executeで設定されます。類似のクエリを大量に発行するような場 合にはプリペアドクエリをひとつ生成し、それにさまざまなパラメータを渡し て実行するとパフォーマンスがかせげます。このパラメータは自動的にクォー トされます。

クエリがSELECT文の場合、その結果は関係プロトコルを実装するコレ クションとして返されます。詳細はgauche.collection - コレクションフレームワークおよび util.relation - リレーションフレームワークを見てください。

いちばん外側にあるguardはエラーを捕捉するためのものです。 dbiに関連したエラーは<dbi-error>コンディションを継承して いるものと見なされます。いくつかの特有のエラーはdbiモジュールで 定義されています。特定のdbd層はさらに固有のエラーを定義していま す。

次節ではユーザレベルのAPIについて説明します。すなわち、dbiを使 う際に必要となる手続に関する説明です。そのあとのセクションではドライバ APIを説明をします。すなわち特定のdbdドライバをdbiフレー ムワークで使えるようにするために使うAPIの説明です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.16.1 DBIのユーザAPI

DBIのコンディション

dbi API が投げる可能性のあるコンディションがいくつか定義されて います。コンディションの詳細については例外を見てください。

Condition Type: <dbi-error>

dbi-関連のコンディションのベースクラス。<error>を継承し ています。

Condition Type: <dbi-nonexistent-driver-error>

dbi-connectは指定されたドライバが見つからない場合にこのコンディ ションを投げます。<dbi-error>を継承しています。

Instance Variable of <dbi-nonexistent-driver-error>: driver-name

要求されたドライバの名前を文字列として保持している。

Condition Type: <dbi-unsupported-error>

呼び出されたメソッドが基盤となるドライバでサポートされていない場合、こ のコンディションが投げられます。<dbi-error>を継承しています。

Condition Type: <dbi-parameter-error>

プリペアドクエリへ渡されたパラメータの数がプリペアドステートメントの中 のものと一致しないとき、このコンディションが投げられます。

上の3つのエラー以外に、dbiがプリペアドSQL文を構文解析するのにド ライバを利用す場合、不正なSQL文がdbi-prepareに渡されると、 <sql-parse-error>が投げられます (text.sql - SQLのパーズと構築参照)。

データベースへの接続

Function: dbi-connect dsn :key username password

dsn(データソース名)で指定されたデータソースを使ってデータベース に接続します。dsnは以下の構文をもつ文字列です。

 
dbi:driver:options

driverは特定のドライバ名です。対応するドライバモジュールがなけれ ばなりません。すなわち、dbd.driverがシステムにインストー ルされていなければなりません。たとえば、dsn"dbi:mysql:" ではじまるとすると、dbi-connectdbd.mysqlをロードしよう とします。

options部分の解釈はドライバに依存します。通常この部分のフォーマッ トはkey1=value1;key2=value2;...のようになっていますが、ドライバ によっては別の解釈になります。たとえば、mysqlドライバでは、 optionsの最初の部分でデータベース名を指定することができます。 optionsの正確な仕様については各ドライバのドキュメントをチェック してください。

接続のために必要な追加情報はキーワード引数であたえます。 usernameおよびpasswordは共通でサポートされている引数です。 ドライバは他にもキーワード引数を認識します。

データベースへの接続が成功したら、コネクションオブジェクト (<dbi-connection>のサブクラスのインスタンス)が返ります。さもな ければ、エラーがあがります。

Class: <dbi-connection>

データベースシステムへの接続のベースクラス。各ドライバはこのクラスのサ ブクラスを定義し、これにデータベース特有のコネクションに関する情報を持 たせます。

Method: dbi-open? (c <dbi-connection>)

データベースへの接続がオープン状態(アクティブ状態)にあるかどうかを確か めます。

Method: dbi-close (c <dbi-connection>)

データベースへの接続を閉じます。これによりこの接続に関連付けられたリソー スが解放されます。いったん閉じたcに対してはどのようなdbi操作もで きません。(dbi-open?だけは例外)。すでに閉じられたコネクションに 対してdbi-closeを呼んでもなにも起りません。

ドライバは通常<dbi-connection>がガベージコレクションされたとき にコネクションを閉じますが、このことを期待したコードを書くのはいただけ ません。GCのタイミングというのは予測不可能だからです。ユーザプログラム は適切なタイミングでdbi-closeを呼ぶようにすべきです。

Function: dbi-list-drivers

解っているドライバのモジュール名のリストを返します。

Class: <dbi-driver>

ドライバのベースクラス。高レベルのdbi APIを使うかぎり、これが必 要になることはありません。

Function: dbi-make-driver driver-name

dbi-connectから呼ばれる低レベル関数、通常この関数を呼ぶ必要はあ りません。

driver-nameで指定されたドライバモジュールをロードし、当該のドラ イバクラスのインスタンスを生成してそれを返します。

クエリの準備と発行

Method: dbi-prepare conn sql :key pass-through …

SQL文の文字列表現sqlからデータベースコネクションconn用の クエリオブジェクト(<dbi-query>のインスタンスもしくはそのサブク ラスのインスタンス)を生成してそれを返します。

sql?であらわされているパラメータスロットを持ちます。

 
(dbi-prepare conn "insert into tab (col1, col2) values (?, ?)")

(dbi-prepare conn "select * from tab where col1 = ?")

これらのスロットはdbi-executeを使って実際にクエリを発行したとき に埋められます。パラメータスロットを使うのは以下の利点があるからです。 (1) クォートが自動的にほどこされます。不適切なクォートによるセキュリティ ホールを気にする必要はありません。 (2) いくつかのドライバでは準備の段階でサーバへテンプレートSQL文を送る 機能がサポートされていて、実行段階ではパラメータを送るだけで済みます。 これは似たようなクエリを大量に一度に発行するときには効率のよいやりかた です。

バックエンドでプリペアド文がサポートされていない場合(でSQLテンプレー トが?パラメータを持つ場合)、ドライバはsqlを解析するのに text.sqlモジュールを使います。与えられたSQL文が正しい構文でなけ れば、<sql-parse-error>コンディションが発生します。

キーワード引数pass-throughに真の値を渡して、SQLの解釈を抑制し、 sqlをそのままバックエンドのデータベースシステムに渡すことができ ます。text.sqlで理解できないようなSQLの拡張をバックエンドがサポー トしている場合に役立ちます。

ドライバがプリペアド文をtext.sql抜きでバックエンドに処理させた 場合、pass-through引数は無視されます。ドライバは他のキーワード 引数を取ることもあります。詳細はそれぞれのドライバのドキュメントを参照 してください。

注意:SQL文のケース畳み込みは実装依存です。DBMSのなかにはテーブ ル名やカラム名は大文字小文字の区別をしないものもあり、一方で区別するも のもあります。ポータブルなSQL文を書きたいのなら、識別子をクォートしま しょう。すなわち常に名前をダブルクォートで囲むようにします。

Class: <dbi-query>

dbi-prepareによって作成されたプリペアドクエリに関する情報を保持 します。以下のスロットが定義されています。

Instance Variable of <dbi-query>: connection

<dbi-connection>オブジェクトを含みます。

Instance Variable of <dbi-query>: prepared

ドライバがクエリを準備する場合、このスロットがプリペアド文を保持します。 このスロットをどのように使うかはおのおののドライバによります。したがっ て、クライアントはこの値に依存してはいけません。

Method: dbi-open? (q <dbi-query>)

クエリがdbi-executeに渡せる状態になっているときにのみ、 #tを返します。

Method: dbi-close (q <dbi-query>)

クエリを破棄し、当該クエリに関連づけられたリソースを解放します。この操 作を実行後は、dbi-open?qに対して#fを返します。 そして、当該クエリは他の用途にはつかえません。qがガベージコレク ションにより回収された場合、リソースは解放されますが、アプリケーション が明示的にクエリを閉じるようにすることを強く勧めます。

Method: dbi-execute (q <dbi-query>) parameter …

dbi-prepareによって作成さればクエリを実行します。当該クエリが期 待するのと同じ数のパラメータを渡す必要があります。

発行されたクエリがselect文の場合dbi-executeリレーションを表わすオブジェクトを返します。リレーションは 行とカラムの値をカプセル化したもので、カラム名のようなメタ情報も同様で す。結果へアクセスの方法については後述の「クエリの結果を見る」を見てく ださい。

クエリがSELECT以外のcreateinsertdeleteなどの 場合、クエリクロージャー返り値は不定です。

Method: dbi-do conn sql :optional options parameter-value …

この手続はクエリを作成し、すぐに実行したいときに便利です。これは次の式 と同じですが、この場合はドライバはオーバーヘッドを避けるため、中間のク エリを作らないようにこのメソッドをオーバーロードします。

 
(dbi-execute (apply dbi-prepare conn sql options)
             parameter-value …)
Method: dbi-escape-sql conn str

str中の特殊文字をエスケープした文字列を返します。

SQLの公式標準ではこのような文字としてはシングルクォート(')につ いてだけ規定しています。しかし、印字可能文字ではない文字については規定 がありません。また、データベースシステムによっては他のエスケープ文字を 使うものもあります。それゆえ、自分でエスケープしようとせずに、このメソッ ドを使う必要があります。

 
;; c を利用可能なDBIコネクションとする
(dbi-escape-sql c "don't know")
  ⇒ "don''t know"

クエリの結果を見る

クエリがselect文である場合、<collection><relation>の両方のオブジェクトが返ります。行のコレクション (すなわち、<collection> APIの実装)ですから、行にアクセスするに はmapfor-each、その他のジェネリック関数が使えます。 また、カラム名やアクセサを取り出すにはリレーションAPIが使えます。 リレーションAPIについてはutil.relation - リレーションフレームワークをコレクションAPIにつ いてはgauche.collection - コレクションフレームワークを見てください。

クエリから戻ったオブジェクトの実際のクラスはドライバによりますが、 以下のメソッドを使うことができます。

Method: dbi-open? result

クエリの結果がまだアクティブであるかどうかをチェックします。 結果はdbi-closeによって明示的に閉じられるかデータベースへのコネ クションが閉じられると非アクティブになります。

Method: dbi-close result

クエリの結果を閉じます。結果に関連付けられていたリソースが解放されます。 resultは、いったん閉じると使えなくなります。ただし、 dbi-open?にだけは渡せます。

ドライバは通常、結果がガベージコレクタによって回収される時にリソースを 解放しますが、アプリケーションはこれに依存してはいけません。結果を使い おわったら明示的にdbi-closeを呼ぶことをおすすめします。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.16.2 DBI用のドライバを書く

特定のデータベースシステムのドライバを書くということは、 dbd.fooモジュールを実装することです。ここでfooはド ライバの名前になります。

このモジュールは以下に説明するいくつかのクラスとメソッドを実装しなけれ ばなりません。

実装するDBIクラス

以下のクラスを定義しなければなりません。

実装するDBIメソッド

ドライバは以下のメソッドを実装しなければなりません。

Method: dbi-make-connection (d <foo-driver>) (options <string>) (options-alist <list>) :key username password …

このメソッドはdbi-connectから呼ばれ、データベースへの接続を担い、 コネクションオブジェクトを作成します。コネクションオブジェクトを返さな ければなりません。コネクションが確立できない場合には、 <dbi-error>をあげなければなりません。

optionsdbi-connectに与えられるデータソースネーム(DSN)の オプションパートです。options-alistoptionsを解析した結果 の連想リストです。両方ともに用意して、ドライバが自明ではない方法で options文字列を解釈できるようにします。

たとえば、DSNとして "dbi:foo:myaddressbook;host=dbhost;port=8998"が与えられたとする と、fooのdbi-make-connectionoptionsとして "myaddressbook;host=dbhost;port=8998"を受け取り、 options-alistとして (("myaddressbook" . #t) ("host" . "dbhost") ("port" . "8998")) を受け取ります。

options-alistの後ろならどのようなキーワード引数でも dbi-connectに渡せます。DBIプロトコルは現在のところは usernameおよびpasswordのみを指定します。 ドライバはその他のキーワード引数を定義できます。 ドライバ特有のキーワード引数にはドライバ名を接頭辞として付けることをお 勧めします。たとえば、dbd.fooなら、:foo-whateverのように です。

どのようなオプションを使えるようにするか、あるいはオプションの構文をど うするかはドライバを書く人しだいです。基本的な考え方は、DSN はデータの ソースを識別するためのものであり、その役割りはWWWにおけるURLのようなも のだということです。それゆえ、データベースのホスト名、ポート番号、それ にデータベース名などが含まれることになるでしょう。しかし、ユーザ名やパ スワードのような認証に関する情報を含めてはいけません。というわけで、そ の手の情報はキーワード引数で渡すのです。

Method: dbi-prepare (c <foo-connection>) (sql <string>) :key pass-through …

このメソッドは<dbi-query>あるいはそのサブクラスのインスタンスで あるプリペアドクエリオブジェクトを生成し、それを返すものでなくてはなり ません。sqlによるクエリがデータベースに発行されるのは、プリペア ドクエリオブジェクトがdbi-executeに渡されたときです。

このメソッドは返されるクエリオブジェクトのconnectionスロットに cを設定しなけばなりません。

sqlはSQL文です。これには'?'で表現されたプレイスホルダが含 まれることがあります。クエリクロージャはこのプレイスホルダと同じ数の引 数をとらなければなりません。内部的にsqlをどのようにパーズするか、 クエリクロージャが呼ばれたとき完全なSQL文を構築するか、sqlをバッ クエンドのサーバに送って文を準備し、クエリクロージャはパラメータだけを 送るようにするかなどはドライバに依存します。

ドライバがSQL文を内部的にわたす場合、キーワード引数pass-through を認識しなければいけません。もし、真の値が与えられたら、ドライバは sqlを不透明なものとして扱い、これをそのままクエリクロージャが呼 ばれた際に渡さなければなりません。

ドライバがその他のキーワード引数を定義することもできます。 その場合、ドライバ特有のキーワード引数にはドライバ名を接頭辞として付け ることをお勧めします。たとえば、dbd.fooなら、 :foo-whateverのようにです。

Method: dbi-execute-using-connection (c <foo-connection>) (q <dbi-query>) (params <list>)

このメソッドはdbi-executeから呼ばれます。qが保持するクエ リを発行しなければなりません。クエリがパラメータ化されている場合、 dbi-executeに与えられた実際のパラメータはparams引数に渡さ れます。

qselect-型のクエリの場合は、このメソッドは適切なリレー ションオブジェクトを返さなければなりません。

Method: dbi-escape-sql (c <foo-connection>) str

デフォルトのエスケープメソッドでは十分でないとき、ドライバは特別のエス ケープを行うためにこのメソッドをオーバーロードすることができます。たと えば、MySQLではバックスラッシュ文字はシングルクォートと同様に特別あつ かいしますので、dbi-escape-sqlメソッドを持っています。

Method: dbi-open? (c <foo-connection>)
Method: dbi-open? (q <foo-query>)
Method: dbi-open? (r <foo-result>)
Method: dbi-close (c <foo-connection>)
Method: dbi-close (q <foo-query>)
Method: dbi-close (r <foo-result>)

これらのメソッドでコネクションおよび結果の状態を調べ、コネクションおよ び結果を閉じます。closeメソッドはコネクションや結果が利用しているリソー スを解放しなければなりません。ドライバはdbi-closeがすでに閉じら れたコネクションや結果に対しても適用できるようにしておかなければなりま せん。

Method: dbi-do (c <foo-connection>) (sql <string>) :optional options parameter-value …

この機能を実装するのにデフォルトメソッドはdbi-prepareおよび dbi-executeを使っています。これだけでも動きますが、 ドライバは効率のために中間のクエリオブジェクトの生成をスキップするため にこのメソッドをオーバーロードできます。

DBIのユーティリティ関数

以下の関数は上述のメソッドを実装するための低レベルのユーティリティです。

Function: dbi-parse-dsn data-source-name

dbi-connectに与えられたデータソースネーム(DSN)文字列を解析し、 以下の3つの値を返す。(1) ドライバ名(文字列) (2) DSNのオプション部分(文 字列) (3) 解析済オプション(連想リスト)。与えられた文字列がDSN構文に準 拠していない場合には<dbi-error>があがります。

典型的なドライバを書く場合には必要ありません。構文解析は dbi-make-connectionを呼ぶ前にすんでいるからです。このメソッドは プロキシのようなメタドライバという類のものを書くときに便利です。

Function: dbi-prepare-sql connection sql

プレイスホルダを含むSQL文 sql をパーズし、実際の値をパラメータと して渡されたときに完全なSQLを生成するクロージャを作成します。 バックエンドがプリペアド文をサポートしていない場合は、ドライバ中でクエ リを準備するのにこの関数を使うことになります。

connectionはデータベースへのDBIコネクションです。SQL中の値は適切 にエスケープされている必要があります(上述のdbi-escape-sqlをみて ください)。

 
;; c は正しいdbiコネクションを持っているとする
((dbi-prepare-sql c "select * from table where id=?") "foo'bar")
 => "select * from table where id='foo''bar'"

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.17 dbm - 汎用DBMインタフェース

Module: dbm

DBM系のライブラリはキーでインデックスされた値をファイルに格納する簡単な方法を 提供します。一種の永続的な連想記憶と言えるでしょう。

このモジュールが定義する抽象クラス<dbm>は、DBM系ライブラリへの 統一されたインタフェースを提供します。dbmモジュールだけをインポートすれば、 既にオープンされたデータベースを操作することができます。

データベースをオープンしたり作成したりするには、dbmインタフェースを実装した モジュールが必要になります。デフォルトのビルド時コンフィグレーションでは、 以下の実装がGaucheに含まれます。他の様々なdbmライブラリへのバインディングが 拡張パッケージとして提供されています。 それぞれのモジュールは、dbmインタフェース共通の手続きの他に、 直接実装を操作できる低レベルの手続きも提供します。 システムによっては以下のインタフェースの全てが実装されているわけではないことに 注意してください。Gaucheではシステムが提供する実装のみを定義します。

dbm.fsdbm

ファイルシステムdbm (dbm.fsdbm - ファイルシステムdbm参照).

dbm.gdbm

GDBMライブラリ (dbm.gdbm - GDBMインタフェース参照).

dbm.ndbm

NDBMライブラリ (dbm.ndbm - NDBMインタフェース参照).

dbm.odbm

DBMライブラリ (dbm.odbm - オリジナルのDBMインタフェース参照).

以下にdbmデータベースの使用例を示します。

 
(use dbm)         ; dbm abstract interface
(use dbm.gdbm)    ; dbm concrete interface

; open the database
(define *db* (dbm-open <gdbm> :path "mydb" :rw-mode :write))

; put the value to the database
(dbm-put! *db* "key1" "value1")

; get the value from the database
(define val (dbm-get *db* "key1"))

; iterate over the database
(dbm-for-each *db* (lambda (key val) (foo key val)))

; close the database
(dbm-close *db*)

<dbm>抽象クラスは、コレクションフレームワークと ディクショナリフレームワークを実装しています。 (それぞれgauche.collection - コレクションフレームワークgauche.dictionary - ディクショナリフレームワーク参照。)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.17.1 DBMデータベースのオープンとクローズ

Class: <dbm>

DBM系のデータベースのための抽象クラスです。 <dictionary>クラスを継承します (gauche.dictionary - ディクショナリフレームワーク参照)。 データベースへの共通のオペレーションを 定義します。以下のインスタンススロットを持ちます。これらのスロットの値は dbm-openによってデータベースがオープンされる前にセットされて いなければなりません。

具体クラスは、データベースの操作をより細かく行うための追加のスロット(例えばロックを 行うかどうか)を持つかもしれません。

Instance Variable of <dbm>: path

データベースファイルのパス名。dbmの実装によっては、このパスにサフィックスが追加されます。

Instance Variable of <dbm>: rw-mode

読み書きのモードを指定します。以下の値のいずれかを取ります。

:read

データベースはdbm-openによって読みだし専用モードでオープンされます。 オープンされる時点でデータベースは存在していなければなりません。

:write

データベースはdbm-openによって読み書き可能なモードでオープンされます。 データベースが存在しなければ、dbm-openは新しいデータベースを作成します。

:create

dbm-openによって新しいデータベースが作成され、読み書き可能なモードでオープンされます。 既にデータベースが存在していた場合、その内容はクリアされます。

Instance Variable of <dbm>: file-mode

データベースが作成されるときのファイルパーミッションを指定します。 デフォルトは#o664です。

Instance Variable of <dbm>: key-convert
Instance Variable of <dbm>: value-convert

デフォルトでは、dbmデータベースはキーにも値にも文字列しか使うことはできません。 これらのスロットによって、それ以外のSchemeオブジェクトを取り扱う方法を指定することが できます。以下の値のいずれかが可能です。

#f

デフォルトの値です。キーあるいは値は変換されません。それらは文字列でなければなりません。

#t

キーあるいは値はwriteを使って文字列に変換されデータベースに格納されます。 そしてreadを使って文字列からSchemeオブジェクトへと変換されます。 後でreadで読みこめるようなキーあるいは値のみを扱うことができます。 (但し、dbmライブラリは書き込み時にそれが後で読み込めるかどうかのチェックは行いません)。 キーの比較は文字列に変換された後で行われるので、同じ値となるキーは同じ文字列表現を 持つ必要があります。

二つの手続きのリスト

どちらの手続きも一つの引数を取ります。最初の手続きはSchemeオブジェクトを受け取り、 文字列を返します。キーあるいは値をデータベースに格納する時に呼ばれます。 二つ目の手続きは文字列を受け取りSchemeオブジェクトを返します。データベースから キーあるいは値を取り出す時に呼ばれます。 キーの比較は文字列に変換された後で行われるので、同じ値となるキーは同じ文字列に 変換される必要があります。

Metaclass: <dbm-meta>

<dbm>クラス及びそのサブクラスのメタクラスです。

Method: dbm-open (dbm <dbm>)

DBMデータベースをオープンします。dbmは、<dbm>クラスを継承した 具体クラスのインスタンスでなければなりません。また、そのスロットには適切な値が セットされている必要があります。オープンに成功したらdbm自身が返されます。 失敗した場合はエラーが報告されます。

Method: dbm-open (dbm-class <dbm-meta>) options …

DBMインスタンスを作成してオープンするための便利なメソッドです。 次のように定義されます。

 
(define-method dbm-open ((class <class>) . initargs)
  (dbm-open (apply make class initargs)))

データベースファイルはガベージコレクトされる際にクローズされますが、 変更を正しくデータベースに反映するには、明示的にクローズした方が良いでしょう。

Method: dbm-close (dbm <dbm>)

データベースdbmをクローズします。データベースがクローズされると、 それ以降のアクセスオペレーションはエラーとなります。

Method: dbm-closed? (dbm <dbm>)

データベースdbmが既にクローズされていたら#tを返します。

Function: dbm-type->class dbmtype

Sometimes you don’t know which type of dbm implementation you need to use in your application beforehand, but rather you need to determine the type according to the information given at run-time. This procedure fulfills the need.

The dbmtype argument is a symbol that names the type of dbm implementation; for example, gdbm for dbm.gdbm, and fsdbm for dbm.fsdbm. We assume that the dbm implementation of type foo is provided as a module dbm.foo, and its class is named as <foo>.

This procedure first checks if the required module has been loaded, and if not, it tries to load it. If the module loads successfully, it returns the class object of the named dbm implementation. If it can’t load the module, or can’t find the dbm class, this procedure returns #f.

 
(use dbm)

(dbm-type->class 'gdbm)
  ⇒ #<class <gdbm>>

(dbm-type->class 'nosuchdbm)
  ⇒ #f

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.17.2 DBMデータベースのアクセス

データベースがオープンされたら、以下のアクセスメソッドが使えます。

Method: dbm-put! (dbm <dbm>) key value

valueをキーkeyと関連付けて保存します。

Method: dbm-get (dbm <dbm>) key :optional default

キーkeyに関連付けられた値を返します。もし値が存在しなければ、defaultが 与えられていればそれを返し、そうでなければエラーを報告します。

Method: dbm-exists? (dbm <dbm>) key

キーkeyに関連付けられた値が存在すれば#tを返します。

Method: dbm-delete! (dbm <dbm>) key

キーkeyに関連付けられた値を消去します。値が存在しない場合は何もしません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.17.3 DBMデータベース上の繰り返し処理

全データベースを渡り歩く処理のために、以下のメソッドが用意されています。

Method: dbm-fold (dbm <dbm>) procedure knil

基本的な繰り返し処理です。データベース内の各キー/値のペアに関して、手続き procedure(procedure key value r), のように呼ばれます。ここでrは、最初のprocedureの呼び出しの時にはknil が、以降の呼び出しの時にはその直前のprocedureが返した値が渡されます。 最後のprocedureの戻り値がdbm-foldの戻り値となります。 データベース中にデータがひとつもなければknilがそのまま返されます。

次の例は、データベース中の整数の値を全て加算します。

 
(dbm-fold dbm (lambda (k v r) (if (integer? v) (+ v r) r)) 0)
Method: dbm-for-each (dbm <dbm>) procedure

データベース内の各キー/値のペアに関して、手続きprocedureを呼び出します。 procedureにはキーと値が渡されます。procedureの戻り値は捨てられます。

Method: dbm-map (dbm <dbm>) procedure

データベース内の各キー/値のペアに関して、手続きprocedureを呼び出します。 procedureにはキーと値が渡されます。procedureの戻り値はリストに 集められてdbm-mapの戻り値となります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.17.4 DBMデータベースインスタンスの管理

各DBM実装は、データベースを格納するのに独自の方法を使います。 レガシーなDBMは、pathスロットの値にそれぞれ‘.dir’と ‘.pag’を付けた名前の2つのファイルを使います。 fsdbmpathの下にディレクトリを作ります。 DBMデータベースが他のデータベースサーバによってバック アップされる場合は、pathはそのサーバで単なるキーと して使われるでしょう。

以下のメソッドは、そのようなバリエーションを隠し、 データベースそれ自体を管理する簡易な方法を提供します。 最初の引数に、具体的なDBMデータベースを実装している クラスを渡す必要があります。

Generic Function: dbm-db-exists? class name

nameで指定されたclassクラスのデータベースが 存在する場合は#tを返します。

 
;; Returns #t if testdb.dir and testdb.pag exist
(dbm-db-exists? <odbm> "testdb")
Generic Function: dbm-db-remove class name

nameで指定されるclassクラスのデータベース 全体を削除します。

Generic Function: dbm-db-copy class from to

fromで指定されたclassクラスのデータベースを toへコピーします。 classのdbm実装がロックをサポートしている限り、fromの一貫性は 保たれます (つまり、コピー中に他のプロセスがfromを 変更しようとした場合であっても、toが壊れたデータベースになることは ありません)。 もしコピー先のtoが既に存在するデータベースで あった場合、toの元の内容は失われます。 コピーが中断された場合にtoが不完全な状態のままになるかどうかは dbm実装に依存します。dbm実装の多くはトランザクショナルな振る舞い、 すなわち、コピーが失敗した場合に元のtoを復元することを試みます。 しかし確実な操作のためには、コピーが失敗した場合には呼び出し側でtoの 状態を確認することが必要です。

 
(dbm-db-copy <gdbm> "testdb.dbm" "backup.dbm")
Generic Function: dbm-db-move class from to

fromで指定されたclassクラスのデータベースを toへ移動、あるいはリネームします。dbm-db-copyと同じく、 classのdbm実装がロックをサポートしていれば データベースの一貫性は保証されます。移動先のtoが既に 存在していた場合、その元の内容は失われます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.17.5 DBMデータベースのダンプとリストア

Most dbm implementations use some kind of binary format, and some of them are architecture dependent. That makes it difficult to pass around dbm databases between different machines. A safe way is to write out the content of a dbm database into some portable format on the source machine, and rebuild another dbm database from it on the destination machine.

The operation is so common that Gauche provides convenience scripts that does the job. They are installed into the standard Gauche library directory, so it can be invoked by gosh <scriptname>.

To write out the content of a dbm database named by dbm-name, you can use dbm/dump script:

 
$ gosh dbm/dump [-o outfile][-t type] dbm-name

The outfile argument names the output file. If omitted, the output is written out to stdout. The type argument specifies the implementation type of the dbm database; e.g. gdbm or fsdbm. The program calls dbm-type->class (see section DBMデータベースのオープンとクローズ) on the type argument to load the necessary dbm implementation.

The dumped format is simply a series of S-expressions, each of which is a dotted pair of string key and string value. Character encodings are assumed to be the same as gosh’s native character encoding.

The dumped output may contain S-expressions other than dotted pair of strings to include meta information. For now, programs that deals with dumped output should just ignore S-expressions other than dotted pairs.

To read back the dumped dbm format, you can use dbm/restore script:

 
$ gosh dbm/restore [-i infile][-t type] dbm-name

The infile argument names the dumped file to be read. If omitted, it reads from stdin. The type argument specifies the dbm type, as in dbm/dump script. The dbm-name argument names the dbm database; if the database already exists, its content is cleared, so be careful.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.17.6 DBM実装を書く

When you write an extension module that behaves like a persistent hashtable, it is a good idea to adapt it to the dbm interface, so that the application can use the module in a generic way.

The minimum procedures to conform the dbm interface are as follow:

Besides above, you may define the following methods.

It is generally recommended to name the implementation module as dbm.foo, and the class of the implementation as <foo>. With this convention it is easier to write an application that dynamically loads and uses dbm implementation specified at runtime.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.18 dbm.fsdbm - ファイルシステムdbm

Module: dbm.fsdbm

Implements fsdbm. Extends dbm.

Class: <fsdbm>

fsdbmは、直接ファイルシステムを扱うDBM実装です。 基本的には、ファイル名をキー、ファイルの内容を値として使います。 他のDBM実装とは違い、これは他の特別なライブラリに依存しません (純粋にSchemeのみにより実装されています)。 したがって、他のDBM実装が使えないときでも、いつでも使うことが できます。

明らかに、たくさんのエントリを持っていたり、エントリの追加や 削除が頻繁に起こるようなデータベースには向いていません。 エントリの数が相対的に小さく、キーが小さいのに値が大きいような 場合に向いています。

<fsdbm>のインスタンスに与えられるデータベース名は、 データを格納するディレクトリの名前として使われます。

データのファイルは、fsdbmインスタンスのpathの サブディレクトリに格納され、キーによりハッシュされます。 キーに英数字でない文字がある場合はエンコードされます。 例えば、’:’は、_3aにエンコードされます。 キーがファイル名としては長すぎる場合は、いくつかに 分割され、その最後の文字列片以外はディレクトリ名として 使われます。長いキー名は問題を引き起こすかも知れないことに 注意して下さい。例えば、いくつかの古い’tar’コマンドは、256文字 を越える長いパス名を扱えません(それぞれのパスコンポーネント ではなく、パス名全体でです)。

fsdbmは、全てのDBMプロトコルを実装しています (dbm - 汎用DBMインタフェース参照)。 fsdbm特有の手続きというものはありません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.19 dbm.gdbm - GDBMインタフェース

Module: dbm.gdbm

Provides interface to the gdbm library. Extends dbm.

Class: <gdbm>

<dbm> を継承します。GDBM ライブラリのための実装を提供します。 このモジュールは、すでにあなたのシステムにすでに GDBM がある場合にのみ インストールされます(バージョン 1.8.0 が推奨されますが、いくつかの制限が あるだけで古い 1.7.x でも動作します)。

Instance Variable of <gdbm>: sync
Instance Variable of <gdbm>: nolock
Instance Variable of <gdbm>: bsize

統合された DBM インターフェース (see section dbm - 汎用DBMインタフェース) の 他に、このモジュールでは GDBM API への直接のアクセスを提供する以下の 低レベルな手続きを提供しています。これらの API の詳細については GDBM の マニュアルを見て下さい。

Function: gdbm-open path :optional size rwmode fmode error-callback
Variable: GDBM_READER
Variable: GDBM_WRITER
Variable: GDBM_WRCREAT
Variable: GDBM_NEWDB
Variable: GDBM_FAST
Variable: GDBM_SYNC
Variable: GDBM_NOLOCK
Function: gdbm-close gdbm-object
Function: gdbm-closed? gdbm-object
Function: gdbm-store key value :optional flag
Variable: GDBM_INSERT
Variable: GDBM_REPLACE
Function: gdbm-fetch gdbm-object key
Function: gdbm-delete gdbm-object key
Function: gdbm-firstkey gdbm-object
Function: gdbm-nextkey gdbm-object key
Function: gdbm-reorganize gdbm-object
Function: gdbm-sync gdbm-object
Function: gdbm-exists? gdbm-object key
Function: gdbm-strerror errno
Function: gdbm-setopt gdbm-object option value
Variable: GDBM_CACHESIZE
Variable: GDBM_FASTMODE
Variable: GDBM_SYNCMODE
Variable: GDBM_CENTFREE
Variable: GDBM_COALESCEBLKS
Function: gdbm-version
Function: gdbm-errno

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.20 dbm.ndbm - NDBMインタフェース

Module: dbm.ndbm

Provides interface to the ’new’ dbm library, a.k.a. ndbm. Extends dbm.

Class: <ndbm>

<dbm> を継承します。NDBM ライブラリのための実装を提供します。 このモジュールはあなたのシステムにすでに NDBM がある場合にのみ インストールされます。

統合された DBM インターフェース (see section dbm - 汎用DBMインタフェース) の 他に、このモジュールでは NDBM API への直接のアクセスを提供する以下の 低レベルな手続きを提供しています。これらの API の詳細については NDBM の マニュアルを見て下さい。

Function: ndbm-open path flags mode
Function: ndbm-close ndbm-object
Function: ndbm-closed? ndbm-object
Function: ndbm-store ndbm-object key content :optional flag
Function: ndbm-fetch ndbm-object key
Function: ndbm-delete ndbm-object key
Function: ndbm-firstkey ndbm-object
Function: ndbm-nextkey ndbm-object
Function: ndbm-error ndbm-object
Function: ndbm-clear-error ndbm-object

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.21 dbm.odbm - オリジナルのDBMインタフェース

Module: dbm.odbm

Provides interface to the legacy dbm library. Extends dbm.

Class: <odbm>

<dbm> を継承しています。レガシーな DBM ライブラリのための実装を 提供します。このモジュールは、あなたのシステムにすでに DBM がある場合にのみ インストールされます。

レガシー DBM の最大の制限は、データベースを一時に一つしか開けないことです。 複数の <odbm> のインスタンスを作ることができますが、一時に一つしか 開くことが出来ず、一つ以上開こうとするとエラーになります。

統合された DBM インターフェース (see section dbm - 汎用DBMインタフェース) の 他に、このモジュールでは DBM API への直接のアクセスを提供する以下の 低レベルな手続きを提供しています。これらの API の詳細については DBM の マニュアルを見て下さい。

Function: odbm-init path
Function: odbm-close
Function: odbm-store key value
Function: odbm-fetch key
Function: odbm-delete key
Function: odbm-firstkey
Function: odbm-nextkey key

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.22 file.filter - ファイルのフィルタ

Module: file.filter

このモジュールは、フィルター型のコマンド、 すなわち入力を読み込み、処理をして結果を書き出すような場合に 共通するパターンに使えるユーティリティ手続きを提供します。 共通するパターンとは:

Function: file-filter proc :key input output temporary-file keep-output? rename-hook

二つの引数、入力ポートと出力ポートを引数としてprocを呼び出し、 その結果を返します。 入力ポートと出力ポートはキーワード引数により決定されます。

input

この引数は入力ポートかファイル名を示す文字列でなければなりません。 入力ポートの場合、それはそのままprocに渡されます。 文字列が渡された場合は、そのファイル名を持つファイルを入力用にオープンし、 そのポートがprocに渡され、またこのポートはprocが戻った時に閉じられます。 この引数が省略された場合は、現在の入力ポートが渡されます。

output

この引数は出力ポートかファイル名を示す文字列でなければなりません。 出力ポートの場合、それはそのままprocに渡されます。 文字列が渡された場合は、そのファイル名を持つファイルを出力用にオープンし、 そのポートがprocに渡されます (但し、temporary-file 引数が渡された時はそれに指定されるファイルが一時ファイルとしてオープンされます)。 オープンされたポートはprocが戻った時に閉じられます。 この引数が省略された場合は、現在の出力ポートが渡されます。

temporary-file

値は真偽値か文字列でなければなりません。#fでない値が渡され、 かつ出力がファイルである場合、新たな一時ファイルが出力用に作成されprocに 渡されます。 そしてprocが正常に返って来た時点で、一時ファイルはoutputに指定された ファイルへとリネームされます。

この引数が#tの場合、一時ファイル名は出力ファイル名をもとに作られます。 この引数が文字列の場合はそれがsys-mkstempに渡されます。

もし一時ファイル名が"/""./""../"以外の 文字で始まっていた場合は、outputに与えられたファイル名のディレクトリが 一時ファイルの前に追加されます。

デフォルトの値は#f (一時ファイルを使わない) です。

keep-output?

真の値が与えられた場合、procがエラーになった場合でも出力ファイルを削除しません。 デフォルトでは、出力ファイル(もしくはtemporary-fileが与えられた場合は そのファイル)はエラーの場合には削除されます。

leave-unchanged

一時ファイルを使う場合に、この引数に真の値が与えられると、 出力ファイルが既に存在して一時ファイルに生成された内容と全く同じだった場合に、 出力ファイルをそのままにします。これは、出力ファイルに触ることで何らかの アクションが起きるようになっている場合に(例: make)、 不必要なアクションを起こさないために便利です。 デフォルトの値は#f (常に出力ファイルを置き換える) です。

Function: file-filter-fold proc seed :key reader input output temporary-file keep-output? rename-hook

file-filter’の便利なラッパー手続きです。 入力からreaderによって読まれるデータに対して次々にprocを 呼び出します。readerのデフォルトはread-lineです。 procの引数は、読まれたデータ、シード値、出力ポートです。 procは何かを出力することもできますし、値を返せばそれが 次のシード値として受け渡されてゆきます。他のキーワード引数は file-filterにそのまま渡されます。

例えば次のコードは、‘file.txt’を1行づつ読み、#/regexp/に マッチする行を行番号つきで出力します。

 
(file-filer-fold
  (^[line nc out]
    (when (#/regexp/ line) (format out "~3d: ~a\n" nc line))
    (+ nc 1))
  1 :input "file.txt")
Function: file-filter-map proc :key reader input output temporary-file keep-output? rename-hook
Function: file-filter-for-each proc :key reader input output temporary-file keep-output? rename-hook

file-filter-foldに似たユーティリティです。 foldに対するmapfor-eachに相当します。

手続きprocは二つの引数、入力から読まれたデータと出力ポートを受けとります。 procの結果は、file-filter-mapでは集められてリストとして 戻り値となり、file-filter-for-eachでは捨てられます。

キーワード引数の意味はfile-filter-foldと同じです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.23 file.util - ファイルシステムユーティリティ

Module: file.util

ファイルやディレクトリを扱う便利な手続き群を提供します。 これらの手続きはファイルシステムで述べられたプリミティブなシステム手続きの上に 構築されています。

このモジュール内の多くの手続きはfollow-link?というキーワード引数を取ります。 これは手続きがシンボリックリンクに出会ったときの動作を指定します。follow-link?が 真であれば、手続きはリンクの指す先のファイルに作用します。これがデフォルトの振舞いです。 follow-link?#fが渡された場合は手続きはリンクそのものに作用します。

名前つけ規則に関する注記:ファイルやディレクトリを 作成するのに"create"という語を使う処理系と"make"を 使う処理系があります。ファイルやディレクトリを削除するのにも"remove""delete"の流派があります。どちらも同じくらい広く使われているようなので、 Gaucheでは両方の名前を提供することにしました。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.23.1 ディレクトリユーティリティ

Function: current-directory :optional new-directory

引数無しで呼ばれた場合、カレントディレクトリを返します。 文字列new-directoryが与えられた場合はプロセスのカレントディレクトリを new-directoryに変更します。変更が出来なかった場合はエラーとなります。

この関数はChezSchemeやMzSchemeなどいくつかのScheme処理系に見られます。

Function: home-directory :optional user

名前または整数のユーザidで与えられたユーザuserのホームディレクトリを 返します。userが省略された場合はカレントユーザが使われます。 与えられたユーザが見付けられないか、ホームディレクトリを決定できなかった場合は #fが返されます。

Windowsネイティブ環境では、この関数はカレントユーザに対してのみ動作します。

Parameter: temporary-directory

一時ファイルを作るのに適したディレクトリ名を保持しているパラメータです。 デフォルトの値はsys-tmpdirの戻り値です (パス名参照)。 sys-tmpdirとの違いは、これはパラメータなので アプリケーションが実行時に変更できることです。 ライブラリは柔軟性を高めるためにできるだけsys-tmpdirよりは こちらを利用するのが良いでしょう。

Function: directory-list path :key children? add-path? filter filter-add-path?

ディレクトリpath中のエントリのリストを返します。 リストは文字列順にソートされます。

デフォルトではエントリのベースネーム(パスの最後のコンポーネント)のみが 返されますが、キーワード引数add-path?に真の値が与えられた時は pathが各エントリの前に追加されます。 children?に真の値が与えられた時は、カレントディレクトリと親ディレクトリが リストから除かれます。

filter引数は、もし与えられれば、一つの引数を取る 手続きでなければなりません。ディレクトリ中の各エントリを引数としてその手続きが呼ばれ、 真を返したエントリのみが結果に含まれます。 filterに与えられるエントリはデフォルトではベース名のみですが、 引数filter-add-path?が真ならばpathが前に追加された名前となります。

pathがディレクトリでない場合はエラーが報告されます。

 
(directory-list "test")
 ⇒ ("." ".." "test.scm" "test.scm~")

(directory-list "test" :add-path? #t)
 ⇒ ("test/." "test/.." "test/test.scm" "test/test.scm~")

(directory-list "test" :children? #t)
 ⇒ ("test.scm" "test.scm~")

(directory-list "test" :children? #t :add-path? #t
   :filter (lambda (e) (not (string-suffix? "~" e))))
 ⇒ ("test/test.scm")
Function: directory-list2 path :key children? add-path? filter follow-link?

directory-listに似ていますが、ふたつの値を返します。最初の値は path内にあるサブディレクトリのリストで、次の値はそれ以外のエントリのリストです。 キーワード引数children?add-path?filterdirectory-listと同じ意味をもちます。

偽の値をfollow-link?に与えると、path内のシンボリックリンクを 辿りません;すなわち、path内にディレクトリへのシンボリックリンクがあった場合、 デフォルト、もしくはfollow-link?に真の値が与えられた場合は それは最初のリスト(サブディレクトリ)に入りますが、follow-link? に偽の値が与えられた場合は後者のリスト(その他のエントリ)に入ります。

Function: directory-fold path proc seed :key lister follow-link?

ディレクトリ探索の最も基本的な手続きです。基本的な動作は以下に示すような再帰的なものです。

デフォルトのlisterdirectory-listを次のように呼び出すものです。

 
(lambda (path seed)
  (values (directory-list path :add-path? #t :children? #t)
          seed))

listerpath自身への参照 (".") やその親ディレクトリへの参照を 返してはなりません。また、listerの戻り値は現在のディレクトリからアクセス可能な パス名でなければなりません。例えばpath"/usr/lib/foo"であり、 そのディレクトリが"libfoo.a""libfoo.so"を含んでいた場合、 lister'("/usr/lib/foo/libfoo.a" "/usr/lib/foo/libfoo.so") のようなリストを返す必要があります。

キーワード引数follow-link?はディレクトリを指しているシンボリックリンクに対して listerを呼ぶかどうかを決定します。follow-link?が真(デフォルト値)である 場合はそのようなシンボリックリンクに対してもlisterが呼ばれます。 一方、follow-link?が偽であればシンボリックリンクに対してはprocが呼ばれます。

次の例は、与えられたpath以下からemacsのバックアップファイル ("~"で終る名を持つファイル) のリストを返します。

 
(use srfi-13) ;; for string-suffix?
(directory-fold path
                (lambda (entry result)
                  (if (string-suffix? "~" entry)
                      (cons entry result)
                      result))
                '())

次の例は与えられたpath以下全てのファイルとディレクトリ名をリストにして 返します。lister引数を使ってディレクトリ名そのものを結果に 含めていることに注目して下さい。

 
(directory-fold path cons '()
  :lister (lambda (path seed)
            (values (directory-list path :add-path? #t :children? #t)
                    (cons path seed))))
Function: make-directory* name :optional perm
Function: create-directory* name :optional perm

ディレクトリnameを作成します。nameに至るパスが存在しない 場合は必要なディレクトリが作成されます (Unixのmkdir -pコマンドと 同様です)。ディレクトリnameが既に存在していた場合は何もしません。 permは作成されるディレクトリのパーミッションビットを指定します。

Function: remove-directory* name
Function: delete-directory* name

ディレクトリnameとその内容を再帰的に消去します (Unixのrm -rコマンドと同様です)。シンボリックリンクは辿られません。

Function: copy-directory* src dst :key if-exists backup-suffix safe keep-timestamp keep-mode follow-link?

srcが通常のファイルであれば、copy-fileと同じように その内容をdstにコピーします。しかしsrcがディレクトリの場合は、 再帰的にディレクトリを辿り、その全てをdstへとコピーします。 cp -rコマンドに相当するものだと考えて良いでしょう。

srcがディレクトリの場合、デフォルトではその下にあるシンボリックリンクは 辿られず、リンクそのものがコピーされます。リンク先の内容をもコピーしたい 場合はfollow-link?キーワード引数に真の値を与えてください。 つまり、follow-link?キーワード引数のデフォルト値は#fです。 (このデフォルト値はcopy-fileと逆であることに注意してください。 copy-fileではfollow-link?はデフォルトで真であり、 リンクそのものをコピーしたい場合に明示的に#fを与える必要があります。)

他のキーワード引数の意味はcopy-fileと同じです。 詳細はcopy-fileを参照してください。

Function: create-directory-tree dir spec

specで指定されるディレクトリツリーをdirの下に作成します。 特定のディレクトリ構造を一気にセットアップする際に便利です。

spec引数は次に示される構造をもつS式です。

 
<spec> : <name>                             ; 空のファイル
       | (<name> <option> ...)              ; 空のファイル
       | (<name> <option> ... <string>)     ; 固定内容のファイル
       | (<name> <option> ... <procedure>)  ; 内容を生成するファイル
       | (<name> <option> ... (<spec> ...)) ; ディレクトリ

<name> : 文字列かシンボル

<option> ... : キーワードと値の交代リスト

specの最初と2番目の形式では、名前nameを持つ空のファイルが作られます。 3番目の形式では与えられた文字列がファイルの内容となります。

4番目の形式では、手続きがファイルのパス名を引数として呼び出され、 その手続きがcurrent output portに出力した内容がファイルの内容となります。 引数に渡されるパス名はdir引数からの相対パスです。 手続きが呼ばれる時、その親ディレクトリは既につくられています。

最後の形式は、名前nameを持つディレクトリを作成し、 その子供として再帰的に指定されたspecによるファイル/ディレクトリを作成します。

optionによって、作られるファイル/ディレクトリの属性を細かく指定できます。 今のところ、次のオプションが認識されます。

:mode mode

整数modeでパーミッションのモードビットを指定します。

:owner uid
:group gid

整数uid/gidで作成されるエントリのオーナー/グループを指定します。 作成されるエントリのオーナー/グループを変更するには、 呼び出すプロセスに特権が必要かもしれません。

:symlink path

ファイルを作成するspecでのみ有効なオプションで、 pathを指すシンボリックリンクを作成します。

Function: check-directory-tree dir spec

specで記述されるディレクトリ階層がdirの下に存在するかどうかを 調べ、存在すれば#t、そうでなければ#fを返します。

specの形式は上で説明したcreate-directory-treeと同じです。

specがオプションを含んでいる場合、該当するファイル/ディレクトリの 属性もそのオプションに合致するかどうかチェックされます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.23.2 パスネームユーティリティ

Function: build-path base-path component …

パス名のコンポーネントcomponentbase-pathに追加します。 Componentはシンボルupまたはsameであっても 構いません; Unixではそれらは".."または"."と等価です。 このAPIはMzSchemeから採られました。

Function: absolute-path? path
Function: relative-path? path

pathがそれぞれ絶対パスまたは相対パスならば#tを返します。

Function: expand-path path

pathがチルダ表記を含んでいたらそれを展開したものを返します。 そうでなければpathそのものを返します。この手続きはpathが 存在しアクセス可能であるかどうかはチェックしません。

Function: resolve-path path

pathexpand-pathと同様に展開し、 続いてpathの各コンポーネントに対してそれがシンボリックリンクであればリンク先の ものに置き換えてゆきます。pathが存在しないパスを指していたり、 シンボリックリンクの先が存在しなかったり、読み出せないディレクトリがあった場合は エラーとなります。

Function: simplify-path path

pathから、親ディレクトリへの参照("..")と自分自身への参照(".")を 出来る限り取り除きます。この手続きはファイルシステムへはアクセスしません。

Function: decompose-path path

パス名pathのディレクトリ部、拡張子を除いたファイル名、 そして拡張子の3つの値を返します。パス名が拡張子を持たない場合、 最後の値は#fになります。パス名がディレクトリセパレータで 終わっている場合は2番目と3番目の値が#fになります。 (後置されたディレクトリセパレータに関するこの取扱いは、 sys-dirname/sys-basenameと異なることに注意して下さい。 sys-dirname等は後置されたディレクトリセパレータを無視するという シェル等の慣習に従っています。)

 
(decompose-path "/foo/bar/baz.scm")
  ⇒ "/foo/bar", "baz", "scm"
(decompose-path "/foo/bar/baz")
  ⇒ "/foo/bar", "baz", #f

(decompose-path "baz.scm")
  ⇒ ".", "baz", "scm"
(decompose-path "/baz.scm")
  ⇒ "/", "baz", "scm"

;; Boundary cases
(decompose-path "/foo/bar/baz.")
  ⇒ "/foo/bar", "baz", ""
(decompose-path "/foo/bar/.baz")
  ⇒ "/foo/bar", ".baz", #f
(decompose-path "/foo/bar.baz/")
  ⇒ "/foo/bar.baz", #f, #f
Function: path-extension path
Function: path-sans-extension path

それぞれ、pathの拡張子と、pathから拡張子を除いたものを返します。 pathが拡張子を持っていない場合はそれぞれ#fpathが返されます。

 
(path-extension "/foo/bar.c")       ⇒ "c"
(path-sans-extension "/foo/bar.c")  ⇒ "/foo/bar"

(path-extension "/foo/bar")         ⇒ #f
(path-sans-extension "/foo/bar")    ⇒ "/foo/bar"
Function: path-swap-extension path newext

pathの拡張子がnewextに置換されたものが返されます。pathが 拡張子を持たない場合は、pathに "." とnewextが追加されます。

newext#fの場合は、pathの拡張子が除かれたものが 返されます。

 
(path-swap-extension "/foo/bar.c" "o")  ⇒ "/foo/bar.o"
(path-swap-extension "/foo/bar.c" "")   ⇒ "/foo/bar."
(path-swap-extension "/foo/bar.c" #f)   ⇒ "/foo/bar"

(path-swap-extension "/foo/bar" "o")  ⇒ "/foo/bar.o"
(path-swap-extension "/foo/bar" "")   ⇒ "/foo/bar."
(path-swap-extension "/foo/bar" #f)   ⇒ "/foo/bar"
Function: find-file-in-paths name :key paths pred

名前nameを持ち、述語predを満たすファイルをパス名のリストpaths から探します。見つかった場合はファイルの絶対パス名を、見つからなかった場合は #fを返します。

nameが絶対パス名で与えられた場合はそれが存在するかどうかと predを満たすかどうかのみがチェックされます。

pathsのデフォルト値は環境変数PATHから取られます。また、 predのデフォルト値はfile-is-executable? (ファイル属性ユーティリティ参照)です。すなわち、デフォルトでは この手続きはコマンドサーチパスから実行可能ファイルを探すのに使えます。

 
(find-file-in-paths "ls")
  ⇒ "/bin/ls"

;; アプリケーション"myapp"のユーザプレファレンスファイルを探す例
(find-file-in-paths "userpref"
  :paths `(,(expand-path "~/.myapp")
           "/usr/local/share/myapp"
           "/usr/share/myapp")
  :pred  file-is-readable?)
Function: null-device

nullデバイス名を返します。cygwinを含むunixプラットフォームでは "/dev/null"、mingwを含むWindowsネイティブプラットフォームでは "NUL"が返されます。

Function: console-device

コンソールデバイス名を返します。cygwinを含むunixプラットフォームでは "/dev/tty"、mingwを含むWindowsネイティブプラットフォームでは "CON"が返されます。

そのデバイスが実際に現在のプロセスから利用可能であるかどうかはチェックされません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.23.3 ファイル属性ユーティリティ

Function: file-type path :key follow-link?
Function: file-perm path :key follow-link?
Function: file-mode path :key follow-link?
Function: file-ino path :key follow-link?
Function: file-dev path :key follow-link?
Function: file-rdev path :key follow-link?
Function: file-nlink path :key follow-link?
Function: file-uid path :key follow-link?
Function: file-gid path :key follow-link?
Function: file-size path :key follow-link?
Function: file-atime path :key follow-link?
Function: file-mtime path :key follow-link?
Function: file-ctime path :key follow-link?

これらの手続きはpathで示されるファイルやディレクトリのアトリビュートを 返します。アトリビュート名は<sys-stat>のスロット名に対応しています。 ファイルの状態を参照して下さい。pathで示されるファイルが 存在しなければ#fが返されます。

pathがシンボリックリンクだった場合、オプショナルな引数 follow-link? に偽の値が与えられていない限り、これらの手続きは リンクの指す先のファイルに関する情報を返します。

MzSchemeとChickenにはfile-sizeがあります。 Chickenにはfile-modification-timeがあり、これはfile-mtimeと 同じです。

Function: file-is-readable? path
Function: file-is-writable? path
Function: file-is-executable? path

pathが存在して、現在の実効ユーザがそれぞれ読み取り/書き込み/実行可能なら#tを 返します。 このAPIはSTkから取られました。

Function: file-is-symlink? path

pathが存在して、それがシンボリックリンクなら#tを返します。 (参照:ファイルの状態file-is-regular?, file-is-directory?).

Function: file-eq? path1 path2
Function: file-eqv? path1 path2
Function: file-equal? path1 path2

path1path2で示されるファイルを比較します。 file-eq?file-eqv?path1path2が 全く同一のファイルを参照しているかどうか、すなわち、同じデバイス上にあり同じ inode番号を持つかどうかをチェックします。二つの手続きの違いは、 path1path2の最後のコンポーネントがシンボリックリンクで あった場合に、file-eq?はリンクそのものの比較をするが file-eqv?はリンクを辿った先のファイルの比較をする、という点です。

file-equal?path1path2をその内容まで考慮して比較します。 すなわち、二つのファイルがfile-eqv?の意味で同一でなかった場合、 file-equal?はファイルの内容を比較し、全てが一致した場合に#tを返します。

path1path2ともにディレクトリが与えられた場合の file-equal?の動作は未定義です。将来、ディレクトリ内容を スキャンするような拡張が加えられるかもしれません。

Generic Function: file-mtime=? f1 f2
Generic Function: file-mtime<? f1 f2
Generic Function: file-mtime<=? f1 f2
Generic Function: file-mtime>? f1 f2
Generic Function: file-mtime>=? f1 f2

二つのファイルの変更時間を比較します。それぞれの引数に対して、 次のような型のオブジェクトが渡せるようなメソッドが定義されています。

 
;; "foo.c" より "foo.o" が新しいかどうか調べる
(file-mtime>? "foo.c" "foo.o")

;; "foo.log"が過去24時間以内に更新されたかどうかを調べる
(file-mtime>? "foo.c" (- (sys-time) 86400))
Generic Function: file-ctime=? f1 f2
Generic Function: file-atime=? f1 f2

file-mtime=?と同じですが、ファイルの属性変更時間とアクセス時間に 関して比較します。 <, <=, >, >=を使う関数も同様に定義されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.23.4 ファイル操作

Function: touch-file path :key (time #f) (type #f) (create #t)
Function: touch-files paths :key (time #f) (type #f) (create #t)

pathもしくはリストpaths中の各パスの タイムスタンプを現在の時刻に更新します。 指定されたパスが存在しなかった場合、キーワード引数create#fでなければ、 その名前で大きさゼロのファイルが作成されます。

キーワード引数timeが与えられて#fでない場合、それは 非負の実数でなければなりません。現在の時刻のかわりにその値がタイムスタンプとして使われます。

キーワード引数type#f(デフォルト)か、シンボルatimeもしくは mtimeです。シンボルの場合は、それぞれアクセス時刻か変更時刻のみが更新されます。

註:touch-filesはファイルをひとつづつ処理するので、各ファイルの タイムスタンプが完全に同一にはならない可能性があります。

これらの手続きはシステムコールsys-utimeを使って作られています (ファイルの状態参照)。

Function: copy-file src dst :key if-exists backup-suffix safe keep-timestamp keep-mode follow-link?

ファイルsrcdstへコピーします。コピー元ファイルsrcは 存在していなければなりません。コピー先ファイルdstが存在していた場合の ふるまいはキーワード引数if-existsによって以下のように指定されます。

:error

(デフォルト) dstが存在していたらエラーを通知する。

:supersede

dstsrcのコピーで置き換える。

:backup

dstの名前を変えてキープする。

:append

dstの末尾にsrcの内容を追加する。

#f

dstが存在していたらコピーをせず#fを返す。

copy-fileはコピーが完了したら#tを返します。

srcがシンボリックリンクであった場合、copy-fileは デフォルトでリンクを辿ります。つまり、ファイルの実体がコピーされます。 srcが存在しないパスを指すシンボリックリンクであった場合は エラーが通知されます。

キーワード引数follow-link?#fを与えることで、 copy-linkにシンボリックリンクそのものをコピーさせることも できます。この場合、srcが存在しないパスを指すシンボリックリンクで あっても構いません。

if-exists:backupである場合、 dstがリネームされる名前は dstにキーワード引数backup-suffixで指定されるサフィックスを 付けたものとなります。デフォルト値は".orig"です。

デフォルトではcopy-fileは直接dstにコピーを行いますが、 キーワード引数safeに真の値が与えられた場合は、dstと同じディレクトリ 内の一時ファイルにまずコピーし、それが完了した時点でdstへとリネームします。 (safeが真でかつif-exists:appendであった場合は、 dstがあればまずその内容を一時ファイルにコピーし、そこにsrcの内容を 追加し、最後にそれをdstへリネームします。) コピーが何らかの理由で中断された場合、ファイルシステムはコピー前の状態へと 「ロールバック」されます。

キーワード引数keep-timestampに真の値が与えられた場合は、 copy-fileはコピー後にコピー先のファイルのタイムスタンプを コピー元のタイムスタンプに合わせます。

キーワード引数keep-modeに真の値が与えられた場合は、 コピー先のファイルのパーミッションビットはコピー元のそれに合わせられます。 keep-modeが偽の場合(デフォルト)は、コピー先が既に存在して safe引数が偽の場合にコピー先のもとのパーミッションが保持され、 そうでなければ#o666がumaskセッティングによってマスクされた 値となります。

Function: move-file src dst :key if-exists backup-suffix

ファイルsrcdstへ移動します。移動元ファイルsrcは 存在していなければなりません。移動先ファイルdstが存在した場合の ふるまいはキーワード引数if-existsによって以下のように指定されます。

:error

(デフォルト) dstが存在していたらエラーを通知する。

:supersede

dstsrcで置き換える。

:backup

dstの名前を変えてキープする。

#f

dstが存在していたら移動をせず#fを返す。

move-fileは移動が完了したら#tを返します。

if-exists:backupである場合、dstがリネームされる 名前はdstにキーワード引数backup-suffixで指定されるサフィックスを 付けたものとなります。デフォルト値は".orig"です。

ファイルsrcdstは別のファイルシステム上にあっても構いません。 その場合、move-fileはまずsrcdstと同じディレクトリの 一時ファイルにコピーし、それをdstにリネームし、それから srcを消去します。

Function: remove-file filename
Function: delete-file filename

[R7RS] 指定された名前のファイルを消去します。ファイルが存在しなかったり、ディレクトリであったり、 パーミッションがなく消去できなかった場合等にはエラーが報告されます。 delete-fileはR7RSで定義されています。

sys-unlinkと似ていますが、sys-unlinkはファイルが無かった場合に エラーをあげず#fを返すことに注意。(ディレクトリ操作参照。)

Function: remove-files paths
Function: delete-files paths

リストpaths中の各パスを削除します。パスがファイルの場合は unlinkし、ディレクトリの場合はremove-directory*を 使って再帰的にその内容を消去します。存在しないパスは単に無視されます。

delete-filesremove-filesの別名です。

Function: file->string filename options …
Function: file->list reader filename options …
Function: file->string-list filename options …
Function: file->sexp-list filename options …

ファイル filename から読み込むための便利手続き。 これらの手続きは、まず、指定された名前のファイルをオープンし、その オープンしたファイルに対してそれぞれ port->stringport->listport->string-list および port->sexp-list を呼びます(入力ユーティリティ手続き参照)。すべての内容が読み込まれる かまたは読み込み中にエラーシグナルがあがれば、ファイルはクローズされます。

これらの手続きはcall-with-input-fileと同じキーワード引数を取ります。 ファイルが見つからなかった場合の振舞いは キーワード引数:if-does-not-existによって指定できます。 それが:errorならエラーが報告され、 #fなら#fが返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.23.5 ロックファイル

ファイルやディレクトリを作成する排他性は、しばしばプロセス間のロックに 使われます。以下の手続きはパッケージ化されたインタフェースを提供します。

Function: with-lock-file lock-name thunk :key type retry-interval retry-limit secondary-lock-name retry2-interval retry2-limit perms abandon-timeout

lock-nameという名前を持つファイルもしくはディレクトリ (ここでは ロックファイルと呼びます) を排他的に作成し、 thunkを実行します。thunkから戻ってくるか、エラーが投げられたら、 ロックファイルは削除されます。thunkが正常に戻ってきた場合、 その戻り値がwith-lock-fileの戻り値となります。

ロックファイルが既に存在していた場合、with-lock-fileはタイムアウトになるまで 少し待ってリトライすることを続けます。細かい動作はキーワード引数で指定できます。

with-lock-file実行中に深刻なエラーによって、ロックファイルを消せずに プロセスが終了してしまう可能性があります。そのため、 with-lock-fileは、ロックファイルのタイムスタンプが非常に古い場合には ロックを盗むことを許しています。例えば、通常アプリケーションはたかだか数秒しか ロックしないはずなのに、10分前のタイムスタンプを持つロックファイルを見つけたとしたら、 以前のプロセスがクリーンアップをせずに落ちてしまったことは十分考えられるでしょう。 この振る舞いも、キーワード引数で制御することができます。

内部的には、安全な「盗ロック」を実現するために、二つのロックファイルが使われています。 主ロックファイル(lock-nameで指定される名前を持つもの)の作成と削除の 操作がそれぞれ、副ロックファイル(secondary-lock-fileで指定される名前を 持つもの。指定が省略された場合はlock-nameにサフィックス.2をつけたもの) によって保護されます。 副ロックは、二つ以上のプロセスが主ロックファイルを同時に盗もうとした場合を保護します。

副ロックファイルによるロックはほぼ常に極めて短い期間に 限定されるため、事故により副ロックファイルが残されてしまう可能性は 主ロックファイルに比べ非常に小さいです。それでももし副ロックファイルが 残されてしまった場合は、with-lock-fileは単に諦めます。副ロックファイルまで 盗むことはしません。

with-lock-fileがタイムアウトまでにロックを獲得できなけば、 <lock-file-failure>コンディションが投げられます。

以下のキーワード引数が認識されます。

type

シンボルfiledirectoryのどちらか。

fileを指定した場合、open(2)O_EXCLフラグを使う 排他的ファイル作成を利用したロックファイルを使います。これがデフォルトの動作です。 ほとんどのプラッフォームで動作しますが、NFSの実装の一部に、 排他的ファイル作成のセマンティクスが正しく実装されていない場合があります。

directoryを指定した場合は、mkdir(2)の排他性を利用した ロックディレクトリを使います。これはどんなプラットフォームでも動作する はずですが、fileを指定した場合より遅いかもしれません。

retry-interval
retry-limit

時間を秒で指定する非負の実数を取ります。前者は主ロック獲得を再試行する 時間間隔、後者は再試行を繰り返す総時間を指定します。 デフォルトはそれぞれ、1秒と10秒です。再試行をしないようにするには、 retry-limitに0を渡してください。

secondary-lock-name

副ロックファイル(もしくはディレクトリ)の名前を指定します。 省略された場合は、lock-nameにサフィックス.2をつけたものが使われます。 副ロックファイルの名前は、同じ(主)ロックファイルを使うプログラム全てで 一致していなければなりません。 特に変える必要が無ければデフォルトのままにするのが良いでしょう。

retry2-interval
retry2-limit

retry-intervalretry-limitに似ていますが、 こちらは副ロックファイルの再試行間隔と最長再試行時間を指定します。 副ロックが衝突する確率は通常極めて低いので、これらのパラメータを 調整する必要は滅多に無いでしょう。デフォルトの値はそれぞれ 1秒と10秒です。

perms

ロックファイルもしくはディレクトリのパーミッションを、非負正確整数の ビットマスクで指定します。デフォルトは、ロックファイルに対して#o644、 ロックディレクトリに対しては#o755です。

ロックを獲得/解放したり盗んだりするには、ロックファイル自身のパーミッションではなく、 ロックファイルが置かれるディレクトリのパーミッションが関係することに注意してください。

abandon-timeout

時間の長さ(秒)を非負の実数で指定します。 with-lock-fileがロックファイルを見つけて、そのタイムスタンプが 現在の時刻マイナスこの時間よりも古いものだった場合、ロックを盗みます。 ロックを盗むことを禁止したければこの引数に#fを渡してください。 デフォルトは600秒です。

Condition type: <lock-file-failure>

with-lock-fileがロックを獲得できなかった場合に投げられる コンディションです。<error>を継承します。

Instance Variable of <lock-file-failure>: lock-file-name

獲得しようとした主ロックファイルの名前です。

Gaucheは、OSによりサポートされるfcntlロックの機能も、 gauche.fcntlモジュールによって提供しています。 fcntlロックを使うべきかwith-lock-fileを使うべきかは、 アプリケーションによります。

fcntlロックの利点は次の通りです。

多くの場合、最も便利なのは最初の性質でしょう。プロセスが予想外に落ちてしまった 場合でも、ロックが残されてしまうことを心配しないで済みます。

けれども、fcntlロックには欠点もあります。

二番目の欠点のせいで、アプリケーション全体を知っていてコードを好きにできる場合でなければ、 fcntlロックを安全に使うことが非常に難しくなっています。 アプリケーションコードが、そのアプリケーションで使うためだけのファイルをロックするのに fcntlロックを使うのは何も問題ありません。 しかしライブラリ開発者は、ライブラリ内でfcntlロックを使うなら、 そのライブラリのユーザや他のライブラリが将来に渡って同じファイルを決してロックしない ということを保証しなければなりません(通常、そんなことは不可能です)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.24 math.const - 定数

Module: math.const

いくつかの一般的に用いられる数学定数を定義しています。

Constant: pi
Constant: pi/2
Constant: pi/4
Constant: pi/180
Constant: 1/pi
Constant: 180/pi

それぞれ、π、π/2、π/4、π/180、1/π、180/πです。

Constant: e

Napier’s constant.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.25 math.mt-random - Mersenne Twister乱数発生器

Module: math.mt-random

Makoto MatsumotoとTakuji Nishimuraにより開発された、 “Mersenne Twister”アルゴリズムに基づく、 仮想的な乱数発生器(RNG)を提供します。 高速で、2^19937-1という極めて長大な周期を持ちます。 アルゴリズムの詳細については、MTを参照して下さい。

乱数発生器の通常の用途には、srfi-27を使うことをお勧めします。 srfi-27はこのモジュールの上に実装されていますが、ポータブルなAPIを提供しています。 srfi-27にない機能が必要な時のみこのモジュールを直接使うようにしてください。

Class: <mersenne-twister>

Mersenne Twister RNGの状態をカプセル化するクラスです。 このクラスのそれぞれのインスタンスは独自の状態を持ち、 個別のシードで初期化されていれば、それぞれがランダムビットの 独立したソースになり得ます。

ランダムシードの値は初期化引数:seedにより初期化時に与えるか、 以下で説明するmt-random-set-seed!を使います。

 
(define m (make <mersenne-twister> :seed (sys-time)))

(mt-random-real m) ⇒ 0.10284287848537865
(mt-random-real m) ⇒ 0.463227748348805
(mt-random-real m) ⇒ 0.8628500643709712
…
Function: mt-random-set-seed! mt seed

Mersenne Twister RNG mtにランダムシードの値seedをセットします。 seedは任意の正の正確整数か、任意長のu32vector (srfi-4 - 単一型のベクタ参照)が使えます。 整数の場合は、初期化のために低位の32ビットが使われます。 u32vectorの場合は、初期化のために624までの要素が使われます。

Function: mt-random-get-state mt
Function: mt-random-set-state! mt state

Mersenne Twister RNG mtを取り出して再インストールします。 状態は、625要素のu32vectorで表現されます。 状態はどこにでも保存することができ、仮想的なランダムシーケンスの 生成を続行するために、<mersenne-twister>のインスタンスとして リストアできます。

Function: mt-random-real mt
Function: mt-random-real0 mt

0.0と1.0の間のランダムな実数を返します。 1.0は範囲に含まれません。 mt-random-realは、0.0も範囲に含みませんが、 mt-random-real0は含みます。 0.0を含まないのは、SRFI-27ドラフトに依拠しています。

Function: mt-random-integer mt range

0からrange-1までの正の正確整数をランダムに返します。 rangeはいかなる正の正確整数でも構いません。

Function: mt-random-fill-u32vector! mt u32vector
Function: mt-random-fill-f32vector! mt f32vector
Function: mt-random-fill-f64vector! mt f64vector

与えられたユニフォームベクタをランダムな数値で埋めます。 mt-random-fill-u32vector!では、要素は0と2^32-1の間の 正の正確整数で埋められます。 mt-random-fill-f32vector!mt-random-fill-f64vector! では、0.0と1.0(含まれない)の間の不正確実数で埋められます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.26 math.prime - 素数

Module: math.prime

このモジュールは、素数を扱うユーティリティ関数を提供します。

素数のシーケンス

Variable: *primes*

素数の無限遅延シーケンスです。

 
;; show 10 prime numbers from 100-th one.
(take (drop *primes* 100) 10)
 ⇒ (547 557 563 569 571 577 587 593 599 601)
Function: reset-primes

*primes*から大きな素数を取り出すと、それ以前の素数も全てメモリに 残り続けます。*primes*変数がシーケンスの頭を抱えているからです。 もう素数を必要としないことがわかっている場合、それらのメモリがガベージコレクト されることが望ましいかもしれません。reset-primes手続きは *primes*をまだ現実化されていない遅延シーケンスに再束縛し、 次のGCで計算済みの素数シーケンスが回収されるようにします。

Function: primes

新たな素数の遅延シーケンスを返します。 その時だけ素数を使いたい、という時に便利です。返されたシーケンスへの 参照が無くなれば、シーケンスはガベージコレクトされます。 (ある素数の計算には素数のシーケンスが最初から必要なので、 たとえシーケンスの頭ではなく途中への参照だけを持っていたとしても、 遅延シーケンスの中のサンクにはシーケンスの頭への参照が保持されています。 シーケンスがGCされるためには、いかなる部分への参照も残さないようにしなければなりません)。

primesが返す各シーケンスは独立しているので、素数の計算もそれぞれで (重複して)行われることになります。

単純なルールとして、プログラム中で何度も素数を使う必要があるのなら 変数*primes*を利用するのが良いでしょう。各素数の計算は一度しか 行われず、余分な計算を省くことができます。しかしその場だけ素数が欲しいなら、 primesを呼んで、仕事が済んだらシーケンスを捨ててしまえば、 不要なシーケンスがメモリに残りつづけることを心配しなくても済みます。

素数かどうかを調べる

Function: small-prime? n

比較的小さな正整数 (*small-prime-bound*以下の正整数) に対して、 それが素数であるかどうかを判定し、素数なら#tを返します。 nがそれ以上である場合は常に#fを返します。

この手続きは確実に素数であるとわかるものを素早く判別する時に便利です。 #tが返れば確実に素数であるとわかるからです (入力が大きな素数の時に #fを返すことはありえますが)。 これに対し、下に述べるMiller-Rabin法では、合成数は確実に判別できますが、 素数であるかどうかは確実には言えません。

Variable: *small-prime-bound*

これより小さな数に対しては、small-prime?は決定的に 素数かどうかを判別します。現在の実装ではこの数は3.4e14よりちょっと大きな数です。

Function: miller-rabin-prime? n :key num-tests random-integer

2以上の正確な整数nが素数かどうかを、確率的なMiller-Rabin法を使って判定します。 この手続きが#fを返したなら、nは確実に合成数です。 この手続きが#tを返した場合、nはおそらく素数ですが、 疑陽性である確率もわずかにあります。

ただし、nがある数(*small-prime-bound*) より小さければ、アルゴリズムは決定的で、#tが返るnは確実に素数です。

n*small-prime-bound*以上の場合は 確率的テストを用います。デフォルトでは7回、ランダムにベース整数値を選んで Miller-Rabinテストを適用します。試行回数はnum-testsキーワード引数で 変更可能です。合成数に対して誤って#tを返してしまう確率は たかだか(expt 4 (- num-tests))です。

確率的テストでは、miller-rabin-prime?はデフォルトで この手続き固有の、固定したランダムシードを使います。固定値なのは再現性を確保するためです。 異なる乱数系列を使いたければ、ランダムな整数生成手続きを random-integerキーワード引数に与えてください。 手続きは正整数kを取り、0からk-1までのランダムな整数値を 返すものでなければなりません。

Function: bpsw-prime? n

nが素数かどうかをBaillie-PSW法を用いて判定します (http://www.trnicely.net/misc/bpsw.html)。 このアルゴリズムは2^64 (約1.8e19) 以下の入力に対しては決定的であり、 正しい答えを返します。入力がそれ以上の場合、合成数に対して#tが返る可能性が あります (具体的な数はまだ見つかっていませんが)。素数に対して#fが返ることは 決してありません。

Miller-Rabin法より遅いですがカジュアルに使う分には十分に速いので、 上記の入力範囲で確実な答えを得たい場合は便利でしょう。

素因数分解

Function: naive-factorize n :optional divisor-limit

正整数nを、(sqrt n)までの素数で順に割ってみることで 素因数分解します。戻り値は小さい順に並べられた素因数のリストです。

 
(naive-factorize 142857)
  ⇒ (3 3 3 11 13 37)

この方法は極めてナイーブなものですが、目安としてどの素因数も1e7程度以下であれば それなりに使えます。例えば次の例は2.4GHz Core2マシンで0.4秒で答えが返ります (ただし、初回の実行は遅延素数シーケンスの実現化があるので1.3秒ほどかかりますが)。

 
(naive-factorize 3644357367494986671013))
  ⇒ (10670053 10670053 32010157)

もちろんnがより大きなオーダーの素因数を含んでいると、性能は急激に 悪化します。安全に使うにはnを1e14程度のオーダーに止めておくのが良いでしょう。

オプショナル引数divisor-limitを与えると、試行する素数の上限を指定 できます。この引数がある場合、naive-factorizeは因数fdivisor-limit以下の素数で割りきれなければ、そこで諦めてfを 結果に含めます。この場合、結果の最後の要素は合成数であるかもしれないわけです。 これは、より高度な素因数分解アルゴリズムを適用する前にありきたりの素因数を 除外するのに便利です。

 
(naive-factorize 825877877739 1000)
  ⇒ (3 43 6402154091)

;; whereas
(naive-factorize 825877877739)
  ⇒ (3 43 4591 1394501)

この手続きは高速化のために小さなnに対する結果はメモ化しています。

Function: mc-factorize n

正整数nをモンテカルロ素因数分解法 (R. P. Brent, An improved Monte Carlo factorization algorithm, BIT 20 (1980), 176-184. http://maths-people.anu.edu.au/~brent/pub/pub051.html)により 素因数分解します。

この手続きはnaive-factorizeよりも大きな数に使えます (目安としては1e20程度まで)。

アルゴリズムは確率的なので、同じnに対しても実行時間はばらつきますが、 nの素因数が全て2^64より小さければば、かならず確定的な答えを返します。

今のところ、nが2^64以上の素因数を含んでいる場合、この手続きは 永遠にそれを分割しようとしてループしてしまいます。現実的なアプリケーションは 何らかの方法で一定の時間でルーチンを中断して諦めるメカニズムが必要でしょう。 全ての入力に大して確定的な素数判定が実装されれば、この欠陥も修正されます。

その他の関数

Function: jacobi a n

Jacobi symbol (a/n) を計算します (http://en.wikipedia.org/wiki/Jacobi_symbol)。

Function: totient n

オイラーのトーシェント関数です。nは非負整数です。

現在の実装は上のmc-factorizeを使っており、 nが大きな素因数を持っている場合は非常に長い時間がかかります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.27 os.windows - Windowsのサポート

Module: os.windows

This module is only available on Windows-native Gauche, and provides Windows-specific procedures. You can check gauche.os.windows feature with cond-expand macro (see section 機能条件式) to conditionalize windows-specific code.

 
(cond-expand
  [gauche.os.windows
   (use os.windows)
   ... Windows-specific code ...]
  [else
   ... Unix code ...])

Currently there aren’t enough procedures provided here, but eventually we want to support simple scripting on Windows.

Unless otherwise noted, when Windows API returns an error value, a <system-error> condition is thrown.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.27.1 Windows dialogs

Currenly we only have MessageBox API.

Function: sys-message-box window message :optional caption flags

Calls Windows MessageBox API. The window argument should be a handle for a window, or #f; at the moment we don’t provide any API that retrieves window handles, so you should always pass #f here. The message argument takes a string for the content of the message box. Optional caption argument takes a string to be used in the window title.

The flags argument is an integer; it should be logior of values from one or more of the following groups. See the Windows reference manual for the details.

Buttons

MB_ABORTRETRYIGNORE, MB_CANCELTRYCONTINUE, MB_HELP, MB_OK (default), MB_OKCANCEL, MB_RETRYCANCEL, MB_YESNO, MB_YESNOCANCEL

Icon

Default is no icon. Possible values: MB_ICONEXCLAMATION, MB_ICONWARNING, MB_ICONINFORMATION, MB_ICONASTERISK, MB_ICONQUESTION, MB_ICONSTOP, MB_ICONERROR, MB_ICONHAND

Default button

MB_DEFBUTTON1 (default), MB_DEFBUTTON2, MB_DEFBUTTON3, MB_DEFBUTTON4

Modality

MB_APPLMODAL (default), MB_SYSTEMMODAL, MB_TASKMODAL

Other options

MB_DEFAULT_DESKTOP_ONLY, MB_RIGHT, MB_RTLREADING, MB_SETFOREGROUND, MB_TOPMOST, MB_SERVICE_NOTIFICATION

Return value is one of the following integer constants, indicating which button is pressed: IDABORT, IDCANCEL, IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN, or IDYES


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.27.2 Windows console API

Most of these procedures corresponds to Windows Console API one-to-one. See the Windows reference for the detail description of what each API does.

Attaching and detaching

Function: sys-alloc-console
Function: sys-free-console

[Windows] Calls AllocConsole and FreeConsole, respectively.

Function: sys-generate-console-ctrl-event event pgid

[Windows]

Constant: CTRL_C_EVENT
Constant: CTRL_BREAK_EVENT

[Windows]

Console codepage

Function: sys-get-console-cp
Function: sys-get-console-output-cp
Function: sys-set-console-cp codepage
Function: sys-set-console-output-cp codepage

[Windows]

Function: sys-get-console-cursor-info handle
Function: sys-set-console-cursor-info handle size visible

[Windows]

Function: sys-set-console-cursor-position handle x y

[Windows]

Console mode

Function: sys-get-console-mode handle
Function: sys-set-console-mode handle mode

[Windows]

Constant: ENABLE_LINE_INPUT
Constant: ENABLE_ECHO_INPUT
Constant: ENABLE_PROCESSED_INPUT
Constant: ENABLE_WINDOW_INPUT
Constant: ENABLE_MOUSE_INPUT
Constant: ENABLE_PROCESSED_OUTPUT
Constant: ENABLE_WRAP_AT_EOL_OUTPUT

[Windows]

Screen buffer

Function: sys-create-console-screen-buffer desired-access share-mode inheritable

[Windows]

Constant: GENERIC_READ
Constant: GENERIC_WRITE

[Windows]

Constant: FILE_SHARE_READ
Constant: FILE_SHARE_WRITE

[Windows]

Function: sys-set-console-active-screen-buffer handle

[Windows]

Function: sys-scroll-console-screen-buffer handle scroll-rectangle clip-rectangle x y fill

[Windows]

Class: <win:console-screen-buffer-info>

[Windows]

Instance Variable of <win:console-screen-buffer-info>: size.x
Instance Variable of <win:console-screen-buffer-info>: size.y
Instance Variable of <win:console-screen-buffer-info>: cursor-position.x
Instance Variable of <win:console-screen-buffer-info>: cursor-position.y
Instance Variable of <win:console-screen-buffer-info>: attributes
Instance Variable of <win:console-screen-buffer-info>: window.left
Instance Variable of <win:console-screen-buffer-info>: window.top
Instance Variable of <win:console-screen-buffer-info>: window.right
Instance Variable of <win:console-screen-buffer-info>: window.bottom
Instance Variable of <win:console-screen-buffer-info>: maximum-window-size.x
Instance Variable of <win:console-screen-buffer-info>: maximum-window-size.y
Constant: FOREGROUND_BLUE
Constant: FOREGROUND_GREEN
Constant: FOREGROUND_RED
Constant: FOREGROUND_INTENSITY
Constant: BACKGROUND_BLUE
Constant: BACKGROUND_GREEN
Constant: BACKGROUND_RED
Constant: BACKGROUND_INTENSITY

[Windows]

Function: sys-get-console-screen-buffer-info handle

[Windows]

Function: sys-get-largest-console-window-size handle

[Windows]

Function: sys-set-screen-buffer-size handle x y

[Windows]

Console input/output

Class: <win:input-record>

[Windows]

Instance Variable of <win:input-record>: event-type
Instance Variable of <win:input-record>: key.down
Instance Variable of <win:input-record>: key.repeat-count
Instance Variable of <win:input-record>: key.virtual-key-code
Instance Variable of <win:input-record>: key.unicode-char
Instance Variable of <win:input-record>: key.ascii-char
Instance Variable of <win:input-record>: key.control-key-state
Instance Variable of <win:input-record>: mouse.x
Instance Variable of <win:input-record>: mouse.y
Instance Variable of <win:input-record>: mouse.button-state
Instance Variable of <win:input-record>: mouse.event-flags
Instance Variable of <win:input-record>: window-buffer-size.x
Instance Variable of <win:input-record>: window-buffer-size.y
Instance Variable of <win:input-record>: menu.command-id
Instance Variable of <win:input-record>: focus.set-focus
Function: sys-get-number-of-console-input-events handle

[Windows]

Function: sys-get-number-of-console-mouse-buttons

[Windows]

Function: sys-peek-console-input handle
Function: sys-read-console-input handle

[Windows]

Function: sys-read-console handle buf

[Windows]

Function: sys-read-console-output handle buf w h x y region

[Windows]

Function: sys-read-console-output-attribute handle buf x y

[Windows]

Function: sys-read-console-output-character handle len x y

[Windows]

Function: sys-set-console-text-attribute handle attr

[Windows]

Function: sys-set-console-window-info handle absolute window

[Windows]

Function: sys-write-console handle string

[Windows]

Function: sys-write-console-output-character handle string x y

[Windows]

Function: sys-get-console-title

[Windows]

Standard handles

Function: sys-get-std-handle which
Function: sys-set-std-handle which handle

[Windows]

Constant: STD_INPUT_HANDLE
Constant: STD_OUTPUT_HANDLE
Constant: STD_ERROR_HANDLE

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.28 rfc.822 - RFC822メッセージ形式

Module: rfc.822

電子メールを交換する際に使用されるテキストのフォーマットである、“インターネット・ メッセージ・フォーマット”をパーズ/生成する手続きを定義しています。 最新の仕様は、RFC2822 (RFC2822) にあります。 このフォーマットは最初 RFC 822 で定義されたため、未だに“RFC822形式”と 呼ばれています。それがこのモジュール名の由来です。 以下では、このフォーマットを“RFC822形式”と呼びます。

メッセージヘッダのパーズ

Function: rfc822-read-headers iport :key strict? reader

入力ポート iport から、メッセージ・ヘッダの終わりに達するまで、 RFC822 形式のメッセージを読み込みます。 ヘッダ・フィールドは以下のフォーマットのリストに展開、分離されます。

 
((name body) …)

Name … はフィールド名で、body … は対応するフィールドの ボディ、ともに文字列です。 フィールド名は小文字に変換されます。フィールドのボディは、行折り返しが 取り除かれる以外は変更されません。 フィールドの順番は保存されます。

デフォルトでは、パーザの動作は寛容です。ヘッダをパーズ中に EOF に 出会うとそれをメッセージの終端とみなします。継続(折り返し)行でもなく、 新しいヘッダフィールドの始端でもない行は無視します。このふるまいは キーワード引数 strict? に真の値を渡すことで変更することができます。 真を渡すと、このような不正な形式のヘッダに対してエラーを発生させるように なります。

キーワード引数 readeriport から一行読み込む手続きを とります。デフォルトは read-line です。ほとんどの場合これで 十分のはずです。

Function: rfc822-header->list iport :key strict? reader

これはrfc822-read-headersの古い名前です。 互換性のために残してありますが、新しいコードは rfc822-read-headersを使って下さい。

Function: rfc822-header-ref header-list field-name :optional default

rfc822-read-headers が返すパーズ済みのヘッダリストから 特定のフィールドを得るためのユーティリティ手続きです。

Field-name は小文字の文字列でフィールド名を指定します。 与えられた名前をもつフィールドが header-list 中にあれば、 その値を文字列で返します。そうでない場合、もし default が 与えられていればそれが返り、与えられていなければ #f が返されます。

この手続きはrfc822-read-headersの結果だけでなく、 文字列をキーにしたリストのリスト ((name value option ...) ...) という構造からvalue部分を取り出すのに使えます。 例えばparse-cookie-stringの結果をrfc-822-header-refに渡せます。 (parse-cookie-stringについてはsee section rfc.cookie - HTTPクッキー参照。)

 
(rfc822-header-ref 
  '(("from" "foo@example.com") ("to" "bar@example.com"))
  "from")
 ⇒ "foo@example.com"

;; If no entry matches, #f is returned by default
(rfc822-header-ref 
  '(("from" "foo@example.com") ("to" "bar@example.com"))
  "reply-to")
 ⇒ #f

;; You can give the default value for no-match case
(rfc822-header-ref 
  '(("from" "foo@example.com") ("to" "bar@example.com"))
  "reply-to" 'none)
 ⇒ none

;; By giving the default value, you can distinguish
;; the no-match case and there's actually an entry with value #f.
(rfc822-header-ref 
  '(("from" "foo@example.com") ("reply-to" #f))
  "reply-to" 'none)
 ⇒ #f

基本的なフィールドパーザ

RFC2822メッセージの「構造化」されたヘッダフィールドをパーズするために、 いくつかの手続きが提供されています。これらの手続きはヘッダフィールドの 本体部を処理します。たとえば、ヘッダフィールドが、 "To: Wandering Schemer <schemer@example.com>" であれば、これらの 手続きは "Wandering Schemer <schemer@example.com>" をパーズします。

ほとんどの手続きは入力ポートを引数にとります。通常は最初に、ヘッダフィールド 全部を rfc822-read-headers でパーズし、ヘッダの本体を rfc822-header-ref で取得してから、その本体用に入力文字列ポートを オープンして、それをこれらの手続きを用いてパーズします。

このように複雑になっているのは、フィールドのタイプによって別々の トークン化スキームが必要になるからです。RFC2822 では多くの場合 トークン間にコメントがあらわれことを許しているので、初心な正規表現では うまくいきません。RFC2822 のコメントはネスト可能で、正規表現では表現 しきれないからです。 そういうわけで、このレイヤの手続きは、いろいろな構文に対応できるよう 十分な柔軟性があるように設計されています。標準的なタイプのヘッダについては 高水準のパーザも提供されています。後述の「特定フィールド用パーザ」を 参照してください。

Function: rfc822-next-token iport :optional tokenizer-specs

基本的なトークナイザです。まず、もしあれば、白空白および/または コメント (CFWS) を iport から読み飛ばします。それから、 tokenizer-specs にしたがってトークンをひとつ読み込みます。 トークンを読み込む前に、iport が EOF に到達したら、EOF が 返されます。

tokenizer-specs はトークナイザ仕様のリストです。 トークナイザ仕様は、文字集合または文字集合と手続きのペアのどちらかです。

CFWS を読み飛ばしたあと、この手続きは iport の先頭の一文字 を見て、tokenizer-specs のひとつひとつに対してチェックします。 その文字が含まれている文字集合がみつかれば、トークンを次のようにして 引き出します。トークナイザ仕様が文字集合だけの場合、その文字集合に 属している文字の並びがトークンを構成します。トークナイザ仕様が文字集合と 手続きのペアだったら、その手続きを iport とともに呼びだし、 トークンを読み込みます。

もし、先頭の文字がどの文字集合ともマッチしなければ、その文字が iport から取り出され、それが返されます。

デフォルトの tokenizer-specs は以下のようになっています。

 
(list (cons #["] rfc822-quoted-string)
      (cons *rfc822-atext-chars* rfc822-dot-atom))

ここで rfc822-quoted-string および rfc822-dot-atom は 後述するトークナイザ手続きで、*rfc822-atext-chars* は RFC2822 で 規定された atext の文字集合に束縛されています。 つまり、rfc822-next-token はデフォルトでは RFC2822 で規定された quoted-string あるいは dot-atom のトークンを引き出します。

tokenizer-specs をつかって、ヘッダフィールドのパーズ方法を カスタマイズすることができます。たとえば、(1) 英字で構成された単語、または (2) クウォート文字列、のトークンを取り出したいときには、 rfc822-next-token をこんなふうに呼べます。

 
(rfc822-next-token iport
   `(#[[:alpha:]] (#["] . ,rfc822-quoted-string)))
Function: rfc822-field->tokens field :optional tokenizer-specs

これは便利関数です。フィールド本体 field に対応する入力文字列ポート を生成し、それに対して、rfc822-next-token を全入力を消費するまで、 繰り返しよび、トークンのリストを返します。Tokenizer-specs は、 rfc822-next-token に渡されます。

Function: rfc822-skip-cfws iport

iport から、すべてのコメントおよび/または白空白文字を消費し、 白空白でもコメントでもない、先頭の文字を返します。返された文字は、 iportに残ります。

Constant: *rfc822-atext-chars*

atom を構成する有効な文字集合に束縛されています。

Constant: *rfc822-standard-tokenizers*

デフォルトの tokenizer-specs に束縛されています。

Function: rfc822-atom iport
Function: rfc822-dot-atom iport
Function: rfc822-quoted-string iport

それぞれ、atomdot-atom および quoted-string に 対応するトークナイザです。quoted-string 中の二重引用符および エスケープのためのバックスラッシュは rfc822-quoted-string に よって取り除かれます。

特定フィールド用パーザ

Function: rfc822-parse-date string

RFC822 形式の日付文字列を取り、8つの値を返します。

 
year, month, day-of-month, hour, minutes, seconds, timezone,
day-of-week.

timezone は UT(グリニッジ標準時)からの分単位のオフセットです。 day-of-week は日曜日から数えた曜日で、情報が不足している場合は #f です。 monthは1から12までの整数です。 文字列がパーズ不可能ならば、全ての要素が #f になります。

Function: rfc822-date->date string

RFC822形式の日付フォーマットをパーズし、SRFI-19 の <date> オブジェクト (日付 参照) を返します。string がパーズできないときは かわりに #f を返します。

SRFI-19の日付からRFC822形式の日付文字列を作成するには、 後で述べるdate->rfc822-dateが使えます。

メッセージの構築

Function: rfc822-write-headers headers :key output continue check

This is a sort of inverse function of rfc822-read-headers. It receives a list of header data, in which each header data consists of (<name> <body>), and writes them out in RFC822 header field format to the output port specified by the output keyword argument. The default output is the current output port.

By default, the procedure assumes headers contains all the header fields, and adds an empty line in the end of output to indicate the end of the header. You can pass a true value to the continue keyword argument to prevent this, enabling more headers can be added later.

I said “a sort of” above. That’s because this function doesn’t (and can’t) do the exact inverse. Specifically, the caller is responsible for line folding and make sure each header line doesn’t exceed the “hard limit” defined by RFC2822 (998 octets). This procedure cannot do the line folding on behalf of the caller, because the places where line folding is possible depend on the semantics of each header field.

It is also the caller’s responsibility to make sure header field bodies don’t have any characters except non-NUL US-ASCII characters. If you want to include characters outside of that range, you should convert them in the way allowed by the protocol, e.g. MIME. The rfc.mime module (see section rfc.mime - MIMEメッセージ処理) provides a convenience procedure mime-encode-text for such purpose. Again, this procedure cannot do the encoding automatically, since the way the field should be encoded depends on header fields.

What this procedure can do is to check and report such violations. By default, it runs several checks and signals an error if it finds any violations of RFC2822. You can control this checking behavior by the check keyword argument. It can take one of the following values:

:error

Default. Signals an error if a violation is found.

#f, :ignore

Doesn’t perform any check. Trust the caller.

procedure

When rfc822-write-headers finds a violation, the procedure is called with three arguments; the header field name, the header field body, and the type of violation explained below. The procedure may correct the problem and return two values, the corrected header field name and body. The returned values are checked again. If the procedure returns the header field name and body unchanged, an error is signaled in the same way as :error is specified.

The third argument passed to the procedure given to the check argument is one of the following symbols. New symbols may be added in future versions for more checks.

incomplete-string

Incomplete string is passed.

bad-character

Header field contains characters outside of US-ASCII or NUL.

line-too-long

Line length exceeds 998 octet limit.

stray-crlf

The string contains CR and/or LF character that doesn’t consist of proper line folding.

Function: date->rfc822-date date

SRFI-19の<date>オブジェクト(日付参照) を取り、そのrfc822日付形式表現の文字列を返します。 rfc822-date->dateの逆関数です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.29 rfc.base64 - Base64エンコーディング

Module: rfc.base64

このモジュールでは、RFC 2045 (RFC2045)の6.3節 およびRFC 4648 (RFC4648)で 定義されている Base64 フォーマットへエンコード/デコードするいくつかの 手続きを定義しています。

Function: base64-encode :key line-width url-safe

現在の入力ポートからバイト・ストリームを読み込み、それを Base64 フォーマットに エンコードし、現在の出力ポートに文字ストリームとして書き出します。 現在の入力ポートから EOF を読み込むと変換を終了します。

一行あたりの文字数がline-widthに与えられた文字数を越えないように、 改行文字が適切に出力に挿入されます。line-widthのデフォルト値は RFC2045に従い76となっています。line-width#fまたは0 を与えることで改行を抑制することができます。

url-safeに真の値を与えると、標準の Base64 と異なったエンコーディングテーブルを使って 入力をエンコードします。このエンコーディングでは、+の代わりに -が、/の代わりに_が使われます。このエンコーディングは ファイル名やURLの一部として使うのに適しています。url-safeの デフォルト値は偽です。

Function: base64-encode-string string :key line-width url-safe

string の内容を Base64 でエンコードされたフォーマットに変換します。 入力となる文字列は、完全文字列でも不完全文字列でも良いです。 常にバイト・シーケンスとして扱われます。

Function: base64-decode :key url-safe

現在の入力ポートから文字ストリームを読み込み、それを Base64 フォーマットとして デコードし、現在の出力ポートにバイトストリームとして書き出します。 変換は EOF か、終端文字 (=) を読み込むと終了します。 Base64 でエンコードされた文字として適当でない文字は沈黙のまま無視されます。

Function: base64-decode-string string :key url-safe

Base64 でエンコードされた文字列 string をデコードして文字列を返します。 変換は string の終わりか、終端文字 (=) で終了します。 Base64 でエンコードされた文字として適当でない文字は沈黙のまま無視されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.30 rfc.cookie - HTTPクッキー

Module: rfc.cookie

RFC 6265で定義されている「クッキー」情報を パースしたり構築したりするための手続きを定義しています。

Function: parse-cookie-string string :optional version

リクエスト・ヘッダの Cookie の値のクッキー文字列 string を パースします。通常、CGI プログラムでは、同じ情報は環境変数 HTTP_COOKIE を通して利用できます。

リクエスト・ヘッダ Cookie2 を通してクッキーのバージョンが分かる 場合は、version へ整数のバージョンとして渡されなければなりません。 そうでなければ、parse-cookie-stringstring からバージョンを 取り出します。

結果は以下のフォーマットを持ちます。

 
((<name> <value> [:path <path>] [:domain <domain>] [:port <port>])
 …)

<name> は属性名で、<value> は対応する値です。 属性が値を持たない場合、<value>#f になります。 (属性が NULL 値を持つ場合は、"" となることに注意。) 属性がパスやドメイン、ポート番号のオプションを持つ場合は、 キーワード-値のペアの形式で与えられます。

註: 特定のクッキーの値を簡単に取り出すには rfc822-header-refが使えます (rfc.822 - RFC822メッセージ形式参照)。

Function: construct-cookie-string specs :optional version

与えられたクッキーの仕様のリストから、Set-cookie2Set-cookie ヘッダに適切なクッキー文字列を作ります。

オプションの version 引数は、クッキー・プロトコルのバージョンを 指定するものです。0 は古い Netscape スタイルのフォーマットで、1 は RFC2965 スタイルのフォーマットです。省略された場合、1 が指定されたものと されます。

クッキーの仕様は以下のフォーマットを持ちます。

 
(<name> <value> [:comment <comment>] [:comment-url <url>]
                [:discard <bool>] [:domain <domain>]
                [:max-age <age>] [:path <path>]
                [:port <port-list>] [:secure <bool>] [:http-only <bool>]
                [:version <version>] [:expires <date>])
<name>

文字列。クッキーの名前。

<value>

クッキーの値。文字列か、値が必要なければ #f

<comment> <url> <domain> <path> <port-list>

文字列。

<bool>

真偽値。

<age> <version>

整数。

<date>

整数(エポックからの秒数)か、Netscape のクッキー仕様に従うフォーマットされた 日付文字列。

属性値は適切にクォートされます。指定された属性が version に不適切な 場合は無視されます。古いスタイルと新しいスタイルの両方のクッキー文字列を 作るために同じ仕様を渡すことができます。

戻り値はそれぞれのクッキー文字列のリストです。(Set-cookie を使う) 古いスタイルのプロトコルでは、それぞれを独立したヘッダとして送らなければ なりません。(Set-cookie2 ヘッダを使う)新しいプロトコルでは、 それらをカンマで繋ぎ、一度に送ることができます。詳細は RFC6265 を見て下さい。

いくつかの例を示します。

 
(construct-cookie-string
   `(("name" "foo" :domain "foo.com" :path "/"
                   :expires ,(+ (sys-time) 86400) :max-age 86400)))
 ⇒ ("name=foo;Domain=foo.com;Path=/;Max-age=86400")

(construct-cookie-string
   `(("name" "foo" :domain "foo.com" :path "/"
                   :expires ,(+ (sys-time) 86400) :max-age 86400))
   0)
 ⇒
 ("name=foo;Domain=foo.com;Path=/;Expires=Sun, 09-Sep-2001 01:46:40 GMT")

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.31 rfc.ftp - FTPクライアント

Module: rfc.ftp

このモジュールはFTPサーバーにアクセスするための便利関数群を提供します。

Class: <ftp-connection>

ひとつのサーバーへのFTPコネクションを保持するオブジェクト。以下の公開 スロットがあります。

Instance Variable of <ftp-connection>: transfer-type

FTPの転送タイプ。以下のシンボルのどれかひとつでなければなりません。 asciibinary (デフォルト)、およびimage

Instance Variable of <ftp-connection>: passive

パッシブコネクションを使うとき真。

Instance Variable of <ftp-connection>: log-drain

このスロットは<log-drain>のインスタンス(gauche.logger - ユーザレベルのロギング参照) を保持しているかあるいは#fでなければなりません。<log-drain> のインスタンスを保持している場合、FTP通信のログがそこに記録されます。

Condition Type: <ftp-error>

このタイプの例外はFTPサーバーがエラーコードを返したときに投げられます。 <error>を継承しています。メッセージフィールドにはステータスコー ドを含むサーバーからの返答が含まれます。

Function: call-with-ftp-connection host proc :key passive port username password account log-drain

高水準の便利関数で、ひとつのFTPサーバーへのFTPコネクションをオープンし、 与えられた手続きを呼びます。

接続するサーバーはhostで指定します。オプションでユーザー名、ポー ト番号をuser@servername:portという形式で指定 できます。もしあれば、hostの部分をキーワード引数にすることもでき ます。

hostへのFTPコネクション確立が成功したら、procが引数を1つとっ て呼ばれます。この引数は<ftp-connection>のインスタンスです。 procから返ったときにこのコネクションはクローズされ、procの 返り値がcall-with-ftp-connectionから返されます。例外がなげられ たら、その例外がcall-with-ftp-connectionから外へでる前にFTPコネ クションはクローズされます。

キーワード引数passiveに真値を与えると、FTPコネクションはパッシブ モードになります。デフォルトではアクティブモードです。

キーワード引数portusernameおよびpasswordは それぞれ、ポート番号、ユーザー名、パスワードを指定するのに使います。 省略された場合のデフォルトは、ポート番号が21,username"anonymous"password"anonymous@"にセットされま す。ポート番号とユーザー名はhost引数で指定されたものが優先されま す。

キーワード引数accountが与えられた場合には、その値が、ログイン時 にサーバーからの要求でFTPのACCTコマンドに渡されます。デフォルト では空文字列""です。

キーワード引数log-drainが生成したFTPコネクションの log-drainスロットに設定されます。

Function: ftp-transfer-type conn

指定したFTPコネクションconnの転送タイプを返します。セッターを適 用することもできます。たとえば、(set! (ftp-transfer-type conn) 'ascii) とします。

Function: ftp-passive? conn

指定したFTPコネクションがパッシブモードである場合でその場合に限り真を 返します。

Function: ftp-login host :key passive port username password account log-drain

hostで指定されたFTPサーバーに接続し、ユーザー認証をすませ、新し く生成した<ftp-connection>のインスタンスを返します。この手続き はcall-with-ftp-connectionを使ったときに暗黙の内に呼ばれます。 host引数およびキーワード引数のセマンティクスは call-with-ftp-connectionと同じです。

Function: ftp-quit conn

FTPのQUITコマンドをコネクションconnに送り、コネクションを シャットダウンします。この手続きはcall-with-ftp-connectionを使っ たときに暗黙の内に呼ばれます。

いったんシャットダウンしたコネクションをつかっての通信はできません。

Function: ftp-chdir conn dirname

リモートディレクトリをdirnameに変更します。

Function: ftp-remove conn path

pathで指定したリモートファイルを削除します。

Function: ftp-help conn :optional option …

FTPコマンドHELPを送ります。Optionは文字列でなければなりま せん。これはHELPコマンドの引数にわたされます。

Function: ftp-mkdir conn dirname

ディレクトリdirnameを作成します。作成されたディレクトリ名が返り ます。

Function: ftp-current-directory conn

現在のリモートディレクトリを返します。

Function: ftp-site conn arg

FTPコマンドSITEを引数argとともに送ります。SITEコマ ンドのセマンティクスはサーバーに依存します。返り値はサーバーのリプライ です。

Function: ftp-rmdir conn dirname

dirnameで指定したリモートディレクトリを削除します。返り値はサー バーのリプライです。

Function: ftp-stat conn :optional pathname

FTPコマンドSTATをサーバーに送信します。 RFC959ではこのコマンドのセマンティクスをいくつか定義しています。詳細は RFC959を見てください。返り値はサーバーのリプライです。

Function: ftp-system conn

FTPコマンドSYSTをつかってサーバーのオペレーティングシステムを問 合せます。返り値はステータスコードを含まないサーバーのリプライです。

 
(call-with-ftp-connection "localhost" ftp-system)
  ⇒ "UNIX Type: L8"
Function: ftp-size conn path

pathで指定したリモートファイルのサイズを問合せます。 整数値が返ります。

註:コネクションがasciiモードかbinaryモードかによって、返されるサイズは 異なるかもしれません。ftpサーバによっては、binaryモードでしか sizeリクエストに答えないものもあります。この関数を呼ぶ際には、 コネクションに望みのtransfer typeがセットされているようにしてください。

Function: ftp-mdtm conn path

pathで指定したリモートファイルの更新時刻を問合せます。この関数は サーバーからのリプライをステータスコードも含めそのまま返します。解析済 みの結果が欲しいときは後述のftp-mtimeを使ってください。

Function: ftp-mtime conn path :optional local-time?

pathで指定したリモートファイルの更新日時を問合せます。結果は <date>オブジェクト(srfi-19 - 時間のデータ型と手続き参照)で 返ります。local-time?が真値に設定されている場合、日付はローカル 時刻で返ります。そうでない場合は日付はUTCです。

Function: ftp-noop conn

FTPコマンドNOOPを送り、サーバーからのリプライを返します。

Function: ftp-list conn :optional path

pathで指定されたリモートディレクトリあるいはリモートファイル内の ファイルに関する情報を返します。path指定がない場合は現在のリモー トディレクトリで、結果のフォーマットはls(1)のフォーマットと非常 によく似ています。文字列のリストが返り、それぞれの文字列はサーバーのリ プライの行に対応します。正確なフォーマットについてはサーバーに依存しま す。

Function: ftp-name-list conn :optional path
Function: ftp-ls conn :optional path

pathで指定したパスもしくは現在のリモートディレクトリにあるファイ ル名リストを返します。ただし、他の情報は含みません。ftp-lsftp-name-listの別名です。便利なので定義してあります。

リモートディレクトリにファイルが含まれていないときにはサーバーはエラー を返すことがあることに注意してください。

Function: ftp-get conn path :key sink flusher

リモートファイルpathを検索します。検索データはsinkで与えら れた出力ポートに送られます。すべてのデータが検索されたあと、 flusherで与えられた手続きをポートsinkを引数として呼びます。 返り値はftp-getから返されたものです。

sinkおよびflusherのデフォルト値はそれぞれ、新しく作成され た文字列ポートとget-output-stringです。すなわち、ftp-get はデフォルトでは検索データを文字列として返します。巨大なファイルの場合 このデフォルトの挙動ではありがたくありません。

Function: ftp-put conn from-file :optional to-file

from-fileで指定したローカルファイルをto-fileで指定した名前 でリモートサーバーに送信します。to-fileが省略された場合にはベー ス名としてfrom-fileが使われます。返り値はサーバーのレスポンスで す。

Function: ftp-put-unique conn from-file

from-fileで指定したローカルファイルをリモートサーバーへ送信しま す。リモート側のファイル名重複しないことを保証します。返り値は2つで、 最終的なリモートサーバーからのレスポンスとリモートファイル名です。2つ めの値は、リモートサーバーがRFC1123をサポートしていない(ほとんどない) 場合に#fになります。

Function: ftp-rename conn from-name to-name

from-nameで指定したリモートファイル名をto-nameに変更する。 返り値は最終的なサーバーのレスポンスです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.32 rfc.hmac - HMAC鍵付きハッシング

Module: rfc.hmac

このモジュールは、RFC 2104で定義されている、メッセージ認証のための 鍵付きハッシングのHMACアルゴリズムを実装しています。

シンプルなバッチ処理での鍵付きハッシングでは、高レベルなAPIである hmac-digesthmac-digest-stringが使えます。 あるいは、<hmac>オブジェクトを作成して、入力となるデータで その状態を更新することもできます。

Class: <hmac>

HMACアルゴリズムの状態情報を保持します。 鍵とハッシングアルゴリズムは、キーワード引数:key:hasherを それぞれ使って、生成時に与えます。 <md5> (rfc.md5 - MD5メッセージダイジェスト参照)や <sha256> (rfc.sha - SHAメッセージダイジェスト参照)などのような ダイジェストインタフェース(util.digest - メッセージダイジェストフレームワーク参照)を 実装するいかなるクラスオブジェクトを渡すこともできます。

例:

 
(make <hmac> :key (make-byte-string 16 #x0b) :hasher <md5>)
Method: hmac-update! (hmac <hmac>) data

(不完全かもしれない)文字列で表現されるdataにより、 hmacの内部状態を更新します。

Method: hmac-final! (hmac <hmac>)

hmacの内部状態を終了させ、不完全文字列でハッシュされた文字列を 返します。 “hexified”(16進化)された結果を得るために、digest-hexify (util.digest - メッセージダイジェストフレームワーク参照)を使うことが できます。 一旦終了されると、hmacに対してはhmac-update!hmac-final!を呼ぶことはできません。

Method: hmac-digest :key key hasher

<hmac>オブジェクトを作り、現在の入力ポートからの データストリームをハッシュし、不完全文字列でそのハッシュされた 結果を返します。

Method: hmac-digest-string string :key key hasher

<hmac>オブジェクトを作り、stringにあるデータをハッシュし、 不完全文字列でそのハッシュされた結果を返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.33 rfc.http - HTTP

Module: rfc.http

このモジュールは、RFC2616 "Hypertext Transfer Protocol – HTTP/1.1" で定義されているHTTP/1.1に対する簡単なクライアントAPIを提供します。 (RFC2616).

現在のAPIは、プロトコルの一部のみ実装されています。 HTTP/1.0のサーバーとはうまく通信できません。 また、HTTP/1.1の先進的機能、例えば永続的接続などはサポートしていません。 これらの機能は、将来のバージョンで追加されるでしょう。

Condition Type: <http-error>

サーバから接続が切られた場合や、サーバの返したHTTPレスポンスのフォーマットが 正しくない場合に投げられるコンディションです。<error>を継承します。

Function: http-get server request-uri :key sink flusher redirect-handler secure …
Function: http-head server request-uri :key redirect-handler secure …
Function: http-post server request-uri body :key sink flusher redirect-handler secure …
Function: http-put server request-uri body :key sink flusher redirect-handler secure …
Function: http-delete server request-uri :key sink flusher redirect-handler secure …

serverに、それぞれHTTPのGET、HEAD、POST、PUT、DELETEリクエストを送り、 サーバの応答を返します。

デフォルトでは、 サーバがステータスコード 300, 301, 302, 303, 305 の応答を返し、 RFC2616による自動リダイレクトが許されている場合は、 これらの手続きは自動的に応答のメッセージヘッダの "location" で返されるURIに対して リクエストを再送します。 この動作はredirect-handlerキーワード引数でカスタマイズしたり 抑制したりできます。下の"キーワード引数"を参照してください。

必須の引数: server引数では、文字列でHTTPサーバ名を指定します。 サーバ名は、オプションでコロンに続いてポート番号を付加できます。 IPアドレスも使えます。IPv6アドレスは角括弧で囲んでください。

また、"unix:/path"という形式でUnixドメインソケットに接続することもできます。 /path部分にはソケットへの絶対パスを指定します。 Examples: "w3c.org", "mycompany.com:8080", "192.168.0.1:8000", "[::1]:8000"

request-uri引数は文字列かリストです。 文字列の場合、RFC2616で規定されているリクエストURIと解釈されます。 通常これはHTTP URLのパス部分です。 文字列はそのままサーバに渡されるので、呼び出し側で必要な 文字コード変換やurlエンコーディングを行う必要があります。

request-uriがリストの場合は、次の形式でなければなりません。

 
(path (name value) ...)

ここでpathはリクエストURIのパスコンポーネントまでを指定する 文字列です。与えられたnamevalueのalistから、 httpリクエスト手続きはHTML4で定められた application/x-www-form-urlencoded形式の クエリ文字列を構成し、pathにアペンドします。 例えば次のふたつのリクエストは同じ効果を持ちます。 二番目の呼び出しではurlエスケープが自動的に行われることに注目してください。

 
(http-get "example.com" "/search?q=foo%20bar&n=20")

(http-get "example.com" '("/search" (q "foo bar") (n 20)))

request-encodingキーワード引数が与えられた場合、 namevalueはまずその文字エンコーディングに変換されたのちに urlエスケープされます。そうでない場合はgaucheの内部 エンコーディングがそのまま使われます。

いくつかの手続きは、リクエストメッセージのボディを指定するbodyを第3引数として 取ります。bodyは文字列かリストで、文字列の場合はそのまま送られ、 リストの場合はmultipart/form-data形式にエンコードされて送られます。

bodyがリストの場合、それはパラメータ指定のリストです。 各パラメータ指定は、("submit" "OK")のような名前と値のリスト、 もしくは("upload" :file "logo.png" :content-type "image/png") のように名前の後にキーワード-値リストを付加したものです。

最初の形式は使うのが簡単で、またrequest-uriのクエリパラメータリストと 同じ形式なのでGETとPOSTでルーチンを共有したい場合にも便利でしょう。 この形式では、各値はMIMEパートにtext/plainとして置かれます。 文字コードは下に述べるrequest-encodingキーワード引数により変換されます。

二番目の形式では、MIMEパートの属性についてより細かな指定を行うことができます。 以下のキーワードが特別に扱われます。

:value

パラメータの値を指定します。簡潔な(name val)形式は (name :value val)の省略形です。

:file

指定された名前のファイルの中身をパラメータの値として挿入します。 ファイルのアップロードに便利です。このオプションは:valueより 優先されます。MIMEタイプは、指定が無ければ application/octet-streamとなります。

:content-type

MIMEタイプをオーバライドします。与えられた値にcharsetパラメータが ついていない場合は自動的に付加されます。

:content-transfer-encoding

content-transfer-encodingを 7bitbinaryquoted-printablebase64の いずれかで指定します。指定が無ければbinaryが使われます。

残りのキーワードはMIMEパートのヘッダにそのまま使われます。

戻り値: 全ての手続きは3つの値を返します。

1つ目は、RFC2616で定義されているステータスコードの文字列値(例えば、成功時の 200、"Not found"の404など)です。

2つ目は、パーズされたヘッダのリストで、リストの要素は(header-name value …)です。header-nameはヘッダの文字列名(例えば、 "content-type"や"location"など)で、valueは対応する値の文字列値です。 ヘッダ名は小文字に変換されます。値は、RFC2822で定義されている無指定行区切 (ソフト・ライン・ブレイク)が除かれる以外はそのままです。 サーバが同じ名前のヘッダを1つ以上返した場合は、 1つのリストに統合されます。それ以外では、2つ目の戻り値に おけるヘッダのリストの順番は、サーバの応答での順番と同じです。

3つ目の戻り値は、サーバの応答におけるメッセージボディです。 デフォルトでは、文字列で表現されたメッセージボディそのものです。 サーバの応答がボディを持たない場合、3つ目の戻り値は#fです。 キーワード引数によって、メッセージボディがどのように扱われるかを制御できます。 例えば、中間的な文字列を作らずに、返されたメッセージボディを直接ファイルに 格納することが出来ます。詳細は以下で説明しています。

キーワード引数: デフォルトで、これらの手続きはリクエストメッセージに"Host"ヘッダ・フィールドを 追加するだけです。他のヘッダ・フィールドを追加するためにキーワード引数を 与えることができます。

 
(http-get "foo.bar.com" "/index.html"
  :accept-language "ja"
  :user-agent "My Scheme Program/1.0")

以下のキーワード引数は手続きによって解釈され、リクエストヘッダには現れません。

request-encoding

request-uribodyがリストで与えられた場合、パラメータの 名前や値はまずこの引数で指定される文字エンコーディングへと変換され、 その後、application/x-www-form-urlencodedmultipart/form-data MIME形式にしたがったエンコーディングが行われます。 この引数が省略された場合はGaucheの内部文字エンコーディングが使われます。

multipart/form-dataについては、パラメータにcontent-typeヘッダを 与えることでパラメータごとに文字エンコーディングの設定をオーバライドできます。 詳しくは上のbody引数の説明を参照してください。

request-uribodyに文字列を与えた場合は、文字エンコーディング変換は 行われません。呼び出し側で望みの文字コードにあらかじめ変換しておいてください。

proxy

httpプロキシサーバを、hostnameまたはhostname:port形式の 文字列で指定します。省略された場合、パラメータhttp-proxyの値が 使われます。

redirect-handler

サーバが3xxステータスコードを返した場合のリダイレクトの処理を指定します。 #f, #tもしくは手続きを渡すことができます。省略時は#tとなります。

#fが渡された場合は、リダイレクトは処理されません。3xxステータスコードを 持つレスポンスもそのままhttp-*から返されます。

手続きが渡された場合は、サーバが3xxステータスコードを返すとその手続きが4つの引数で 呼ばれます。最初の引数はリクエストメソッド(シンボル、例:GET)、 次がレスポンスステータスコード(文字列、例:"302")、 次がパーズされたレスポンスヘッダ、そして最後がレスポンスボディです(レスポンスボディが あれば文字列、なければ#f)。

この手続きは、ペアか#fを返さねばなりません。ペアの場合、それは (method . url)という形で、methodがシンボルによるリクエストメソッド、 urlが文字列で次にリクエストすべきURLを表します。 ペアが返されれば、http-*手続きはそのURLへ、指定されたメソッドでリクエストを 再送します。(メソッドを返すことで、例えばPOSTリクエストのリダイレクトをGETリクエストに 置き換えることが可能です)。 手続きが#fを返した場合、リダイレクトは行われません。

redirect-handler#t(デフォルト値)の場合、 パラメータhttp-default-redirect-handlerの値が redirect-handlerに渡されたかのように振る舞います。 このパラメータの初期値は、標準的なリダイレクトの振る舞いをする手続きになっています。 下のhttp-default-redirect-handlerの項目を参照してください。

リダイレクトのループは自動的に検出され、<http-error>が投げられます。

no-redirect

これは互換性のためだけに残されている、古い引数です。真の値を与えると、 redirect-handler#fを渡したのと同じ効果を持ちます。

secure

真の値が与えられた場合、セキュアな接続(https)を使います。 セキュアな接続が実行中のプラットフォームで利用できない場合はエラーが投げられます。 下の「セキュアな接続」の項も参照してください。

auth-user, auth-password

これらのキーワード引数が与えられた場合、Basic認証用のAuthorizationヘッダが リクエストに付加されます。将来はBasic認証以外の認証方式もサポートするかもしれません。

sink, flusher

これらのキーワード引数によりリプライメッセージ・ボディがどのように扱われるかを カスタマイズできます。sinkには出力ポートを、flusherには2引数を 取る手続きを渡さなければなりません。

手続きがメッセージ・ボディを受信し始めると、sinkへ受け取った データ片をフィードします。手続きがメッセージ・ボディを受信し終わると、 flusherに与えられた手続きが、sinkと(手続きからの2つ目の 戻り値と同じフォーマットの)メッセージ・ヘッダ・フィールドのリストとともに 呼び出されます。flusherの戻り値が、手続きからの3つ目の戻り値と なります。

したがって、sinkのデフォルト値は、新しく開かれた文字列ポートで、 flusherのデフォルト値は(lambda (sink headers) (get-output-string sink)) とも言えます。

以下のサンプルは、(とても大きい可能性のある)文字列バッファを作らずに、 メッセージ・ボディを直接ファイルに保存します。

 
(call-with-output-file "page.html"
  (lambda (out)
    (http-get "www.schemers.org" "/"
       :sink out :flusher (lambda _ #t))))

The module also provides some utility procedures.

Parameter: http-user-agent :optional value

user-agentヘッダに渡される値のデフォルト値を指定するパラメータです。 デフォルトの値はgauche.http/* (*部分はGaucheのバージョン) になっています。 各アプリケーションは適切な値を設定するようにしてください。

Parameter: http-proxy :optional value

このパラメータの値がhttp-get等のhttpプロキシのデフォルトの値として 使われます。デフォルトの値は#f (プロキシを使用しない) です。

Parameter: http-default-redirect-handler :optional value

http-*手続きにredirect-handlerキーワード引数が与えられなかった 場合のデフォルトの動作を指定します。この値を変える場合、それは redirect-handler引数のプロトコルに従う手続きでなければなりません。 上のhttp-*手続きの項目を参照してください。

デフォルトの動作は以下の通りです。

300, 301, 305, 307

元のリクエストがGETHEADの場合に限り、同じリクエストを使って locationヘッダに与えられたURLにリダイレクトします。

302

locationヘッダに与えられたURLにリダイレクトします。 元がHEADリクエストならHEADを、それ以外ならGETリクエストを 使います。

厳密に言えばこれはRFC2616違反ですが、RFC2616の注記にもあるように、 多くのユーザエージェントがこの振る舞いをするので、それに合わせてあります。 (将来は変えるかもしれません。)

303

locationヘッダに与えられたURLにリダイレクトします。 元がHEADリクエストならHEADを、それ以外ならGETリクエストを 使います。

other than above

リダイレクトしません。

次のコードは、デフォルトの振る舞いを特定のリクエストでインターセプトする例です。

 
(http-get server uri
  :redirect-handler
  (^[method status headers body]
    (if (and (equal? status "302")
             (not (member method '(GET HEAD))))
        #f
        ((http-default-request-handler) method status headers body))))
Function: http-compose-query path params :optional encoding

クエリパラメータのリストからリクエストURIを生成する補助関数です。 encoding引数はクエリパラメータの文字エンコーディングを指定します。

 
(http-compose-query "/search" '((q "$foo") (n 20)))
 ⇒ "/search?q=%24foo&n=20"

(http-compose-query "" '((x "a b") (x 2)))
 ⇒ "?x=a%20b&x=2"

path#fの場合は、クエリパラメータの部分だけが返されます (次の例と直前の例を比べてみてください)。

 
(http-compose-query #f '((x "a b") (x 2)))
 ⇒ "x=a%20b&x=2"
Function: http-compose-form-data params port :optional encoding

A helper procedure to create multipart/form-data from a list of parameters. The format of params argument is the same as the list format of body argument of http request procedures. The result is written to an output port port, and the boundary string used to compose MIME message is returned. Alternatively you can pass #f to the port to get the result in a string. In that case, two values are returned, the MIME message string and the boundary string.

Encoding specifies the character encodings to be used.

Function: http-status-code->description code

HTTPステータスコードcodeの簡単な説明を返します。 codeは整数か、整数を表す文字列です(例: "404")。 codeが知られているものでなかった場合は#fが返されます。

 
(http-status-code->description 404)
  ⇒ "Not Found"

セキュアな接続

When you pass a true value to secure keyword argument, The request-making APIs such as http-get use a secure connection. That is, it connects with https instead of http.

The secure connection may not available on some platforms. Use the following procedure to check if you can use secure connections:

Function: http-secure-connection-available?

Returns #t if running Gauche can use secure connection, #f otherwise.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.34 rfc.icmp - ICMPパケット

Module: rfc.icmp

このモジュールではICMPパケットの構築および解析用の基本的ユーティリティ を提供しています。

以下の関数のbufferは十分なサイズの書き込み可能なu8vectorでなけれ ばなりません。

解析用の関数はbufferのほかにoffsetを引数としてとります。こ れはICMPパケットの開始位置を示すものです。このオフセットを用いて、ICMP 部分を取り出して新しいバッファを作るということなしに、IPパケット全体を buffer内に格納できます。

Function: icmp4-fill-echo! buffer ident sequence data

bufferにICMPv4のエコーリクエストパケットを詰め込みます。 Dataはu8vectorでなければなりません。チェックサムフィールドはゼロ のままです。チェックサムはicmp4-fill-checksum!を使って埋め込み ます。

Function: icmp4-fill-checksum! buffer size

buffer中のsize(バッファではなくパケットの長さ)分のパケット のICMPv4チェックサムを計算し、そのパケットのチェックサムフィールドをう めます。

Function: icmp6-fill-echo! buffer ident sequence data

bufferにICMPv6のエコーリクエストパケットを詰め込みます。 Dataはu8vectorでなければなりません。チェックサムフィールドは ゼロのままで、ここはカーネルが埋めることになっています。したがって、 ユーザーが自分で埋める必要はありません。

Function: icmp-packet-type buffer offset
Function: icmp-packet-code buffer offset
Function: icmp-packet-ident buffer offset
Function: icmp-packet-sequence buffer offsetj

それぞれICMPパケットのタイプ、コード、識別子、シーケンスフィールドを抜 き出します。これらの関数はICMPv4/v6で共通です。

Function: icmp4-describe-packet buffer offset
Function: icmp6-describe-packet buffer offset

それぞれ与えられたICMPv4およびICMPv6パケットの簡単な説明を印字します。

Function: icmp4-message-type->string type
Function: icmp4-unreach-code->string code
Function: icmp4-redirect-code->string code
Function: icmp4-router-code->string code
Function: icmp4-exceeded-code->string code
Function: icmp4-parameter-code->string code
Function: icmp4-security-code->string code
Function: icmp6-message-type->string type
Function: icmp6-unreach-code->string code
Function: icmp6-exceeded-code->string code
Function: icmp6-parameter-code->string code

ICMPv4およびICMPv6のタイプとコードの説明テキストを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.35 rfc.ip - IPパケット

Module: rfc.ip

このモジュールは生のIPパケットを解析する基本的なユーティリティを提供し ます。

次からの関数におけるpacket引数はユニフォームベクタ (gauche.uvector - ユニフォームベクタ参照)型でなければなりません。これにはIPヘッダを 含む生のIPパケットが入ります。以下の関数はIPv4およびIPv6パケットの両方 で動きます。しかしながら、生のIPv6ソケットから読むときは、IPv6ヘッダを 含まないパケットが返ります。とうわけで、通常これらの関数が必要になるこ とはないでしょう。

Function: ip-version packet offset

与えられたIPパケットのIPバージョン番号(4または6)が返ります。

Function: ip-header-length packet offset

与えられたパケットのIPヘッダ(IPヘッダオプションもすべて含む)のオクテッ トで数えたサイズを返します。

Function: ip-protocol packet offset

与えられたパケットのIPプロトコル番号を返します。

Function: ip-source-address packet offset
Function: ip-destination-address packet offset

与えられたパケットの送信元アドレスと送信先アドレスをそれぞれ整数で返し ます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.36 rfc.json - JSONのパーズと構築

Module: rfc.json

RFC7159で規定される、JSON形式をパーズしてS式に直す手続きと、 S式をJSON形式に変換する手続きが提供されます。

Condition type: <json-parse-error>

パーズ手続きparse-jsonparse-json-stringは、 無効なJSON構文に出会った時にこのコンディションを投げます。 <error>を継承し、次のスロットを追加で持ちます。

Instance Variable of <json-parse-error>: position

エラーが起きた入力位置(文字数)。

Function: parse-json :optional input-port

JSON表記をinput-port (省略された場合はcurrent-input-port)から 読み込みパーズして、結果をS式で返します。 パーズエラーが起きた場合は<json-parse-error>コンディションを投げます。

下のテーブルに、JSONのデータ型がどのようにSchemeにマップされるかを示します。

true, false, null

シンボルtrue, false and null。 (json-special-handlerで変更可能)

配列

Schemeのベクタ。 (json-array-handlerで変更可能)

オブジェクト

Schemeの連想リスト。キーは文字列で、値はSchemeオブジェクト。 (json-object-handlerで変更可能)

数値

Schemeの不正確な実数。

文字列

Schemeの文字列。

parse-json内で使っているパーザは文字の先読みを行う可能性があるので、 parse-jsonが戻って来た時点で、パーズされたJSON式以降のいくつかの文字が portから既に読まれてしまっている可能性があります。 すなわち、複数のJSON式を読み出すのに、portに対してparse-json を繰り返し呼び出すのはうまくいきません。複数のJSON式をパーズしたい場合は parse-json*を使ってください。

Function: parse-json* :optional input-port

input-portから、EOFに達するまでJSON式を繰り返し読み取り、 パーズ結果をリストにして返します。

Function: parse-json-string str

文字列strをJSONとしてパーズし、結果をS式で返します。 パーズエラーが起きた場合は<json-parse-error>コンディションを投げます。

JSONのデータ型とSchemeの型とのマッピングについては上のparse-json を参照してください。

Parameter: json-array-handler
Parameter: json-object-handler
Parameter: json-special-handler

これらのパラメータの値は、引数をひとつ取る手続きでなければなりません。 json-array-handlerの値の手続きに渡される引数は、 JSON配列の要素のリストです。 json-object-handlerでは JSONオブジェクトのキーと値をconsしたもののリスト、 json-special-handlerでは シンボルfalse, true, nullのいずれかです。

parse-jsonはJSON配列やオブジェクト、false、true、nullに 出会う度に、 このパラメータの値の手続きを起動して、JSONに対応するSchemeオブジェクトを 得ます。

これらのパラメータのデフォルト値はそれぞれlist->objectidentityidentityです。

次の例では、JSONオブジェクトをハッシュテーブルに変換しています。

 
(use gauche.parameter)
(parameterize ([json-object-handler (cut alist->hash-table <> 'string=?)])
  (parse-json-string "{\"a\":1, \"b\":2}"))
 ⇒ #<hash-table ...>
Condition type: <json-construct-error>

construct-jsonconstruct-json-stringは、 JSONに変換できないSchemeオブジェクトを見つけるとこのコンディションを投げます。 <error>を継承し、次のスロットを追加で持ちます。

Instance Variable of <json-construct-error>: object

JSON表現に変換できなかったSchemeオブジェクト。

Function: construct-json obj :optional output-port
Function: construct-json-string obj

SchemeオブジェクトobjのJSON表現を作ります。 construct-jsonは結果をoutput-portに書き出します。デフォルトは current-output-portです。construct-json-stringは結果を文字列で返します。 RFC4627はJSONテキストはobjectかarrayであると定めているので、 objはJSONオブジェクトかJSON配列へと変換できるSchemeオブジェクト でなければなりません。

objがJSONにマップできないオブジェクトを含んでいた場合は <json-construct-error>コンディションが投げられます。

Schemeオブジェクトは以下のようにJSONへと変換されます。

シンボルfalse, #f

false

シンボルtrue, #t

true

シンボルnull

null

リスト、 <dictionary>のインスタンス

JSONオブジェクト (リストはキーと値の連想リストでなければならない)

文字列

文字列

実数

数値

<sequence>のインスタンス (文字列とリストを除く)

JSON配列


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.37 rfc.md5 - MD5メッセージダイジェスト

Module: rfc.md5

このモジュールは、RFC 1321(RFC1321参照)で 定義されている、MD5メッセージダイジェストアルゴリズムを実装しています。 このモジュールは、util.digest (util.digest - メッセージダイジェストフレームワーク参照) を拡張しています。

Class: <md5>

このクラスのインスタンスは、MD5ダイジェストアルゴリズムの内部状態を 保持しています。

このクラスは、util.digestフレームワークのインターフェースである、 digest-update!digest-final!digestdigest-stringを実装しています。 これらのメソッドの詳細な説明は、util.digest - メッセージダイジェストフレームワーク を参照して下さい。

ダイジェスタフレームワークに加えて、このモジュールはショートカット手続きを 提供します。

Function: md5-digest

現在の入力ポートからEOFまで読み込み、そのダイジェストを不完全文字列で 返します。

Function: md5-digest-string string

stringにあるデータをダイジェストし、その結果を不完全文字列で 返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.38 rfc.mime - MIMEメッセージ処理

Module: rfc.mime

RFC2045からRFC2049で定義されている、 多目的インターネットメール拡張(Multipurpose Internet Mail Extensions; MIME) メッセージを扱う便利な手続きです。MIME特有のヘッダフィールドやメッセージボディを パーズしたり作成したりするAPIが提供されます。

このモジュールは主としてビルディングブロックとなる低レベルの手続きに フォーカスしており、アプリケーション特有のモジュールがこの上に 構築されることを意図しています。例えばrfc.httpはPOSTリクエストの ボディをmultipart/form-dataとして構築する際にこのモジュールを 利用します(rfc.http - HTTP参照)。

このモジュールは、rfc.822モジュールと一緒に使うことを 想定しています(rfc.822 - RFC822メッセージ形式参照)。

Utilities for header fields

MIME特有のヘッダフィールドをパーズしたり生成したりする便利な手続き。

Function: mime-parse-version field

fieldがそのMIMEバージョンのヘッダフィールドとして有効であれば、 そのメジャーバージョン番号とマイナーバージョン番号をリストにして 返します。そうでなければ、#fを返します。 fieldには#fを渡せるので、rfc822-header-refの 戻り値を直接渡すこともできます。rfc822-read-headersにより 返されるパーズ済みヘッダのリストを渡すことで、以下のように MIMEのバージョンを得ることができます。(現在は、(1 0)です。)

 
(mime-parse-version (rfc822-header-ref headers "mime-version"))

注意: fieldはトークンの間にコメントを含むかもしれないので、 #/\d+\.\d+/のような単純な正規表現では不十分です。

Function: mime-parse-content-type field

“content-type”ヘッダフィールドをパーズし、次のようなリストを 返します。

 
(type subtype (attribute . value) …)

ここで、typesubtypeはそれぞれ、MIMEメディアタイプと サブタイプを文字列で表したものになります。

 
(mime-parse-content-type "text/html; charset=iso-2022-jp")
 ⇒ ("text" "html" ("charset" . "iso-2022-jp"))

fieldが有効なcontent-typeフィールドでない場合は、 #fが返ります。

Function: mime-parse-content-disposition field

RFC2183に定められたContent-Dispositionヘッダフィールドをパーズします。 (mime-parse-content-disposition "attachment; filename=genome.jpeg;\ modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";") ⇒ ("attachment" ("filename" . "genome.jpeg") ("modification-date" . "Wed, 12 Feb 1997 16:29:51 -0500"))

Function: mime-parse-parameters :optional iport
Function: mime-compose-parameters params :optional oport :key start-column

これらは、(RFC2045の5.1節にあるような)ヘッダフィールドの値のparameter 部分をパーズしたり作成したりするための低レベルのユーティリティ手続きです。

mime-parse-parametersはヘッダフィールドの値のパラメータ部分を 入力ポートiportから読んでパーズし、パラメータの名前と値の 連想リストを返します。 mime-compose-parametersはその逆で、連想リストをとり、 パラメータ部分を構成してoportへと書き出します。 iportoportはそれぞれ省略された場合、 current-input-portcurrent-output-portを デフォルトとします。また、oport#fを渡すと mime-compose-parametersは結果をポートに書き出すかわりに 文字列として返します。

 
(call-with-input-string
   "; name=foo; filename=\"foo/bar/baz\""
   mime-parse-parameters)
 ⇒ (("name" . "foo") ("filename" . "foo/bar/baz"))

(mime-compose-parameters
 '(("name" . "foo") ("filename" . "foo/bar/baz"))
 #f)
 ⇒ "; name=foo; filename=\"foo/bar/baz\""

mime-compose-parametersはヘッダ行が長くなりすぎる場合に パラメータ間に折り返し改行を入れようとします。パラメータ部分が始まる カラム数はstart-columnで与えることができます。

将来は、これらの手続きにRFC2231のパラメータ値拡張を透過的に処理させる予定です。

Function: mime-decode-word word

RFC2047でエンコードされたwordをデコードします。 wordがRFC2047でエンコードされたものでない場合は、そのまま 返されます。

この手続きはword全体がRFC2047の規定する“encoded-word”である場合にのみ デコードを行うことに注意してください。複数のエンコードされた部分や エンコードされていない部分が混ざっているフィールドを扱う場合は、 下に示すmime-decode-textを使います。

 
(mime-decode-word "=?iso-8859-1?q?this=20is=20some=20text?=")
 ⇒ "this is some text"
Function: mime-decode-text text

text中に含まれるすべてのencoded wordをデコードした文字列を返します。 この手続きは、エンコードされていない部分とエンコードされている部分が混ざっていたり、 複数のエンコードされている部分を持つヘッダフィールドボディを処理することが できます。そのようなフィールドの例はemailのSubjectフィールドです。

 
(mime-decode-text "Yamada Taro (=?utf-8?B?5bGx55SwIOWkqumDjg==?=)")
 ⇒ "Yamada Taro (山田 太郎)"

この手続きを「構造化された」ヘッダフィールドボディ (RFC2822 2.2.2節参照) に適用する際には注意が必要です。 構造化されたヘッダフィールドボディをパーズする正式な方法は、 最初にトークンに分割して、それから各wordを mime-decode-wordを使ってデコードするというものです。 なぜならデコード後のテキスト中に、パージングに影響を与える文字が含まれている かもしれないからです。 (ただし、単に参考情報を人間にわかりやすいように表示するだけの目的の場合は、 簡便のためにヘッダフィールド全体をこの手続きで一度にデコードしてしまっても 良いでしょう)。

Function: mime-encode-word word :key charset transfer-encoding

wordをRFC2047フォーマットにエンコードします。キーワード引数 charsetは文字列かシンボルで文字エンコーディングスキームを指定します。 デフォルトはutf-8です。charsetの指定がGaucheの 内部文字エンコーディングと異なっており、wordが完全な文字列である場合は、 まずwordcharsetのエンコーディングへと変換され、 その上でトランスファーエンコーディングがかけられます。

 
(mime-encode-word "this is some text")
 ⇒ "=?utf-8?B?dGhpcyBpcyBzb21lIHRleHQ=?="

キーワード引数transfer-encodingは各オクテットを伝達上安全な 文字列へどエンコードする方法を指定します。サポートされている値は、 Base64を指定するシンボルbBbase64、 およびQuoted printableを指定する Qqquoted-printableです。 これ以外の値を渡した場合はエラーが通知されます。デフォルトはBase64です。

この手続きは結果のencoded wordの長さを気にしませんが、 RFC2047によればencoded wordは75オクテットまでに収めることが 要請されています。この要請に対応するには下に示す mime-encode-textを使って下さい。

(註:ほとんどのGaucheの手続きでは、キーワード引数encodingにより 文字エンコーディングを指定します。しかしこの手続きの文脈では 2つの「エンコーディング」が存在しているので、混乱を避けるために RFC文書で使われている“charset”および“transfer-encoding”の用語を 使うこととしました。)

Function: mime-encode-text text :key charset transfer-encoding line-width start-column force

textを、必要ならばRFC2047フォーマットに従いエンコードします。 また、結果が長すぎる場合の行の折り返しも考慮します。

キーワード引数charsettransfer-encodingの意味は mime-encode-wordと同じです。

もしwordが印字可能なASCII文字のみで構成されていた場合は エンコーディングは行われず、行の折り返しのみが処理されます。 但し、force引数に真の値が与えられた場合はASCIIのみのtextも エンコードされます。

line-widthは結果に現れる行の最大値を指定します。デフォルトは76です。 encoded wordがこれを越える場合は、複数のencoded wordへと結果は分割され、 間にCR LF SPCシーケンス(RFC2822で定義される“folding white space”)が挿入されます。 line-width#f0を渡すことで 行の折り返しを抑制することができます。 encoded wordには文字数でいくらかのオーバヘッドがあるため、 あまり小さいline-widthには意味がありません。現在の実装では 30以下の値は拒否されます。

start-columnキーワード引数は、ヘッダフィールド名を入れるために エンコード結果の最初の行だけを短くするのに使えます。 例えばSubjectヘッダフィールドのボディをエンコードする際に、 (string-length "Subject: ")の値を渡してやれば、 結果を直接"Subject: "の後に連結することができるわけです。 デフォルトの値は0です。

この手続きはstructured header fieldをエンコードするようには設計 されていません。structured header fieldには、どの部分がエンコード 可能でどの部分にfolding white spaceが挿入可能かについてさらなる 制約があるためです。安全な方法は、まず必要な部分をエンコードし、 それから折り返しを考慮しつつstructured header fieldを組み立てることです。

Streaming parser

メッセージ全体が読み込まれる前にメッセージボディをどのように 扱うかをコントロールできるように、ストリームパーザが用意されて います。

Function: mime-parse-message port headers handler

基本的なストリームパーザです。portは、メッセージを読み込む 入力ポートです。headersrfc822-read-headersにより パーズされたヘッダのリストです。つまり、この手続きは、 portから読み込まれたメッセージのヘッダ部分がパーズされた 後に使われることを想定しています。

 
(let* ((headers (rfc822-read-headers port)))
  (if (mime-parse-version (rfc822-header-ref headers "mime-version"))
     ;; parse MIME message
     (mime-parse-message port headers handler)
     ;; retrieve a non-MIME body
     ...))

mime-parse-messageheadersを解析し、 メッセージボディのそれぞれについて、2引数をもって handlerを呼び出します。

 
(handler part-info xport)

part-infoは、以下で説明するような、メッセージのこのパートの 情報をカプセル化した<mime-part>ストラクチャです。

xportは入力ポートで、最初はメッセージボディの先頭を指しています。 ハンドラはこのポートからメッセージボディを読み込むことが出来ます。 xportはMIMEバウンダリを認識し、パートの最後に到達したら EOFを返します。 (元のportから直接読み込まないようにして下さい。 そうしてしまうと、vportの内部状態がおかしくなります)。

handlerは、パートをメモリに読み込んだり、ディスクに保存したり、 あるいはそのパートを無視したりできます。ただ、何をするにせよ、 vportがEOFを返すまでデータを読まなければなりません。

handlerの戻り値は、part-infocontentスロットに セットされます。

メッセージが、ネストしたマルチパートメッセージを含んでいる場合は、 handlerは深さ優先でそれぞれの“葉”のパートに対して呼ばれます。 handlerは、part-infoストラクチャを調べることで、 そのネストのレベルを知ることができます。

メッセージはマルチパートである必要はありません。メッセージが MIME mesasgeタイプである場合は、handlerは囲まれたメッセージの ボディに対して呼ばれます。メッセージが、textapplication などの他のメディアタイプの場合は、handlerは単にメッセージボディに 対して呼ばれます。

Class: <mime-part>

MIMEパートのメタ情報を含むストラクチャです。 これは、そのパートのヘッダが読み込まれた時点で構築され、 そのパートのボディを読み込むハンドラに渡されます。

以下のスロットを持ちます。

Instance Variable of <mime-part>: type

MIMEメディアタイプの文字列。そのパートのcontent-typeヘッダが 省略された場合は、適切なデフォルト値がセットされます。

Instance Variable of <mime-part>: subtype

MIMEメディアのサブタイプの文字列。そのパートのcontent-type ヘッダが省略された場合は、適切なデフォルト値がセットされます。

Instance Variable of <mime-part>: parameters

content-typeヘッダフィールドに渡されるパラメータの連想リスト。

Instance Variable of <mime-part>: transfer-encoding

content-transfer-encodingヘッダフィールドの値。 このヘッダフィールドが省略された場合は、適切なデフォルト値が セットされます。

Instance Variable of <mime-part>: headers

rfc822-read-headersによりパーズされた、ヘッダフィールドのリスト。

Instance Variable of <mime-part>: parent

それがマルチパートメッセージあるいはカプセル化されたメッセージの パートである場合は、それを含んでいるパートの<mime-part> ストラクチャを指します。そうでなければ#fを返します。

Instance Variable of <mime-part>: index

同じ親を持つパートの中でのそのパートのシーケンス番号。

Instance Variable of <mime-part>: content

そのパートのメディアタイプがmultipart/*あるいはmessage/*で ある場合は、このスロットにはそれに含まれるパートのリストが 入っています。そうでなければ、handlerの戻り値が 格納されています。

Instance Variable of <mime-part>: source

このスロットはMIMEメッセージを作成する時のみ使われます。 呼び出し元は、このスロットにファイル名をセットすることで、 MIMEメッセージのこのパートにファイルの内容を挿入することができます。 詳しくは下のmime-compose-messageの項を参照してください。

Function: mime-retrieve-body part-info xport outp

メッセージボディを取得するための手続きです。 mime-parse-messageへ渡される、handlerの ビルディングブロックとなることを意図しています。

part-infoは、<mime-part>のオブジェクトです。 xportはハンドラに渡された入力ポートで、 そこからMIMEパートが読みこまれるものです。

この手続きは、xportからEOFに達するまで読み込み、 part-infotransfer-encodingも見て、 ボディを適切にデコードします。つまり、base64やquoted-printable のエンコーディングは適切に処理されます。結果が出力ポートoutpへと 出力されます。

この手続きは文字セットの変換は扱いません。 必要であれば、呼び出し側がoutpとしてCES変換ポートを 使う必要があります(gauche.charconv - 文字コード変換参照)。

典型的なケースのために、いくつかの便利な手続きがmime-retrieve-body の上に定義されています。

Function: mime-body->string part-info xport
Function: mime-body->file part-info xport filename

MIMEメッセージのボディを読み込み、転送(transfer)エンコーディングを デコードし、それぞれ文字列として返すか、ファイルへ書き出します。

MIMEメッセージパーザの最もシンプルな使い方は次のように なります。

 
(let ((headers (rfc822-read-headers port)))
  (mime-parse-message port headers
                      (cut mime-body->string <> <>)))

これは、メッセージの全てをメモリに読み込み、 一番上層の<mime-part>オブジェクトを返します。 (“葉”である<mime-part>オブジェクトのcontentフィールドは、 そのパートのボディを文字列として保持しています。) 内容の転送エンコーディング(content transfer encoding)は認識され処理 されますが、文字セットの変換は行われません。

メッセージボディを直接ファイルに書き出したり、MIMEメディアタイプや 他のヘッダ情報に基づいていくつかのボディをスキップしたいかもしれません。 その場合は、ロジックをハンドラのクロージャに入れることができます。 それが、このモジュールが、オールインワンの手続きではなく、 ビルディングブロックを提供している理由です。

Message composer

Function: mime-compose-message parts :optional port :key boundary
Function: mime-compose-message-string parts :key boundary

Composes a MIME multipart message. Mime-compose-message emits the result to an output port port, whose default is the current output port. Mime-compose-message-string makes the result into a string. You can give a boundary string via boundary argument; when omitted, a fresh boundary string is automatically generated by mime-make-boundary below.

Mime-compose-message returns the boundary string. Mime-compose-message-string returns two values, the result string and the boundary string.

The content of the message is provided by the parts argument, which can be a list of instances of <mime-part> (see above) or lists that describe parts. The list form is supported for the caller’s convenience, and internally it is converted to a list of <mime-part>s.

The syntax of each part element in parts are defined as follow.

 
<part>           : <mime-part> | <mime-part-desc>

<mime-part>      : an instance of the class <mime-part>

<mime-part-desc> : (<content-type> (<header> ...) <body>)
<content-type>   : (<type> <subtype> <header-param> ...)
<header-param>   : (<key> . <value>) ...
<header>         : (<header-name> <encoded-header-value>)
                 | (<header-name> (<header-value> <header-param> ...))
<body>           : a string
                 | (file <filename>)
                 | (subparts <part> ...)

Note: In the first form of <header>, <encoded-header-value> must already be encoded using RFC2047 or RFC2231 if the original value contains non-ascii characters. In the second form, we plan to do RFC2231 encoding on behalf of the caller; but the current version does not implement it. The caller should not pass encoded words in this form, since it may result double-encoding when we implement the auto encoding feature; for the time being, the second form restricts ASCII-only values.

If <body> is a string, it is used as the part’s content. If <body> is (file filename), the content is read from the named file. If <body> is (subparts part …), the part becomes nested MIME part.

It is the caller’s responsibility to give the proper content. For example, if <body> is in the third form, the part must have multipart content type.

The caller needs to provide proper content-transfer-encoding header, depending on the application. If none is given, the content is inserted into the message as is, which may be appropriate for some applications, but if you want to use the result in email message you certainly want to encode binary part with base64, for example.

Function: mime-make-boundary

MIMEマルチパートメッセージのboundaryとして使えるユニークな文字列を返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.39 rfc.quoted-printable - Quoted-printableエンコーディング

Module: rfc.quoted-printable

このモジュールでは、RFC 2045 (RFC2045参照)の セクション6.7で定義されている、Quoted-printableフォーマットにエンコード/から デコードするためのいくつかの関数を定義しています。

Function: quoted-printable-encode :key line-width binary

現在の入力ポートからバイトストリームを読み込み、それをQuoted-printable フォーマットにエンコードし、現在の出力ポートへ結果の文字ストリームを 書き出します。この変換は、現在の入力ポートからEOFを読み出すと終了します。 キーワード引数line-widthは、出力に現れる行の最大長を指定します。 エンコードされた行の長さがこの値を越えそうな場合は、「ソフトラインブレーク」が 適宜挿入され、各行の長さがこの値を越えないように調整されます。 ソフトラインブレークはquoted-printableフォーマットのデコード時に 取り除かれます。 line-widthのデフォルト値は76です。(最小の意味のある値は4です。) line-width#fまたは0を渡せば、 ソフトラインブレークは挿入されません。 デフォルトでは、quoted-printable-encodeは入力中の改行に対して CR-LFシーケンスを出力します(「ハードラインブレーク」)。 しかし、binaryキーワード引数に真の値が与えられた場合、 入力中のオクテット#x0aおよび#x0dはそれぞれ =0A=0Dのようにエンコードされます。 詳しくはRFC2045の6.7節を参照してください。

Function: quoted-printable-encode-string string :key line-width binary

stringの内容をQuoted-printableエンコードされたフォーマットに 変換します。入力の文字列は、完全文字列でも不完全文字列でも構いません。 常にバイトシーケンスとして処理されます。

キーワード引数はquoted-printable-encodeと同じです。

Function: quoted-printable-decode

現在の入力ポートから文字ストリームを読み込み、それをQuoted-printable フォーマットからデコードし、結果のバイトストリームを現在の出力ポートへ 書き出します。 この変換は、EOFを読み出すと終了します。 不正なシーケンス(’=’の後に16進文字が続かない、など)に出会うと、それらを リテラルのまま出力へコピーします。

Function: quoted-printable-decode-string string

Quoted-printableエンコードされた文字列stringをデコードし、 その結果を文字列で返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.40 rfc.sha - SHAメッセージダイジェスト

Module: rfc.sha

このモジュールは、RFC 4634で定義されている US Secure Hash Algorithmを実装しています。 提供されるアルゴリズムはSHA-1, SHA-224, SHA-256, SHA-384および SHA-512です (後の4つを総称してSHA-2と呼ぶこともあります)。

このモジュールは、util.digest (util.digest - メッセージダイジェストフレームワーク参照) を拡張しています。

Module: rfc.sha1

これはSHA-1だけを提供していた古いモジュールです。互換性のため、 この名前はrfc.shaの別名として残されています。 新たに書くコードはrfc.shaを使ってください。

Class: <sha1>
Class: <sha224>
Class: <sha256>
Class: <sha384>
Class: <sha512>

これらのクラスのインスタンスは、SHAダイジェストアルゴリズムの内部状態を 保持しています。

このクラスは、util.digestフレームワークのインターフェース、 digest-update!digest-final!digestdigest-stringを実装しています。 これらのメソッドの詳細な説明は、util.digest - メッセージダイジェストフレームワークを 参照して下さい。

ダイジェスタフレームワークに加えて、このモジュールはショートカット 手続きを提供します。

Function: sha1-digest
Function: sha224-digest
Function: sha256-digest
Function: sha384-digest
Function: sha512-digest

現在の入力ポートからデータをEOFまで読み込み、そのダイジェストを 不完全文字列で返します。

Function: sha1-digest-string string
Function: sha224-digest-string string
Function: sha256-digest-string string
Function: sha384-digest-string string
Function: sha512-digest-string string

stringのデータをダイジェストし、その結果を不完全文字列で 返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.41 rfc.uri - URIの解析と作成

Module: rfc.uri

RFC 2396 (RFC2396)で定義されている Uniform Resource Identifiers、 またRFC 2397で定義されているData URI Schemeをパーズおよび構築する 手続き群を提供します。

First, lets review the structur of URI briefly. The following graph shows how the URI is constructed:

 
URI-+-scheme
    |
    +-specific--+--authority-+--userinfo
                |            +--host
                |            +--port
                +--path
                +--query
                +--fragment

Not all URIs have this full hierachy. For exmaple, mailto:admin@example.com has only scheme (mailto) and specific (admin@example.com) parts.

Most popular URI schemes, however, organize resources in a tree, so they adopt authority (which usually identifies the server) and the hierarchical path. In the URI http://example.com:8080/search?q=key#results, the authority part is exmaple.com:8080, the path is /search, the query is key and the fragment is results. The userinfo can be provided before hostname, such as anonymous in ftp://anonymous@example.com/pub/.

We have procedures that decompose a URI into those parts, and that compose a URI from those parts.

URIのパーズ

Function: uri-ref uri parts

Extract specific part(s) from the given URI. You can fully decompose URI by the procedures described below, but in actual applications, you often need only some of the parts. This procedure comes handy for it.

The parts argument may be a symbol, or a list of symbols, to name the desired parts. The recognized symbos are as follows.

scheme

The scheme part, as string..

authority

The authority part, as string. If URI doesn’t have the part, #f.

userinfo

The userinfo part, as string. If URI doesn’t have the part, #f.

host

The host part, as string. If URI doesn’t have the part, #f.

port

The port part, as integer. If URI doesn’t have the part, #f.

path

The path part, as string. If URI isn’t hierarchical, this returns the specific part.

query

The query part, as string. If URI doesn’t have the part, #f.

fragment

The fragment part, as string. If URI doesn’t have the part, #f.

scheme+authority

The scheme and authority part.

host+port

The host and port part.

userinfo+host+port

The userinfo, host and port part.

path+query

The path and query part.

path+query+fragment

The path, query and fragment part.

 
(define uri "http://foo:bar@example.com:8080/search?q=word#results")

(uri-ref uri 'scheme)             ⇒ "http"
(uri-ref uri 'authority)          ⇒ "//foo:bar@example.com:8080/"
(uri-ref uri 'userinfo)           ⇒ "foo:bar"
(uri-ref uri 'host)               ⇒ "example.com"
(uri-ref uri 'port)               ⇒ 8080
(uri-ref uri 'path)               ⇒ "/search"
(uri-ref uri 'query)              ⇒ "q=word"
(uri-ref uri 'fragment)           ⇒ "results"
(uri-ref uri 'scheme+authority)   ⇒ "http://foo:bar@example.com:8080/"
(uri-ref uri 'host+port)          ⇒ "example.com:8080"
(uri-ref uri 'userinfo+host+port) ⇒ "foo:bar@example.com:8080"
(uri-ref uri 'path+query)         ⇒ "/search?q=word"
(uri-ref uri 'path+query+fragment)⇒ "/search?q=word#results"

You can extract multiple parts at once by specifying a list of parts. A list of parts is returned.

 
(uri-ref uri '(host+port path+query))
  ⇒ ("example.com:8080" "/search?q=word")
Function: uri-parse uri
Function: uri-scheme&specific uri
Function: uri-decompose-hierarchical specific
Function: uri-decompose-authority authority

URIの一般的なパーザです。これらの関数はURIエンコーディングを デコードしません。URIスキームによってどの部分をデコードすべきかが 異なるからです。パージングを行った後に、後述のuri-decode等を 使ってデコードを行ってください。

uri-parseは最も手軽な手続きで、uriを以下に示す部分に 分割し、多値で返します。 もし該当する部分がuriに無かった場合は、その部分には#fが返ります。

以下の手続きはより詳細に、段階をふんでuriを分割してゆくものです。

uri-scheme&specific は URI uri を引数に取り、 スキーム部分と、そのスキーム特有の部分を表す2つの値を返します。 uri がスキーム部分を持たない場合、#f を返します。

 
(uri-scheme&specific "mailto:sclaus@north.pole")
  ⇒ "mailto" and "sclaus@north.pole"
(uri-scheme&specific "/icons/new.gif")
  ⇒ #f and "/icons/new.gif"

URI が階層的な記法を用いている場合、すなわち、 “//authority/path?query#fragment” のような場合、スキーム特有の部分を uri-decompose-hierarchical に渡すと、authoritypathqueryfragment の4つの値が返ります。

 
(uri-decompose-hierarchical "//www.foo.com/about/company.html")
  ⇒ "www.foo.com", "/about/company.html", #f and #f
(uri-decompose-hierarchical "//zzz.org/search?key=%3fhelp")
  ⇒ "zzz.org", "/search", "key=%3fhelp" and #f
(uri-decompose-hierarchical "//jjj.jp/index.html#whatsnew")
  ⇒ "jjj.jp", "/index.html", #f and "whatsnew"
(uri-decompose-hierarchical "my@address")
  ⇒ #f, #f, #f and #f

さらに、階層的 URI の authority の部分を uri-decompose-authority に渡すと、userinfohostport が返ります。

 
(uri-decompose-authority "yyy.jp:8080")
  ⇒ #f, "yyy.jp" and "8080"
(uri-decompose-authority "[::1]:8080")  ;(IPv6 host address)
  ⇒ #f, "::1" and "8080"
(uri-decompose-authority "mylogin@yyy.jp")
  ⇒ "mylogin", "yyy.jp" and #f
Function: uri-decompose-data uri

Data URI文字列uriをパーズします。data:スキームは有っても無くても 構いません。渡されたuriがdata uriとして無効な文字列であればエラーが投げられます。

二つの値、パーズされたContent-Typeおよびデコードされたデータを返します。 Content-Typeがtext/*であればデコードされたデータは文字列で、 そうでなければu8vectorで返されます。

Content-Typeはmime-parse-content-typeでパーズされます (rfc.mime - MIMEメッセージ処理参照)。結果のデータ形式は次のようなリストです。

 
(type subtype (attribute . value) …).

いくつか例を示します。

 
(uri-decompose-data
 "data:text/plain;charset=utf-8;base64,KGhlbGxvIHdvcmxkKQ==")
  ⇒ ("text" "plain" ("charset" . "utf-8")) and "(hello world)"

(uri-decompose-data
 "data:application/octet-stream;base64,AAECAw==")
  ⇒ ("application" "octet-stream") and #u8(0 1 2 3)

URIの構築

Function: uri-compose :key scheme userinfo host port authority path path* query fragment specific

与えられたコンポーネントから URI を構成します。 妥当な URI を作成するためのコンポーネントの組み合わせはたくさんあります。 以下のダイアグラムは、考え得る組み合わせの方法を示しています。

 
        /-----------------specific-------------------\
        |                                            |
 scheme-+------authority-----+-+-------path*---------+-
        |                    | |                     |
        \-userinfo-host-port-/ \-path-query-fragment-/

キーワード引数に #f が与えられた場合、それはキーワード引数が 指定されないことと等価です。これは URI をパーズした結果を渡す場合に 特に有用です。

コンポーネントに適切でない文字が含まれている場合は、 url-compose に渡す前に正しくエスケープされなければなりません。

いくつかの例を示します。

 
(uri-compose :scheme "http" :host "foo.com" :port 80
             :path "/index.html" :fragment "top")
  ⇒ "http://foo.com:80/index.html#top"

(uri-compose :scheme "http" :host "foo.net"
             :path* "/cgi-bin/query.cgi?keyword=foo")
  ⇒ "http://foo.net/cgi-bin/query.cgi?keyword=foo"

(uri-compose :scheme "mailto" :specific "a@foo.org")
  ⇒ "mailto:a@foo.org"

(receive (authority path query fragment)
   (uri-decompose-hierarchical "//foo.jp/index.html#whatsnew")
 (uri-compose :authority authority :path path
              :query query :fragment fragment))
  ⇒ "//foo.jp/index.html#whatsnew"
Function: uri-merge base-uri relative-uri relative-uri2 …

引数は、完全な、あるいは部分的なURIを表す文字列です。 この手続きは、RFC3986 Section 5.2. “Relative Resolution” に 示されるアルゴリズムに従い、relative-uribase-uriからの相対 として解決します。

relative-uri2 … が与えられた場合は、まずrelative-uribase-uriを基準に解決され、その結果を新たな基準として次の relative-uri2を解決し、以下同様に続けます。

 
(uri-merge "http://example.com/foo/index.html" "a/b/c")
 ⇒ "http://example.com/foo/a/b/c"

(uri-merge "http://example.com/foo/search?q=abc" "../about#me")
 ⇒ "http://example.com/about#me"

(uri-merge "http://example.com/foo" "http://example.net/bar")
 ⇒ "http://example.net/bar"

(uri-merge "http://example.com/foo/" "q" "?xyz")
 ⇒ "http://example.com/foo/q?xyz"
Function: uri-compose-data data :key content-type encoding

与えられたdataからData URIを構築して文字列で返します。

data引数は文字列かu8vectorでなければなりません。

content-typeキーワード引数は、#f (デフォルト)、 content typeを表現する文字列 (例: "text/plain;charset=utf-8")、 もしくはパーズされたcontent type (例: ("application" "octet-stream"))です。 #fである場合は、dataが完全な文字列であれば text/plainにGaucheのネイティブ文字エンコーディングに基づくcharsetを つけたもの、dataがそれ以外であればapplication/octet-streamが 使われます。

encodingキーワード引数は#f (デフォルト)、 もしくはシンボルuriまたはbase64です。これは文字エンコーディングではなく トランスファーエンコーディングであることに注意。 #fの場合は、テキストデータならuriが、バイナリデータならbase64が 使われます。

 
(uri-compose-data "(hello world)")
 ⇒ "data:text/plain;charset=utf-8,%28hello%20world%29"

(uri-compose-data "(hello world)" :encoding 'base64)
 ⇒ "data:text/plain;charset=utf-8;base64,KGhlbGxvIHdvcmxkKQ=="

(uri-compose-data '#u8(0 1 2 3))
 ⇒ "data:application/octet-stream;base64,AAECAw=="

URIのエンコードとデコード

Function: uri-decode :key :cgi-decode
Function: uri-decode-string string :key :cgi-decode :encoding

URI エンコーディング、すなわち、%でエスケープされた URI 文字列を デコードします。uri-decode は現在の入力ポートから入力を受け取り、 デコードした結果を現在の出力ポートに書き出します。 uri-decode-stringstring を入力とし、デコードした 文字列を返します。

cgi-decode が真の場合は、+ がスペース文字に置換されます。

uri-decode-stringには、外部の文字エンコーディングを指定する encodingキーワード引数を与えることができます。この引数が与えれた 場合、デコードされたオクテットの列を指定された文字エンコーディングであると してGaucheの内部文字エンコーディングへと変換したものが返されます。

Function: uri-encode :key :noescape
Function: uri-encode-string string :key :noescape :encoding

安全でない文字を、%によるエスケープでエンコードします。 uri-encode は現在の入力ポートから入力を受け取り、 結果を現在の出力ポートに書き出します。 uri-encode-stringstring を入力とし、エンコードした 文字列を返します。

デフォルトでは、RFC3986 で"非予約文字"として規定されていない文字は エスケープされます。noescape 引数に異なる文字セットを渡すことで、 それらがエンコードされるのを抑止することができます。 例えば古いRFC2396では"非予約文字"がいくつか多かったのですが、 *rfc2396-unreserved-char-set* (下記参照) を渡すことで それらの文字がエスケープされるのを防ぐことができます。

マルチバイト文字は、デフォルトではGauche のネイティブなマルチバイト表現の オクテット・ストリームとしてエンコードされます。ただし uri-encode-stringにはencodingキーワード引数を渡すことができて、 その場合はまずstringが指定された文字エンコーディングへと変換されます。

Constant: *rfc2396-unreserved-char-set*
Constant: *rfc3986-unreserved-char-set*

これらの定数はそれぞれ、RFC2396とRFC3986で定義されている 「非予約文字」の文字集合に束縛されています。 (文字集合の操作については、文字集合およびsrfi-14 - 文字集合ライブラリ を参照して下さい。)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.42 rfc.zlib - zlib圧縮ライブラリ

Module: rfc.zlib

このモジュールは、Zlib圧縮ライブラリに対するバインディングを提供します。 Zlibのほとんどの機能がこのモジュールを通して利用可能です。

Zlibは、ZLIB圧縮データフォーマット(RFC1950)、DEFLATE圧縮データフォー マット(RFC1951)、GZIPファイルフォーマット(RFC1952)の読み書きをサポー トするライブラリです。また、CRC32とAdler32チェックサムの計算のための 関数も提供します。

圧縮・展開機能はポートを通して提供されます。圧縮をチューンするパラ メータの詳細については、Zlibのドキュメントも合わせて参照してください。

Condition types

Zlib処理中のエラーを示すために、以下のコンディションタイプが定義されています。

Condition Type: <zlib-error>

<error>のサブクラスで、以下のコンディションタイプのスーパークラスです。 このクラスはzlib特有のエラーをまとめて捕捉するための抽象クラスとして設けられています。 rfc.zlibが投げるZlib固有のエラーは常に以下の特定のクラスのインスタンス、 もしくはそのインスタンスを含む複合コンディションです。

Condition Type: <zlib-need-dict-error>
Condition Type: <zlib-stream-error>
Condition Type: <zlib-data-error>
Condition Type: <zlib-memory-error>
Condition Type: <zlib-version-error>

<zlib-error>のサブクラスです。それぞれ、Zlibの Z_NEED_DICT_ERRORZ_STREAM_ERRORZ_DATA_ERRORZ_MEMORY_ERRORZ_VERSION_ERROR に対応します。

データの読み出し時にエラーが発生した場合、 <zlib-error>のサブクラスと <io-read-error>の合成コンディションが投げられます。入力が伴わ ない場合、例えば圧縮ストリームの初期化エラーのときには、合成され ていない<zlib-error>のサブクラスが投げられます。ただの引数の型の エラーのように、Zlibの関数が呼び出されない場合は、ただの<error>が 投げられるかもしれません。

Compression/decompression ports

Class: <deflating-port>
Class: <inflating-port>

圧縮と展開の機能はポートを通じて提供されます。 deflating portは出力されたデータを圧縮する出力ポートです。 inflating portは圧縮されたデータソースからデータを展開しつつ 読み込む入力ポートです。

inflating portが読み出す圧縮データが壊れていた場合、読み出し時に <io-read-error><zlib-data-error>の 合成コンディションが投げられます。

Function: open-deflating-port drain :key compression-level buffer-size window-bits memory-level strategy dictionary owner?

新たな<deflating-port>のインスタンス、 すなわち書き込まれたデータを圧縮し出力ポートdrainに書き出す出力ポートを作成して、 そのポートを返します。Zlibの関数deflateInit2()deflateSetDictionary()とを合わせた手続きです。

compression-levelには1から9の整数を指定することができ、 大きい数が高い圧縮率を意味します。引数が省略された場合は、 デフォルトの圧縮レベルと見なされます。これは通常6です。

compression-levelをわかりやすく指定するために以下の定数が定義されています。

Constant: Z_NO_COMPRESSION
Constant: Z_BEST_SPEED
Constant: Z_BEST_COMPRESSION
Constant: Z_DEFAULT_COMPRESSION

buffer-sizeは、ポートのバッファサイズを指定します。デフォルトは4096バイトです。

window-bitsはウィンドウサイズを指定します。 通常この値は8から15までの整数で、圧縮に使われるウィンドウサイズの 2を底とするlogをとった値です。 大きい数であるだけ圧縮率が高くなりますが、 そのぶんメモリの使用量が増加します。デフォルトは15です。

window-bitsよって、2つばかり特別なモードを指定することができます。 window-bitsに-8から-15の整数を指定された場合は、 ZLIBヘッダとトレイラのない生のdeflateデータを作成します。 この場合Adler32チェックサムも計算されません。 実際のウィンドウサイズはwindow-bitsの絶対値によって計算されます。

window-bitsが24から31の間である場合、 ポートはgzipエンコーディングを使うようになります。すなわち、 zlibラッパの代わりに、シンプルなgzipヘッダとトレイラが圧縮データの前後に書き出されます。 gzipヘッダはファイル名やその他のデータ、コメントを持たず、変更時刻は0、 ヘッダCRCはなし、OS名は不明を意味する255になります。gzipストリームが書き出 されるときは、zstream-adler32で取得できるチェックサムはAdler32 ではなくCRC32になります。 実際のウィンドウサイズは window-bits-16 で決定されます。

memory-levelは、圧縮の内部状態のためにどれだけのメモリを割り当て るかを指定するパラメータです。1ではメモリの使用量は最小ですが、遅 くなり圧縮率が低下します。9では高速な処理のためにメモリが最も多く 使われます。デフォルトは8です。

strategyで圧縮アルゴリズムをチューンできます。 以下の定数がstrategyに有効な値として定義されています。

Constant: Z_DEFAULT_STRATEGY

通常のデータに適する、デフォルトのアルゴリズムを使います。

Constant: Z_FILTERED

フィルタにより生成されたデータに適したアルゴリズムを使います。 このようなデータは小さな値とランダムな分散を持つことが多いため、 このアルゴリズムではハフマンエンコーディングをより優先し、 文字列一致の使用をやや抑えます。

Constant: Z_HUFFMAN_ONLY

ハフマンエンコーディングのみを使います (文字列一致を使いません)。

Constant: Z_RLE

マッチ距離を1に制限します (ランレングスエンコーディングを強制することになります)。 Z_HUFFMAN_ONLYと同じくらい高速で、かつpngイメージデータに対して 良い圧縮率が得られます。

Constant: Z_FIXED

動的ハフマンエンコーディングを禁止します。特殊なアプリケーションで、 単純なデコーダを使いたい場合に便利です。

strategyの選択は圧縮率と速度にのみ影響を与えます。 どの値を選んでも圧縮されたデータは正しく展開できます。

dictionaryには圧縮に使う辞書を文字列で与えます。辞書を与える場合、 圧縮と展開で全く同じものを使う必要があります。 辞書の詳細についてはzlibのドキュメントを参照してください。

デフォルトでは、deflating portはそれ自身がクローズされても drainをクローズしません。drainの後始末を気にしたくない 場合はowner?引数に真の値を与えてください。 その場合、deflating portがクローズされすべてのデータが書き出されたのちに drainは自動的にクローズされます。

注意: deflating portは必ず明示的にクローズしてください。 そうしなければ圧縮データの終わりの部分(バッファされているデータおよびトレイラ)が drainに書き出されないかもしれません。 ポートを明示的にクローズせずにガベージ・コレクタに任せた場合、 出力ポートのクローズ手続きはファイナラザから呼び出されることになります。 複数のごみに対してファイナライザの呼ばれる順番は不定なので、 deflating portより先にdrainのファイナライザが呼ばれて そのポートがクローズされてしまうことがあります。 こうなると、圧縮データの終わりの部分の出力がエラーになってしまいます。

Function: open-inflating-port source :key buffer-size window-bits dictionary owner?

圧縮データを読み出せる入力ポートsourceを取り、 新たな<inflating-port>のインスタンス、すなわち 展開されたデータを読み出すことのできる入力ポートを作成し、そのポートを返します。 これはZlibの関数inflateInit2()inflateSetDictionary() を合わせた手続きです。

buffer-sizeowner?の意味は open-deflating-portと同じです。

window-bitsの意味もほぼ同じですが、 32が足された値が与えられた場合にはZLIBとGZIPのヘッダ自動判定が有効になります。

dictionaryは圧縮時の辞書と同じものを指定しなければなりません。 辞書を使って圧縮されたデータを展開する際に、dictionary引数 を指定しなかったり、異なる辞書を与えた場合は <io-read-error><zlib-need-dict-error>の 合成コンディションが投げられます。

Operations on inflating/deflating ports

Function: zstream-total-in xflating-port
Function: zstream-total-out xflating-port
Function: zstream-adler32 xflating-port
Function: zstream-data-type xflating-port

xflating-portはinflating portかdeflating portでなければ なりません。さもなくばエラーが通知されます。

z_stream構造体のtotal_intotal_outadlre32およびdata_typeフィールドの値を返します。

data_typeフィールドの値は以下の定数のうちのいずれかです。

Constant: Z_BINARY
Constant: Z_TEXT
Constant: Z_ASCII
Constant: Z_UNKNOWN
Function: zstream-params-set! deflating-port :key compression-level strategy

圧縮率とストラテジを動的に変更するための手続きです。

Function: zstream-dictionary-adler32 deflating-port

deflating portの作成時に辞書を指定すると、辞書のAdler32チェックサム が計算されます。この手続きはそのチェックサムを返します。 open-deflating-portに辞書を与えなかったなら、#fが返ります。

Function: deflating-port-full-flush deflating-port

ポートのデータをフルフラッシュし、圧縮状態をリセットします。 展開ルーチンはinflate-sync手続きを使って 入力をこの地点までスキップすることができます。

Function: inflate-sync inflating-port

圧縮データを、deflating-port-full-flushによって フルフラッシュしたポイントまで読み飛ばします。 <zlib-data-error>が投げられたときに使用するとよいでしょう。 フルフラッシュポイントに達したときは読み飛ばしたバイトの数を、EOFまで達 したときは#fを返します。

Miscellaneous API

Function: zlib-version

Zlibのバージョンを文字列で返します。

Function: deflate-string string options …

与えられた文字列を圧縮し、zlib圧縮されたデータを文字列で返します。 すべてのオプション引数はそのままopen-deflating-portに渡されます。

Function: inflate-string string options …

Zlib圧縮されたデータを文字列で受け取り、展開されたデータを文字列で 返します。 すべてのオプション引数はそのままopen-deflating-portに渡されます。

Function: gzip-encode-string string options …
Function: gzip-decode-string string options …

deflate-stringおよびinflate-stringと似ていますが、 GZIPフォーマットを使います。これは deflate-stringおよびinflate-stringwindow-bitsに15以上の値指定するのと同じです。

Function: crc32 string :optional checksum

文字列stringのCRC32チェックサムを計算して返します。checksum 引数が与えられた場合は、それをstringによるチェックサムで更新した 値が返されます。

Function: adler32 string :optional checksum

文字列stringのAdler32チェックサムを計算して返します。checksum 引数が与えられた場合は、それをstringによるチェックサムで更新した 値が返されます。

Adler32はCRC32と比較して高速に計算することが可能なアルゴリズムです が、小さなデータのチェックサムの信頼性にいくらか問題があることがわ かっています。詳しくはRFC3309を見てください。これが問題になる場合 はCRC32を使用してください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.43 slib - SLIBインタフェース

Module: slib

このモジュールはAubrey Jaffer氏のSLIBへのインタフェースです。 SLIBがインストールされている場合、(use slib) とすれば SLIBの機能が使えるようになります。 SLIBそのものはGaucheのディストリビューションには含まれていません。 あなたのシステムにまだインストールされていない場合は http://www-swiss.ai.mit.edu/~jaffer/SLIB.htmlから入手することができます。

このモジュールはrequireを再定義し、Gaucheオリジナルのrequireを シャドウします。requireにシンボルが渡された場合はSLIBのrequire のように動作します。requireに文字列が渡された場合はGaucheのrequire のように動作します。provideprovided?についても同様です。

requireでロードされる、SLIBで導入されるすべての定義は、 slibモジュール内で行われます。

 
(use slib)         ; load and set up slib
(require 'getopt)  ; load SLIB's getopt module
(require "foo")    ; load Gauche's foo module

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.44 sxml.ssax - 関数的なXMLパーザ

Module: sxml.ssax

sxml.*モジュールは、XML構造のS式表現に基づく Oleg KiselyovのSXMLフレームワーク(SSAX参照)の適合です。

SSAXは、SXMLフレームワークのパーザ部分です。以下は、 SSAXのウェブページからの引用です。

SSAXは関数的なXMLパージングフレームワークで、DOM/SXMLパーザ、SAXパーザ、 字句解析・構文解析手続きのサポートライブラリから構成されます。 パッケージ内の手続きは、XML文書の様々な部分をトークナイズ、あるいは パーズするために独立して使うことができます。 このフレームワークは、XML名前空間、文字、内部および外部解析済み実体、 属性値の正規化、処理命令とCDATAセクションをサポートしています。 パッケージは、ある程度の妥当性検査を行うSXMLパーザ: SAXパーザの インスタンスであるDOMモードのパーザ(SSAXと呼ばれます)を含んでいます。

現在のバージョンは、SXMLツールセットの最新の’公式な’リリース(4.9)よりも 新しい、SSAXのCVSバージョンをベースにしており、パッケージSXML-gauche-0.9は、 SXML-4.9をベースにしています。 SXMLのリリース4.9以降では、重要な変更があります。 現在のAPIでは、大文字の接頭辞SSAX:の代わりに小文字のssax:を 使います。Gaucheはデフォルトで文字の大小を区別するために、この違いは 問題となります。 後方互換性のためにエイリアスされた名前が定義されていますが、 大文字の接頭辞付きの名前の使用は推奨されません。

マニュアルのこのパートの内容はSSAXのソースコードから抽出されたもので、 単にそのコメントをTexinfoのフォーマットに変換しただけです。 オリジナルのテキストは、Oleg Kiselyovによるものです。 変換により生じた誤字・誤植やフォーマットエラーの責任は、 Shiro Kawaiにあります。

このマニュアルのエントリは、低レベルの構造から高レベルのユーティリティへと “ボトムアップ”の方法で並べられています。 もし、あなたが単にXMLドキュメントをパーズしたりSXMLを得たいだけならば、 SSAXの高レベルのパーザ - XMLからSXMLへssax:xml->sxmlを チェックして下さい。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.44.1 SSAXデータタイプ

TAG-KIND

シンボル’START、’END、’PI、’DECL、’COMMENT、 ’CDSECTは、マークアップトークンを識別するものです。

UNRES-NAME

XML文書で、マークアップトークン: 開始タグ、PIターゲット、属性名に 与えられる名前(XML勧告ではGIと呼ばれます)です。 GINCNameである場合、UNRES-NAMEはこのNCNameが Schemeのシンボルに変換されたものになります。 GIQNameならば、UNRES-NAMEは、シンボルのペア、 (PREFIX . LOCALPART)となります。

RES-NAME

展開された名前、つまりUNRES-NAMEの解決されたバージョンです。 名前空間URIが空でない場合の要素や属性名では、RES-NAMEはシンボルのペア、 (URI-SYMB . LOCALPART)です。そうでない場合は、1つのシンボルです。

ELEM-CONTENT-MODEL

以下のシンボルのうちの1つです。

ANY何でもよく、ENDタグがあるもの。
EMPTY-TAG内容がなく、ENDタグのないもの。
EMPTY内容がなく、次のトークンがENDタグであるもの。
PCDATA文字データのみで、子要素がないもの。
MIXED
ELEM-CONTENT
URI-SYMB

名前空間を表すシンボル、あるいはURIを表すためにユーザが選んだ他のシンボルです。 前者の場合、URI-SYMBは不正なURI文字が%でクォートされた 文字列をシンボルに変換したものです。

NAMESPACES

効力を持つ名前空間を表すリストです。リストの要素は、以下のフォームのうちの1つです。

(prefix uri-symb . uri-symb)

あるいは、

(prefix user-prefix . uri-symb)

user-prefixは、そのURIを表現するためにユーザにより選ばれたシンボル。

(#f user-prefix . uri-symb)

ユーザが選んだプリフィックスとuri-symbolの指定。

(*DEFAULT* user-prefix . uri-symb)

デフォルト名前空間の宣言。

(*DEFAULT* #f . #f)

デフォルト名前空間を宣言しない。この記法は、それ以前の宣言を上書き することを表す。

NAMESPACESのリストは、同じPREFIXについていくつかの要素を含むかも しれません。リストの先頭に近いものが効力を持ちます。

ATTLIST

ペア(NAME . VALUE)の順序付きのコレクションで、NAMERES-NAMEUNRES-NAMEです。このコレクションはADTです。

STR-HANDLER

3引数の手続き (string1 string2 seed)で、 新しいseedを返します。 この手続きは、文字データstring2が後に続く、文字データstring1を 扱うものです。string2は、“\n”や“”のような短い文字列です。

ENTITIES

ペア (named-entity-name . named-entity-body)の連想リストで、 named-entity-nameはその実体が宣言されたシンボル、 named-entity-bodyは文字列か、(外部実体の場合は) (そこから実体が読み込める)入力ポートを返す手続きです。 named-entity-bodyはまた、#fかも知れません。 これは、named-entity-nameがその時点で展開されていることを 示します。 このnamed-entity-nameへの参照は、WFC非再帰違反としてエラーに なります。

XML-TOKEN

kindtokenという2つのスロットを持つレコードです。 このレコードは、XML勧告によれば、「開始タグ、終了タグ、空要素タグ、 実体参照、文字参照、コメント、CDATAセクションの区切り、 文書型宣言、処理命令の形を取る」マークアップを表します。

kind

TAG-KIND

head

UNRES-NAME。’COMMENTと’CDSECTというkindのXMLトークンでは、 そのheadは#fになります。

例を示します。

 
<P>  => kind='START, head='P
</P> => kind='END, head='P
<BR/> => kind='EMPTY-EL, head='BR
<!DOCTYPE OMF ...> => kind='DECL, head='DOCTYPE
<?xml version="1.0"?> => kind='PI, head='xml
&my-ent; => kind = 'ENTITY-REF, head='my-ent

文字参照は、対応する文字へと透過的に解決されるので、XMLトークンとしては 表現されません。

XML-DECL

elemsentitiesnotationsという3つのスロットを持つレコードです。

このレコードは、XML文書のデータタイプを表現します。それは、 宣言された要素とその属性のリスト、宣言された記法、 解析済み一般実体の置換文字列やロードされる手続きのリストなどです。 通常、xml-declレコードは、それを作るには他にたくさんの方法 (例えばファイルからロードするなど)があるにも関わらず、DTDかXML Schemaから 作られます。

elems: decl-elemか#fの(連想)リスト。後者は、パーザに、 要素と属性の妥当性検査を行わないように指示します。

decl-elem: 1つの要素の宣言: (elem-name elem-content decl-attrs); elem-nameはその要素のUNRES-NAMEelem-contentELEM-CONTENT-MODELdecl-attrsATTLISTか、(attr-name . value)の 連想リスト。 この要素は、要素のパージングを扱うユーザ手続きを宣言できます。 (例えば、カスタムな妥当性検査を行ったり、タグに出会うたびに IDのハッシュを構築するなど。)

decl-attr: ATTLISTの要素で、1つの属性 (attr-name content-type use-type default-value) の宣言: attr-nameはその宣言された属性のUNRES-NAMEcontent-typeはシンボルCDATANMTOKENNMTOKENS、 あるいは列挙されたタイプの文字列のリスト。 use-typeはシンボルREQUIREDIMPLIEDFIXED。 default-valueは、デフォルト値としての文字列か、与えられなければ#f

Function: make-empty-attlist
Function: attlist-add attlist name-value
Function: attlist-null?
Function: attlist-remove-top attlist
Function: attlist->alist attlist
Function: attlist-fold

名前-値の属性リストを扱うユーティリティ手続きです。

Function: make-xml-token kind head
Function: xml-token? token

XML-TOKENレコードのコンストラクタと述語です。

Macro: xml-token-kind token
Macro: xml-token-head token

XML-TOKENレコードのアクセッサマクロです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.44.2 SSAXの低レベルパージングコード

これらは、プリミティブな字句解析ユニット(名前、空白、タグ)や、 より一般的な断片を扱います。 これらのパーザのほとんどは、適切なコンテキストで呼ばれなければなりません。 例えば、ssax:complete-start-tagは、開始タグが検知されそのGIが 読み込まれたときにのみ呼ばれなければなりません。

Function: ssax:skip-S port

次のように定義されるS(空白)をスキップします。

 
 [3] S ::= (#x20 | #x9 | #xD | #xA)

この手続きは、portのスキャン中に遭遇した最初の空白ではない文字を 返します。この文字は、入力ストリームに残されます。

Function: ssax:ncname-starting-char? a-char

NCNameがa-charで始まるかどうかを検査します。

Function: ssax:read-NCName port

portで現在の位置から始まるNCNameを読み込み、それをシンボルとして 返します。

Function: ssax:read-QName port

(名前空間)完全修飾名、QNameportの現在の位置から読み込みます。

REC-xml-namesは、

 
 [6] QName ::= (Prefix ':')? LocalPart
 [7] Prefix ::= NCName
 [8] LocalPart ::= NCName

戻り値は、UNRES-NAMEです。

Variable: ssax:Prefix-XML

定義済みのXML名前空間の接頭辞、つまり、’xmlです。

Function: ssax:read-markup-token port

この手続きは、マークアップトークンのパージングを開始します。 ストリームの現在の位置は、#\<でなければなりません。 この手続きは、見ているマークアップトークンがどの種類のものか見当を つけるに十分な程度、入力ストリームをスキャンします。 この手続きは、そのトークンを表現するxml-token構造を返します。 通常、その時点のマークアップの読み込みは完了していないことに注意して下さい。 特に、開始タグトークンの属性はスキャンされていません。

特定の値が返されたときの戻り値とportでの位置を詳細に説明します。

PI-token

PIターゲットのみが読み込まれました。 処理命令の読み込みを完了してそれを無視するためには、ssax:skip-piを呼びます。 (PIの内容が、属性-値のペアの場合は、)ssax:read-attributesも 便利です。

END-token

終了タグが完全に読み込まれました。 現在の位置は、終了の#\>文字の直後です。

COMMENT

コメントが完全に読み込まれスキップされました。 現在の位置は、コメントが終了する“-->”の直後です。

CDSECT

現在の位置は、"<!CDATA["の直後です。 残りを読むためには、ssax:read-cdata-bodyを使います。

DECL

この宣言マークアップを識別するキーワード(“<!”に続くもの)を 読み込んだところです。現在の位置は、(通常は空白文字である) そのキーワードの直後です。

START-token

この開始タグのキーワード(GI)を読み込んだところです。 属性はまだスキャンされていません。 また、このタグが空の要素を持つかどうかも分かりません。 このトークンのパージングを終了するためには、 ssax:complete-start-tagを使います。

Function: ssax:skip-pi port

現在の位置は、PIの内側です。 PIの残りをスキップします。

Function: ssax:read-pi-body-as-string port

現在の位置は、PITargetを読み込んだ直後です。 PIのボディを読み込んで、それを文字列として返します。 ポートでは、PIを終了する’?>’の直後の文字を指します。

 
 [16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
Function: ssax:skip-internal-dtd port

ポートでの現在の位置は、内部DTDサブセットの内側です (例えば、内部DTDサブセットの始まりである#\[ を読み込んだところ)。 このDTDを終了する、組み合わせとなる“]>”までをスキップします。

Function: ssax:read-cdata-body port str-handler seed

この手続きは、CDATAセクションを開始する文字列、"<![CDATA["を 読み込んだ後に呼ばれなければなりません。 現在の位置は、CDATAのボディの最初の位置です。 この手続きは、CDATAのボディのデータを読み込み、それらを STR-HANDLER(文字データのコンシューマ)へ渡します。

str-handlerは、string1 string2 seedを取る手続き STR-HANDLERです。 STR-HANDLERの最初の引数string1は、改行を含みません。 2番目の引数string2は、改行を含むことがよくあります。 STR-HANDLERの最初の呼び出しでは、seedはssax:read-cdata-bodyの 第3引数として渡されるものです。 この最初の呼び出しの結果は、文字データのコンシューマの引数seedとして渡され、 以降同じように続きます。 STR-HANDLERの最後の呼び出しの結果は、ssax:read-cdata-body から返されるものです。 基本的な’fold’イテレータに似ています。

CDATAセクションでは、以下の3つだけの例外を除いて、全ての文字は その表面上の値を持ちます。

Function: ssax:read-char-ref port
 
 [66]  CharRef ::=  '&#' [0-9]+ ';'
                  | '&#x' [0-9a-fA-F]+ ';'

この手続きは、文字参照を表す“&#”を読み込んだ後に呼ばれなければ なりません。 この手続きは、この参照を読み込んで対応する文字を返します。 portでの現在の位置は、文字参照の終わりとなる“;”の後と なります。 WFC: XML-Spec.html#wf-Legalcharも参照のこと。

XML勧告のセクション“4.1 文字と実体参照”によると、

“[定義: 文字参照は、ISO/IEC 10646文字セットにある特定の文字を参照する。 例えば、利用できる入力デバイスからは直接アクセスできないものなど。]”

したがって、入力ストリームの現在の文字エンコーディングに関係なく、 文字コードを文字に変換するために関数ucscode->charを使います。

Function: ssax:handle-parsed-entity port name entities content-handler str-handler seed

解析済み実体参照を展開し処理します。

戻り値は、content-handlerstr-handlerから返された値です。

こちらも参照のこと。

 
  WFC: XML-Spec.html#wf-entdeclared
  WFC: XML-Spec.html#norecursion
Function: ssax:read-attributes port entities

この手続きは、Attribute*を読み込みパーズします。

 
 [41] Attribute ::= Name Eq AttValue
 [10] AttValue ::=  '"' ([^<&"] | Reference)* '"'
                 | "'" ([^<&'] | Reference)* "'"
 [25] Eq ::= S? '=' S?

この手続きは、Name(UNRES-NAME)とValue(文字列)のペアである ATTLISTを返します。 portでの現在の文字は、NCNameの開始文字ではなく、空白ではない文字です。

’AttValue’を読み込むときには、以下のルールに留意して下さい。 “属性の値がアプリケーションに渡されるか妥当性が検査される前に、 XMLプロセッサはそれを以下のように正規化しなければならない:

こちらも参照のこと。

 
 WFC: XML-Spec.html#CleanAttrVals
 WFC: XML-Spec.html#uniqattspec
Function: ssax:resolve-name port unres-name namespaces apply-default-ns?

与えられた適切なnamespacesの宣言を用いて、unres-nameres-nameに変換する。 最後の引数apply-default-ns?は、デフォルト名前空間の適用を行うか どうかを決めます(例えば、属性名には適用しないなど)。

REC-xml-names/#nsc-NSDeclaredによれば、接頭辞“xml”は 名前空間名“http://www.w3.org/XML/1998/namespace”に定義済みで束縛されていると されます。

この手続きは、名前空間の制約をテストします: http://www.w3.org/TR/REC-xml-names/#nsc-NSDeclared

Function: ssax:uri-string->symbol uri-str

uri-strを適切なシンボルに変換します。

Function: ssax:complete-start-tag tag port elems entities namespaces

この手続きは、開始タグのマークアップのパージングを完了するためのものです。 この手続きは、開始タグトークンが読み込まれた後に呼ばれなければなりません。 tagUNRES-NAMEです。 elemsxml-decl::elemsのインスタンスで、 手続きに、要素とそれらの属性の妥当性検査を行わないように 指示するために、#fを指定することができます。

この手続きはいくつかの値を返します。

elem-gi

RES-NAME

attributes

要素の属性。(res-name . string)というペアのATTLIST。 このリストは、xmlns属性を含みません

namespaces

パージング中の開始タグに含まれる名前空間(再)宣言により修正された後の 名前空間の入力リスト。

終了時のportでの現在の位置は、開始タグのマークアップを終了する #\>の後になります。

こちらも参照のこと。

 
 VC: XML-Spec.html#enum
 VC: XML-Spec.html#RequiredAttr
 VC: XML-Spec.html#FixedAttr
 VC: XML-Spec.html#ValueType
 WFC: XML-Spec.html#uniqattspec (after namespaces prefixes are resolved)
 VC: XML-Spec.html#elementvalid
 WFC: REC-xml-names/#dt-NSName

XML勧告では明示されていませんが、xmlnsxmlns:属性は、 (そのデフォルト値を指定するために宣言されることが出来ますが) 宣言される必要がないことに注意して下さい。

Function: ssax:read-external-id port

この手続きは、ExternalIDをパーズします。

 
 [75] ExternalID ::= 'SYSTEM' S SystemLiteral
                 | 'PUBLIC' S PubidLiteral S SystemLiteral
 [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
 [12] PubidLiteral ::=  '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
 [13] PubidChar ::=  #x20 | #xD | #xA | [a-zA-Z0-9]
                | [-'()+,./:=?;!*#@$_%]

この手続きは、ExternalIDが期待されるところで呼ばれます。 つまり、現在の文字は、それぞれSYSTEMPUBLICトークンを開始する #\S#\Pでなければなりません。 この手続きは、SystemLiteralを文字列として返します。 PubidLiteralは、存在したとしても無視されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.44.3 SSAXの高レベルのパーザとスキャナ

これらは、全体的な実体(ドキュメント)あるいはその高レベルな断片 (プロローグ、ルート要素など)をパーズします。

Function: ssax:scan-Misc port

そのコンテキストでのMiscをスキャンします。

 
[1]  document ::=  prolog element Misc*
[22] prolog ::= XMLDecl? Misc* (doctypedec l Misc*)?
[27] Misc ::= Comment | PI |  S

以下の関数は、プロローグかエピローグのコンテキストで呼ばれます。 これらのコンテキストでは、空白文字は完全に無視されます。 ssax:scan-Miscからの戻り値は、PIトークンかDECLトークン、 STARTトークン、EOFのいずれかです。 コメントは無視され報告されません。

Function: ssax:read-char-data port expect-eof? str-handler seed

この手続きは、XML文書かXML要素の文字内容を読むためのものです。

 
 [43] content ::=
        (element | CharData | Reference | CDSect | PI
         | Comment)*

具体的には、この手続きはCharDataを読み込み、CDSectと 文字実体を展開し、コメントをスキップします。 この手続きは、名前付き参照、EOF、PIあるいは開始/終了タグの開始地点で 停止します。

port

読み込むポート。

expect-eof?

EOFがノーマルかどうか、つまり、文字データがEOFで終わるかどうかを 表す真偽値。解析済み実体を処理している間はEOFはノーマル。

str-handler

STR-HANDLER

seed

STR-HANDLERの最初の呼び出し時に渡される引数。

この手続きは2つの結果、seedtokenを返します。

seedstr-handlerの最後の呼び出しの結果、あるいは str-handlerが一度も呼ばれなかった場合はオリジナルのseedです。

tokenはEOFオブジェクト(これはexpect-eof?#tの場合のみ)か、

CDATAセクションと文字参照はインラインで展開され返されません。 コメントは無視されます。

XML勧告が要求するように、文字データ中の全ての空白文字は保存されなければなりません。 しかし、CR文字(#xD)は、LF文字(#A)の前に現れるか #xA文字で置き換えられた場合は、無視されなければなりません。 XML勧告のセクション2.10と2.11を参照して下さい。 また、正規のXML勧告も参照して下さい。

Function: ssax:assert-token token kind gi error-cont

tokenが、予想されたkindのもので、予想されたgiを 持つことを確認します。gi引数は、実際には2つのシンボル、 名前空間URIかその接頭辞と、そのローカル名のペアでしょう。 アサーションが失敗したら、error-contに3つの引数、token kind gi を渡されて評価されます。 error-contの結果が返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.44.4 SSAXの高レベルのパーザ - XMLからSXMLへ

これらのパーザは、SSAXパーザをインスタンス化するための構文的フォームのセットです。 ユーザは、完全な妥当性検査、妥当性検査なし、特定の妥当性検査を行うために このパーザをインスタンス化できます。 ユーザは、どのPIについて通知されたいかを指定します。 ユーザは、解析済み文字と要素のデータで何をしたいかを知らせます。 後者のハンドラは、パージングがSAXやDOMモデルに従うかを決定します。

Macro: ssax:make-pi-parser my-pi-handlers

1つの処理命令(PI)をパーズして処理するパーザを作ります。

my-pi-handlers: (PI-TAG . PI-HANDLER)のペアの連想リスト。 PI-TAGNCNameのシンボル、PIターゲット。 PI-HANDLERport pi-tag seedを引数とする手続きで、 portではPIターゲットの後の最初のシンボルを指しています。 ハンドラは、PIを終了する組み合わせとなる’?>’を含む、 PIの残りを読み込みます。ハンドラは新しいseedを返します。 PI-TAGの1つは、シンボル*DEFAULT*でしょう。 これに対応するハンドラは、他のハンドラが扱わないPIを処理します。 *DEFAULT* PI-TAGが指定されていない場合は、 ssax:make-pi-parserは、PIのボディをスキップするパーザを 作ります。

ssax:make-pi-parserが返すのは、port pi-tag seedを 取る手続きで、ユーザ指定のハンドラに従い現在のPIをパーズします。

Macro: ssax:make-elem-parser my-new-level-seed my-finish-element my-char-data-handler my-pi-handlers

その文字内容や子要素をも含む1つの要素をパーズし処理するパーザを作ります。 このパーザは通常、ドキュメントのルート要素の適用されます。

my-new-level-seed

elem-gi attributes namespaces expected-content seed
を引数に取る手続きで、elem-giは処理されようとしている要素のRES-NAMEです。 この手続きは、要素の内容を処理するハンドラに渡されるseedを生成します。

my-finish-element

elem-gi attributes namespaces parent-seed seed
を引数に取る手続きです。この手続きは、elem-giのパージングが完了した時に 呼ばれます。 seedは、最後に呼ばれたパーザからの(あるいは、 要素が空要素であった場合は、my-new-level-seedからの)結果です。 parent-seedは、my-new-level-seedへ渡されたのと同じseedです。 この手続きは、パーザの結果となるseedを生成するためのものです。

my-char-data-handler

STR-HANDLER

my-pi-handlers

ssax:make-pi-handlerを参照して下さい。

生成されたパーザは: start-tag-head port elems entities namespaces preserve-ws? seed
を引数に取る手続きです。 この手続きは、開始タグのトークンが読み込まれた後に呼ばれなければなりません。 start-tag-headは要素の開始タグのUNRES-NAMEです。 elemsxml-decl::elemsのインスタンスです。 ssax:complete-start-tag::preserve-ws?も参照して下さい。

こちらも参照のこと。

 
 VC: XML-Spec.html#elementvalid
 WFC: XML-Spec.html#GIMatch
Macro: ssax:make-parser user-handler-tag user-handler-proc ...

XMLパージングフレームワークのインスタンスである、XMLパーザを作ります。 これは、提供されるユーザハンドラによって、SAX、DOM、あるいは特化された パーザになります。

user-handler-tagはシンボルで、タグに続く手続き的な式を識別します。 以下にタグと対応する手続きのシグネチャを示します。 全てのタグが指定される必要はありません。 いくつかが省略されると、合理的なデフォルトのものが適用されます。

tag: DOCTYPE

ハンドラ手続きの引数: port docname systemid internal-subset? seed

internal-subset?#tなら、ポートでの現在の位置は内部DTDサブセットの 開始となる#\[を読んだ直後です。 手続きから戻る前に、このサブセットの残りの読み込みを完了しなければなりません (あるいは、それを読むことに興味がなければ、skip-internal-subsetを呼ばなければなりません)。 終了時のポートでの位置は、DOCTYPE宣言全体のあとの最初のシンボルでなければなりません。

ハンドラ手続きは4つの値:
elems entities namespaces seed
を生成しなければなりません。 elemsについては、xml-decl::elemsを参照して下さい。 妥当性検査をオフにするためには、#fになるでしょう。 namespacesは、通常、選択されたURI-SYMBに対してUSER-PREFIXを含む でしょう。 デフォルトのハンドラ手続きは、内部サブセットがあってもそれをスキップし、 (values #f '() '() seed)を返します。

tag: UNDECL-ROOT

ハンドラ手続きの引数: elem-gi seed
elem-giはルート要素のUNRES-NAMEです。 この手続きは、パージング中のXML文書がDOCTYPE宣言を含まない時に 呼ばれます。 ハンドラ手続きは、上ではDOCTYPEハンドラですが、4つの値:
elems entities namespaces seed
を生成しなければなりません。 デフォルトのハンドラ手続きは、(values #f '() '() seed)を返します。

tag: DECL-ROOT

ハンドラ手続きの引数: elem-gi seed
elem-giは、ルート要素のUNRES-NAMEです。 この手続きは、パージング中のXML文書がDOCTYPE宣言を含む場合に呼ばれます。 このハンドラ手続きは、新しいseedを生成しなければなりません (そして、ハンドラが望めば、ルート要素の名前がDOCTYPEにマッチするかを 検証します)。 デフォルトのハンドラ手続きは、それ自身を返す手続きです。

tag: NEW-LEVEL-SEED

ハンドラ手続きの引数: ssax:make-elem-parsermy-new-level-seedを参照して下さい。

tag: FINISH-ELEMENT

ハンドラ手続きの引数: ssax:make-elem-parsermy-finish-elementを参照して下さい。

tag: CHAR-DATA-HANDLER

ハンドラ手続きの引数: ssax:make-elem-parsermy-char-data-handlerを参照して下さい。

tag: PI

ハンドラ手続きの引数: ssax:make-pi-parserを参照して下さい。
デフォルトの値は、'()です。

生成されるパーザは、
PORT SEEDを取る手続き、
です。

この手続きは、ドキュメントのプロローグをパーズして、 その残りを処理するために(ssax:make-elem-parserで作られた)パーザへ 引き継いで終了します。

 
 [1]  document ::=  prolog element Misc*
 [22] prolog ::= XMLDecl? Misc* (doctypedec | Misc*)?
 [27] Misc ::= Comment | PI |  S

 [28] doctypedecl ::=  '<!DOCTYPE' S Name (S ExternalID)? S?
                        ('[' (markupdecl | PEReference | S)* ']' S?)? '>'
 [29] markupdecl ::= elementdecl | AttlistDecl
                      | EntityDecl
                      | NotationDecl | PI
                      | Comment

いくつかの便利なユーティリティ手続きがあります。

Function: ssax:reverse-collect-str fragments

fragments(そのいくつかはテキスト文字列)のリストを渡すと、 そのリストを逆順にして隣り合ったテキスト文字列を連結します。

Function: ssax:reverse-collect-str-drop-ws fragments

fragments(そのいくつかはテキスト文字列)のリストを渡すと、 そのリストを逆順にして隣り合ったテキスト文字列を連結します。 “重要でない”空白文字、つまり、最初や最後、要素の間にある空白文字を 削除します。文字データに含まれる空白文字には影響を与えません。 この手続きは、パーズされたSXMLにある“重要でない”空白文字を “知的に”削除するために使います。空白文字に関して、厳密に XML勧告に準拠したい場合は、代わりに手続き ssax:reverse-collect-strを使って下さい。

Function: ssax:xml->sxml port namespace-prefix-assig

これは、上のSSAXパーザのインスタンスで、portから読み込まれる XMLドキュメントのSXML表現を返します。 namespace-prefix-assigは、(USER-PREFIX . URI-STRING) のリストで、特定のURI-STRINGで識別されるある名前空間を USER-PREFIXに割り当てます。これは空リストでも構いません。 この手続きは、SXMLツリーを返します。 ポートでの位置は、ルート要素の後の最初の文字を指します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.45 sxml.sxpath - SXMLクエリ言語

Module: sxml.sxpath

SXPathは、XML Information set (Infoset)のインスタンスのS式フォームである SXMLのためのクエリ言語です。

これは最初にOleg Kiselyovによって書かれ、Dmitry LizorkinとKirill Lisovsky によって改良されました。 このモジュールにはまた、Dmitry LizorkinとKirill LisovskyによりSXPathのために 書かれたたくさんの手続きが盛り込まれています。

現在のバージョンは、sxpathlib.scm v3.915、sxpath.scm v1.1、sxpath-ext.scm v1.911を ベースにしています。

このマニュアルは、そのほとんどがオリジナルのソースファイルのコメントより 導出されています。

このモジュールは3つのレイヤから構成されます。

  1. SXMLツリーへのアクセスやその変換の手段を提供する基本的なコンバータや アプリケータ(適用子)。
  2. 省略形のSXPathを取り、与えられたノードセットから指定されたパスを満足する ノードセットを選択するScheme関数を返す、高レベルなクエリ言語コンパイラ。
  3. W3CのXPathコア関数ライブラリのSXML版を実装する拡張ライブラリ。

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.45.1 SXPathの基本的なコンバータとアプリケータ

コンバータは、以下を満たす関数です。

 
  type Converter = Node|Nodeset -> Nodeset

コンバータは、述語としての役割を担うこともあります。 その場合、コンバータが、ノードやノードセットに適用され、空ではないノードセットを 返す場合、述語としてのコンバータは満足したものとみなされます。 このファイルを通して、nilノードセットは失敗を表す#fと等価です。

Function: nodeset? x

与えられたオブジェクトがノードセットならば、#tを返します。

Function: as-nodeset x

xがノードセットならば、それをそのまま返し、そうでなければそれを リストでラップして返します。

Function: sxml:element? obj

objがSXMLの要素であれば#tを返し、そうでなければ#fを 返す述語です。

Function: ntype-names?? crit

関数ntype-names??は、判定基準として受け付け可能なノード名のリストを取り、 関数を返します。この関数は、ノードに適用された際、そのノード名が判定基準リストに 含まれていれば#tを、含まれていなければ#fを返す関数です。

 
 ntype-names?? :: ListOfNames -> Node -> Boolean
Function: ntype?? crit

関数ntype??は、型に関する判定基準を取り、関数を返します。 この関数は、ノードに適用された際、そのノードがそのテストを満足するかを 返します。

 
  ntype?? :: Crit -> Node -> Boolean

判定基準critは、以下のシンボルのうちの1つです。

id

そのノードが正しい名前(id)を持っているかをテストします。

@

そのノードがattributes-listであるかをテストします。

*

そのノードがElementであるかをテストします。

*text*

そのノードがテキストノードであるかをテストします。

*data*

そのノードがデータノード(テキスト、数値、真偽値などで、ペアではない)であるか をテストします。

*PI*

そのノードがPIノードであるかをテストします。

*COMMENT*

そのノードがCOMMENTノードであるかをテストします。

*ENTITY*

そのノードがENTITYノードであるかをテストします。

*any*

どんなタイプのノードに対しても#tを返します。

Function: ntype-namespace-id?? ns-id

この関数は、名前空間IDを取り、述語Node -> Booleanを 返します。この述語はまさにその名前空間IDを持つノードに対しては #tを返します。ns-idは文字列です。 (ntype-namespace-id?? #f)は、完全修飾されていない名前を 持つノードに対して#tを返します。

Function: sxml:invert pred

この関数は、述語を取り、それを反対にして返します。 与えられた述語が#fや’()を返す場合、反対にされたものは 与えられたノード(#t)を返します。

Function: node-eq? other
Function: node-equal? other

等価な述語としてのコンパータにカリー化します。すなわち、

 
  ((node-eq? a) b)    ≡ (eq? a b)
  ((node-equal? a) b) ≡ (equal? a b)
Function: node-pos n
 
 node-pos:: N -> Nodeset -> Nodeset, or
 node-pos:: N -> Converter

ノードセットのN番目の要素を選択し、1つの要素を持つノードセットを返します。 N番目の要素が存在しなければ、空のノードセットを返します。 ((node-pos 1) Nodeset)は、ノードセットの先頭ノードがあればそれを選択します。 ((node-pos 2) Nodeset)は、2番目のノードがあればそれを選択します。 Nは負の数でも構いません。その場合、ノードはリストの末尾から数えられます。 ((node-pos -1) Nodeset)は、空ではないノードセットの最後のノードを選択します。 ((node-pos -2) Nodeset)は、最後から2番目のノードがあればそれを選択します。

Function: sxml:filter pred?
 
 filter:: Converter -> Converter

フィルタリングを行う、フィルタアプリケータです。 引数のコンバータは、#fあるいはnilとなることが失敗を意味する述語と みなされます。

Function: take-until pred?
 
 take-until:: Converter -> Converter, or
 take-until:: Pred -> Node|Nodeset -> Nodeset

述語としてのコンバータとノードセットが与えられると、 ノードセットの各要素に述語を適用し、 述語が#fあるいはnil以外を返すと、 (その述語が失敗した)その時点までに処理された要素を返します。 take-untilは、上のフィルタのバリエーションの1つです。 take-untilは、その述語を満足する最初の要素(それ自体は含まない)まで、 順序付けられた入力のセットの要素をパスします。 ((take-until (not pred)) nset)により返されるノードセットは、 ((filter pred) nset)により返されるノードセットのサブセット – 具体的には接頭辞 –になります。

Function: take-after pred?
 
take-after:: Converter -> Converter, or
take-after:: Pred -> Node|Nodeset -> Nodeset

述語としてのコンバータとノードセットを与えると、 述語をノードセットの各要素に適用し、 述語が#fかnil以外を返すと、 まだ述語が適用されていない要素を返します。 つまり、述語を満足する最初の要素の後に続く要素を返します。 take-aftertake-untilを一緒に使うと、 入力のノードセットを3つのパート: 述語を満足する最初の要素、その要素の前の部分、その要素の後の部分に 分けます。

Function: map-union proc lst

procをlstの各要素に適用し、結果のリストを返します。 procがノードセットを返す場合、それを結果につなぎ合わせます。

別の観点から見ると、map-unionはConverter->Converter関数で、 結合を行いたいコンテキストでの引数としてのコンバータに 位置します。

Function: node-reverse node-or-nodeset
 
node-reverse :: Converter, or
node-reverse:: Node|Nodeset -> Nodeset

ノードセットでのノードの順番を逆順にします。 この基本的なコンバータは、逆順のドキュメントオーダーを実装するために 必要です。(XPath勧告を参照して下さい。)

Function: node-trace title
 
 node-trace:: String -> Converter

(node-trace title)は、それ自身を返すコンバータです。 また、自身が適用されるノードやノードセットを、’title’という プリフィックスを付けてプリントします。 このコンバータは、デバッグの際にとても便利です。

コンバータの組み合わせに続くものは、コンバータを一変させる、 あるいはコンバータのシーケンスを1つの強力なコンバータにつなぎ合わせる 高階関数です。そのゴールは、XPathのロケーションパスに対応する コンバータとなることです。

別の観点から見ると、コンバータは、コンバータ群の適用の固定され 名前の付いたパターンとみなせます。 以下に挙げるのは、XPathのロケーションパスの仕様を実装する そのようなパターンの完全なセットです。 結局のところ、これら全てのコンビネータはいくつかの基本的なブロック、 通常の関数的なコンポジション、map-unionとfilterアプリケータ、 ノードセットユニオンなどから構築することができます

Function: select-kids test-pred?
 
select-kids:: Pred -> Node -> Nodeset

ノードを与えると、述語(実際はコンバータ)を満足するその子要素の (順序付けられた)サブセットを返します。

 
select-kids:: Pred -> Nodeset -> Nodeset

上と同じですが、ノードセットの全てのノードの子要素から選択します。

Function: node-self pred
 
 node-self:: Pred -> Node -> Nodeset, or
 node-self:: Converter -> Converter

select-kidsに似ていますが、自身をその子要素に適用するのでは なく、ノードそれ自身に適用します。 結果のノードセットは、1つのコンポーネントを含むか、 空(ノードが述語を満足しない場合)になります。

Function: node-join . selectors
 
 node-join:: [LocPath] -> Node|Nodeset -> Nodeset, or
 node-join:: [Converter] -> Converter

上のタイトルコメントで説明されるようなロケーションステップ あるいはロケーションパスのシーケンスをつなぎ合わせます。

Function: node-reduce . converters
 
 node-reduce:: [LocPath] -> Node|Nodeset -> Nodeset, or
 node-reduce:: [Converter] -> Converter

コンバータの通常の関数的なコンポジションです。 見方を変えると、((apply node-reduce converters) nodeset)(foldl apply nodeset converters)と等価です。 すなわち、コンバータのリストをノードセットをseedとして畳み込みや分解 を行うようなものです。

Function: node-or . converters
 
 node-or:: [Converter] -> Converter

このコンビネータは、全てのコンバータを与えられたノードに適用し、 それらの結果のユニオンを作ります。 このコンビネータは、XPathのロケーションパスでの’|’オペレーション であるユニオンに対応します。

Function: node-closure test-pred?
 
 node-closure:: Converter -> Converter

述語としてのコンバータを満足するノードの全ての子孫を選択します。 このコンビネータはselect-kidsに似ていますが、孫要素やその 子要素達にも適用を行います。 このコンビネータは、XPathの軸である“descendant::”を実装します。 概念的には、このコンビネータは以下のように表現することができます。

 
 (define (node-closure f)
      (node-or
        (select-kids f)
	 (node-reduce (select-kids (ntype?? '*)) (node-closure f))))

この定義は、字面の通り、フィックスポイントのような何かで、 永久に実行し続けます。しかし、いつかは(select-kids (ntype?? '*)) が空のノードセットを返すことは明白です。その時点では、以降の イテレーションはその結果に影響を及ぼさず停止されることができます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.45.2 SXPathクエリ言語

Function: sxpath abbrpath . ns-binding

Evaluates an abbreviated SXPath

 
 sxpath:: AbbrPath -> Converter, or
 sxpath:: AbbrPath -> Node|Nodeset -> Nodeset

AbbrPathはリストです。これは、以下の書き換えルールに従って 完全なSXPathに変換されます。

 
 (sxpath '()) -> (node-join)
 (sxpath '(path-component ...)) ->
                (node-join (sxpath1 path-component) (sxpath '(...)))
 (sxpath1 '//) -> (node-or
                     (node-self (ntype?? '*any*))
                     (node-closure (ntype?? '*any*)))
 (sxpath1 '(equal? x)) -> (select-kids (node-equal? x))
 (sxpath1 '(eq? x))    -> (select-kids (node-eq? x))
 (sxpath1 '(or@ ...))  -> (select-kids (ntype-names??
                                          (cdr '(or@ ...))))
 (sxpath1 '(not@ ...)) -> (select-kids (sxml:invert
                                         (ntype-names??
                                          (cdr '(not@ ...)))))
 (sxpath1 '(ns-id:* x)) -> (select-kids
                                      (ntype-namespace-id?? x))
 (sxpath1 ?symbol)     -> (select-kids (ntype?? ?symbol))
 (sxpath1 ?string)     -> (txpath ?string)
 (sxpath1 procedure)   -> procedure
 (sxpath1 '(?symbol ...)) -> (sxpath1 '((?symbol) ...))
 (sxpath1 '(path reducer ...)) ->
                (node-reduce (sxpath path) (sxpathr reducer) ...)
 (sxpathr number)      -> (node-pos number)
 (sxpathr path-filter) -> (filter (sxpath path-filter))

sxpathには、いくつかのラッパ関数があります。

Function: if-sxpath path

sxpathは、常にリストを返し、それはSchemeでは#tとなります。 if-sxpathは、空リストの代わりに#fを返します。

Function: if-car-sxpath path

もし存在すれば、最初に見つかったノードを返します。 そうでなければ、#fを返します。

Function: car-sxpath path

もし存在すれば、最初に見つかったノードを返します。 そうでなければ、空リストを返します。

Function: sxml:id-alist node . lpaths

与えられたノードについて、(ID_value . element)の ペアのリストをインデックスとして構築します。 lpathsは、タイプIDの属性のロケーションパスです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.45.3 SXPathの拡張

W3CのXPathコア関数ライブラリのSXML版です。

Function: sxml:string object

XPathのstring関数(XPath勧告のセクション4.2)に対応するものです。 与えられたオブジェクトを文字列に変換します。 注意:

  1. ノードセットを変換する時は、ドキュメントオーダーは保持されません。
  2. number->string関数は、その結果をXPath勧告の仕様とは少し違った フォームで返します。
Function: sxml:boolean object

XPathのboolean関数(XPath勧告のセクション4.3)に対応するものです。 引数を真偽値に変換します。

Function: sxml:number obj

XPathのnumber関数(XPath勧告のセクション4.4)に対応するものです。 引数を数値に変換します。 注意:

  1. 引数は(まだ?)オプションではありません。
  2. string->numberの変換は、IEEE 754の四捨五入ではありません。
  3. NaNは、0として表現されます。
Function: sxml:string-value node

XPath勧告のセクション5.1 - 5.7にしたがって、与えられたノードの 文字列値を返します。

Function: sxml:node? node

XPathの仕様2.3にしたがい、このテストはいかなるXPathノードに 対しても真を返します。 SXMLの補助的なリストや属性のリストは除外されます。

Function: sxml:attr-list obj

与えられたSXMLノードの属性のリストを返します。 与えられたノードが要素ではないか、属性のリストを持っていない場合は、 空リストが返されます。

Function: sxml:id id-index

SXML要素を、そのユニークなIDによって選択します(XPath勧告 4.1)。 objectを引数に取るコンバータを返します。 このobjectは、ノードセットか、’string’関数により 文字列に変換できるデータタイプです。

id-indexは、( (id-value . element) (id-value . element) ... )です。

このインデックスは、要素をそのユニークなIDによって選択するために使われます。

XPathオブジェクトの比較子:

Function: sxml:equality-cmp bool-op number-op string-op

XPathの等値比較: =!=のためのヘルパです。 bool-opnumber-op’string-opはそれぞれ、 真偽値、数値、文字列のペアのための比較子です。

Function: sxml:equal? a b
Function: sxml:not-equal? a b

XPathの等値比較: =!=に対応するもので、 デフォルトの等値テストを使います。

Function: sxml:relational-cmp op

2つのXPathオブジェクトの関係比較( <><=>= ) を作ります。 opは、比較を行う手続き: <><=>=です。

XPathの軸。 結果のノードセットにおける順序は維持されます。

Function: sxml:attribute test-pred?

属性の軸です。

Function: sxml:child test-pred?

子要素の軸です。 この関数は、’select-kids’に似ていますが、処理命令やコメント、 実体ノードについては、空の子リストを返します。

Function: sxml:parent test-pred?

親の軸です。

述語を与えると、RootNode -> Converter関数を返します。 この関数は、rootnodeに適用されると、node -> parentと なります。

このようなコンバータは、 ((sxml:parent test-pred) rootnode) を使って構築され、それが適用されたノードの親を帰します。 ノードセットに適用された場合、そのノードセットにあるノードの 親のリストを返します。 rootnodeはSXMLツリー全体のルートノードである必要はありません。 興味の対象となるブランチ(枝)のルートノードでも構いません。 parent::軸は、どんなSXMLノードにも使えます。

Function: sxml:ancestor test-pred?

祖先の軸です。

Function: sxml:ancestor-or-self test-pred?

祖先と自分の軸です。

Function: sxml:descendant test-pred?

子孫の軸です。

Function: sxml:descendant-or-self test-pred?

子孫と自分の軸です。

Function: sxml:following test-pred?

後続するものの軸です。

Function: sxml:following-sibling test-pred?

後続する兄弟の軸です。

Function: sxml:namespace test-pred?

名前空間の軸です。

Function: sxml:preceding test-pred?

先行するものの軸です。

Function: sxml:preceding-sibling test-pred?

先行する兄弟の軸です。

ポピュラーなショートカット:

Function: sxml:child-nodes nodeset
 
((sxml:child sxml:node?) nodeset)
Function: sxml:child-elements nodeset
 
((select-kids sxml:element?) nodeset)

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.46 sxml.tools - SXML構造を操作する

Module: sxml.tools

現在のバージョンは、sxml-toolsのCVSのリビジョン3.13から導出されています。

マニュアルのエントリは主に、オリジナルのソースコードから導出されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.46.1 SXMLの述語

Function: sxml:empty-element? obj

与えられた要素objが空なら#tを返す述語です。 空要素は、ネストした要素、テキストノード、PI、コメントや実体を 持ちませんが、属性や名前空間IDは持つかもしれません。 それは、XMLのempty-elementのSXML版です。

Function: sxml:shallow-normalized? obj

与えられたobjが浅く正規化されたSXML要素であれば#tを返します。 要素それ自体は正規化されていなければなりませんが、ネストした要素は テストされません。

Function: sxml:normalized? obj

与えられたobjが正規化されたSXML要素であれば#tを返します。 要素それ自体とその全てのネストした要素が正規化されていなければなりません。

Function: sxml:shallow-minimized? obj

与えられたobjが浅く最小化されたSXML要素であれば#tを返します。 要素それ自体は最小化されていなければなりませんが、そのネストした要素は テストされません。

Function: sxml:minimized? obj

与えられたobjが最小化されたSXML要素であれば#tが返されます。 要素それ自体とその全てのネストした要素が最小化されていなければなりません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.46.2 SXMLへのアクセッサ

Function: sxml:name obj

与えられたSXMLノードの名前を返します。 これは単にcarのエイリアスに過ぎませんが、カプセル化のために導入されています。

Function: sxml:element-name obj

sxml:nameの、与えられたobjがSXML要素ではない場合は#fを返す バージョンです。objがSXML要素であれば、その名前を返します。

Function: sxml:node-name obj

sxml:nameの、与えられたobjがSXMLノードでない場合は#fを返す、 安全なバージョンです。objがSXMLノードであれば、その名前を返します。

Function: sxml:ncname obj

与えられたobjの完全修飾名(Namespaces in XML production [6])のローカルパート、 つまりその完全修飾名を“:”で分割した後ろの部分を返します。 与えられたノードの名前がNCName(Namespaces in XML production [4])であれば、 それをそのまま返します。 SXMLの名前はシンボルですが、この関数は文字列を返すことに注意して下さい。

Function: sxml:name->ns-id sxml-name

与えられた名前の名前空間ID部分を返します。与えられた名前がLocalNameの 場合は#fを返します。

Function: sxml:content obj

与えられたSXML要素かノードセット(テキストノードと要素ノードのみ)の内容を、 ドキュメントオーダーにしたがった文字列のリストとネストした要素として 返します。このリストは、objが空要素や空リストの場合は空です。

Function: sxml:content-raw obj

正規化されたSXML要素の全ての内容を、attr-listaux-listを除いて 返します。 したがってそれは、PICOMMENTENTITY ノードとともに、sxml:contentによって返されるTEXTELEMENTも含みます。 戻り値は、ドキュメントオーダーにしたがったノードのリストか、 objが空要素や空リストの場合は空リストになります。 この関数は、sxml:contentよりも高速です。

SXMLの通常のフォームでは、1つの要素は次のようなリストで表現されます。

 
  (name attr-list aux-list content …)

attr-list@で始まるリストで、aux-list@@で 始まるリストです。

最小化されたフォームでは、aux-listは空であれば省略できます。 attr-listは、それが空でかつaux-listがなければ省略できます。

以下の手続きは、attr-listaux-listを抽出するものです。

Function: sxml:attr-list-node obj

与えられたobjattr-listを返します。 attr-listが存在しなければ#fを返します。

Function: sxml:attr-as-list obj

attr-listをリストにラップして返します。 attr-listが存在せずaun-listがある場合は’((@))を返します。 両方とも存在しない場合には’()を返します。

Function: sxml:aux-list-node obj

与えられたobjaux-listを返します。 attr-listが存在しなければ#fを返します。

Function: sxml:aux-as-list obj

aux-listをリストにラップして返します。 aux-listが存在しなければ’()を返します。

Function: sxml:attr-list-u obj

与えられた要素かノードセットの属性のリストを返します。 ((sxpath '(@ *)) obj)と類似です。 属性のリストがない場合は空リストが返されます。

Function: sxml:aux-list obj

与えられた要素かノードセットの補助ノードのリストを返します。 ((sxpath '(@@ *)) obj)と類似です。 補助ノードのリストがない場合は空リストが返されます。

Function: sxml:aux-list-u obj

与えられた要素かノードセットの補助ノードのリストを返します。 ((sxpath '(@@ *)) obj)と類似です。 補助ノードのリストがない場合は空リストが返されます。

-uが付くものは、正規化されていないSXMLノードに対しても 使えるということを示しています。(’u’は’universal’の意味です。)

Function: sxml:aux-node obj aux-name

与えられたSXML要素objaux-nameという名前の付いている最初の 補助ノードを返します。そのようなノードがない場合は#fを返します。 注意: 複数のノードがあっても最初に見つかったノードのみを 返します。したがって、ユニークな名前を持っているノード群に対して 使われることを意図しています。

Function: sxml:aux-nodes obj aux-name

与えられたSXML要素objaux-nameとうい名前の付いている補助ノードの リストを返します。そのようなノードがなければ’()を返します。

Function: sxml:attr obj attr-name

与えられたSXML要素objattr-nameという属性へのアクセッサです。 戻り値は、その属性が存在すればその属性の値、与えられた要素に そのような属性がなければ#fです。

Function: sxml:num-attr obj attr-name

与えられたSXML要素objattr-nameという数値の属性への アクセッサです。 戻り値は、その属性が存在してその値がstring->numberにより数値へ 変換できる場合はその属性を数値としてその属性の値、 与えられた要素にそのような属性がないかその値が数値へ変換できない場合は #fです。

Function: sxml:attr-u obj attr-name

与えられたSXML要素objattr-nameという名前の属性への アクセッサです。objは、(通常はSXML要素の内容である) 属性リストやノードセットでも構いません。

戻り値は、その属性が存在すればその属性の値、与えられた要素に そのような属性がない場合は#fです。

-uが付くものは、正規化されていないSXMLノードに対しても 使えるということを示しています。(’u’は’universal’の意味です。)

Function: sxml:ns-list obj

与えられた要素の名前空間のリストを返します。 ((sxpath '(@@ *NAMESPACES* *)) obj)と類似です。 名前空間のリストがない場合は空リストが返されます。

Function: sxml:ns-id->nodes obj namespace-id

SXML要素objで、与えられたnamespace-idに対応する名前空間の 連想リストのリストを返します。 ((sxpath '(@@ *NAMESPACES* namespace-id)) obj)と類似です。 与えられたnamespace-idに対応する名前空間の連想リストがない場合は 空リストが返されます。

Function: sxml:ns-id->uri obj namespace-id

与えられたnamespace-idに対応するURIを返します。 与えられたnamespace-idに対応する名前空間の連想リストがない場合は #fを返します。

Function: sxml:ns-uri->id obj uri

与えられた名前空間URIに対応する名前空間IDを返します。

Function: sxml:ns-id ns-assoc

与えられた名前空間連想リストに対応する名前空間IDを返します。

Function: sxml:ns-uri ns-assoc

与えられた名前空間連想リストに対応するURIを返します。

Function: sxml:ns-prefix ns-assoc

与えられた名前空間連想リストに対応する名前空間接頭辞を返します。 与えられた名前空間IDの(XML文書における)オリジナルの接頭辞は、 それが名前空間IDと異なる場合は、名前空間連想リストの3番目の要素として 格納されなければなりません。 名前空間連想リストでオリジナルの接頭辞が省略されている場合は、 代わりに名前空間IDが使われます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.46.3 SXMLのモディファイヤ

正規化されたSXMLデータのコンストラクタやミューテータです。 これらの関数は、正規化されたSXMLデータに最適化されています。 任意の正規化されていないSXMLデータには適用できません。

ほとんどの関数には2つのバージョンがあります。

  1. 与えられた要素のその場での更新のために副作用が意図された関数。 これらの名前はエクスクラメーションマークで終わっています。 このバージョンの戻り値は、特に断りのない限り未定義です。 例としては、sxml:change-content!が挙げられます。
  2. 変更された要素を返す、副作用のない純粋な関数。 例としては、sxml:change-contentが挙げられます。
Function: sxml:change-content obj new-content
Function: sxml:change-content! obj new-content

与えられたSXML要素の内容を、new-contentに変更します。 new-contentが空リストの場合は、objは空要素に変更されます。 結果のSXML要素は正規化されています。

Function: sxml:change-attrlist obj new-attrlist
Function: sxml:change-attrlist! obj new-attrlist

結果のSXML要素は正規化されています。 new-attrlistが空の場合は、objのcadrは(@)になります。

Function: sxml:change-name obj new-name
Function: sxml:change-name! obj new-name

SXML要素の名前を破壊的に変更します。

Function: sxml:add-attr obj attr

属性attrが追加されたSXML要素objを返します。 与えられた名前の属性がすでに存在する場合は#fを返します。 attr(attr-name attr-value)です。 sxml:add-attr!に対応する純粋関数的な関数です。

Function: sxml:add-attr! obj attr

要素objに属性attrを追加します。 与えられた名前の属性がすでに存在する場合は#fを返します。 結果のSXMLノードは正規化されています。 sxml:add-attrに対応するその場で更新する関数です。

Function: sxml:change-attr obj attr

属性attrの値が変更されたSXML要素objを返します。 与えられた名前の属性がない場合は#fを返します。 attr(attr-name attr-value)です。

Function: sxml:change-attr! obj attr

要素objの属性の値を変更します。 attr(attr-name attr-value)です。 そのような属性がない場合は#fを返します。

Function: sxml:set-attr obj attr
Function: sxml:set-attr! obj attr

要素objの属性attrをセットします。 そのような属性がない場合は新しい属性として追加されます。

Function: sxml:add-aux obj aux-node

補助ノードaux-nodeが追加されたSXML要素objを返します。

Function: sxml:add-aux! obj aux-node

要素objに補助ノードaux-nodeを追加します。

Function: sxml:squeeze obj
Function: sxml:squeeze! obj

与えられたSXML要素objとその子孫について、空のリストである属性 および補助リストを排除します(最小化)。 最小化され、正規化されたSXML要素が返されます。

Function: sxml:clean obj

与えられたSXML要素objとその子孫について、空リストである属性と、 全ての補助リストを削除します。 最小化され、正規化されたSXML要素が返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.46.4 SXPathの補助的ユーティリティ

これらは、SXPathの機能を拡張する便利なユーティリティです。

Function: sxml:add-parents obj . top-ptr

SXMLノードセットに’親へのポインタ’を追加したものを返します。 親へのポインタは、(*PARENT* thunk)というフォームを持つ 補助ノードです。thunkは親要素を返します。

Function: sxml:node-parent rootnode

高速な’node-parent’関数を返します。 すなわち、SXML要素を1引数として取り、補助リストで *PARENT*ポインタを使ってその親ノードを返す関数を返します。 ’*TOP-PTR*はルートノードへのポインタとして使われます。 ルートノードに対して適用されると空リストを返します。

Function: sxml:lookup id index

要素をそのIDを使って探します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.46.5 SXMLからマークアップへの変換

SXMLからXMLやHTMLなどのマークアップされたテキストを生成する手続き。 もっと高尚な変換器を得たいだけならば、 sxml.serializer - SXMLからXMLとXHTMLのシリアライゼーションをチェックして下さい。

Function: sxml:clean-feed . fragments

’fragments’をフィルタします。 fragmentsは、文字列、文字、数値、手続き、#f、他のフラグメントの リストです。 この関数はツリーを深さ優先でトラバースし、 文字列、文字、実行された手続きのリストを返し、 #fと’()を無視します。

全ての意味のあるフラグメントは文字列で、 この関数の結果に(apply string-append ... )を 適用すると、そのstring-valueを返します。

これは、Oleg KiselyovのSRV:send-replyの変種であるとみなすことが できるでしょう。 SRV:send-replyはフラグメントを印字(display)しますが、 この関数は意味のあるフラグメントのリストを返し、ごみをふるい落とします。

Function: sxml:attr->xml attr

属性のXMLマークアップを作ります。

Function: sxml:string->xml string

与えられた文字列中の全ての<>&``'を対応する文字実体参照に置き換えた、文字列あるいは 文字列のリストを返します。 sxml:string->htmlも参照して下さい。

Function: sxml:sxml->xml tree

SXML->XML変換に特化され最適化されたバージョンのノードディスパッチです。

Function: sxml:attr->html attr

属性のHTMLマークアップを作ります。

Function: sxml:string->html string

与えられた文字列で、それがエンコーディングを必要とする文字、 <>&を含まないことをチェックします。 text.html-lite - シンプルなHTMLドキュメントの構築html-escape-stringも参照して下さい。

Function: sxml:non-terminated-html-tag? tag

この述語は、“終了タグのない”HTML 4.0のタグに対して#tを 返します。

Function: sxml:sxml->html tree

SXML->HTML変換に対して特化され最適化されたバージョンのノードディスパッチ です。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.47 sxml.serializer - SXMLからXMLとXHTMLのシリアライゼーション

Module: sxml.serializer

このモジュールはSXMLからXMLとHTMLへの変換を行う、様々な機能を持つ シリアライザを提供します。XSLT2.0とXQuery1.0に部分的に適合しています。 (http://www.w3.org/TR/2005/CR-xslt-xquery-serialization-20051103/)。 sxml.toolsのsxml:sxml->xmlとsxml:sxml->htmlよりも強力です。

マニュアルのエントリは主に、オリジナルのソースコードから取られてています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.47.1 簡単なSXML変換

高レベルの、簡単に使えるシリアライザです。大抵の目的にはこれで十分使えるでしょう。

Function: srl:sxml->xml sxml-obj :optional port-or-filename

sxml-objをXMLへと変換します。出力は人間が読みやすいように インデントされます。

port-or-filenameが与えられなければ、 戻り値はsxml-objの変換結果の文字列になります。

port-or-filenameがポートならば、そのポートにXMLを書き出します。 戻り値は不定です。

port-or-filenameが文字列ならば、その名のファイルにXMLを書き出し ます。戻り値は不定です。そのようなファイルが既に存在する場合、 結果は不定です。

Function: srl:sxml->xml-noindent sxml-obj :optional port-or-filename

srl:sxml->xmlと同じように動作しますが、インデントを行いません。

引数port-or-filenamesrl:sxml->xmlと同様に動作します。

Function: srl:sxml->html sxml-obj :optional port-or-filename

sxml-objをHTMLへと変換します。出力は人間が読みやすいように インデントされます。

引数port-or-filenamesrl:sxml->xmlと同様に動作します。

Function: srl:sxml->html-noindent sxml-obj :optional port-or-filename

srl:sxml->htmlと同じように動作しますが、インデントを行いません。

引数port-or-filenamesrl:sxml->xmlと同様に動作します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.47.2 カスタムSXML変換

以下の手続きはXMLシリアライザの全ての設定パラメータへのアクセスを提供 します。

Function: srl:parameterizable sxml-obj :optional port-or-filename params*

汎用シリアライズ手続、この実装でサポートするすべてのシリアライズパラメー タのパラメータ化。

sxml-obj - シリアライズするSXMLオブジェクト。

port-or-filename - #fあるいはポートあるいは文字列のどれか。 srl:sxml->xmlのものと同じ働きをします(簡単なSXML変換)。

params - 各パラメータはパラメータ名(シンボル)とパラメータ値との コンス対。利用可能なパラメータ名とその値については後述。

method - シンボルxmlhtmlのどちらか。XMLメソッド とHTMLメソッドの相違についての詳しい説明は、XSLT 2.0 と XQuery Serialization (http://www.w3.org/TR/2005/CR-xslt-xquery-serialization-20051103/) を参照してください。

indent - 出力の XML が読みやすさのために空白を含むかどうか (#tあるいは#f)。文字列を設定することもでき、その場合 インデント単位に使われます。

omit-xml-declaration? - XML宣言を省略するかどうか。デフォルトは #t

standalone - XML ドキュメントを XML 宣言でスタンドアローンとし て定義するかどうか。yesnoomitのうちいずれかの シンボルでなければならない。デフォルトは omit

version - XML宣言中のXMLバージョン。文字列または数。デフォルト は"1.0"

cdata-section-elements - SXML要素の名前(シンボル)のリスト。これ らの要素の中身はCDATAセクションと同様にエスケープされる。

ns-prefix-assig - (cons prefix namespace-uri)のリスト。 各prefixはシンボルで、namespace-uriは文字列。 対応する接頭辞をつけて与えられた名前空間をシリアライズする。

注意: パラメータ名が期待された名前ではない場合、あるいはパラメータ値 が不正な形式である場合にはそのようなパラメータは警告なしで無視されます。

使用実例:

 
(srl:parameterizable
  '(tag (@ (attr "value")) (nested "text node") (empty))
  (current-output-port)
  '(method . xml)  ; XML出力をデフォールト
  '(indent . "\t")  ; インデントは一つのタブ
  '(omit-xml-declaration . #f)  ; XML宣言をつける
  '(standalone . yes)  ; 「standalone」宣言もつける
  '(version . "1.0"))  ; XMLのバージョン
 
param ::= (cons param-name param-value)
param-name ::= symbol

cdata-section-elements
value ::= (listof sxml-elem-name)
sxml-elem-name ::= symbol

indent
value ::= 'yes | #t | 'no | #f | whitespace-string

method
value ::= 'xml | 'html

ns-prefix-assig
value ::= (listof (cons prefix namespace-uri))
prefix ::= symbol
namespace-uri ::= string

omit-xml-declaration?
value ::= 'yes | #t | 'no | #f

standalone
value ::= 'yes | #t | 'no | #f | 'omit

version
value ::= string | number
Function: srl:sxml->string sxml-obj cdata-section-elements indent method ns-prefix-assig omit-xml-declaration? standalone version

srl:parameterizableと同様ですが、文字列を返し、パラメータ解析のオーバヘッ ドはありません。この関数のインタフェースはこのライブラリの将来のバージョ ンでは変更されるかもしれません。

Function: srl:display-sxml sxml->obj port-or-filename cdata-section-elements indent method ns-prefix-assig omit-xml-declaration? standalone version

srl:parameterizableと同様ですが、結果を port-or-filenameに書き出します。パラメータ解析のオーバーヘッドはありません。 この関数のインタフェースはこの ライブラリの将来のバージョンでは変更されるかもしれません。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.48 text.console - テキスト端末制御

Module: text.console

このモジュールは文字端末を制御する簡単なインタフェースを提供します。 今のところ、vt100互換端末とWindowsコンソールがサポートされています。

curses等の外部ライブラリには依存しておらず、Gaucheだけで使うことができますが、 出来ることは限られています。 例えばシフトキーだけが押されたタイミングでイベントを受け取ることはできません。 より細かい制御には、何らかの外部拡張ライブラリが必要になるでしょう。

このモジュールの機能の例としては、 Gaucheソースのexamplesディレクトリにある‘snake.scm’を見てください。

コンソールオブジェクト

Class: <vt100>

vt100互換端末を表します。このクラスのインスタンスは 以降のジェネリックファンクションの “console” 引数に渡せます。

Instance Variable of <vt100>: iport

端末に接続されている入力ポートです。デフォルトは標準入力です。

Instance Variable of <vt100>: oport

端末に接続されている出力ポートです。デフォルトは標準出力です。

Instance Variable of <vt100>: input-delay

端末は、特殊キーが押された場合、ESCから始まるエスケープシーケンスを送って来ます。 実際にESCキーが押された場合と区別するために、入力の間隔を測っています。 後続の入力がinput-delayμs以内に来なかった場合はそこでシーケンスが終了したと みなし、受け取っているシーケンスが有効なエスケープシーケンスを構成しなければ 個別にキー入力されたとみなします。デフォルトは1000、すなわち1msです。

Class: <windows-console>

Windowsコンソールを表します。このクラス自体は全てのプラットフォームで 定義されていますが、有用なメソッドはWindowsネイティブのランタイムでしか提供されません。

パブリックなスロットはありません。

アプリケーションは実行時にどの種類のコンソールが利用可能かを見極めなければなりません。 推奨される手順は次のとおりです。

次の手続きは上の手順を実装しています。

Function: make-default-console :key if-not-available

実行中のプロセスで使える端末のインスタンスを作成して返します。

適切な端末クラスが無い場合、振る舞いはif-not-availableキーワード引数に 依存します。デフォルトである:errorの場合はエラーが報告されます。 #fの場合はこの手続きが#fを返します。

Function: vt100-compatible? string

環境変数TERMの値を文字列で受け取り、それが<vt100>端末として 扱えるなら#tを、そうでなければ#fを返します。

Console control

Generic function: call-with-console console proc :key mode

Takes over the control of the console, and calls proc with console as the only argument. The console is set to the mode, which must be a symbol with-terminal-mode accepts: raw, rare or cooked. By default the console is set to rare mode, which turn off the echoing and passes most of keystrokes to the program, but it intercepts terminal controls (like Ctrl-C for interrupt and Ctrl-Z for suspend; the actual key depends on terminal settings, though.)

If proc raises an unhandled error, this generic function resets the terminal mode before returning. It does not clear the screen.

Generic function: putch console char

Display a character at the current cursor position, and move the current cursor position.

Generic function: putstr console string

Display a string from the current cursor position, and move the current cursor position.

Generic function: beep console

Ring the beep, or flash the screen (visible bell) if possible.

Generic function: getch console

Fetch a keypress from the console. This blocks until any key is pressed.

The return value may be one of the following values:

A character

A key for the character is pressed. It may be a control code if the control key is pressed with the key; that is, if the user presses Ctrl-A, #\x01 will be returned.

A symbol

Indicates a special key; the following keys are supported: KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_HOME, KEY_END, KEY_INS, KEY_DEL, KEY_PGDN, KEY_PGUP, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12. (Note: DELETE key is usually mapped to #\x7f, but it depends on the terminal).

A list of symbol ALT and a character.

Indicates the character key is pressed with Alt key. For example, if the user presses Alt-a, (ALT #\a) is returned (assuming CAPSLOCK is off).

EOF

Indicates the input is closed somehow.

Modifier keys except ALT are not treated separately but included in the returned keycode. Assuming CAPSLOCK is off, if the user press a, Shift+a, and Ctrl+a, the returned value is #\a, #\A and #\x01, respectively. Ctrl+Shift+a can’t be distinguished from Ctrl+a. ALT+a, ALT+Shift+a, and ALT+Ctrl+a will be (ALT #\a), (ALT #\A) and (ALT #\x01), respectively.

Generic function: chready? console

Returns true if there’s a key sequence to be read in the console’s input.

Generic function: query-cursor-position console

Returns two values, the current cursor’s x and y position. The top-left corner is (0,0).

Generic function: move-cursor-to console row column

Move cursor to the specified position. The top-left corner is (0,0).

Generic function: reset-terminal console

Reset terminal. Usually this sets the character attributes to the default, clears the screen, and moves the cursor to (0, 0).

Generic function: clear-screen console

Clear entire screen.

Generic function: clear-to-eol console

Clear characters from the current cursor position to the end of the line.

Generic function: clear-to-eos console

Clear characters from the current cursor position to the end of the screen.

Generic function: hide-cursor console
Generic function: show-cursor console

Hide/show the cursor.

Generic function: cursor-down/scroll-up console

If the cursor is at the bottom line of the screen, scroll up the contents and clear the bottom line; the cursor stays the same position. If the cursor is not at the bottom line of the screen, move the cursor down.

Generic function: cursor-up/scroll-down console

If the cursor is at the top line of the screen, scroll down the contents and clear the top line; the cursor stays the same position. If the cursor is not at the top line of the screen, move the cursor up.

Generic function: query-screen-size console

Returns two values, the width and height of the screen.

Note: This may affect what’s shown in the console. It is recommended that you only call this before redrawing the entire screen and save the result.

Generic function: set-character-attribute console spec

Set the console so that the subsequent characters will be written with attributes specified by spec.

The character attributes spec is a list in the following format:

 
(<fgcolor> [<bgcolor> . <option> ...])

where:

 
<fgcolor> : <color> | #f     ; #f means default
<bgcolor> : <color> | #f
<color>  : black | red | green | yellow | blue | magenta | cyan | white
<option> : bright | reverse | underscore

For example, you can set characters to be written in red with black background and underscore, you can call:

 
(set-character-attribute con '(red black underscore))

That the options may seem rather limited in the age of full-color bitmap displays. That’s what it used to be, young lads.

Generic function: reset-character-attribute console

Reset character attributes to the default.

Generic function: with-character-attribute console attrs thunk

Sets the console’s attributes to attrs and calls thunk, then restores the attributes. Even if thunk throws an error, attributes are restored.

Note: You should be able to nest this, but currently nesting isn’t working.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.49 text.csv - CSVテーブル

Module: text.csv

RFC4180に定義されたフォーマットを含む、 CSV (カンマ区切りの値) の表をパーズ/生成するための手続きを提供します。 区切り文字やクオート文字をカスタマイズすることで、CSVに似たフォーマットを 広くカバーすることができます。

CSV の表は、改行で区切られた record の連続で構成されます。 それぞれのレコードは、区切り文字(デフォルトではカンマ)で区切られた 複数の field を含みます。フィールドは、クォートされている (二重引用符で囲まれている)場合は、カンマや改行を含むことができます。 クォートされたフィールドに二重引用符を含めるには、2つの連続する 二重引用符を使います。通常、フィールドの前後の空白は無視されます。

CSV様のファイルの使われ方は多様なので、このモジュールでは柔軟に組み合わせることが出来る 多層的なAPIを提供します。

低レベルAPI

一番下の層のAPIは、テキストとリストのリストとを相互変換するものです。

Function: make-csv-reader separator :optional (quote-char #\")

入力ポートを省略可能引数として取る手続きを返します。 手続きが呼ばれると、ポート(省略された場合は現在の入力ポート)からレコードを1つ読み込み、 フィールドのリストを返します。入力ポートが EOF に達すると、EOF を返します。

Function: make-csv-writer separator :optional newline (quote-char #\") special-char-set

出力ポートとフィールドのリストの2つの引数を取る手続きを返します。 手続きが呼ばれると、separator で区切られたフィールドを 正しくエスケープして出力ポートに出力します。各フィールドの値は文字列でなければなりません。 separatorは文字または文字列です。

レコードの区切り文字列をnewline で指定することもできます。例えば、ファイルが Windows の プログラムでも読めるように、"\r\n" を渡すことができます。

フィールドの出力は、その中に特殊文字が含まれていた場合にクオートされます。 separator, quote-char, newlineに含まれる文字は 自動的に特殊文字と認識されます。さらに special-char-setに文字セットを渡すと、それも特殊文字として扱われます。 省略時は#[;\s]が使われます。

中レベルAPI

スプレッドシート等で作成されたCSVファイルはしばしば、 不要な行や列を含んでいたり、 行や列の内容から有効なデータの場所を探したりする必要があります。 そのために役に立つユーティリティが用意してあります。

スプレッドシート等によって生成される典型的なCSVには以下の特徴があります。

  1. 最初の方に、「ヘッダ行」があります。一番最初の行とは限りませんが、 データの実体より前に現れ、データの各列の意味を決めます。 しばしば、見た目のためだけに余分な列が挿入されたり、 編集によって列が入れ替わったりすることもあるので、 ヘッダ行を見てどの列に何があるか判断する必要があります。
  2. ヘッダ行の後に続く「レコード行」。ここにデータの実体があります。 見た目のためだけに余分な行が挿入されていることもあります。 また、データの終わりがきちんと示されていないことも多いです (例えば、 CSVの末尾に全ての列が空文字列である行がぞろぞろとくっついている、等。)

中レベルCSVパーザの主目的は、低レベルパーザの出力である文字列のリストのリストを 受け取って、ヘッダ行を探し出し、続くレコード行をヘッダ行の内容に沿って タプルに変換することにあります。ここで、タプルは単なる文字列のリストですが、 指定されたヘッダ仕様に沿った順序で並べてあるものとします。

Function: csv-rows->tuples rows header-specs :key required-slots allow-gap?

入力行(文字列のリスト)のリストを、タプルのリストに変換します。 タプルはスロットの値のリストです。

まずheader-specにマッチするヘッダ行が探されます。ヘッダ行が見つかったら、 それにしたがって後続の行をレコード行として解釈し、各行をタプルへと変換します。 ヘッダ行が見つからなければ、#fが返されます。

header-specsはヘッダ仕様のリストです。各要素は文字列、正規表現、 あるいは文字列を取る述語手続きのいずれかです。文字列の場合、その文字列と正確に 一致する内容を持つ列が選ばれます。正規表現なら内容がそれと一致する列、 述語手続きなら、内容がその述語を満たすような列がそれぞれ選ばれます。

header-specsに現れる順番が、出力タプルの列の順番を決めます。

required-slotsは、入力行が有効なレコード行かそうでないかを決めます。 required-slotsの構造は以下のとおりです。

 
   <required-slots> : (<spec> ...)
   <spec> : <header-spec> | (<header-spec> <predicate>)

<header-spec>はどの列の要素をチェックすべきかを指定します。 header-slotに現れる要素のいずれかとequal?でなければなりません。 <header-spec>だけが<spec>として与えられていた場合は、 その列が空であってはいけない、ということを意味します。 <header-spec><predicate>が与えられた場合は、 レコード行の該当列の値(文字列)が述語手続き<predicate>に与えられ、 それが偽を返したらその行は有効でないということになります。

allow-gap?#tであった場合、無効な行を飛ばしながら、 入力データは最後まで処理されます。allow-gap?#fであった場合(デフォルト)は、 無効な行が出てきた時点で処理を打ちきります。

例を見てみましょう。次のデータが‘data.csv’というファイルに書かれているとします。 スプレッドシートからエクスポートされるデータによくあるように、不要な行や列が混ざっています。

 
,,,,,,,,
"Exported data",,,,,,,,
,,,,,,,,
,,Year,Country,,Population,GDP,,Note
,,1958,"Land of Lisp",,39994,"551,435,453",,
,,1957,"United States of Formula Translators",,115333,"4,343,225,434",,Estimated
,,1959,"People's Republic of COBOL",,82524,"3,357,551,143",,
,,1970,"Kingdom of Pascal",,3785,,,"GDP missing"
,,,,,,,,
,,1962,"APL Republic",,1545,"342,335,151",,

ここから、Country, Year, GDP, Populationの値からなるタプルのリストを 次のとおり取り出せます。

 
(use text.csv)
(use gauche.generator)

(call-with-input-file "data.csv" 
  (^p (csv-rows->tuples 
       (generator->list (cute (make-csv-reader #\,) p))
       '("Country" "Year" "GDP" "Population"))))
 ⇒
  (("Land of Lisp" "1958" "551,435,453" "39994")
   ("United States of Formula Translators" "1957" "4,343,225,434" "115333")
   ("People's Republic of COBOL" "1959" "3,357,551,143" "82524")
   ("Kingdom of Pascal" "1970" "" "3785"))

無関係な行は無視されており、また結果の列はheader-specsに渡した順序に 整列されていることに注目してください。

“Kingdom of Pascal”の列の後にギャップ(空の列)があるため、 csv-rows->tuplesはそこで処理を打ち切っています。 ギャップの後の“APL Republic”まで含めたければ、csv-rows->tuples:allow-gap? #tを渡してください。

次の例では、:required-slots引数を与えて、 Year、Country、GDPのデータの一つ以上が欠けている行を除外しています。 したがって“Kingdom of Pascal”は結果から除かれます。一方、 :allow-gap?引数のために“APL Republic”が含まれます。 (この例ではYearが4桁ぴったりの数値であるかどうかもチェックしています。)

 
(call-with-input-file "data.csv" 
  (^p (csv-rows->tuples 
       (generator->list (cute (make-csv-reader #\,) p))
       '("Country" "Year" "GDP" "Population")
        :required-slots '(("Year" #/^\d{4}$/) "Country" "GDP")
        :allow-gap? #t)))
 ⇒
 (("Land of Lisp" "1958" "551,435,453" "39994")
  ("United States of Formula Translators" "1957" "4,343,225,434" "115333")
  ("People's Republic of COBOL" "1959" "3,357,551,143" "82524")
  ("APL Republic" "1962" "342,335,151" "1545"))

以下の二つの手続きはcsv-rows->tuplesの材料です。

Function: make-csv-header-parser header-specs

行(文字列のリスト)を受け取り、それがheader-specsで指定される条件に 一致するかどうかを調べます。 (header-specsについては上のcsv-rows->tuplesを見てください。) もし引数が条件を満たしたなら、置換ベクタを返します。 置換ベクタはタプルの位置を入力行の列番号へとマップするもので。 一致しなかった場合は#fを返します。

置換ベクタは整数のベクタです。K番目の要素がIであることは、 タプルのK番目の要素が入力のI番目の列から取られることを示します。

例を見てみましょう。入力に、以下の形の行がヘッダ行として含まれていることが わかっているとします。

 
(define *input-row* '("" "" "Year" "Country" "" "Population" "GDP" "Notes"))

こういう形の行を見つけたいわけですが、取り出すデータとしては、 CountryYearGDPPopulationだけが この順で必要であるとします。この場合、ヘッダパーザを 次のとおり構成できます。

 
(define header-parser
  (make-csv-header-parser '("Country" "Year" "GDP" "Population")))

このヘッダパーザを想定される入力行に適用すれば、置換ベクタが返ってきます:

 
(header-parser *input-row*)
 ⇒ #(3 2 6 5)

返り値の意味は、タプルの第0要素(Country)は入力の第3列にあり、 タプルの第1要素(Year)は入力の第2列にある、といった具合です。 置換ベクタはレコード行をパーズしてタプルを生成するのに使えます。

Function: make-csv-record-parser header-slots permuter :optional required-slots

入力の1行をタプルへと変換する手続きを作って返します。

permutermake-csv-header-parserが返す置換ベクタです。

header-slots及びrequired-slots引数については 上のcvs-rows->tuplesを見てください。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.50 text.diff - テキストストリームの相違点を計算する

Module: text.diff

このモジュールでは、util.lcs (util.lcs - 最長共通サブシーケンス参照)を 使って、2つのテキストストリーム、あるいは2つの文字列の相違点を計算します。

Function: diff src-a src-b :key reader eq-fn

テキストソースsrc-asrc-bから“編集リスト”を生成します。

それぞれのテキストソース、src-asrc-bは入力ポートか文字列です。 もし文字列であれば、それは内部的に文字列ポートに変換されます。 そして、2つのソースからのテキストストリームは、それらに対してreaderを繰り返し 呼ぶことによってシーケンスに変換されます。デフォルトのreaderread-lineで、 2つのシーケンスは編集リストを計算するためにlcs-edit-listに渡されます。 lcs-edit-listには、等値を検査する関数eq-fnも渡されます。

編集リストとは、src-aからsrc-bへテキストシーケンスを 変更するためのコマンドのセットです。編集リストの詳細な説明は、 lcs-edit-listを参照してください。

 
(diff "a\nb\nc\nd\n" "b\ne\nd\nf\n")
⇒
  (((- 0 "a"))
   ((- 2 "c") (+ 1 "e"))
   ((+ 3 "f")))
Function: diff-report src-a src-b :key reader eq-fn writer

2つのテキストソースのdiffをとって、その結果をきれいに表示するための 簡易手続きです。この手続きは、2つのテキストソースの相違点を計算する ためにlcs-foldを呼び出します。src-asrc-breadereq-fnの意味は、diffの場合と同じです。

writerは2つの引数、テキスト要素とタイプ(シンボル+、 シンボル-、あるいは#fのいずれか)を取る手続きです。 テキスト要素がsrc-aにしかない場合は、writerがそのテキスト要素と -とともに呼ばれます。テキスト要素がsrc-bにしかない場合は、 writerはそのテキスト要素と+とともに呼ばれます。 テキスト要素が両方のソースにある場合は、writerはそのテキスト要素と #fとともに呼ばれます。writerのデフォルトの手続きは、 渡されたテキスト要素を現在の出力ポートにユニファイドdiffのようなフォーマットで 出力します。

 
(diff-report "a\nb\nc\nd\n" "b\ne\nd\nf\n")

displays:

 
- a
  b
- c
+ e
  d
+ f

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.51 text.gettext - 地域化メッセージ

Module: text.gettext

このモジュールは地域化メッセージを扱うユーティリティを提供します。 API は GNU の gettext と互換性があり、メッセージは ‘*.po’ および ‘*.mo’ ファイルから読み込まれます。それゆえ、GNU の gettext toolchain をつかって地域化メッセージを準備することができます。しかし、このコードは Alex Shinn によってスクラッチから書き起こされたものであり、GNU の gettext ライブラリには依存していません。

この実装は GNU の gettext API を以下のように拡張したものです。

SRFI-29 (srfi-29 - 地域化参照) はこれとは別のメッセージの地域化 機構を提供しています。ポータブルなプログラムでは、SRFI-29 の方がよいこと もありますが、一般には、Gauche スクリプトでは、text.gettext の 方を推奨します。それは、既存のメッセージファイルとの互換性と柔軟性の ためです。

gettext-互換の API

Function: textdomain domain-name :optional locale dirs cdir cached? lookup-cached?

アプリケーション用に、デフォルトのドメインとそのほかのパラメータを 設定します。この設定は、以降の gettext の呼出しに影響を与えます。

domain は文字列または文字列のリストで、ドメイン(‘.mo’ あるいは ‘.po’ ファイル名)を C の gettext と同じように指定します。 #fdomain-name として渡すと、デフォルトのドメインアクセサ 手続きが得られます。また、複数のドメインを domain-nameにあたえる ことができます。

 
(textdomain '("myapp" "gimp"))  ; search 1st myapp, then gimp
(gettext "/File/Close")         ; "Close" from gimp unless overridden

locale は文字列または文字列のリストで、標準的なUnixのフォーマット LANG[_REGION][.ENCODING] です。フォールバックを指定するロケール のリストを渡すこともできます。

 
(textdomain "myapp" '("ru" "uk"))  ; search 1st Russian then Ukranian,
(gettext "Hello, World!")          ; which are somewhat similar

dirs は実際のメッセージカタログを含む ‘LOCALE/CDIR/’ ディレクトリのサーチパスです。これは常にシステムのデフォルト(たとえば、 ‘"/usr/share/locale"’)に追加されます。そして、コロンで区切られた GETTEXT_PATH 環境変数を継承します。

cdir はカテゴリーディレクトリで、既定値を LC_CATEGORY 環境変数または、適当なシステム既定値(たとえば LC_MESSAGES)に 設定します。一般にはこれを指定する必要はありません。

cached? は個別のメッセージをキャッシュするかを意味し、デフォルトは #t です。

lookup-cached? は、これらのパラメータで生成されるディスパッチをキャッシュするかどうか を意味し、デフォルトは #t です。

textdomain はこれらのパラメータを内部の make-gettext に 渡し、その結果を、gettext で使われるグローバルなディスパッチに 束縛します。これらのクロージャを複数の別々のドメインやロケールを一度に 扱うのに便利なように、手で構築することができます。(これらは サーバ環境で便利です。) 後述の make-gettext を参照してください。

textdomain はそのドメインの情報をもつ アクセサ手続きを 返します。詳細については、後述の make-gettext を参照してください。

Function: gettext msg-id

msg-id の翻訳されたメッセージを返します。もし、翻訳された メッセージがなければ、msg-id それ自身を返します。

Function: ngettext msg-id :optional msg-id2 num

gettextと似ていますが、複数形を処理するのに使うことが できます。単数形のメッセージを msg-id へ、複数形のメッセージを msg-id2 に渡します。num 引数は、複数形を決定 するのに使われます。もし、メッセージカタログが見つからなければ、 num が 1 のときは、msg-id が返り、そうでなければ、 msg-id2 が返ります。

Function: bindtextdomain domain dirs

ドメイン domain のサーチパスを dirs に設定します。 単一のディレクトリ名であったり、ディレクトリのリストであったりします。

Function: dgettext domain msg-id
Function: dcgettext domain msg-id locale

domain 中の msg-id の翻訳されたメッセージを返します。 dcgettextlocale も引数としてとります。

低水準の柔軟な API

以下の手続きはより柔軟性のあるインタフェースで、この上で、gettext-互換の API が書かれています。

Function: make-gettext :optional domain locale dirs gettext-cached? lookup-cached?

アクセサ手続き を生成して返します。返された手続きは、 地域化されたメッセージを検索するメソッドをカプセル化しています。

引数の意味は上述の textdomain と同じです。 実際は、textdomainmake-gettext を呼び、その後、 それは、結果をこのグローバルパラメータに束縛します。make-gettext を直接呼んで、自分自身で、アクセサ手続きを管理することもできます。

 
(define my-gettext (make-gettext "myapp"))
(define _ (my-gettext 'getter))
(_ "Hello, World!")

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.52 text.html-lite - シンプルなHTMLドキュメントの構築

Module: text.html-lite

HTML ドキュメントを簡単に構築するための手続きを提供します。 例えば、以下のコードは HTML のテーブルを構築します。

 
(html:table
  (html:tr (html:th "Item No") (html:th "Quantity"))
  (html:tr (html:td 1) (html:td 120))
  (html:tr (html:td 2) (html:td 30))
  (html:tr (html:td 3) (html:td 215)))

詳細については、以下の html:element の説明を見てください。

このモジュールでは生成されたHTMLドキュメントに関して、 例えばアトリビュートに有効な値が入っているか、要素の内容はDTDを満たしているか、 等のチェックをほとんど行いません。また、HTMLをパーズする関数も提供されません。 それが「lite」の名の由来です。

Function: html-escape
Function: html-escape-string string

HTML に含まれる"安全でない"文字をエスケープします。 html-escape は、現在の入力ポートから文字列を読み込み、 結果を現在の出力ポートへ書き出します。html-escape-stringstring を入力とし、文字列を返します。

Function: html-doctype :key type

HTML ドキュメントの文書型宣言を返します。 type は、以下のいずれかを指定します (デフォルトは:html-4.01-strict です)。

:html-4.01-strict, :html-4.01, :strict

HTML 4.01 Strict DTD

:html-4.01-transitional, :transitional

HTML 4.01 Transitional DTD

:html-4.01-frameset, :frameset

HTML 4.01 Frameset DTD

:xhtml-1.0-strict, :xhtml-1.0

XHTML 1.0 Strict DTD

:xhtml-1.0-transitional

XHTML 1.0 Transitional DTD

:xhtml-1.0-frameset

XHTML 1.0 Frameset DTD

:xhtml-1.1

XHTML 1.1 DTD

Function: html:element args

element の HTML 要素を構築します。現時点では以下の要素が サポートされています。 (HTML 4.01 DTD http://www.w3.org/TR/html4/sgml/dtd.html に定義されている要素です)。

 
a        abbr       acronym    address     area      b
base     bdo        big        blockquote  body      br
button   caption    cite       code        col       colgroup
dd       del        dfn        div         dl        dt
em       fieldset   form       frame       frameset
h1       h2         h3         h4          h5        h6
head     hr         html       i           iframe    img
input    ins        kbd        label       legend    li
link     map        meta       nofrmaes    noscript  object
ol       optgroup   option     p           param     pre
q        samp       script     select      small     span
strong   style      sub        sup         table     tbody
td       textarea   tfoot      th          thead     title
tr       tt         ul         var

これらの手続きは、テキスト・セグメントのツリーを返すので、 write-tree でポートに書き出したり、tree->string で 文字列に変換したりできます (text.tree - 怠惰なテキスト構築参照)。

要素のアトリビュートは要素の内容に先立つキーワード-値の表記で指定することができます。

 
(tree->string (html:a :href "http://foo/bar" "foobar"))
  ⇒
  "<a href=\"http://foo/bar\">foobar</a\n>"

(tree->string
  (html:table :width "100%" :cellpading 0 "content here"))
  ⇒
  "<table width=\"100%\" cellpadding=\"0\">content here</table\n>"

属性に与える真偽値は特別な意味を持ちます。 #t が与えられると、属性は値なしでレンダリングされます。 #f が与えられると、属性それ自体がレンダリングされません。

 
(tree->string (html:table :border #t))
  ⇒ "<table border></table\n>"

(tree->string (html:table :border #f))
  ⇒ "<table></table\n>"

属性の値における特別な文字は、手続きによってエスケープされますが、 要素の内容にある特別な文字はエスケープされません。それをエスケープ するのは呼び出し側の責任です。

内容を持たない HTML 要素に内容を与えると手続きはエラーを通知します。 手続きは、与えられた属性が妥当であるか、与えられた内容がその要素に とって妥当であるかのチェックはしません。

注意: これらの手続は改行を終了タグの>の前に挿入することに注意してくだ さい。つまり、HTMLをレンダリングすると以下のようになります。

 
<table><tr><td>foo</td
><td>bar</td
></tr
></table
>

終了ダグの後に改行をいれないのは意図的なものです。改行が意味をも つかどうかは外側の文脈に依存するからです。たとえば、<head>要素 の直下にある要素の後に改行を入れてもテキストの内容には影響を与えませんが、 <p>要素中で改行を入れるとそれは内容の一部となってしまいます。

3つ可能性があって、(1) 改行は全く入れない、(2) タグの中だけで改行を入 れる。(3) 安全な場所にのみ改行をいれる。最初の場合はHTMLは一行になり、 正当なHTMLではあるものの、行指向のツールで処理するには不便です。3番目 の場合はレンダリングするのにDTDをちゃんと見るツールが必要になります。 というわけで、ここでは2番目のアプローチを取っています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.53 text.parse - 入力ストリームのパージング

Module: text.parse

入力ポートに対して単純な解析を行うユーティリティのコレクションです。 API は Oleg Kiselyov 氏の入力解析ライブラリ (OLEG1) に 触発され、互換性を持つものです。氏のライブラリは、他のたくさんの ライブラリで使われています。特に、Scheme のみで書かれたパーサ/ジェネレータ である SSAX (SSAX) が挙げられます。

このモジュールは、氏の input-parse.scmlook-for-str.scm の代わりに使うことができます。

Gauche で効果的になるように手続きを再実装しました。特に、string-set! の使用は完全に取り除きました。インターフェースを少し拡張したので、 文字セットや述語、文字のリストにも使うことができます。

これらの手続きは、与えられた入力ポートに対してシーケンシャルに動作します。 それは、ポートから必要なだけ読み、余分な文字をバッファリングしないということです。

Function: find-string-from-port? str in-port :optional max-no-chars

入力ポート in-port から、文字列 str を探します。 オプショナル引数 max-no-chars は、ポートから読み込まれる最大文字数を 制限します。省略されると、検索する範囲は EOF までとなります。

str が見つかると、手続きはすでに読み込んだ文字の数を返します。 in-port の次回の読み込みは、str の次の文字を返します。 str が見つからない場合、#f が返ります。

注意: この手続きはその名前に「?」がつきますが、Scheme の慣習に反し、 真偽値ではない値を返すことがあります。

Function: peek-next-char :optional port

現在の文字を破棄し、port から次の文字を読みます。一文字先読みするのに 便利です。port が省略されると、現在の入力ポートが使われます。

以下の手続きでは、char-list は次のどれかを意味します。

これらにより文字の集合が表現されます。シンボル *eof* が含まれる場合、 EOF の条件もまた含まれます。*eof* が含まれない場合、EOF の条件は エラーとして扱われます。

Function: assert-curr-char char-list string :optional port

port から文字を読みます。その文字が char-list に含まれている場合は その文字を返します。そうでなければ、string を含むメッセージとともに エラーを通知します。

Function: skip-until char-list/number :optional port

char-list/number は、文字のリストか数です。 数の場合、たくさんの文字を読んで、#f を返します。 入力が十分に長くない場合は、エラーが通知されます。 char-list/number が文字のリストの場合、その文字リストに属する文字に 出会うまで port を読み込み、その文字を返します。 port が省略された場合、現在の入力ポートが使われます。

Function: skip-while char-list :optional port

char-list に属しない文字に出会うまで、port を読み込みます。 文字はストリームに残されます。EOF に達したら EOF が返されます。 port が省略された場合、現在の入力ポートが使われます。

この例では、入力から空白スペースをスキップしています。ポートからの次の 読み込みは、最初の空白スペースでない文字を返します。

 
(skip-while #[\s] port)
Function: next-token prefix-char-list break-char-list :optional comment port

prefix-char-list に含まれる文字はいくつでもスキップします。 そして、break-char-list に含まれる文字に出会うまで、文字を 蓄積します。蓄積された文字群は文字列として返されます。 中断文字は port に残されます。

手続きが EOF に達し、*eof*break-char-list に含まれていない 場合、comment が含まれたメッセージとともにエラーが通知されます。

Function: next-token-of char-list/pred :optional port

読み込んだ文字が char-list/pred にある限り蓄積し、文字列として 返します。char-list/pred に含まれない最初の文字はポートに残されます。

char-list/pred は文字のリストか文字を取る述語です。述語の場合、 それぞれの文字がその述語に渡され、真の値が返る場合はその文字は char-list/pred に属するとみなされます。

Function: read-string n :optional port

組み込みのread-string (データの読み込み参照) とほぼ同じですが、 入力が既にEOFに達していた場合は""を返します。

Olegのライブラリに依存しているコードの互換性のために用意されています。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.54 text.progress - テキスト端末上で進捗を表示する

Module: text.progress

このモジュールはバーチャートを文字を用いて表示し、テキスト端末上に処理 の進行状況を出すためのユーティリティです。

進捗を示すプログレスバーの汎用的なフォーマットは1行のテキストで構成されます。 この1行はいくつかの部分に分解できます。タイトルを表示するヘッダ部、その後に プログレスバーがあって、数値部、時間部と続きます。以下がその例です(foo からはじまる1行だけが実際に表示されます。

 
<-header-> <-------bar---------> <-num-><-time->      <---info---->
foo       |#############        |123/211   01:21 ETA  compiling...
          ^
          separator

バーチャートに使う文字などのいろいろなバリエーションや、数値で表わす 進捗のフォーマットなども設定可能です。

内部的にはプログレスバーは2つの数値、最大値(ゴール)と現在の値を保持し ています。バーは最大値に対する現在の値の比を示すものです。数値で表わす 進捗はデフォルトでは最大値の上に現在の値を表示します。しかし、たとえば、 現在の値だけを表示するとか、100分率で表示するなどの設定が可能です。

プログレスバーは2つの状態「進捗中」および「終了」のどちらかの状態を保 持しています。「進捗中」なら、どの時点でも表示されるテキストには #\returnが続きます。これにより次の表示が前の表示を上書きするこ とになります。時間部はETA(残り時間)を表示します。終了してしまえば、最 後の行の後に#\newlineが続きます。時間部には終了までにかかった時 間が表示されます。

このモジュールはmake-text-progress-barという手続きを1つだけ提供 します。この手続きはプログレスバーの機能をクロージャに包んでそれを返し ます。

Function: make-text-progress-bar :key header header-width bar-char bar-width num-width num-format time-width info info-width separator-char max-value port

プログレスバー上の操作を包んだ手続きを返します。返された手続きに操作を 示すシンボルとオプションで数値を引数として渡して使います。

proc 'show

プログレスバーを再表示する。他のどの操作も状態の再表示を行います。 したがって、現在のステータスを再表示しなければない特別な理由がないかぎ りこれを使う必要はありません。

proc 'set value

現在の値をvalueにセットし、プログレスバーを再表示します。 valueが最大値を超えていたら、超過分を切り捨て最大値にします。

proc 'inc value

value分だけ現在の値を増し、プログレスバーを再表示します。現在の 値が最大値を超えたら、超過分を切り捨て最大値にします。

proc 'finish

プログレスバーを「終了」状態にし、再表示します。時間部はトータルの経過 時間を表示します。また、表示行末は#\newlineとし、上書きされない ようにします。プログレスバーはいったん「終了」状態になれば、「進捗中」 状態に戻す方法はありません。

proc 'set-info text

“info”部に表示されるテキストを変更します。 info部を使うには、make-text-progress-barinfo-widthキーワード 引数に正の値が指定されている必要があります。

proc 'set-header text

“header”部に表示されるテキストを変更します。

キーワード引数を使って表示をカスタマイズできます。

header

ヘッダ部に表示するテキスト。このテキストは、作られたプログレスバーに set-headerメッセージを送ることで後で変更可能です。

header-width

ヘッダ部の幅、文字数で指定します。ヘッダ部のテキストは左詰めです。ヘッ ダ部に置くテキストが幅よりも長い場合には超過分の文字は切り捨てられます。 デフォルトは14文字です。

bar-char

バーチャートを描くのに使う文字。デフォルトは#\#です。

bar-width

バーチャート部の幅で、文字数で指定します。デフォルトでは40文字です。

num-width

数値部の幅で、文字数で指定します。デフォルトでは9で、これを0に設定する と数値部を隠せます。

num-format

数値部を整形する手続き。引数が2つ渡されます。ひとつは現在の値、もうひ とつは最大値です。この手続きは文字列を返さなければなりません。デフォル トでは以下の手続きです。

 
(lambda (cur max)
  (format "~d/~d" cur max))
time-width

時間部の幅で、文字数で指定します。デフォルトでは7文字で、これを0に設定 すると時間部を隠せます。

info

info部に表示されるテキストです。このテキストは、作られたプログレスバーに set-infoメッセージを送ることで後から変更できます。 info部を表示するには、info-widthキーワード引数に正の値を 与えておく必要があります。

info-width

info部の表示幅です。デフォルトは0で、info部は表示されません。

separator-char

バー部分の前後に置く文字です。デフォルトでは#\|です。 セパレータを表示したくなければ、#fを渡します。

max-value

プログレスバーの最大値です。デフォルトでは100です。

port

プログレスバーを表示する出力ポートです。デフォルト値は make-text-progress-barが呼ばれた時点での現在の出力ポートです。

以下は簡単なカスタマイズをした例です。

 
(use text.progress)

(define (main args)
  (define (num-format cur max)
    (format "~d/~d(~3d%)" cur max
            (round->exact (/. (* cur 100) max))))

  (let ((p (make-text-progress-bar :header "Example"
                                   :header-width 10
                                   :bar-char #\o
                                   :num-format num-format
                                   :num-width 13
                                   :max-value 256)))
    (do ((i 0 (+ i 1)))
        ((= i 256) (p 'finish))
      (p 'inc 1)
      (sys-select #f #f #f 50000))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.55 text.sql - SQLのパーズと構築

Module: text.sql

このモジュールはSQL文のパーズと構築のためのユーティリティを提供します。

このモジュールは現在まだ開発途上にあります。まだトークン列を生成するルー チンしかありません。SQLのS式構文を定義して、SQLとその構文との間の変換 ルーチンを提供する計画です。

註: SQLに安全に渡せるように文字列をエスケープするルーチンをお探しなら、 DBIのユーザAPIdbi-escape-sqlを見てください。

Function: sql-tokenize sql-string

SQL文sql-stringをトークン列に分解します。返り値はトークンのリス トで、各トークンは以下の形式のひとつで表現されます。

 
<symbol>              特殊区切り子、以下のどれか
                      + - * / < = > <> <= >= ||
<character>           特殊区切り子、以下のどれか
                      #\, #\. #\( #\) #\;
<string>              通常の識別子
(delimited <string>)  区切られた識別子
(parameter <num>)     位置パラメータ (?)
(parameter <string>)  名前つきパラメータ (:foo)
(string    <string>)  文字列リテラル
(number    <string>)  数値リテラル
(bitstring <string>)  バイナリ文字列  <string> は "01101" な感じ
(hexstring <string>)  Binary string.  <string> は "3AD20" な感じ

トークンに分解できない文字列がくると<sql-parse-error>コンディショ ンがあがります。

Condition Type: <sql-parse-error>

SQLパーズエラーを示すコンディション。<error>を継承。

Instance Variable of <sql-parse-error>: sql-string

元のSQL文字列を保持。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.56 text.tr - 文字変換

Module: text.tr

このモジュールは、入力ストリームから指定の文字を置き換えて出力する、 文字変換(transliterate)機能を提供します。 Unixのtr(1)コマンドで実装され、sedperlにも 採り入れられている機能です。

Gaucheのtrはマルチバイト文字/文字列を正しく扱います。

Function: tr from-list to-list :key :complement :delete :squeeze :table-size :input :output

inputから文字を読み込み、その文字がfrom-list内にあれば 対応するto-list内の文字に置き換えて、outputへと書き出します。 from-listに無い文字はそのままoutputへと渡されます。

inputoutputの既定値はそれぞれ現在の入力ポートと 出力ポートです。

from-listto-listは文字列でなければなりません。 その中には次のような表記を使うことができます。それ以外の文字はそのまま使われます。

x-y

文字xから文字yまでの文字の昇順の並びと解釈されます。 xyは含まれます。文字の並びはGaucheの内部文字エンコーディングに よって決定されるので、一般にはxyは同じキャラクタクラスの中に 止めておいた方が安全でしょう。xyより小さくなければなりません。

x*n

文字xn個の並び。nは10進数で表記された数値です。 これはto-listでのみ有効で、from-listで使うとエラーになります。 nが省略されるか0の場合、xto-listの長さがfrom-list の長さに達するまで繰り返されます(その場合、to-listの残りは無視されます)。

\x

文字xそれ自身。特殊文字そのものを埋め込みたい場合に使います。 文字列のリーダも\を解釈するので、\\と書かねばならないことに注意 して下さい。

グラフィカルでない文字のための構文はありません。文字列リーダの構文を使って そのような文字を文字列に含めることができます。

いくつか例を挙げます。

 
;; 大文字と小文字を交換します
(tr "A-Za-z" "a-zA-Z")

;; ひらがなとかたかなを交換します
(tr "ぁ-んァ-ン" "ァ-ンぁ-ん")

;; 7ビットのノングラフィカルな文字を`?'に変換します
(tr "\x00-\x19\x7f" "?*")

to-listfrom-listより短い場合、動作はキーワード引数deleteに 依存します。もし真の値がdeleteに与えられれば、from-listに現われて to-listに対応するものがない文字は入力から取り除かれます。そうでなければ そのような文字はそのまま出力されます。

真の値がcomplementに与えられた場合、from-listの文字の 補集合がfrom-listとして使われます。この文字集合は極めて大きくなる 可能性があることに注意してください。従って、一般にこのオプションは to-listに‘*’を使ってそれらの文字を一文字にマッピングするか、 deleteオプションと併用するかしないとあまり意味がありません。

真の値がsqueezeに与えられた場合、同じ文字への置換が2つ以上並ぶ場合に 2つめ以降の文字が削除されます。to-listが空の場合は、from-list に含まれる文字で同一文字が並んだ場合に2つめ以降の文字が削除されます。

内部的に、trはキャラクタのマッピングのためにテーブルを使用します。 但し、Gaucheでは極めて大きな文字セットを扱うため、テーブルはキャラクタコードの 小さい文字のみに対して使われます(デフォルトではコード255以下の文字)。 もし、より大きな文字を頻繁に変換することが分かっていて、メモリを余分に使っても 速度を上げたい場合は、このテーブルの大きさをtable-sizeキーワード引数で 指定することができます。例えばEUC-JPコードで大量の平仮名と片仮名を変換する場合は、 table-sizeを42483以上にすると、全ての変換がテーブルルックアップで 行われます。

trが変換テーブルを計算するのにいくらかオーバーヘッドがあることに 注意して下さい。内側のループでtrを繰り返し呼ぶような場合は 下に示すbuild-transliteratorを使った方が良いでしょう。

Function: string-tr string from-list to-list :key :complement :delete :squeeze :table-size

入力をstringから取って変換結果を文字列で返す以外はtrと同じです。

Function: build-transliterator from-list to-list :key :complement :delete :squeeze :table-size :input :output

実際の変換動作をする手続きを作成して返します。内部データのセットアップを済ませるため、 同じ文字変換セットに対してtrを繰り返し呼ぶような場合は、この手続きを用いることで 初期化のオーバヘッドを軽減することができます。

註記:inputoutputキーワード引数が省略された場合、 作成される変換手続きは、それが使用された時点でのカレント入出力ポート を参照します。

 
(with-input-from-file "huge-file.txt"
  (lambda ()
    (let loop ((line (read-line)))
      (unless (eof-object? line) (tr "A-Za-z" "a-zA-Z")))))

;; 以下の方が効率良く動作します...

(with-input-from-file "huge-file.txt"
  (lambda ()
    (let ((ptr (build-transliterator "A-Za-z" "a-zA-Z")))
      (let loop ((line (read-line)))
        (unless (eof-object? line) (ptr))))))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.57 text.tree - 怠惰なテキスト構築

Module: text.tree

テキストを生成する場合によく使われるシンプルな手続きを定義します。

プログラムでテキストを生成する場合、テキストの断片をつなぎ合わせて行く操作が 非常に多く現われます。しかし単純にstring-appendを繰り返し 呼んでしまうと、中間結果の文字列を保持するためだけにメモリアロケーションが多発しますし、 途中でエラーが発生した場合にその中間結果は結局捨てられてしまうかもしれません (例えば、CGIスクリプト中でHTMLドキュメントを構築してゆくような場合を考えてみて下さい)。

よく知られた効率の良い方法は、テキストの断片をつなぎ合わせるのを本当に必要に なるまで遅らせることです。Schemeではそれは非常に簡単です。単に断片をコンス していって木構造を作り、最後にそれをトラバースしてテキストを作成するのです。 場合によっては、トラバースしながらテキストを出力すれば事足りてしまうので、 中間結果のバッファを持つ必要さえありません。 (Hans Boehmのガベージコレクションライブラリに附属している “cord” ライブラリ はこのテクニックを実装したもので、エディタ等のアプリケーションで非常に効率良く 動作することが知られています)。

木構造をトラバースする手続きなどほんの2〜3行で書けてしまいますが、 「一度、そして一度だけ」の精神の下に、このモジュールを提供します。 インタフェースが統一されてたほうが便利ですしね。

Generic Function: write-tree tree :optional out

treeをテキストの木構造とみなして、出力ポートoutに出力します。 outが省略された場合は現在の出力ポートが使われます。

下に示す2つのメソッドが定義されています。より複雑な動作をさせたい場合は、 単純なリストの替わりにノードとなるクラスを定義して、それにメソッドを定義するようにすれば 動作をカスタマイズできます。

Method: write-tree ((tree <list>) out)
Method: write-tree ((tree <top>) out)

write-treeの既定の動作です。treeがリストなら、その要素それぞれに ついてwrite-treeを呼び出します。それ以外のオブジェクトに関しては displayを呼んで出力します。

Function: tree->string tree

出力文字列ポートを作成してwrite-treeを呼び、生成された文字列を返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.58 util.combinations - 組み合わせ

Module: util.combinations

このモジュールは、いくつかの便利なコンビネーションや順列とそれに関連する 操作の手続きを実装しています。

このモジュールのほとんどの手続きは2つのバージョンを持っています。 1つはアスタリスクの付かない手続き(例えば、permutations)で、 与えられたセットにある全ての要素を区別して扱います。もう1つは、 アスタリスクの付く手続き(例えば、permutations*)で、重複を 考慮します。アスタリスクの付く手続きは、オプショナルなeq引数を取り ます。それは等値性のテストに使われ、デフォルトはeqv?です。

Function: permutations set
Function: permutations* set :optional eq

リストsetの全ての順列のリストを返します。

 
(permutations '(a b c))
  ⇒ ((a b c) (a c b) (b a c) (b c a) (c a b) (c b a))

(permutations '(a a b))
  ⇒ ((a a b) (a b a) (a a b) (a b a) (b a a) (b a a))

(permutations* '(a a b))
  ⇒ ((a a b) (a b a) (b a a))

setがある程度の要素を持っている場合、可能性のある順列の数は 爆発的に大きくなります。注意して使って下さい。 一度にそれぞれの順列を処理したい場合は、下記のpermutations-for-eachの 使用を考慮して下さい。

Function: permutations-for-each proc set
Function: permutations*-for-each proc set :optional eq

リストsetのそれぞれの順列に対して、procを呼び出します。 戻り値は未定義値です。

Function: combinations set n
Function: combinations* set n :optional eq

リストsetn個の要素の可能性のある全ての順列のリストを 返します。

 
(combinations '(a b c) 2)
  ⇒ ((a b) (a c) (b c))

(combinations '(a a b) 2)
  ⇒ ((a a) (a b) (a b))

(combinations* '(a a b) 2)
  ⇒ ((a a) (a b))

setが大きいときは、組み合わせの爆発について注意して下さい。

Function: combinations-for-each proc set n
Function: combinations*-for-each proc set n :optional eq

setn個の要素のそれぞれの組み合わせについてprocを 呼び出します。戻り値は未定義値です。

Function: power-set set
Function: power-set* set :optional eq

リストsetの累乗集合(全てのサブセット)を返します。

 
(power-set '(a b c))
  ⇒ (() (a) (b) (c) (a b) (a c) (b c) (a b c))

(power-set* '(a a b)
  ⇒ (() (a) (b) (a a) (a b) (a a b))
Function: power-set-for-each proc set
Function: power-set*-for-each proc set :optional eq

setのそれぞれのサブセットについてprocを呼び出す。

Function: power-set-binary set

power-setのように、setの累乗集合を返しますが、順番が異なります。 power-set-binaryはサブセットの空間を深さ優先でトラバースしますが、 power-setは横型探索を行います。

 
(power-set-binary '(a b c))
  ⇒ (() (c) (b) (b c) (a) (a c) (a b) (a b c))
Function: cartesian-product list-of-sets
Function: cartesian-product-right list-of-sets

list-of-setsにあるセットのデカルト積を返します。 cartesian-productは左固定順で結果を構築しますが (一番右の要素がまず異なる)、 cartesian-product-rightは右固定順で行います (一番左の要素がまず異なる)。

 
(cartesian-product '((a b c) (0 1)))
  ⇒ ((a 0) (a 1) (b 0) (b 1) (c 0) (c 1))

(cartesian-product-right '((a b c) (0 1)))
  ⇒ ((a 0) (b 0) (c 0) (a 1) (b 1) (c 1))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.59 util.digest - メッセージダイジェストフレームワーク

Module: util.digest

このモジュールは、MD5 (rfc.md5 - MD5メッセージダイジェスト参照)や SHA (rfc.sha - SHAメッセージダイジェスト参照)などの、メッセージ ダイジェストアルゴリズムのためのベースクラスと一般的なインターフェースを 提供します。

Class: <message-digest-algorithm-meta>

メッセージダイジェストアルゴリズムの実装のメタクラスです。

Instance Variable of <message-digest-algorithm-meta>: hmac-block-size

各アルゴリズムに固有のブロックサイズをバイト数で指定します。 (これはアルゴリズムを実装する各クラスオブジェクトのスロットで、 それらのクラスのインスタンスのスロットではありません。通常、新たなダイジェストアルゴリズム クラスを実装する人のみが気にすれば良いスロットです。具体例は ソースツリーの‘ext/digest/sha.scm’を見てください。)

Class: <message-digest-algorithm>

メッセージダイジェストアルゴリズムの実装のベースクラスです。

メッセージダイジェストアルゴリズムの具体サブクラスは、以下のメソッドを 実装しなければなりません。

Generic function: digest-update! algorithm data

メッセージダイジェストアルゴリズムのインスタンスを取り、 それを(不完全な可能性のある)文字列で表されるデータdataで 更新します。

Generic function: digest-final! algorithm

メッセージダイジェストアルゴリズムのインスタンスを終了させ、 そのダイジェストの結果を不完全文字列で返します。

Generic function: digest class

ダイジェストルーチンのラッパです。メッセージダイジェストアルゴリズム classを与え、現在の入力ポートから入力データをEOFまで読み込み、 そのダイジェストの結果を不完全文字列で返します。

Generic function: digest-string class string

ダイジェストルーチンのラッパです。メッセージダイジェストアルゴリズム classを与え、入力データをstringから読み込み、 そのダイジェストの結果を不完全文字列で返します。

Function: digest-hexify digest-result

ユーティリティ手続きです。ダイジェストの結果、digest-resultを 与えると、それを16進文字列に変換します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.60 util.dominator - 支配木

Module: util.dominator

支配木(dominator tree)は制御フローグラフで補助的に使われる構造です。 コンパイラのフロー解析によく使われることが多いですが、一般の有向グラフでも有用です。

Function: calculate-dominators start upstreams downstreams node-comparator

4つの引数で有向グラフが表現されます。循環があっても構いません。 以下ではグラフのノードがNodeという型を持つとして説明します。 実際にはノードの型は何であっても構いません。 アルゴリズムはノードの実際の型とは無関係に動作します。

start :: Node

フローの起点となるノード。startノードとかenterノードとも呼ばれます。

upstreams :: Node -> (Node …)

ノードを取り、その直接の上流ノードのリストを返す関数。

downstreams :: Node -> (Node …)

ノードを取り、その直接の下流ノードのリストを返す関数。

node-comparator

二つのノードが等しいかどうかを決定する比較器。大小比較関数を持っている必要はありませんが、 アルゴリズム内部でハッシュテーブルを使うので、ハッシュ関数は持っている必要があります。 比較器について詳しくはsrfi-114 - 比較器を参照してください。

返り値は(node1 node2)を要素に持つリストです。ここで、 node2node1の直接支配ノード(immediate dominator)です。

与えられたグラフの中にstartから到達不可能なノードがあった場合、 それは単に無視され、結果に含まれません。

(ちょっとした説明: startからノードXに行きたいとします。 複数の経路がありえますが、どの経路を取っても必ずノードYを通らないと Xに行けない場合、YをXの支配ノード(dominator)と呼びます。 支配ノードは複数ありえますが、その中で一つだけ、他のXの支配ノード全てが そのノードの支配ノードでもあるようなノードがあります(言い換えれば、 Xに一番「近い」支配ノードです)。それをXの直接支配ノードと呼びます。)

例を見てみましょう。こんな有向グラフがあるとします。

 
          A (start)
          |
          v
          B <-------+
          |         |
    ------+-----    |
    |          |    |
    v          v    |
    C -------> D ---+
    |          |
    v          v
    E <------- F

このグラフをリストのリストで表現してみましょう。 ここで、内側のリスト(x y z ...)は、xからはyzに 直接行ける、ということを表します。

 
(define *graph* '((A B)
                  (B C D)
                  (C D E)
                  (D F B)
                  (F E)))

すると、各ノードの直接支配ノードが次の通り計算できます。

 
(calculate-dominators 'A
  (^n (filter-map (^g (and (memq n (cdr g)) (car g))) *graph*))
  (^n (assoc-ref *graph* n '()))
  eq-comparator)
  ⇒ ((E B) (F D) (D B) (C B) (B A))

つまり、Eの直接支配ノードはBであり、 FのはDである、という具合です。

この結果自体を木と解釈することができます。これを支配木(dominator tree)と呼びます。

 
              F
              |
              v
        E     D     C
        |     |     |
        |     v     |
        +---> B <---+
              |
              v
              A

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.61 util.isomorph - 同型判定

Module: util.isomorph

二つの構造が同型かどうかを判定する手続きを提供するモジュールです。

Function: isomorphic? obj1 obj2 :optional context

obj1obj2が同型であれば#tを返します。

省略可能な引数contextは、下で説明するobject-isomorphic? の中からisomorphic?を再帰的に呼び出す場合に使います。

 
(isomorphic? '(a b) '(a b)) ⇒ #t

(define x (cons 0 0))
(define y (cons 0 0))
(isomorphic? (cons x x)
             (cons x y))
 ⇒ #f
(isomorphic? (cons x x)
             (cons y y))
 ⇒ #t
Generic Function: object-isomorphic? obj1 obj2 context

このメソッドで、二つのオブジェクトの同型判定処理をカスタマイズできます。 基本的には、オブジェクトのトラバースしたいスロットに対してisomorphic?を順に適用し、 全てが成功すれば#tを、一つでも失敗すれば#fを返すようにします。 contextはトラバースのコンテクストを保持しているオブジェクトです。そのまま isomorphic?に渡して下さい。

デフォルトメソッドは、obj1obj2equal?の意味で等しければ #tを返します。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.62 util.lcs - 最長共通サブシーケンス

Module: util.lcs

このモジュールは、与えられた2つのシーケンスの最長共通サブシーケンスを見つける アルゴリズムを実装しています。アルゴリズムは、Eugene Myersの O(ND)アルゴリズムに基づいています(Myers86)。

このアルゴリズムを使うアプリケーションの1つは、2つのテキストストリームの 相違点を計算するtext.diff - テキストストリームの相違点を計算するです。

Function: lcs seq-a seq-b :optional eq-fn

2つのリスト、seq-aseq-bの最長共通シーケンスを計算して 返します。オプションのeq-fnでは、比較を行う述語を指定します。 省略されると、equal?が使われます。

 
(lcs '(x a b y) '(p a q b))
 ⇒ (a b)
Function: lcs-with-positions seq-a seq-b :optional eq-fn

lcsの詳細バージョンです。引数は同じです。

以下の構造のリストを返します。

 
(length ((elt a-pos b-pos) …))

lengthは、見つかったLCS(最長共通サブシーケンス)の長さを表す整数です。 それに続くのは、LCSの要素のリストで、その要素を構成するそれぞれのサブリスト、 seq-aの中での要素の位置(整数)、seq-bの中での要素の位置(整数) となります。

 
(lcs-with-positions '(a) '(a))
 ⇒ (1 ((a 0 0)))

(lcs-with-positions '(x a b y) '(p q a b))
 ⇒ (2 ((a 1 2) (b 2 3)))

(lcs-with-positions '(x a b y) '(p a q b))
 ⇒ (2 ((a 1 1) (b 2 3)))

(lcs-with-positions '(x y) '(p q))
 ⇒ (0 ())
Function: lcs-fold a-proc b-proc both-proc seed a b :optional eq-fn

2つのリストabから引き出された“編集リスト”に対する 基本的なイテレータです。

a-procb-procboth-procは全て2引数を取る手続きです。 2番目の引数は、計算の中間の値です。最初の値は、a-procではaにしかない要素、 b-procではbにしかない要素、both-procではabの両方に ある要素となります。それぞれの手続きが返す値は、次に呼び出される手続きのうちの1つの 状態を表す値として使われます。seedは、状態を表す値の初期値として使われます。 lcs-foldが返す値は、最後の状態を表す値です。

これらの3つの手続きは、以下の順番で呼ばれます。ここでは、シーケンスaa’ca”bb’cb”となっているとすると、 ここではa’b’a”b”はサブシーケンスで、 cabのLCSの先頭になります。そして、a-procはまず a’のそれぞれの要素に対して呼ばれ、b-procb’のそれぞれの 要素に対して呼ばれ、both-proccに対して呼ばれます。 その後、このプロセスはa”b”を使って繰り返されます。

Function: lcs-edit-list a b :optional eq-fn

2つのリストabから“編集リスト”を計算します。それは、 abに変更するためのコマンド(追加と削除)の最小セットです。 この手続きは、上のlcs-foldの上に構築されています。

 
(+|- position element)

例を挙げます。abがそれぞれ以下のようなリストだとします。

 
a ≡ ("A" "B" "C" "E" "H" "J" "L" "M" "N" "P")
b ≡ ("B" "C" "D" "E" "F" "J" "K" "L" "M" "R" "S" "T")

すると、(lcs-edit-list a b equal?)は以下のリストを返します。

 
(((- 0 "A"))
 ((+ 2 "D"))
 ((- 4 "H") (+ 4 "F"))
 ((+ 6 "K"))
 ((- 8 "N") (- 9 "P") (+ 9 "R") (+ 10 "S") (+ 11 "T"))
)

結果は5つの片からなります。最初のものは1つのディレクティブ、(- 0 ``A'')から なり、これはリストaの位置0にある要素``A''が削除されることを意味します。 2番目のものはまた1つのディレクティブ、(+ 2 ``D'')からなり、これは リストbの位置2にある要素``D''が追加されることを意味します。 3番目のものは、リストaの位置4にある``H''は削除され、リストbの 位置4にある``F''が追加される、などとなります。

もしあなたがPerlのAlgorithm::Diffモジュールを良く知っていれば、 そのdiff手続きが返すものと同じ構造だということが分かるでしょう。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.63 util.levenshtein - Levenshtein編集距離

Module: util.levenshtein

このモジュールは二つのシーケンスの間の編集距離を計算する手続きを提供します。 編集距離とは、ひとつのシーケンスからもうひとつのシーケンスへと変更する 編集操作の数です。3つのアルゴリズムが実装されています。

Levenshtein distance

1要素の削除、1要素の追加、1要素の置き換えそれぞれを1操作と数えます。

Damerau-Levenshtein distance

削除、追加、置き換えに加え、隣合った要素の入れ替えも1操作と数えます。

Restricted edit distance

Optimal string alignmentとも呼ばれます。Damerau-Levenshteinと似ていますが、 隣合った要素の入れ替えを行った場合、それらの要素は以降の編集を受けません。

これらのアルゴリズムは文字列に対して使われることが多いですが、 このモジュールの手続きはシーケンスになら何でも使えます (gauche.sequence - シーケンスフレームワーク参照)。

Function: l-distance seq-A seq-B :key elt= cutoff
Function: l-distances seq-A seq-Bs :key elt= cutoff
Function: re-distance seq-A seq-B :key elt= cutoff
Function: re-distances seq-A seq-Bs :key elt= cutoff
Function: dl-distance seq-A seq-B :key elt= cutoff
Function: dl-distances seq-A seq-Bs :key elt= cutoff

それぞれ、与えられたシーケンス間のLevenshtein距離(l-*)、 Restricted edit距離(re-*)およびDamerau-Levenshtein距離(dl-*)を 計算します。アルゴリズム毎にふたつのAPIが提供されます。 単数形の*-distanceは、二つのシーケンスseq-Aseq-Bを取り、 両者の間の距離を計算します。 複数形の*-distancesは、一つのシーケンスseq-Aと シーケンスのリストseq-Bsをとり、seq-Bsの各シーケンスと seq-Aとの距離を計算します。

あるシーケンスと、多数のシーケンスとの間の距離を計算したいなら、 単数形のAPIを繰り返し呼ぶより複数形のAPIを使う方が効率的です。 複数形のAPIでは内部で使うデータ構造を再利用するので、 重複するアロケーションと初期化の時間を節約できます。

シーケンスは、<sequence>プロトコルを満足するオブジェクトであれば 何でも構いません (gauche.sequence - シーケンスフレームワーク参照)。

キーワード引数elt=はシーケンスの要素同士を比較するのに使われます。 デフォルトはeqv?です。

キーワード引数cutoffは非負の正確な整数でなければなりません。与えられた場合、 二つのシーケンスの距離を比較していて、それがcutoff以上になることがわかったら、 それ以上の計算を打ち切り#fを結果とします。 これは、特にたくさんのシーケンスから、ごく限られた限度以下の距離を持つものを探す 場合に便利です。

現在の実装ではだいたい、Levenshteinが最も速く、Damerau-Levenshteinが最も遅く、 Restricted editがその中間です。置換を1操作と考える必要がなければ、 Levenshteinを使うのが良いでしょう。catからactは Levenshtein距離では2ですが他の距離では1です。 置換を1操作とする必要があれば、re-dl-を 使うことになります。re-の落とし穴は、それが三角不等式を満たさないことです。 つまり、3つのシーケンスX, Y, Zがある時、 (Damerau-)Levenshtein距離Lは常に L(X;Z) <= L(X;Y) + L(Y;Z) を満たしますが、Restricted edit距離には その保証がありません。

 
(l-distance "cat" "act")  ⇒ 2
(l-distances "cat" '("Cathy" "scathe" "stack")
  :elt= char-ci=?)
  ⇒ (2 3 4)

(re-distance "cat" "act") ⇒ 1

(re-distances "pepper"
  '("peter" "piper" "picked" "peck" "pickled" "peppers")
  :cutoff 4)
  ⇒ (2 2 4 4 #f 1)

(dl-distance '(a b c d e) '(c d a b e)) ⇒ 4

註: 単数形のAPIの第2引数にうっかりシーケンスのリストを渡してしまった場合でも、 直ちにエラーになるとは限らないことに注意。リスト自体もシーケンスだからです。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.64 util.match - パターンマッチング

Module: util.match

このモジュールは Andrew Wright のパターンマッチングマクロライブラリを ポートしたものです。このライブラリは Scheme 界では広くつかわれており、 Chez Scheme、PLT Scheme、Scheme48、Chicken および SLIB を含む、様々な Scheme の実装にポートされています。この機能は Common Lisp の destructuring-bind に似ていますがより強力です。

この版では、オリジナルの Wright’s macro のマクロとの互換性が保たれて います。ただし、例外がふたつあって、それは、(1) box はサポートされて いません。Gauche にそれがないからです。(2) 構造のマッチングは Gauche の オブジェクトシステムに統合されています。

まず API のリストを示し、それからパターンの完全な構文のテーブルを示し、 そして例を示します。

パターンマッチング API

Macro: match expr clause …

それぞれの clause は以下のうちどちらかです。

 
(pat body …)
(pat (=> identifier) body …)

まず、expr を各節の pat に照合します。パターンの詳しい 構文については後述します。

pat にマッチする部分が見つかれば、pat 中の パターン変数 は、expr 中の対応する要素に束縛され、その後、body … が評価されます。matchbody …の最後の式の値を返します。

節が 2つ目の形式である場合、identifierclause の失敗継続 に束縛されます。これは引数をもたない手続きで、呼ばれると、あたかも、 pat の照合に失敗したかの如くマッチャーに戻り、match が 残りの節について試行を続けます。それゆえ、body … 内部で 追加のテストを実行することが可能で、もし、満足いくものでなければ、 (identifier) を呼ぶことで、照合結果を拒絶することができます。 より詳しくは、後述の例を見てください。

どの pat もマッチしなければ、match はエラーを報告します。

Macro: match-lambda clause …

ひとつの引数をとり、それに対して clause … を用いて、 match を実行する関数を生成します。機能としては以下の式と同等です。

 
(lambda (expr) (match expr clause …))

例:

 
(map (match-lambda
       ((item price-per-lb (quantity 'lbs))
        (cons item (* price-per-lb quantity)))
       ((item price-per-lb (quantity 'kg))
        (cons item (* price-per-lb quantity 2.204))))
     '((apple      1.23 (1.1 lbs))
       (orange     0.68 (1.4 lbs))
       (cantaloupe 0.53 (2.1 kg))))
 ⇒ ((apple . 1.353) (orange . 0.952)
            (cantaloupe . 2.4530520000000005))
Macro: match-lambda* clause …

match-lambda と同じですが、match をすべての引数のリスト に対して実行します。機能としては以下の式と同等です。

 
(lambda expr (match expr clause …))
Macro: match-let ((pat expr) …) body-expr …
Macro: match-let name ((pat expr) …) body-expr …
Macro: match-let* ((pat expr) …) body-expr …
Macro: match-letrec ((pat expr) …) body-expr …

束縛部分が単なる変数ではなく、パターンを許す、一般化された letlet* および letrec です。 各 expr が評価され、その後、pat と照合され、束縛された パターン変数が body-expr … から見えるようになります。

 
(match-let (
             (((ca . cd) ...)   '((a . 0) (b . 1) (c . 2)))
           )
  (list ca cd))
 ⇒ ((a b c) (0 1 2))

括弧はうんざりという向きには、以下の match-let1 をおためしあれ。

Macro: match-let1 pat expr body-expr …

これは Gauche での拡張で、オリジナルの Wright のコードにはありません。 これは以下のコードと同等です。

 
(match-let ((pat expr)) body-expr …)

構文としては match-let1 は Common Lisp の destructuring-bind に非常によく似ています。

 
(match-let1 ('let ((var val) ...) body ...)
            '(let ((a b) (c d)) foo bar baz)
  (list var val body))
 ⇒ ((a c) (b d) (foo bar baz))
Macro: match-define pat expr

トップレベルの define と同様ですが、変数の代りにパターンが許されます。

 
(match-define (x . xs) (list 1 2 3))

x  ⇒ 1
xs ⇒ (2 3)

パターンの構文

ここにあるのはパターンの構文の要約です。説明の後にあるアスタリスク (*) はオリジナルの Wright のコードにはない、Gauche の拡張で あることを意味します。

 
pat : patvar                       ;; 任意のオブジェクトにマッチし、patvarを束縛
    | _                            ;; 任意のオブジェクト
    | ()                           ;; 空リスト
    | #t                           ;; #t
    | #f                           ;; #f
    | string                       ;; 文字列
    | number                       ;; 数
    | character                    ;; 文字
    | keyword                      ;; キーワード (*)
    | 'sexp                        ;; S式
    | 'symbol                      ;; シンボル(S式の特殊ケース)
    | (pat1 ... patN)              ;; n 要素のリスト
    | (pat1 ... patN . patN+1)     ;; n 以上の要素を含むリスト
    | (pat1 ... patN patN+1 ooo)   ;; n 以上の要素を含むリスト、残りの各要素は
                                   ;; patN+1 にマッチしなければならない
    | #(pat1 ... patN)             ;; n 要素のベクタ
    | #(pat1 ... patN patN+1 ooo)  ;; n 以上の要素を含むベクタ、残りの各要素は
                                   ;; patN+1 にマッチしなければならない
    | ($ class pat1 ... patN)      ;; オブジェクト (patK はスロット順でマッチ)
    | (struct class pat1 ... patN) ;; 同上 (*)
    | (@ class (slot1 pat1) ...)  ;; オブジェクト (スロット名を使う) (*)
    | (object class (slot1 pat1) ...) ;; 同上 (*)
    | (= proc pat)                 ;; procを適用し、結果を pat にマッチさせる
    | (and pat ...)                ;; すべての pat にマッチするか
    | (or pat ...)                 ;; マッチする pat があるか
    | (not pat ...)                ;; どの pat もマッチしないか
    | (? predicate pat ...)        ;; predicate が真、かつ、全 pat がマッチ
    | (set! patvar)                ;; 任意のオブジェクトにマッチし、セッタを束縛
    | (get! patvar)                ;; 任意のオブジェクトにマッチし、ゲッタを束縛
    | `qp                          ;; 擬似パターン

patvar : a symbol except _, quote, $, struct, @, object, =, and, or,
         not, ?, set!, get!, quasiquote, ..., ___, ..k, __k.

ooo : ...                          ;; ゼロまたはそれ以上
    | ___                          ;; ゼロまたはそれ以上
    | ..k                          ;; k またはそれ以上。kは整数。
                                   ;;   例: ..1, ..2 ...
    | __k                          ;; k またはそれ以上。kは整数。
                                   ;;   例: __1, __2 ...

パターン例

単純な構造の分解

 
(match '(0 (1 2) (3 4 5))
  [(a (b c) (d e f))
   (list a b c d e f)])
 ⇒ (0 1 2 3 4 5)

述語パターンの使用

 
(match 123
  [(? string? x) (list 'string x)]
  [(? number? x) (list 'number x)])
 ⇒ (number 123)

let から変数と式を取り出す 反復および述語パターンの利用

 
(define let-analyzer
  (match-lambda
    [('let (? symbol?)
           ((var expr) ...)
       body ...)
     (format "named let, vars=~s exprs=~s" var expr)]
    [('let ((var expr) ...)
       body ...)
     (format "normal let, vars=~s exprs=~s" var expr)]
    [_
     (format "malformed let")]))

(let-analyzer '(let ((a b) (c d)) e f g))
 ⇒ "normal let, vars=(a c) exprs=(b d)"

(let-analyzer '(let foo ((x (f a b)) (y (f c d))) e f g))
 ⇒ "named let, vars=(x y) exprs=((f a b) (f c d))"

(let-analyzer '(let (a) b c d))
 ⇒ "malformed let"

= 関数適用。パターン変数 m は正規表現の適用結果にマッチする

 
(match "gauche-ref.texi"
  ((? string? (= #/(.*)\.([^.]+)$/ m))
   (format "base=~a suffix=~a" (m 1) (m 2))))
 ⇒ "base=gauche-ref suffix=texi"

quasipatternの例です。最初の式では、パターンのうちvalue以外の 部分がクオートされたことになり、従ってシンボルthe, answer, isはパターン変数ではなくリテラルシンボルとなります。 2番目の式がそのことを示しています。入力にあるシンボルwasは パターンのisとマッチしません。もしクオートを行わないと、 全てのシンボルはパターン変数となるので、3番目の例に示すように 任意の4つの要素を持つリストとマッチしてしまいます。

 
(match '(the answer is 42)
  [`(the answer is ,value) value]
  [else #f])
 ⇒ 42

(match '(the answer was 42)
  [`(the answer is ,value) value]
  [else #f])
 ⇒ #f

(match '(a b c d)
  [(the answer is value) value]
  [else #f])
 ⇒ d

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.65 util.record - SLIB-互換のレコード型

Module: util.record

このモジュールは、Guile と SLIB とに互換性のあるレコード型 API を提供します。 これは、Gauche のオブジェクトシステム上に構築されています。

便利なマクロ define-record-type を提供する gauche.record - レコード型 も 参照してください。

Function: make-record-type type-name field-names

あらしいレコード型を表わす新しいクラスを返します。 (これは SLIB では レコード型記述子とよばれているものです。) Gauche では、この新しいクラスは <record> のサブクラスです (gauche.record - レコード型 参照)。

type-name はデバッグの目的で使われる文字列です。 これは、シンボルに変換され、この新しいクラスの名前として設定されます。 field-names はフィールド名のシンボルのリストです。 各々のフィールドはこの新しいクラスのスロットとして実装されます。

以下の手続きでは、rtdmake-record-type によって 生成されたクラスです。

Function: record-constructor rtd :optional field-names

与えられた rtd レコード型のインスタンスを構築する手続きを 返します。返された手続きは field-names とちょうど同じ数の 引数をとります。field-names のデフォルトは '() です。 それぞれの引数は field-names の対応するフィールドの初期値を 設定します。

Function: record-predicate rtd

一つの引数をとり、与えられた引数が rtd と同じ型であり、その場合に 限り #t を返す手続きを返します。

Function: record-accessor rtd field-name

rtd 型の field-name によって名付けられたフィールドへの アクセサ手続きを返します。このアクセサ手続きは、rtd のインスタンスを 一つとり、そのフィールドの値を返します。

Function: record-modifier rtd field-name

rtd 型の field-name によって名付けられたフィールドへの モディファイア手続きを返します。このモディファイア手続きは、 rtd のインスタンスと値のふたつの引数をとり、その引数を 指定されたフィールドに設定します。

 
(define rtd (make-record-type "my-record" '(a b c)))

rtd ⇒ #<class my-record>

(define make-my-record (record-constructor rtd '(a b c)))

(define obj (make-my-record 1 2 3))

obj ⇒ #<my-record 0x819d9b0>

((record-predicate? rtd) obj)  ⇒ #t

((record-accessor rtd 'a) obj) ⇒ 1
((record-accessor rtd 'b) obj) ⇒ 2
((record-accessor rtd 'c) obj) ⇒ 3

((record-modifier rtd 'a) obj -1)

((record-accessor rtd 'a) obj) ⇒ -1

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.66 util.relation - リレーションフレームワーク

Module: util.relation

リレーションに対する共通の操作を提供します。

値の集合を S1, S2, ..., Sn とするとリレーション R はタプルの集合で、 タプルの最初の要素は S1 からの値で、2番目は S2 から、で n 番目は Sn からとなっている。いいかえれば、R は S1, ..., Sn の直積です。 (この定義とリレーションという用語は 1970年のCoddのペーパー "A Relational Model of Data for Large Shared Data Banks", in CACM 13(6) pp.377–387.によるものです。)

この定義はいろいろなデータベースセットにあてはまります。各インスタンス をタプルとみなし、各スロット値を実際の値と見ると、Gaucheのオブジェクト システムの集合はリレーションです。リストのリストもリレーションです。 CSVテーブルからの読み出しストリームはリレーションを生成します。このよ うことから、表現に依存しないリレーション上のジェネリックな操作を実装す るモジュールを用意することは有意義です。

操作という観点からいえば、以下の4つのメソッドがあれば、どのようなデー タ構造もあつかえます。タプルのコレクション(行)を取り出す relation-rows、それから、relation-column-namesrelation-accessorrelation-modifier、これらはメタ情報に アクセスする方法を提供しています。これ以外のすべてのリレーション操作は 上のプリミティブメソッドを使って組まれています。

リレーションの具体的な実装にはダックタイピングが適用できます。 すなわち、リレーションメソッドを使うのに特定のベースクラスを継承する必 要はありません。しかし、利便のためにこのモジュールには、ベースクラス <relation>が用意されています。これは、ミックスインクラスのよう な働きをします。具象クラスは典型的には<relation>および <collection>または<sequence>を継承することになるでしょう。 興味があれば、ソースツリーにある‘lib/util/relation.scm’に含まれる サンプル実装をチェックするとよいでしょう。

このモジュールは現時点では開発途上にあります。計画では共通メソッド上に 有用なリレーション操作関数群を構築することになっています。

基本となるクラスとメソッド

Class: <relation>

リレーションの抽象ベースクラス

Method: relation-column-names (r <relation>)

サブクラスではこのメソッドを必ず実装しなければなりません。 カラムの名前のシーケンスを返す必要があります。カラム名の型はリレーショ ンによります。equal?の意味で相互に異っているかぎり、カラム名に ついて特に制限はありません。

Method: relation-accessor (r <relation>)

サブクラスではこのメソッドは必ず実装しなければなりまん。2つの引数をと り、リレーションrの行とカラム名をとり、指定したカラムの値を返す 手続を返さなければなりません。

Method: relation-modifier (r <relation>)

サブクラスではこのメソッドは必ず実装しなければなりまん。 3つの引数、リレーションrの行、カラム名、値をとり、その値をセット する手続を返さなければなりません。

リレーションが読み込み専用の場合はこのメソッドは#fを返します。

Method: relation-rows (r <relation>)

サブクラスではこのメソッドは必ず実装しなければなりまん。 基盤となっている<collection>クラスまたはそのサブクラス(たとえば <sequence>)のインスタンスを返さなければなりません。

のこりのメソッドは上の4つのメソッドを使って組まれています。とはいえ、 <relation> のサブクラスでは以下のメソッドを性能を得るためにオー バーロードすることもできます。

Method: relation-column-name? (r <relation>) column

columnがリレーションrにおいて有効なカラム名である場合にの み真を返します。

Method: relation-column-getter (r <relation>) column
Method: relation-column-setter (r <relation>) column

リレーションrの行の指定したカラムにアクセスする手続を返します。 Relation-column-getterは、1つの引数、行をとる手続きを返さなけれ ばなりません。Relation-column-setterは2つの引数、行と新しくセッ トする値を取る手続きを返さなければなりません。

リレーションが読み込み専用の場合はrelation-column-setter#fを返します。

Method: relation-ref (r <relation>) row column :optional default

rowはリレーションrの行です。rowcolumnの値を 返します。columnが不正なカラム名である場合、defaultが与え られていればそれを返し、そうでなければ、エラーを示すシグナルがあがりま す。

Method: relation-set! (r <relation>) row column value

rowはリレーションrの行です。valuerowcolumnに設定します。リレーションが読み込み専用の場合には エラーを示すシグナルがあがります。

Method: relation-column-getters (r <relation>)
Method: relation-column-setters (r <relation>)

ゲッタとセッタの完全なリストを返します。通常はデフォルトのメソッドで十 分ですが、たとえばゲッタのリストをキャッシュしたいときもあるでしょう。

Method: relation-coercer (r <relation>)

行をシーケンスに変換する手続きを返します。行がすでにシーケンスで表現さ れているリレーションであれば、行をそのまま返す手続きです。

Method: relation-insertable? (r <relation>)

リレーションrに新しい行を挿入可能な場合にのみ真を返します。

Method: relation-insert! (r <relation>) row

リレーションrに行rowを挿入します。

Method: relation-deletable? (r <relation>)

リレーションrから行を削除可能である場合にのみ真を返します。

Method: relation-delete! (r <relation>) row

リレーションrから行rowを削除します。

Method: relation-fold (r <relation>) proc seed column …

procを各行のカラムcolumn … の値に適用します。 seedは状態の値として渡されます。すなわち、リレーションrの 各行ごとに以下のようにprocを呼びます。

 
(proc v_0 v_1v_i seed)

 where v_k = (relation-ref r row column_k)

呼び出しの結果があたらしいシードの値となり、最終の結果が relation-foldから返ります。

たとえば、リレーションが amount という名前のカラムをもつものと し、リレーション r 内にあるそのカラムをすべて足しあわせたいとす ると以下のように書けます。

 
(relation-fold r + 0 'amount)

具象クラス

Class: <simple-relation>
Class: <object-set-relation>

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.67 util.stream - ストリームライブラリ

Module: util.stream

このモジュールは遅延ストリームのライブラリを提供します。このモジュール には SRFI-40 で定義されている関数および構文が含まれています。

Function: stream? obj

[SRFI-40] objutil.streamの手続きによって作成されたストリームであ る場合にかぎり#tを返します。

Variable: stream-null

[SRFI-40] NULLストリームのシングルトンインスタンス。

Macro: stream-cons object stream

[SRFI-40] ストリームの基本構成子。objectstreamの先頭に追加し、新し いストリームを返します。

Function: stream-null? obj

[SRFI-40] objがNULLストリームの場合にのみ#tを返します。

Function: stream-pair? obj

[SRFI-40] objがNULLストリームではないストリームのときにのみ#tを返します。

Function: stream-car s

[SRFI-40] ストリームsの最初の要素を返します。

Function: stream-cdr s

[SRFI-40] ストリームsの最初の要素をのぞいたのこりの要素をストリームとして 返します。

Macro: stream-delay expr

[SRFI-40] exprの遅延形式であるストリームを返します。

原則として、ストリームを生成する関数はすべからく結果を stream-delayでラップすべきです。

Function: stream obj …

[SRFI-40] 要素がobj …であるような新しいストリームを返します。

Function: stream-unfoldn generator seed n

[SRFI-40] 互いに関連する n 本のストリームを生成します。それぞれの内容は generatorおよびseedを使って生成します。

generatorは現在のシード値とともに呼ばれ、n+1個の値 を返します。

 
(generator seed)
  => seed result_0 result_1 … result_n-1

最初の値は次のシード値になります。Result_kは以下の形式のどれかで なければなりません。

(val)

valk-番目のストリームの次のcar部になります。

#f

k-番目のストリームの新しい情報はありません。

()

k-番目のストリームの最後に到達しました。

以下の例では2つのストリームが作られます。最初のものは奇数の無限ストリー ムで、2つめのものは偶数の無限ストリームです。

 
gosh> (define-values (s0 s1)
        (stream-unfoldn (lambda (i)
                          (values (+ i 2)          ;; next seed
                                  (list i)         ;; for the first stream
                                  (list (+ i 1)))) ;; for the second stream
                        0 2))
#<undef>
gosh> (stream->list (stream-take s0 10))
(0 2 4 6 8 10 12 14 16 18)
gosh> (stream->list (stream-take s1 10))
(1 3 5 7 9 11 13 15 17 19)
Function: stream-map func . streams

[SRFI-40] streamsの各要素にfuncを適用した値を要素とする新しいストリー ムを返します。

Function: stream-for-each func . streams

[SRFI-40] funcstreamsの各要素に適用します。 streamsが終端にたっしたところで停止します。

Function: stream-filter pred? stream

[SRFI-40] pred?をパスする要素のみからなる新しいストリームを返す。

以下の手続きは Alejandro Forero Cuervo が Chicken Schemeのために書いた ライブラリから取ってきたものです。これらの手続きの名前は srfi-1 (srfi-1 - リストライブラリ)の命名規則にならったものです。

Function: stream-xcons a b

(stream-cons b a)のこと。利便性のためだけにある。

Function: stream-cons* elt … stream

streamの前にelt …を連結した新しいストリームを生成し ます。

Function: make-stream n :optional init

n個のinitを要素とする新しいストリームを生成します。 initが省略された場合#fが使われます。nを負の値にする と無限ストリームが生成されます。

Function: stream-tabulate n init-proc

n個の要素をもつ新しいストリームを生成します。k-番目の要素 は init-prock に適用して得られます。nを負の値にする と無限ストリームが生成されます。

Function: stream-iota count :optional start step

startからはじまり、stepずつ要素が増加する整数のストリーム を生成します。ストリームの長さはcountが正ならその値になります。 countが負なら無限ストリームになります。startのデフォルト値 は 0、stepのデフォルト値は 1 です。

Function: stream-format fmt arg …

string->stream(format fmt arg …)に適用した結果の ストリームを返します。

Function: stream->list stream
Function: stream->string stream

Converts a stream to a list or a string. All of stream’s elements are forced; if stream is infinite, these procedures won’t terminate. For stream->string, all stream must be characters, or an error is signaled.

Function: list->stream list

Converts a list to a stream of its elements.

Function: string->stream string :optional stream

Convers a string to a stream of characters. If an optional stream is given, it becomes the tail of the resulting stream.

(stream->list (string->stream "abc" (list->stream ’(1 2 3)))) ⇒ (#\a #\b #\c 1 2 3)

Function: port->stream :optional iport reader closer

Creates a stream, whose elements consist of the items read from the input port iport. The default iport is the current input port. The default reader is read-char.

The result stream terminates at the point where reader returns EOF (EOF itself is not included in the stream). If closer is given, it is called with iport as an argument just after reader reads EOF.

Function: iterator->stream iter

A generic procedure to turn an internal iterator iter into a stream of iterated results.

The iter argument is a procedure that takes two arguments, next and end, where next is a procedure that takes one argument and end is a thunk. Iter is supposed to iterate over some set and call next for each argument, then call end to indicate the end of the iteration. Here’s a contrived example:

 
(stream->list
 (iterator->stream
  (lambda (next end) (for-each next '(1 2 3 4 5)) (end))))
 ⇒ (1 2 3 4 5)

Internally iterator->stream uses the “inversion of iterator” technique, so that iter only iterates to the element that are needed by the stream. Thus iter can iterate over an infinite set. In the following example, iter is an infinite loop calling next with increasing integers, but only the first 10 elements are calculated because of stream-take:

 
(stream->list
 (stream-take
  (iterator->stream
   (lambda (next end)
     (let loop ((n 0)) (next n) (loop (+ n 1)))))
  10))
 ⇒ (0 1 2 3 4 5 6 7 8 9)
Function: stream-lines stream

Splits stream where its element equals to #\n, and returns a stream of splitted streams.

 
(stream->list
 (stream-map stream->string
             (stream-lines (string->stream "abc\ndef\nghi"))))
 ⇒ ("abc" "def" "ghi")
Function: stream= elt= stream …

Returns true iff each corresponding element of stream … are the same in terms of elt=. This procedure won’t terminate if any of streams is infinite.

Function: stream-prefix= stream prefix :optional elt=

Compares initial elements of stream against a list prefix by elt=. Only as many elements of stream as prefix has are checked.

Function: stream-caar s
Function: stream-cadr s

Function: stream-cdddar s
Function: stream-cddddr s

(stream-caar s) = (stream-car (stream-car s)) etc.

Function: stream-ref stream pos

Returns the pos-th element in the stream. Pos must be a nonnegative exact integer.

Function: stream-first s
Function: stream-second s
Function: stream-third s
Function: stream-fourth s
Function: stream-fifth s
Function: stream-sixth s
Function: stream-seventh s
Function: stream-eighth s
Function: stream-ninth s
Function: stream-tenth s

(stream-first s) = (stream-ref s 0) etc.

Function: stream-take stream count
Function: stream-take-safe stream count

Returns a new stream that consists of the first count elements of the given stream. If the given stream has less than count elements, the stream returned by stream-take would raise an error when the elements beyond the original stream is accessed. On the other hand, the stream returned by stream-take-safe will return a shortened stream when the given stream has less than count elements.

 
(stream->list (stream-take (stream-iota -1) 10))
 ⇒ (0 1 2 3 4 5 6 7 8 9)

(stream-take (stream 1 2) 5)
 ⇒ stream

(stream->list (stream-take (stream 1 2) 5))
 ⇒ error

(stream->list (stream-take-safe (stream 1 2) 5))
 ⇒ (1 2)
Function: stream-drop stream count
Function: stream-drop-safe stream count

Returns a new stream that consists of the elements in the given stream except the first count elements. If the given stream has less than count elements, stream-drop returns a stream that raises an error if its element is accessed, and stream-drop-safe returns an empty stream.

Function: stream-intersperse stream element

Returns a new stream in which element is inserted between elements of stream.

Function: stream-split stream pred
Function: stream-last stream
Function: stream-last-n stream count
Function: stream-butlast stream
Function: stream-butlast-n stream count
Function: stream-length stream
Function: stream-length>= stream n
Function: stream-append stream …
Function: stream-concatenate streams
Function: stream-reverse stream :optional tail-stream
Function: stream-count pred stream …
Function: stream-remove pred stream
Function: stream-partition pred stream
Function: stream-find pred stream
Function: stream-find-tail pred stream
Function: stream-take-while pred stream
Function: stream-drop-while pred stream
Function: stream-span pred stream
Function: stream-break pred stream
Function: stream-any pred stream …
Function: stream-every pred stream …
Function: stream-index pred stream …
Function: stream-member obj stream :optional elt=
Function: stream-memq obj stream
Function: stream-memv obj stream
Function: stream-delete obj stream :optional elt=
Function: stream-delete-duplicates stream :optional elt=
Function: stream-grep re stream
Function: write-stream stream :optional oport writer

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.68 util.toposort - トポロジカルソート

Module: util.toposort

トポロジカルソートのアルゴリズムを実装します。

Function: topological-sort graph :optional eqproc

Graphは有向非循環グラフ(DAG)を表現するリストです。 リストの各要素は次の形をしています。

 
(<node> <downstream> <downstream2> ...)

これで、ノード<node>から別のノード<downstream>等への接続が あることを表現します。<node>はどんなオブジェクトであっても構いませんが、 同一性の判定がeqprocで行えなければなりません。eqprocの既定値は eqv?です (等価参照)。 トポロジカルにソートされたノードのリストを返します。

グラフに循環が検出された場合はエラーとなります。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.69 util.unification - ユニフィケーション

Module: util.unification

ユニフィケーションのアルゴリズムを実装します。

基本APIは、抽象的な木に対して動作しますが、木が実際にどう実装されているかは 関知しません。 呼び出し側は、ユニファイする木といっしょに、比較器や操作手続きを渡します。

抽象的な木は、次の構造を持っていると考えます。

 
Tree : Variable | Value | Tuple
Tuple : { Tree ... }

ここで、{...}は単に木の並びを表します。

変数(Variable)は木に束縛されます。値(Value)はそれ自身とのみマッチします。

この木を扱うために、APIは以下の比較器と手続きを引数として取ります。

Variable comparator: var-cmpr

要素が変数であるかどうかを調べ、また二つの変数が同じかどうかを判定 するための比較器です。ハッシュ可能でなければなりません。 比較器については基本的な比較器を参照してください。

Value comparator: val-cmpr

要素が値であるかどうかを調べ、また二つの値が等しいかどうかを 判定するための比較器です。var-cmprval-cmprも 満たさない要素はタプルであるとみなされます。

Tuple folder: tuple-fold

(tuple-folde proc seed tuple1 [tuple2])の形で呼び出される手続きです。 この手続きは、タプル中の要素についてfoldのように動作します (リストをたどる手続き参照)。常にひとつかふたつのタプルを引数とします。

Tuple constructor: make-tuple

(make-tuple proto elements)の形式で呼び出される手続きです。 protoはタプルで、elementsは木のリストです。 protoと同じ型のタプルで、要素だけをelementsと置き換えた 新たなタプルを作成して返します。 unifyはこの手続きを必要としません。

Function: unify a b var-cmpr val-cmpr tuple-fold

二つの木、abを単一化し、置換辞書を返します。 置換辞書は、変数をその値にマップするものです。

引数var-cmprval-cmprtuple-foldに関しては 上のutil.unificationの説明を参照してください。

 
(dict->alist (unify '(a 3 (c b)) '(c b (2 e))
                    symbol-comparator
                    number-comparator
                    fold))
 ⇒ ((e . 3) (a . c) (b . 3) (c . 2))

この例にも見られるように、一つの変数の値が別の変数であったり、 別の変数を含む木であったりする場合があります。 元の木に置換を適用する場合は、辞書にある変数がすべて消去されるまで 再帰的に置換を行う必要があります。

二つの木が単一化できない場合は#fが返されます。

 
(unify '(a (a)) '(x x) symbol-comparator number-comparator fold)
 ⇒ #f
Function: unify-merge a b var-cmpr val-cmpr tuple-fold make-tuple

二つの木、abを単一化し、結果の置換を適用して変数を消去した 新たな木を返します。

引数var-cmprval-cmprtuple-foldmake-tupleに関しては 上のutil.unificationの説明を参照してください。

 
(unify-merge '(a 3 (c b)) '(c b (2 e))
             symbol-comparator
             number-comparator
             fold
             (^[_ elts] elts))
 ⇒ (2 3 (2 3))

二つの木が単一化できなければ、#fが返されます。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.70 www.cgi - CGIユーティリティ

Module: www.cgi

CGIスクリプトを書くのに便利ないくつかの基本的な手続きを提供します。

CGIスクリプトを手軽に書くにはこのモジュールの他に、 rfc.uri (rfc.uri - URIの解析と作成)、 text.html-lite (text.html-lite - シンプルなHTMLドキュメントの構築)、 text.tree (text.tree - 怠惰なテキスト構築) 等のモジュールを併せて 使うとよいでしょう。

注:現在有効な、CGIに関する「正式な」仕様というのはどうも無いようです。 http://w3c.org/CGI/あたりを参照して下さい。

メタ変数

Parameter: cgi-metavariables :optional metavariables

通常、httpdはcgiプログラムに様々な情報を環境変数経由で渡します。 www.cgi中の多くの手続きはその情報(メタ変数)を参照します。 しかし、cgiに関連するプログラムを開発中に環境変数にアクセスするのは 不便な場合もあります。 このパラメータを使うと、メタ変数をオーバライドすることができます。

Metavariablesは2要素のリストのリストです。 内側のリストは、最初の要素が変数名を、2つめの要素がその値を、それぞれ 文字列で与えます。

例えば次のコードはREQUEST_METHODQUERY_STRINGのメタ変数をmy-cgi-procedureの実行期間中に 上書きします。(parameterizeの詳細については gauche.parameter - パラメータを参照して下さい)。

 
(parameterize ((cgi-metavariables '(("REQUEST_METHOD" "GET")
                                    ("QUERY_STRING" "x=foo"))))
  (my-cgi-procedure))
Function: cgi-get-metavariable name

nameで指定されるCGIメタ変数の値を返します。 この関数はまずパラメータcgi-metavariablesを探し、 指定されたメタ変数が見つからなければsys-getenvを呼びます。

CGIスクリプトは、なるべくsys-getenvを直接呼ぶのではなく cgi-get-metavariableを使うのが良いでしょう。 スクリプトの再利用もしやすくなります。

パラメータの取得

Function: cgi-parse-parameters :key :query-string :merge-cookies :part-handlers

CGIプログラムに渡されたquery stringをパーズして、パラメータの連想リストにして 返します。文字列がキーワード引数query-stringに与えられればそれがパーズすべき query stringとなります。その引数が渡されなければこの手続きは メタ変数REQUEST_METHODを参照し、その値によって標準入力もしくは メタ変数QUERY_STRINGからquery stringが取られます。 そのようなメタ変数が定義されておらず、かつ現在の入力ポートが端末である場合、 インタラクティブにデバッグをしているものと考えて、 この手続きはプロンプトを出してユーザにパラメータの入力を促します。

REQUEST_METHODPOSTの場合、この手続きはenctypeとして application/x-www-from-urlencodedmultipart/form-dataの 両方を処理できます。後者は通常、ファイルアップロード機能を持つフォームに使われます。

POSTデータがmultipart/form-dataで送られて来た場合、 各パートの内容がパラメータの値となります。すなわち、アップロードされた ファイルはその内容がひとつの文字列として得られることになります。 元のファイル名のようなその他の情報は捨てられます。これが望ましい動作で ない場合は、part-handlers引数によって動作をカスタマイズすることができます。 詳しくは下の「ファイルアップロードの処理」で説明します。

キーワード引数merge-cookiesに真の値が与えられた場合は、 メタ変数HTTP_COOKIEからクッキーの値が読まれ、解析されて 結果に追加されます。

パラメータは複数の値を取り得るため、結果のパラメータに対応する値は常にリストになります。 パラメータに値が与えられていなければ、結果のパラメータに対する値には#tが置かれます。 次の例を参照して下さい。

 
(cgi-parse-parameters
  :query-string "foo=123&bar=%22%3f%3f%22&bar=zz&buzz")
 ⇒ (("foo" "123") ("bar "\"??\"" "zz") ("buzz" #t))
Function: cgi-get-parameter name params :key :default :list :convert

cgi-parse-parametersが返す、パーズされたQuery文字列paramsから、 名前nameを持つパラメータの値を簡単に取り出すための手続きです。 nameは文字列です。

キーワード引数listに真の値が与えられていなければ、 返される値はスカラー値です。パラメータnameに複数の値が与えられた場合でも、 最初の値のみが返されます。listに真の値が与えられれば、返されるのは 常に値のリストとなります。

キーワード引数convertに手続きを与えると、対応する値が取り出された後でその 手続きが値を引数として呼ばれます。これによって値を文字列から必要な型へと変換することが できます。listに真の値が与えられている場合、変換手続きは各値に対して呼ばれ、 その結果のリストがcgi-get-parameterから返されます。

パラメータnameがQuery中に現れなかった場合は、 defaultに与えられた値がそのまま 返されます。defaultが省略された場合、listが偽であれば#fが、 真であれば()が返されます。

出力の生成

Function: cgi-header :key status content-type location cookies

HTTPリプライメッセージのヘッダを、テキストツリー形式(text.tree - 怠惰なテキスト構築参照) で作成して返します。最も簡単な呼び出しでは次のようになります。

 
(tree->string (cgi-header))
  ⇒ "Content-type: text/html\r\n\r\n"

キーワード引数content-typeによってContent typeを指定できます。 また、cookiesにクッキー文字列のリストを渡すことにより、 クライアントにクッキーを設定できます。クッキー文字列を構築するには手続き construct-cookie-string (rfc.cookie - HTTPクッキー参照) が使えます。

キーワード引数locationは、Locationヘッダを作成して クライアントを別のURIに誘導するのに使えます。また、Statusヘッダを 指定するためにstatusキーワード引数が使えます。クライアントを 別URIに転送するよくある方法は次のようなものです。

 
(cgi-header :status "302 Moved Temporarily"
            :location target-uri)
Parameter: cgi-output-character-encoding :optional encoding

このパラメータの値は次に説明するcgi-mainが出力するデータの 文字符合化法(CES)を指定します。デフォルトの値はGaucheのネイティブエンコーディング です。それ以外の値がセットされている場合、cgi-maingauche.charconvモジュールを用いて出力のエンコーディングの変換を 行います。 (gauche.charconv - 文字コード変換参照)。

便利な手続き

Function: cgi-main proc :key on-error merge-cookies output-proc part-handlers

CGIスクリプトのための便利なラッパー手続きです。 この手続きは、まずcgi-parse-parametersを呼び出してCGIスクリプトに 渡されたパラメータを解析し、続いてその結果を引数としてprocを呼び出します。 キーワード引数merge-cookiesは、与えられればそのまま cgi-parse-parametersに渡されます。

手続きprocはHTTPヘッダを含むドキュメントを テキストツリー構造(text.tree - 怠惰なテキスト構築参照)で 返さなければなりません。cgi-mainはそれをwrite-treeを使って 現在の出力ポートに書き出し、0を返します。

もしproc内でエラーが起こった場合、そのエラーは捕捉されて、エラーを報告する HTMLページが作成されて出力されます。このエラーページは、on-errorキーワード引数に 手続きを渡すことでカスタマイズできます。on-errorに渡された手続きは エラー発生時に<condition>オブジェクト(コンディション参照) を引数として呼ばれ、HTTPヘッダを含むドキュメントをテキストツリー構造で返さねばなりません。

cgi-mainは最終的な結果を出力を書き出す時に パラメータcgi-output-character-encodingを参照し、 必要ならば出力の文字エンコーディングを変換します。

cgi-mainの出力のふるまいはキーワード引数output-procで カスタマイズできます。output-procが渡された場合、それは procの戻り値、あるいはエラーハンドラが作成したテキストツリー構造を 受け取る手続きでなければなりません。その手続きはテキストツリーを フォーマットして現在の出力ポートに出力しなければなりません。 必要ならば文字エンコーディングの変換もその手続き内で行います。

キーワード引数part-handlersは、そのままcgi-parse-parameters に渡されます。この引数によって、ファイルアップロードの際の動作をカスタマイズ できます。詳しくは下の「ファイルアップロードの処理」の項を参照して下さい。

この引数で、一時ファイルを使うように指定した場合、cgi-mainprocから抜ける際に(エラーでも正常終了でも)一時ファイルを 消去します。この機能を他でも利用するにはcgi-add-temporary-fileの項を 参考にして下さい。

procを呼ぶ前に、cgi-mainはカレントエラーポートの バッファリングモードを:lineに変更します。 (バッファリングモードの詳細についてはポート共通の操作port-bufferingの項を参照してください)。 これはwebサーバがcgiスクリプトのエラー出力を捕捉しやすくするためです。

以下の例はCGIに渡されたパラメータ全てをテーブルにして表示します。

 
#!/usr/local/bin/gosh

(use text.html-lite)
(use www.cgi)

(define (main args)
  (cgi-main
    (lambda (params)
      `(,(cgi-header)
        ,(html-doctype)
        ,(html:html
          (html:head (html:title "Example"))
          (html:body
           (html:table
            :border 1
            (html:tr (html:th "Name") (html:th "Value"))
            (map (lambda (p)
                   (html:tr
                    (html:td (html-escape-string (car p)))
                    (html:td (html-escape-string (x->string (cdr p))))))
                 params))))
       ))))
Function: cgi-add-temporary-file filename

この手続きはcgi-mainに渡されるproc中で呼ばれることを 想定しています。 この手続きは、filenameを一時ファイルとして登録し、procが 終了する際に消去されるようにします。cgiスクリプトがエラー終了した場合 などでもごみを残さないようにする便利な方法です。 この手続きを呼んだ後で、procfilenameを消去したり 名前を変えたりしても構いません。

Parameter: cgi-temporary-files

cgi-add-temporary-fileで登録された一時ファイルを保持するパラメータです。

ファイルアップロードの処理

cgi-parse-parametersの項で説明したように、ファイルアップロードは デフォルトでは透過的に扱われます。すなわち、アップロードされた ファイルの内容がパラメータの値となります。 これは望みの動作ではないかもしれません。例えばアップロードされるファイルが 巨大であることが予想されるなら、それを全てメモリに読み込んで持ち回りたくは ないかもしれません。cgi-parse-parameterscgi-mainpart-handlers引数によって、ファイルアップロードの 処理をカスタマイズすることが可能です。 (この引数は、フォームデータがmultipart/form-data enctypeで 送られた場合にのみ意味を持ちます)。

part-handlers引数が与えられている場合、それはリストのリストで、 内部のリストは(name-pattern action kv-list …) の形式で なければなりません。 アップロードされたファイルは、そのパラメータ名がname-patternに マッチした場合にactionで指示されるように処理されます。 (ここで、パラメータ名とはsubmitされたフォームのinput要素に与えられた ’name’属性のことです。アップロードされたファイルの名前ではありません)。

name-patternは文字列のリストか、正規表現か、#tです。 文字列のリストの場合はそれのいずれかとパラメータ名が等しければマッチと みなされます。#tは全てのものにマッチします。

actionは次のいずれかの値でなければなりません。

#f

デフォルトのアクションです。すなわち、アップロードされたファイルの内容が 文字列として読み込まれ、パラメータの値となります。

ignore

アップロードされたファイルの内容を無視します。

file

アップロードされたファイルの内容は一時ファイルへと格納されます。 パラメータの値は、一時ファイルの名前となります。

このアクションを使う場合は、エントリを (name-pattern file prefix) のように書くことも でき、その場合はprefixが一時ファイルのパス名のプリフィクスとして 使われます。例えば("image" file "/var/mycgi/incoming/img") のようにしておくと、"image"パラメータとしてアップロードされた ファイルが‘/var/mycgi/incoming/img49g2Ua’のような一時ファイルに 格納されることになります。

アプリケーションは、この一時ファイルを(必要ならば)適切な場所に 移動しなければなりません。cgi-mainを用いている場合は、 一時ファイルはcgi-mainを抜ける際に(まだあれば)unlinkされます。

file+name

fileと同様ですが、パラメータの値が一時ファイル名と クライアントが送ってきたファイル名からなるリストになります。 クライアントが送信したファイル名を利用したい場合に便利です (ただ、クライアントが常に正しいファイル名を送って来ると仮定しては いけません。例えば、アップロードされたファイルを チェック無しにクライアントが送ってきた名前にrenameするというような ことは避けてください)。

procedure

この場合、アップロードされた内容を処理するために、手続きprocedureが 呼ばれます。手続きは4つの引数を伴って呼ばれます: (procedure name filename part-info iport).

nameはパラメータの名前、filenameはオリジナルファイルの名前 (クライアント側でのパス名)です。part-info<mime-part>オブジェクトで、 このMIMEパートの情報を保持しており、そしてiportは内容を読むための入力ポートです。 これらの引数の詳しい意味についてはrfc.mime - MIMEメッセージ処理を 参照して下さい。独自のprocedureを書く際に、rfc.mimemime-retrieve-bodyのような手続きが使えるかもしれません。

procedure内で一時ファイルを作る場合は、それを cgi-add-temporary-fileで登録しておけば、cgi処理中に エラーが起きた場合でも一時ファイルが消去されるようにすることができます。

actionの後ろにkv-listが与えられた場合、それは キーワード-値リストでなければなりません。次のキーワードがサポートされています。

:prefix

actionfilefile+nameの時のみ有効です。 一時ファイルのプリフィクスを指定します。例えば:prefix "/tmp/foo"を 与えると、ファイルは‘/tmp/fooxAgjeQ’のような名前でセーブされます。

:mode

actionfilefile+nameの時のみ有効です。 一時ファイルのモードをunix式の整数で指定します。デフォルトは#o600です。

ファイルアップロード以外のパラメータはpart-handlersの対象外である ことに注意して下さい。それらのパラメータの値は常に文字列へと変換されます。

簡単な例を示します。例えば次のようなフォームがあったとします。

 
<form enctype="multipart/form-data" method="POST" action="mycgi.cgi">
<input type="file" name="imagefile" />
<input type="text" name="description" />
<input type="hidden" name="mode" value="normal" />
</form>

mycgi.cgi’内で、cgi-parse-parameterspart-handlers引数なしで使った場合は、 例えば次のようなリストがパラメータパージングの結果として得られるでしょう。 (実際の値は、webクライアントがどのようにフォームを埋めたかに依存します)。

 
(("imagefile" #*".....(image file content as a string)....")
 ("description" "my image")
 ("mode" "normal"))

ここでもし、'(("imagefile" file :prefix "/tmp/mycgi"))part-handlersに 渡したなら、替わりに次のような結果が得られるでしょう。 ここで、アップロードされたファイルは‘/tmp/mycgi7gq0B’にセーブ されていることになります。

 
(("imagefile" "/tmp/mycgi7gq0B")
 ("description" "my image")
 ("mode" "normal"))

上の例でシンボルfileのかわりにfile+nameを使えば、 例えば"imagefile"の値として("/tmp/mycgi7gq0B" "logo.jpg") のようなものが得られるでしょう。ここで"logo.jpg"は アップロードされたファイルのクライアント側でのパス名です。 (注意:クライアントは任意の文字列をファイル名として送信することが できるため、その文字列が有効なパス名であることを仮定してはなりません。)


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.71 www.cgi.test - CGIのテスト

Module: www.cgi.test

CGIスクリプトをテストするための便利な手続きを定義しています。 このテストは、指定された環境変数をセットし、実際にスクリプトを 実行し、出力を取得します。ユーザのテスト手続きは、その出力が 期待したものであるかどうかを検査します。

Function: cgi-test-enviornment-ref envvar-name
Function: (setter cgi-test-enviornment-ref) envvar-name value

モジュールにより、CGIスクリプトが実行される際の環境変数の デフォルト値のテーブルが保持されます。プログラマは、これらの 手続きを使ってそのデフォルト値を取得したり設定したりできます。

CGIスクリプトの呼び出し毎に、これらのデフォルト値を上書きしたり、 追加の環境変数を渡したりできます。

デフォルトでは、下記の環境変数が設定されています。

NameValue
SERVER_SOFTWAREcgitest/1.0
SERVER_NAMElocalhost
GATEWAY_INTERFACECGI/1.1
SERVER_PROTOCOLHTTP/1.1
SERVER_PORT80
REQUEST_METHODGET
REMOTE_HOSTremote
REMOTE_ADDR127.0.0.1
Function: call-with-cgi-script script proc :key (environment ()) (parameters #f)

与えられた環境でスクリプトを実行し、そのスクリプトの標準出力のパイプに 接続された入力ポートを1つ引数に取る proc を呼び出します。

引数 script は、プログラムの名前とその引数のリストでなければ なりません。リストの要素はそれぞれ、文字列化されるためにまず x->stringに渡されます。

スクリプトは、変数 environment で与えられる環境と、上述した デフォルトのテスト環境の下で実行されます。 environmentは、キー(car)がその環境変数の名前、 cdrが対応する値であるような連想リストでなければなりません。 両方ともまず x->string に渡されます。 environment とデフォルトのテスト環境に同じ環境変数がある場合は、 environment にあるものが使われます。

さらに、引数 parameters に連想リストが渡された場合、そこから クエリストリングが作られスクリプトに渡されます。 クエリストリングを渡す実際のメソッドは、環境変数 REQUEST_METHODの 値に依存します。REQUEST_METHODGETHEAD で ある場合は、クエリストリングは環境変数 QUERY_STRING に置かれます。 REQUEST_METHODPOST の場合は、クエリストリングは スクリプトの標準入力から取得されます。後者の場合、CONTENT_TYPE には application/x-www-form-urlencodedが、 CONTENT_LENGTH には QUERY_STRINGの長さが、それぞれ自動的に セットされます。REQUEST_METHOD がその他の値の場合は、 parameters は無視されます。 必要であれば、このメカニズムをバイパスして、環境変数 QUERY_STRING を 直接セットアップすることも可能です。

Function: run-cgi-script->header&body script reader :key environment parameters

call-with-cgi-script の便利なラッパー手続きです。 scriptenvironmentparametersは、 そのまま call-with-cgi-scriptに渡されます。 スクリプトの出力は、run-cgi-script->header&bodyにより パーズされます。 最初に、RFC2822ヘッダフィールドが rfc822-read-headers (rfc.822 - RFC822メッセージ形式参照)によりパーズされます。 次に、readerが、スクリプトの出力にパイプされた入力ポートと ともに呼ばれます。

run-cgi-script->header&bodyは、ヘッダのリスト (rfc822-read-headersによりパーズされたもの)と readerの戻り値の2つの値を返します。

Function: run-cgi-script->sxml script :key environment parameters

この手続きでは、run-cgi-script->header&bodyreaderとして、ssax:xml->sxml (sxml.ssax - 関数的なXMLパーザ参照)を使います。 整形式(Well-formed)のHTMLやXMLドキュメントを生成する CGIスクリプトをテストする場合に便利です。

Function: run-cgi-script->string script :key environment parameters
Function: run-cgi-script->string-list script :key environment parameters

これらの手続きは、run-cgi-script->header&bodyreaderとして、それぞれ port->stringport->string-listを使います。

例:

 
(run-cgi-script->string-list "bbs.cgi"
                             :environment '((REMOTE_ADDR . "12.34.56.78"))
                             :parameters '((command . "view")
                                           (page . 1234)))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

12.72 www.css - CSSのパーズと構築

Module: www.css

This module provides tools to convert between S-expression and CSS.

The S-expression CSS (SxCSS) is a convenient way to manipulate CSS in Scheme.

For example, the following CSS and SxCSS are equivalent, and can be converted back and forth:

CSS:

 
body { padding-left: 11em;
       font-family: Georgia, "Times New Roman", Times, serif;
       color: purple;
       background-color: #d8da3d }
ul.navbar li { background: white;
               margin: 0.5em 0;
               padding: 0.3em;
               border-right: 1em solid black }
ul#spec > a { text-decoration: none }
a:visited { color: purple !important }

SxCSS:

 
((style-rule body
   (padding-left (11 em))
   (font-family (:or Georgia "Times New Roman" Times serif))
   (color purple)
   (background-color (color "d8da3d")))
 (style-rule ((ul (class navbar)) li)
   (background white)
   (margin #((0.5 em) 0))
   (padding (0.3 em))
   (border-right #((1 em) solid black)))
 (style-rule ((ul (id spec)) > a) (text-decoration none))
 (style-rule (a (: visited)) (color purple !important)))

See the “CSS in S-expression” section below for the complete specification.

Constructing CSS

Function: construct-css sxcss :optional oport

Take SxCSS and writes out CSS to the given port, defaulted to the current output port.

Parsing CSS

Function: parse-css :optional iport

Read CSS from the given port, defaulted to the current input port, and returns SxCSS.

When it encounters unparsable CSS (either a malformed CSS, or unsupported syntax), it emits a warning message, ignore the unparsable part and tries to continue reading the rest.

NB: Currently we don’t handle @charset directive; we assume the text is already in the port’s encoding. We may support it in future versions.

Function: parse-css-file file :key encoding

Read the CSS text from the given file and parse it using parse-css. Again, we don’t handle @charset directive yet, and you have to pass encoding argument if the CSS text isn’t in the Gauche’s native character encoding.

Function: parse-css-selector-string str

This parses the selector part of the CSS.

 
(parse-css-selector-string "ul li.item span#foo")
  ⇒ (ul (li (class item)) (span (id foo)))

(parse-css-selector-string "h1,h2")
  ⇒ (:or h1 h2)

CSS in S-expression

The following is the complete rules of SxCSS syntax.

 
<sxcss>      : ({<style-rule> | <at-rule>} ...)

<style-rule> : (style-rule <pattern> <declaration> ...)
             | (style-decls <declaration> ...)

<pattern>   : <selector> | (:or <selector> ...)
<seletor>   : <simple-selector>
            | <chained-selector>
<chained-selector> : (<simple-selector> . (<op>? . <chained-selector>))
<op>        : > | + | ~
<simple-selector> : <element-name>
            | (<element-name> <option> ...)
<option>    : (id <name>)                           ; E#id
            | (class <ident>)                       ; E.class
            | (has <ident>)                         ; E[attrib]
            | (= <ident> <attrib-value>)            ; E[attrib=val]
            | (~= <ident> <attrib-value>)           ; E[attrib~=val]
            | (:= <ident> <attrib-value>)           ; E[attrib|=val]
            | (*= <ident> <attrib-value>)           ; E[attrib*=val]
            | (^= <ident> <attrib-value>)           ; E[attrib^=val]
            | ($= <ident> <attrib-value>)           ; E[attrib$=val]
            | (:not <negation-arg>)                 ; E:not(s)
            | (: <ident>)                           ; E:pseudo-class
            | (: (<fn> <ident> ...))                ; E:pseudl-class(arg)
            | (:: <ident>)                          ; E::pseudo-element
<element-name> : <ident> | *
<attrib-value> : <ident> | <string>
<negation-arg> | <element-name> | * | <option>  ; except <negation-arg>

<declaration>  : (<ident> <expr> <expr2> ... <important>?)
<important> : !important
<expr>      : <term>
            | (/ <term> <term> ...)
            | (:or <term> <term> ...)
            | #(<term> <term> ...)             ; juxtaposition
<term>      : <quantity> | (- <quantity>) | (+ <quantity>)
            | <string> | <ident> | <url> | <hexcolor> | <function>
<quantity>  : <number>
            | (<number> %)
            | (<number> <ident>)
<url>       | (url <string>)
<hexcolor>  | (color <string>)  ; <string> must be hexdigits
<function>  | (<fn> <arg> ...)
<arg>       | <term> | #(<term> ...) | (/ <term> <term> ...)

<at-rule>    : <at-media-rule> | <at-import-rule>
                   ; NB: Other at-rules are not supported yet
<at-media-rule>  : (@media (<symbol> ...) <style-rule> ...)
<at-import-rule> : (@import <string> (<symbol> ...))

NB: Negation op is :not instead of not, since (not <negation-arg>) would be ambiguous from the simple node named "not" with one option.

NB: style-decls selector rule is currently won’t appear in the parse-css output; it can be used in SxCSS to make construct-css render declarations only, which can be used in the style attribute of the document, for example.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

A. 参考文献

[R5RS]

R. Kelsey, W. Clinger, J. Rees (eds.), Revised^5 Report on the Algorithmic Language Scheme, Higher-Order and Symbolic Computation, 11(1), September, 1998 and ACM SIGPLAN Notices, 33(9), October, 1998.
http://www.schemers.org/Documents/Standards/R5RS/.

[R7RS]

A. Shinn, J. Cowan, A. A. Greckler (eds.), Revised^7 Report on the Algorithmic Language Scheme
http://trac.sacrideo.us/wg/raw-attachment/wiki/WikiStart/r7rs.pdf.

[1CC]

Carl Bruggeman, Oscar Waddell and R. Kent Dybvig, Representing control in the presence of one-shot continuations, in Proceedings of SIGPLAN ’96, pp. 99–107, 1996.

[Myers86]

Eugene Myers, An O(ND) Difference Algorithm and Its Variations, Algorithmica Vol. 1 No. 2, pp. 251-266, 1986.

[MOP]

Gregor Kiczales, Jim Des Rivieres, Daniel Bobrow, The Art of Metaobject Protocol, The MIT Press.

[Dylan]

Kim Barrett, Bob Cassels, Paul Haahr, David A. Moon, Keith Playford, P. Tucker Withington, A Monotonic Superclass Linearization for Dylan, in Proceedings of OOPSLA 96, October 1996.
http://dl.acm.org/citation.cfm?id=236343.

[FairThreads]

F. Boussinot, Java Fair Threads,
http://www-sop.inria.fr/mimosa/rp/FairThreads/html/FairThreads.html.

[RFC1321]

R. Rivest, The MD5 Message-Digest Algorithm.
April 1992.
http://www.ietf.org/rfc/rfc1321.txt.

[RFC2045]

N. Freed, N. Borenstein: Multipurpose Internet Mail Extension (MIME) Part One: Format of Internet Message Bodies, November 1996.
http://www.ietf.org/rfc/rfc2045.txt.

[RFC2396]

T. Berners-Lee, R. Fielding, L. Masinter, Uniform Resource Identifiers (URI): Generic Syntax, August 1998.
http://www.ietf.org/rfc/rfc2396.txt.

[RFC2616]

R. Fielding et al, Hypertext Transfer Protocol – HTTP/1.1, June 1999.
http://www.ietf.org/rfc/rfc2616.txt.

[RFC2822]

P. Resnick (ed.), Internet Message Format, April 2001.
http://www.ietf.org/rfc/rfc2822.txt.

[RFC2965]

D. Kristol, L. Montulli, HTTP State Management Mechanism, October 2000.
http://www.ietf.org/rfc/rfc2965.txt.

[RFC3174]

D. Eastlake, 3rd and P. Jones, US Secure Hash Algorithm 1 (SHA1).
September 2001.
http://www.ietf.org/rfc/rfc3174.txt.

[RFC4648]

S. Josefsson, Ed.: The Base16, Base32, and Base64 Data Encodings October 2006.
http://www.ietf.org/rfc/rfc4648.txt.

[SRFI-0]

Marc Feeley, Feature-based conditional expansion construct, May 1999.
http://srfi.schemers.org/srfi-0/srfi-0.html.

[SRFI-1]

Olin Shivers, List Library, October 1999.
http://srfi.schemers.org/srfi-1/srfi-1.html.

[SRFI-2]

Oleg Kiselyov, AND-LET*: an AND with local bindings, a guarded LET* special form, March 1998.
http://srfi.schemers.org/srfi-2/srfi-2.html.

[SRFI-4]

Marc Feeley, Homogeneous numeric vector types, May 1999.
http://srfi.schemers.org/srfi-4/srfi-4.html.

[SRFI-6]

William D Clinger, Basic String Ports, July 1999.
http://srfi.schemers.org/srfi-6/srfi-6.html.

[SRFI-8]

John David Stone, receive: Binding to multiple values, August 1999.
http://srfi.schemers.org/srfi-8/srfi-8.html.

[SRFI-9]

Richard Kelsey, Defining Record Types, September 1999.
http://srfi.schemers.org/srfi-9/srfi-9.html.

[SRFI-10]

Oleg Kiselyov, #, external form, January 2000.
http://srfi.schemers.org/srfi-10/srfi-10.html.

[SRFI-11]

Lars T Hansen, Syntax for receiving multiple values, March 2000.
http://srfi.schemers.org/srfi-11/srfi-11.html.

[SRFI-13]

Olin Shivers, String Libraries, December 2000.
http://srfi.schemers.org/srfi-13/srfi-13.html.

[SRFI-14]

Olin Shivers, Character-set Library, December 2000.
http://srfi.schemers.org/srfi-14/srfi-14.html.

[SRFI-17]

Per Bothner, Generalized set!, July 2000.
http://srfi.schemers.org/srfi-17/srfi-17.html.

[SRFI-18]

Marc Feeley, Multithreading Support, April 2000.
http://srfi.schemers.org/srfi-18/srfi-18.html.

[SRFI-19]

Will Fitzgerald, Time Data Types and Procedures, August 2000.
http://srfi.schemers.org/srfi-19/srfi-19.html.

[SRFI-21]

Marc Feeley, Readl-time Multithreading Support, April 2000.
http://srfi.schemers.org/srfi-21/srfi-21.html.

[SRFI-22]

Martin Gasbichler and Michael Sperber, Running Scheme Scripts on Unix, January 2002.
http://srfi.schemers.org/srfi-22/srfi-22.html.

[SRFI-23]

Stephan Housen, Error reporting mechanism, April 2001.
http://srfi.schemers.org/srfi-23/srfi-23.html.

[SRFI-25]

Jussi Piitulainen, Multi-dimensional Array Primitives, June 2002.
http://srfi.schemers.org/srfi-25/srfi-25.html.

[SRFI-26]

Sebastian Egner, Notation for Specializing Parameters without Currying, June 2002.
http://srfi.schemers.org/srfi-26/srfi-26.html.

[SRFI-27]

Sebastian Egner, Sources of Random Bits, June 2002.
http://srfi.schemers.org/srfi-27/srfi-27.html.

[SRFI-28]

Scott G. Miller, Basic Format Strings, June 2002.
http://srfi.schemers.org/srfi-28/srfi-28.html.

[SRFI-37]

Anthony Carrico, Args-fold: a program argument processor, Jan. 2003.
http://srfi.schemers.org/srfi-37/srfi-37.html.

[SSAX]

Oleg Kiselyov, XML and Scheme,
http://pobox.com/~oleg/ftp/Scheme/xml.html.
The SSAX distribution is also available at sourceforge:
http://ssax.sourceforge.net/.

[MT]

M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-dimensionally equidistributed uniform pseudorandom number generator", ACM Trans. on Modeling and Computer Simulation Vol. 8, No. 1, January pp.3-30 1998.
http://dl.acm.org/citation.cfm?id=272995

[OLEG1]

Oleg Kiselyov, Making sense of an input stream,
http://pobox.com/~oleg/ftp/Scheme/parsing.html.

[OLEG2]

Oleg Kiselyov, General ways to traverse collections,,
http://pobox.com/~oleg/ftp/Scheme/enumerators-callcc.html. 2000.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

B. CとSchemeの関数の対応

Cに馴染んだプログラマのために、Cのオペレータとライブラリ関数から Schemeの関数への対応表を示しておきます。

+

R7RS の算術演算手続き +数値の演算参照。

+=

Gauche の inc! マクロ。代入参照。

-

R7RS の算術演算手続き -数値の演算参照。

-=

Gauche の dec! マクロ。代入参照。

->

Gauche の slot-ref が近いものです。インスタンスへのアクセス参照。

* (二項演算子)

R7RS の算術演算手続き *数値の演算参照。

* (単項演算子)

同等の手続きはありません。Scheme には明示的ポインタ表記はありません。

*=

同等の手続きはありません。

/

Cでは引数の型によって二つの異なる意味を持つオペレータです。 実数の除算には/を、整数除算の商を求めるにはquotientを使います。 数値の演算参照。

/=

同等の手続きはありません。

& (二項演算子)

Gauche の logandビット演算参照。

& (単項演算子)

同等の手続きはありません。Scheme には明示的ポインタ表記はありません。

&&

R7RS の構文 and条件式参照。

&=

同等の手続きはありません。

|

Gauche の logiorビット演算参照。

||

R7RS の構文 or条件式参照。

|=

同等の手続きはありません。

^

Gauche の logxorビット演算参照。

=

R7RS の構文 set!代入参照。

==

R7RS の同等性手続き eq?eqv? および equal?等価参照。

<
<=

R7RS の算術演算手続き < および <=数値の比較参照。C の演算子とちがい、Scheme のものは 推移的なものです。

<<

Gauche の ashビット演算参照。

<<=

同等の手続きはありません。

>
>=

R7RS の算術演算手続き < および <=数値の比較参照。C の演算子とちがい、Scheme のものは 推移的なものです。

>>

Gauche の ashビット演算参照。

>>=

同等の手続きはありません。

%

R7RS の演算子 modulo および remainder数値の演算参照。

%=

同等の手続きはありません。

[]

R7RS の vector-ref (ベクタ参照) が近いものです。あるいは、 Gauche のジェネリック関数 ref (gauche.sequence - シーケンスフレームワーク参照)が、 任意の並び用になっています。

.

Gauche の slot-ref がこれに近いものです。インスタンスへのアクセス参照。

~

Gauche の lognotビット演算参照。

~=

同等の手続きはありません。

!

R7RS の手続き not論理値参照。

!=

同等の手続きはありません。

abort

Gauche の sys-abortプログラムの終了参照。

abs

R7RS の abs数値の演算参照。

access

Gauche の sys-accessファイルの状態参照。

acos

R7RS の acos数値の演算参照。

alarm

Gauche の sys-alarmその他のシステムコール参照。

asctime

Gauche の sys-asctime時間参照。

asin

R7RS の asin数値の演算参照。

assert

Gauche には同等の関数はありません。

atan
atan2

R7RS の atan数値の演算参照。

atexit

Gauche には同等の関数はありませんが、exit が呼ばれたとき アクティブな動的ハンドラの「事後」サンクが呼ばれます。 プログラムの終了 および 継続 参照。

atof
atoi
atol

string->number が使えます。数値の変換参照。

bsearch

SRFI-133のvector-binary-searchが使えます。 srfi-133 - ベクタライブラリ参照。

calloc

Scheme ではメモリ割当は自動的に処理されます。

ceil

R7RS の ceiling数値の演算参照。

cfgetispeed
cfgetospeed
cfsetispeed
cfsetospeed

Gauche の sys-cfgetispeedsys-cfgetospeedsys-cfsetispeedsys-cfsetospeedgauche.termios - 端末の制御参照。

chdir

Gauche の sys-chdir他のファイル操作参照。

chmod

Gauche の sys-chmodファイルの状態参照。

chown

Gauche の sys-chownファイルの状態参照。

clearerr

未サポート。

clock

Gauche には同等の関数はありません。sys-times を使って、 CPUタイムに情報を得ることができます。

close

ファイルディスクリプタを直接クローズすることはできませんが、 close-input-port あるいは close-output-port を使うと、 元になるファイルはクローズされます。 いくつかのポートに関連する関数、たとえば、call-with-output-file などは、操作終了時に自動的にファイルをクローズします。 また、それを支配しているポートがGCされたときにクローズされます。 ポート共通の操作参照。

closedir

Gauche には同等の関数はありません。sys-readdir を使うと ディレクトリの内容を一度に読むことができます。 ディレクトリ 参照。

cos
cosh

cos および cosh数値の演算 参照。

creat

デフォルトでは、書き込みのためにファイルをオープンしたときに、暗黙のうちに ファイルが作成されます。ファイル作成のより詳しい制御については ファイルポート を参照してください。

ctermid

Gauche の sys-ctermidシステムへの問い合わせ 参照。

ctime

Gauche の sys-ctime時間 参照。

cuserid

同等の関数はありません。これは新しい POSIX からは削除されています。 別法として、sys-getuid といっしょに、sys-getlogin あるいは sys-getpwuid などの関数が使えます。

difftime

Gauche の sys-difftime時間 参照。

div

R7RS の quotient および remainder を使えます。 数値の演算 参照。

dup
dup2

直接はサポートされていませんが、port-fd-dup! が使えます。

execl
execle
execlp
execv
execve
execvp

Gauche の sys-execプロセス管理 参照。 より高水準のインタフェースについては gauche.process - 高レベルプロセスインタフェース 参照。

exit
_exit

必要なことに応じて、exit あるいは sys-exit を使いましょう。 プログラムの終了 参照。

exp

R7RS の exp数値の演算 参照。

fabs

R7RS の abs数値の演算 参照。

fclose

ファイルストリームを直接クローズすることはできませんが、 close-input-port あるいは close-output-port を使うと、 元になるファイルはクローズされます。 いくつかのポートに関連する関数、たとえば、call-with-output-file などは、操作終了時に自動的にファイルをクローズします。 また、それを支配しているポートがGCされたときにクローズされます。 ポート共通の操作参照。

fcntl

gauche.fcntl モジュールで、sys-fcntl として実装されています。 gauche.fcntl - 低レベルファイル操作 参照。

fdopen

Gauche の open-input-fd-port あるいは open-output-fd-portファイルポート 参照。

feof

同等の操作はありませんが、入力ポートがEOFに達したどうか調べるには peek-charpeek-byteが使えます。 データの読み込み参照。

ferror

未サポート。

fflush

Gauche の flush出力 参照。

fgetc

read-char あるいは read-byte を使いましょう。入力 参照。

fgetpos

Gauche の port-tell を使いましょう。(ポート共通の操作参照。)

fgets

read-line あるいは read-string を使いましょう。入力 参照。

fileno

port-file-numerポート共通の操作 参照。

floor

R7RS の floor数値の演算 参照。

fmod

Gauche の fmod

fopen

この操作に対応するのは、R7RS の open-input-file あるいは open-output-file です。ファイルポート 参照。

fork

Gauche の sys-forkプロセス管理 参照。

forkpty

sys-forkpty を使いましょう。gauche.termios - 端末の制御 参照。

fpathconf

未サポート。

fprintf

直接はサポートされていませんが、Gauche の format は 似たような機能を提供しています。出力参照。 SLIB は printf の実装を持っています。

fputc

write-char あるいは write-byteを使いましょう。出力参照。

fputs

displayを使いましょう。出力 参照。

fread

直接はサポートされていません。 バイナリの数値を読む場合はbinary.io - バイナリI/Oを参照のこと。 バイトのチャンクで読みたければ、 read-uvector! が使えるでしょう (ユニフォームベクタのブロック入出力参照)。

free

Scheme では必要がありません。

freopen

未サポート。

frexp

Gauche の frexp

fscanf

サポートしていません。一般的にはパーザを書かねばなりません。 データを S式で保持しているなら、read が使えます。 構文がごく単純なら、srfi-14 (srfi-13 - 文字列ライブラリ) の string-tokenize や、正規表現 (正規表現) が使えるでしょう。

fseek

Gauche の port-seek(ポート共通の操作参照)を使いましょう。

fsetpos

Gauche の port-seek(ポート共通の操作参照)を使いましょう。

fstat

Gauche の sys-statファイルの状態 参照。

ftell

Gauche の port-tell(ポート共通の操作参照)を使いましょう。

fwrite

直接はサポートされていません。 バイナリの数値を書き出すのなら、binary.io - バイナリI/O参照のこと。 バイト列のチャンクを書き出しすなら、 単に display を使うか、write-uvector が使えます (ユニフォームベクタのブロック入出力参照)。

getc
getchar

read-char あるいは read-byteを使いましょう。入力参照。

getcwd

Gauche の sys-getcwdシステムへの問い合わせ 参照。

getdomainname

Gauche の sys-getdomainnameシステムへの問い合わせ 参照。

getegid

Gauche の sys-getegidシステムへの問い合わせ 参照。

getenv

Gauche の sys-getenv環境の問い合わせ 参照。

geteuid

Gauche の sys-geteuidシステムへの問い合わせ 参照。

gethostname

Gauche の sys-gethostnameシステムへの問い合わせ 参照。

getgid

Gauche の sys-getgidシステムへの問い合わせ 参照。

getgrgid
getgrnam

Gauche の sys-getgrgid および sys-getgrnamUnixのグループとユーザ 参照。

getgroups

Gauche の sys-getgroupsシステムへの問い合わせ参照。

getlogin

Gauche の sys-getloginシステムへの問い合わせ参照。

getpgrp

Gauche の sys-getpgrpシステムへの問い合わせ 参照。

getpid
getppid

Gauche の sys-getpidシステムへの問い合わせ 参照。

getpwnam
getpwuid

Gauche の sys-getpwnam および sys-getpwuidUnixのグループとユーザ 参照。

gets

read-line または read-stringを使いましょう。入力参照。

gettimeofday

Gauche の sys-gettimeofday時間 参照。

getuid

Gauche の sys-getuidシステムへの問い合わせ 参照。

gmtime

Gauche の sys-gmtime時間 参照。

isalnum

直接はサポートされていませんが、R7RS の char-alphabetic? および char-numeric? が使えます。文字 参照。また、文字集合も 使えます。文字集合srfi-14 - 文字集合ライブラリ 参照。

isalpha

R7RS の char-alphabetic?文字 参照。また、 文字集合 および srfi-14 - 文字集合ライブラリ も参照してください。

isatty

Gauche の sys-isatty他のファイル操作 参照。

iscntrl

直接はサポートされていませんが、srfi-14(char-set-contains? char-set:iso-control c) が使えます。 srfi-14 - 文字集合ライブラリ 参照。

isdigit

R7RS の char-numeric?文字 参照。 srfi-14 で、(char-set-contains? char-set:digit c) も 使えます。srfi-14 - 文字集合ライブラリ 参照。

isgraph

直接はサポートされていませんが、srfi-14(char-set-contains? char-set:graphic c) が使えます。 srfi-14 - 文字集合ライブラリ 参照。

islower

R7RS の char-lower-case?文字 参照。 srfi-14(char-set-contains? char-set:lower-case c) も使えます。srfi-14 - 文字集合ライブラリ 参照。

isprint

直接はサポートされていませんが、srfi-14(char-set-contains? char-set:printing c) が使えます。 srfi-14 - 文字集合ライブラリ 参照。

ispunct

直接はサポートされていませんが、srfi-14(char-set-contains? char-set:punctuation c) が使えます。 srfi-14 - 文字集合ライブラリ 参照。

isspace

R7RS の char-whitespace?文字 参照。 srfi-14(char-set-contains? char-set:whitespace c) も使えます。srfi-14 - 文字集合ライブラリ 参照。

isupper

R7RS の char-upper-case?文字 参照。 srfi-14(char-set-contains? char-set:upper-case c) も使えます。srfi-14 - 文字集合ライブラリ 参照。

isxdigit

直接はサポートされていませんが、srfi-14(char-set-contains? char-set:hex-digit c) が使えます。 srfi-14 - 文字集合ライブラリ 参照。

kill

Gauche の sys-killシグナル 参照。

labs

R7RS の abs数値の演算 参照。

ldexp

Gauche の ldexp

ldiv

R7RS の quotient および remainder を使いましょう。 数値の演算参照。

link

Gauche の sys-linkディレクトリ操作参照。

localeconv

Gauche の sys-localeconvロケール 参照。

localtime

Gauche の sys-localtime時間 参照。

log

R7RS の log数値の演算 参照。

log10

直接はサポートされていません。 log10(z)(/ (log z) (log 10)) です。

longjmp

R7RS の call/cc が類似(上位)のメカニズムを提供しています。 継続 参照。

lseek

Gauche の port-seek (ポート共通の操作参照) を使いましょう。

malloc

Scheme では必要ありません。

mblen
mbstowcs
mbtowc

Gauche ではマルチバイト文字列を内部的に処理しますので、一般的には 文字列がマルチバイトであるかどうかを気にする必要はありません。 string-length は常に、サポートされているエンコーディングの 文字列に対して、文字数を返します。文字のエンコーディング変換を したいのであれば、gauche.charconv - 文字コード変換 を参照してください。

memcmp
memcpy
memmove
memset

同等の関数はありません。

mkdir

Gauche の sys-mkdirディレクトリ操作 参照。

mkfifo

Gauche の sys-mkfifo

mkstemp

Gauche の sys-mkstempディレクトリ操作 参照。 tmpnam の代りにこちらを使いましょう。

mktime

Gauche の sys-mktime時間 参照。

modf

Gauche の modf

open

直接はサポートされていません。この操作に対応するのな R7RS の open-input-file または open-output-file です。 ファイルポート 参照。

opendir

直接はサポートされていません。 ディレクトリのエントリをいちどに読むには sys-readdir が使えます。ディレクトリ 参照。

openpty

sys-openpty を使いましょう。gauche.termios - 端末の制御 参照。

pathconf

サポートしません。

pause

Gauche の sys-pauseその他のシステムコール 参照。

perror

Gauche には同等の関数はありません。システムコールは一般的には error (<system-error>) を投げます。これには、失敗した理由の説明が含まれて います。

pipe

Gauche の sys-pipe他のファイル操作 参照。

pow

R7RS の expt数値の演算 参照。

printf

直接はサポートされていませんが、Gauche の format は類似の機能を 提供しています。出力 参照。SLIB には printf の実装があります。

putc
putchar

write-char または write-byte を使いましょう。出力 参照。

puts

display を使いましょう。出力 参照。

qsort

Gauche の sort および sort! はリストをソートする便利な方法を 提供しています。ソートとマージ 参照。

raise

Gauche には同等の関数はありません。Scheme の関数 raise (SRFI-18) は例外を発生させます。シグナル SIG を現在のプロセスに送るには (sys-kill (sys-getpid) SIG) が使えます。

rand

直接はサポートされていませんが、多くのプラットフォーム上で、sys-random のような、よりよい RNG が利用可能です。その他のシステムコール 参照。

read

直接はサポートされていませんが、 read-uvector あるいは read-uvector! (ユニフォームベクタのブロック入出力参照)が使えます。

readdir

直接はサポートされていません。Gauche の sys-readdir は 指定のディレクトリを一度に読みます。ディレクトリ 参照。

readlink

Gauche の sys-readlinkディレクトリ操作 参照。 この関数はシンボリックリンクをサポートしているシステム上で利用可能です。

realloc

Scheme では必要ありません。

realpath

Gauche の sys-normalize-pathname あるいは sys-realpathパス名参照。

remove

Gauche の sys-removeディレクトリ操作参照。

rename

Gauche の sys-renameディレクトリ操作参照。

rewind

直接はサポートされませんが、port-seekが代わりに使えます。 ポート共通の操作参照。

rewinddir

直接はサポートされていません。 ディレクトリエントリを一度に読むには sys-readdir が使えます。 ディレクトリ参照。

rmdir

Gauche の sys-rmdirディレクトリ操作参照。

scanf

サポートしていません。一般的にはパーザを書かねばなりません。 データを S式で保持できるなら、read が使えます。 構文がごく単純なら、srfi-14 (srfi-13 - 文字列ライブラリ) の string-tokenize や、正規表現 (正規表現) が使えるでしょう。

select

Gauche の sys-selectI/Oの多重化参照。

setbuf

必要ありません。

setgid

Gauche の sys-getgid

setjmp

R7RS の call/cc が類似(上位)のメカニズムを提供しています。 継続 参照。

setlocale

Gauche の sys-setlocaleロケール参照。

setpgid

Gauche の sys-setpgidシステムへの問い合わせ参照。

setsid

Gauche の sys-setsidシステムへの問い合わせ参照。

setuid

Gauche の sys-setuidシステムへの問い合わせ参照。

setvbuf

必要ありません。

sigaction

シグナルハンドラを設定するには、set-signal-handler! が使えます。 シグナルの処理参照。

sigaddset
sigdelset
sigemptyset
sigfillset

Gauche の sys-sigset-add! および sys-sigset-delete!シグナルとシグナルセット参照。

sigismember

未サポート。

siglongjmp

R7RS の call/cc が類似(上位)のメカニズムを提供しています。 継続 参照。

signal

シグナルハンドラを設定するのには、with-signal-handlers が使えます。 シグナルの処理 参照。

sigpending

未サポート

sigprocmask

シグナルマスクは内部的に処理されます。シグナルの処理参照。

sigsetjmp

R7RS の call/cc が類似(上位)のメカニズムを提供しています。 継続 参照。

sigsuspend

Gauche の sys-sigsuspendシグナルのマスクと待機参照。

sigwait

Gauche の sys-sigwaitシグナルのマスクと待機参照。

sin
sinh

sin および sinh を使いましょう。数値の演算参照。

sleep

Gauche の sys-sleepその他のシステムコール参照。

sprintf

直接はサポートされていませんが、Gauche の format は類似の機能を 提供しています。出力 参照。SLIB には printf の実装があります。

sqrt

R7RS の sqrt数値の演算参照。

srand

直接はサポートされていませんが、多くのプラットフォーム上で、sys-random のような、よりよい RNG が利用可能です。その他のシステムコール 参照。 また、math.mt-randomはさらに優れたRNGを提供します (math.mt-random - Mersenne Twister乱数発生器参照)。

sscanf

サポートしていません。一般的にはパーザを書かねばなりません。 データを S式で保持できるなら、read が使えます。 構文がごく単純なら、srfi-14 (srfi-13 - 文字列ライブラリ) の string-tokenize や、正規表現 (正規表現) が使えるでしょう。

stat

Gauche の sys-statファイルの状態参照。

strcasecmp

R7RS の string-ci=? および、その他の比較関数。 文字列の比較参照。

strcat

R7RS の string-append文字列を扱うその他の手続き参照。

strchr

SRFI-13 の string-index文字列の探索参照。

strcmp

R7RS の string=? および、その他の比較関数。 文字列の比較参照。

strcoll

未サポート。

strcpy

R7RS の string-copy文字列を扱うその他の手続き参照。

strcspn

直接はサポートされていませんが、文字集合とともに、SRFI-13 の string-skip が使えます。文字列の探索参照。

strerror

Gaucheのsys-strerrorシステムへの問い合わせ参照。

strftime

Gauche の sys-strftime時間参照。

strlen

R7RS の string-length文字列のアクセスと変更参照。

strncat

直接はサポートされていませんが、string-append および substring が使えます。

strncasecmp

SRFI-13 の string-compare-ci は非常に柔軟な(しかし、使うのは 多少難しい)機能を提供しています。文字列の色々な比較参照。 ふたつの文字列の固定長の接頭辞が一致しているかどうかをチェックしたい だけなら、SRFI-13 の string-prefix-ci? が使えます。

strncmp

SRFI-13 の string-compare-ci は非常に柔軟な(しかし、使うのは 多少難しい)機能を提供しています。文字列の色々な比較参照。 ふたつの文字列の固定長の接頭辞が一致しているかどうかをチェックしたい だけなら、SRFI-13 の string-prefix-ci? が使えます。 文字列のプリフィックスとサフィックス参照。

strncpy

SRFI-13 の substring文字列を扱うその他の手続き参照。

strpbrk

直接はサポートされていませんが、文字集合と SRFI-13 の string-skip が使えます。文字列の探索参照。

strrchr

SRFI-13 の string-index-right文字列の探索参照。

strspn

直接はサポートされていませんが、文字集合と SRFI-13 の string-index が使えます。文字列の探索参照。

strstr

SRFI-13 の string-contains文字列の探索参照。

strtod

R7RS の string->numberが使えます。数値の変換参照。

strtok

SRFI-13 の string-tokenize他の文字列操作参照。

strtol
strtoul

R7RS の string->numberが使えます。数値の変換参照。

strxfrm

未サポート。

symlink

Gauche の sys-symlinkディレクトリ操作参照。 この関数は、シンボリックリンクをサポートしているシステム上で利用可能です。

sysconf

未サポート。

system

Gauche の sys-systemプロセス管理参照。 一般的にはプロセスライブラリ(gauche.process - 高レベルプロセスインタフェース) を使うことを推奨します。

tan
tanh

R7RS の tan および Gauche の tanh数値の演算参照。

tcdrain
tcflow
tcflush
tcgetattr
tcgetpgrp
tcsendbreak
tcsetattr
tcsetpgrp

対応する関数はそれぞれ、sys-tcdrainsys-tcflowsys-tcflushsys-tcgetattrsys-tcgetpgrpsys-tcsendbreaksys-tcsetattrsys-tcsetpgrp です。 gauche.termios - 端末の制御参照。

time

Gauche の sys-time時間参照。

times

Gauche の sys-timesシステムへの問い合わせ参照。

tmpfile

厳密には、サポートしません。sys-mkstempを見てください。 ディレクトリ操作参照。

tmpnam

Gauche の sys-tmpnam。この関数は POSIX にあるので提供されていますが、 潜在的なセキュリティリスクがあるため、利用はおすすめできません。 代りに sys-mkstemp を使いましょう。ディレクトリ操作参照。

tolower
toupper

R7RS の char-upcase および char-downcase文字参照。

ttyname

Gauche の sys-ttyname他のファイル操作参照。

tzset

未サポート。

umask

Gauche の sys-umaskディレクトリ操作参照。

uname

Gauche の sys-unameシステムへの問い合わせ参照。

ungetc

直接はサポートされません。プッシュしなおす代りに、peek-char をつかって 先読みしましょう。

unlink

Gauche の sys-unlinkディレクトリ操作参照。

utime

Gauche の sys-utimeファイルの状態参照。

va_arg
va_end
va_start

Scheme は可変長引数を自然に処理できますので、必要ありません。

vfprintf
vprintf
vsprintf

直接はサポートされていませんが、Gauche の format は類似の機能を 提供しています。出力 参照。SLIB には printf の実装があります。

wait

Gauche の sys-waitプロセス管理参照。

waitpid

Gauche の sys-waitpidプロセス管理参照。

wcstombs
wctomb

Gauche はマルチバイト文字列を内部的に処理します。それゆえ、一般的には 文字列がマルチバイトであるかどうかを気にする必要はありません。 string-length は常にサポートされているエンコーディングでの文字列の 文字数を返します。文字エンコーディングを変換したければ、 gauche.charconv - 文字コード変換を参照してください。

write

R7RS の display (see section 出力)。 あるいは write-uvector (ユニフォームベクタのブロック入出力参照)。


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

C. Index - 手続きと構文索引

移動:   $   %   (   *   +   -   .   /   :   <   =   >   ^   ~  
A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Z  
見出し一覧

$
$4.3 手続きを作る

%
%macroexpand5.4 マクロの展開
%macroexpand-15.4 マクロの展開

(
(setter cgi-test-enviornment-ref)12.71 www.cgi.test - CGIのテスト
(setter dict-get)9.8.1 ディクショナリのためのジェネリック関数
(setter object-apply)6.18.6 適用可能なオブジェクト
(setter port-buffering)6.22.3 ポート共通の操作
(setter random-data-seed)グローバルな状態
(setter ref)6.15 ハッシュテーブル
(setter ref)6.18.2 万能アクセサ
(setter ref)標準アクセサ
(setter ref)9.28.1 基本的なシーケンスのアクセサ
(setter subseq)9.28.2 シーケンスのスライス
(setter subseq)9.28.2 シーケンスのスライス
(setter ~)6.18.2 万能アクセサ

*
*6.3.4 数値の演算
*.6.3.4 数値の演算

+
+6.3.4 数値の演算
+.6.3.4 数値の演算

-
-6.3.4 数値の演算
-.6.3.4 数値の演算
->char-set11.7.1 文字セットの構築子

.
.$6.18.3 コンビネータ

/
/6.3.4 数値の演算
/.6.3.4 数値の演算

:
:生成的qualifier
:char-range生成的qualifier
:dispatched生成的qualifier
:do生成的qualifier
:do生成的qualifier
:generator生成的qualifier
:integers生成的qualifier
:let生成的qualifier
:list生成的qualifier
:parallel生成的qualifier
:port生成的qualifier
:port生成的qualifier
:range生成的qualifier
:range生成的qualifier
:range生成的qualifier
:real-range生成的qualifier
:real-range生成的qualifier
:real-range生成的qualifier
:string生成的qualifier
:until生成的qualifier
:vector生成的qualifier
:while生成的qualifier

<
<6.3.3 数値の比較
<=6.3.3 数値の比較
<=?6.2.4.2 比較器にまつわる述語とアクセサ
<?6.2.4.2 比較器にまつわる述語とアクセサ
<gauche-package-description>Utility procedures

=
=6.3.3 数値の比較
=?6.2.4.2 比較器にまつわる述語とアクセサ

>
>6.3.3 数値の比較
>=6.3.3 数値の比較
>=?6.2.4.2 比較器にまつわる述語とアクセサ
>?6.2.4.2 比較器にまつわる述語とアクセサ

^
^4.3 手続きを作る
^a4.3 手続きを作る
^b4.3 手続きを作る
^c4.3 手続きを作る
^d4.3 手続きを作る
^e4.3 手続きを作る
^f4.3 手続きを作る
^g4.3 手続きを作る
^h4.3 手続きを作る
^i4.3 手続きを作る
^j4.3 手続きを作る
^k4.3 手続きを作る
^l4.3 手続きを作る
^m4.3 手続きを作る
^n4.3 手続きを作る
^o4.3 手続きを作る
^p4.3 手続きを作る
^q4.3 手続きを作る
^r4.3 手続きを作る
^s4.3 手続きを作る
^t4.3 手続きを作る
^u4.3 手続きを作る
^v4.3 手続きを作る
^w4.3 手続きを作る
^x4.3 手続きを作る
^y4.3 手続きを作る
^z4.3 手続きを作る
^_4.3 手続きを作る

~
~6.18.2 万能アクセサ

A
abandoned-mutex-exception?9.32.4 スレッド例外
abs6.3.4 数値の演算
absolute-path?12.23.2 パスネームユーティリティ
acons6.6.7 連想リスト
acos6.3.4 数値の演算
acosh6.3.4 数値の演算
add-duration11.8.3 時間に関する手続き
add-duration!11.8.3 時間に関する手続き
add-hook!9.11 gauche.hook - フック
add-job!12.5 control.thread-pool - スレッドプール
add-load-path6.23.1 Schemeファイルのロード
address-familyAddress family
address-infoAddress info
adler32Miscellaneous API
alist->bagCopying and conversion
alist->hash-table6.15 ハッシュテーブル
alist->hash-table11.16 srfi-69 - 基本的なハッシュテーブル
alist->imap12.10 data.imap - 変更不可なマップ
alist->imap12.10 data.imap - 変更不可なマップ
alist->imap12.10 data.imap - 変更不可なマップ
alist->rbtree8.3 廃止されたモジュール
alist->tree-map6.16 ツリーマップ
alist->tree-map6.16 ツリーマップ
alist-consAssociation lists
alist-copy6.6.7 連想リスト
alist-delete6.6.7 連想リスト
alist-delete!6.6.7 連想リスト
all-modules4.13.6 モジュールイントロスペクション
allocate-instance7.3.1 インスタンスの作成
allocate-instance7.3.1 インスタンスの作成
and4.5 条件式
andControl qualifiers
and-let*4.6 変数束縛
and-let14.6 変数束縛
angle6.3.5 数値の変換
any6.6.5 リストをたどる手続き
any$6.18.3 コンビネータ
any-bits-set?11.15.1 Bitwise operators
any-in-queue12.11 data.queue - キュー
any-pred6.18.3 コンビネータ
any?-ec内包表記マクロ
append6.6.6 他のリスト手続き
append!6.6.6 他のリスト手続き
append-ec内包表記マクロ
append-map6.6.5 リストをたどる手続き
append-map!6.6.5 リストをたどる手続き
append-reverse6.6.6 他のリスト手続き
append-reverse!6.6.6 他のリスト手続き
applicable?6.18.1 Procedure class and applicability
apply6.18.1 Procedure class and applicability
apply$6.18.3 コンビネータ
apply-generic7.5.4 メソッド適用のカスタマイズ
apply-method7.5.4 メソッド適用のカスタマイズ
apply-methods7.5.4 メソッド適用のカスタマイズ
apropos9.12 gauche.interactive - インタラクティブセッション
args-fold11.11 srfi-37 - args-fold プログラム引数処理
arithmetic-shift11.15.4 Field of bits
arity6.18.5 手続きのアリティ
arity-at-least-value6.18.5 手続きのアリティ
arity-at-least?6.18.5 手続きのアリティ
array9.1 gauche.array - 配列
array->list9.1 gauche.array - 配列
array->vector9.1 gauche.array - 配列
array-add-elements9.1 gauche.array - 配列
array-add-elements!9.1 gauche.array - 配列
array-concatenate9.1 gauche.array - 配列
array-div-elements9.1 gauche.array - 配列
array-div-elements!9.1 gauche.array - 配列
array-div-left9.1 gauche.array - 配列
array-div-right9.1 gauche.array - 配列
array-end9.1 gauche.array - 配列
array-expt9.1 gauche.array - 配列
array-flip9.1 gauche.array - 配列
array-flip!9.1 gauche.array - 配列
array-for-each-index9.1 gauche.array - 配列
array-inverse9.1 gauche.array - 配列
array-length9.1 gauche.array - 配列
array-map9.1 gauche.array - 配列
array-map9.1 gauche.array - 配列
array-map!9.1 gauche.array - 配列
array-map!9.1 gauche.array - 配列
array-mul9.1 gauche.array - 配列
array-mul-elements9.1 gauche.array - 配列
array-mul-elements!9.1 gauche.array - 配列
array-rank9.1 gauche.array - 配列
array-ref9.1 gauche.array - 配列
array-ref9.1 gauche.array - 配列
array-retabulate!9.1 gauche.array - 配列
array-retabulate!9.1 gauche.array - 配列
array-rotate-909.1 gauche.array - 配列
array-set!9.1 gauche.array - 配列
array-set!9.1 gauche.array - 配列
array-shape9.1 gauche.array - 配列
array-size9.1 gauche.array - 配列
array-start9.1 gauche.array - 配列
array-sub-elements9.1 gauche.array - 配列
array-sub-elements!9.1 gauche.array - 配列
array-transpose9.1 gauche.array - 配列
array?9.1 gauche.array - 配列
as-nodeset12.45.1 SXPathの基本的なコンバータとアプリケータ
ash6.3.6 ビット演算
ash11.15.4 Field of bits
asin6.3.4 数値の演算
asinh6.3.4 数値の演算
assert-curr-char12.53 text.parse - 入力ストリームのパージング
assoc6.6.7 連想リスト
assoc$6.18.3 コンビネータ
assoc-ref6.6.7 連想リスト
assoc-set!6.6.7 連想リスト
assq6.6.7 連想リスト
assq-ref6.6.7 連想リスト
assq-set!6.6.7 連想リスト
assv6.6.7 連想リスト
assv-ref6.6.7 連想リスト
assv-set!6.6.7 連想リスト
atan6.3.4 数値の演算
atan6.3.4 数値の演算
atanh6.3.4 数値の演算
atomアトム
atom-refアトム
atom?アトム
atomicアトム
atomic-update!アトム
attlist->alist12.44.1 SSAXデータタイプ
attlist-add12.44.1 SSAXデータタイプ
attlist-fold12.44.1 SSAXデータタイプ
attlist-null?12.44.1 SSAXデータタイプ
attlist-remove-top12.44.1 SSAXデータタイプ
autoload6.23.4 Autoload

B
bagConstructors
bag->alistCopying and conversion
bag->listCopying and conversion
bag->setCopying and conversion
bag-adjoinUpdaters
bag-adjoin!Updaters
bag-any?The whole set
bag-contains?Predicates
bag-copyCopying and conversion
bag-countThe whole set
bag-decrement!Bag-specific procedures
bag-deleteUpdaters
bag-delete!Updaters
bag-delete-allUpdaters
bag-delete-all!Updaters
bag-differenceSet theory operations
bag-difference!Set theory operations
bag-disjoint?Predicates
bag-element-comparatorAccessors
bag-element-countBag-specific procedures
bag-empty?Predicates
bag-every?The whole set
bag-filterMapping and folding
bag-filter!Mapping and folding
bag-findThe whole set
bag-foldMapping and folding
bag-fold-uniqueBag-specific procedures
bag-for-eachMapping and folding
bag-for-each-uniqueBag-specific procedures
bag-increment!Bag-specific procedures
bag-intersectionSet theory operations
bag-intersection!Set theory operations
bag-mapMapping and folding
bag-memberAccessors
bag-partitionMapping and folding
bag-partition!Mapping and folding
bag-productBag-specific procedures
bag-product!Bag-specific procedures
bag-removeMapping and folding
bag-remove!Mapping and folding
bag-replaceUpdaters
bag-replace!Updaters
bag-search!Updaters
bag-sizeThe whole set
bag-sumBag-specific procedures
bag-sum!Bag-specific procedures
bag-unfoldConstructors
bag-unionSet theory operations
bag-union!Set theory operations
bag-unique-sizeBag-specific procedures
bag-xorSet theory operations
bag-xor!Set theory operations
bag<=?Subsets
bag<?Subsets
bag=?Subsets
bag>=?Subsets
bag>?Subsets
base64-decode12.29 rfc.base64 - Base64エンコーディング
base64-decode-string12.29 rfc.base64 - Base64エンコーディング
base64-encode12.29 rfc.base64 - Base64エンコーディング
base64-encode-string12.29 rfc.base64 - Base64エンコーディング
bcrypt-gensalt12.6 crypt.bcrypt - パスワードハッシュ
bcrypt-hashpw12.6 crypt.bcrypt - パスワードハッシュ
beepConsole control
begin4.7 順次実行
beginControl qualifiers
begin04.7 順次実行
bignum?6.3.2 数値に関する述語
bimap-left9.8.2 汎用ディクショナリ
bimap-left-delete!9.8.2 汎用ディクショナリ
bimap-left-exists?9.8.2 汎用ディクショナリ
bimap-left-get9.8.2 汎用ディクショナリ
bimap-put!9.8.2 汎用ディクショナリ
bimap-right9.8.2 汎用ディクショナリ
bimap-right-delete!9.8.2 汎用ディクショナリ
bimap-right-exists?9.8.2 汎用ディクショナリ
bimap-right-get9.8.2 汎用ディクショナリ
binary-heap-clear!12.8 data.heap - ヒープ
binary-heap-copy12.8 data.heap - ヒープ
binary-heap-delete!12.8 data.heap - ヒープ
binary-heap-empty?12.8 data.heap - ヒープ
binary-heap-find12.8 data.heap - ヒープ
binary-heap-find-max12.8 data.heap - ヒープ
binary-heap-find-min12.8 data.heap - ヒープ
binary-heap-num-entries12.8 data.heap - ヒープ
binary-heap-pop-max!12.8 data.heap - ヒープ
binary-heap-pop-min!12.8 data.heap - ヒープ
binary-heap-push!12.8 data.heap - ヒープ
binary-heap-remove!12.8 data.heap - ヒープ
binary-heap-swap-max!12.8 data.heap - ヒープ
binary-heap-swap-min!12.8 data.heap - ヒープ
binary-port?入出力
bindtextdomaingettext-互換の API
bit-count11.15.2 Integer properties
bit-field6.3.6 ビット演算
bit-field11.15.4 Field of bits
bit-set?11.15.3 Bit within word
bits->generator9.10.1 ジェネレータの生成
bitwise-and11.15.1 Bitwise operators
bitwise-if11.15.1 Bitwise operators
bitwise-ior11.15.1 Bitwise operators
bitwise-not11.15.1 Bitwise operators
bitwise-xor11.15.1 Bitwise operators
boolean6.4 論理値
boolean-hash6.2.3 ハッシュ
boolean=?6.4 論理値
boolean?6.4 論理値
booleansプリミティブデータ型のジェネレータ
booleans->integer11.15.5 Bit as booleans
box11.19 srfi-111 - ボックス
box?11.19 srfi-111 - ボックス
bpsw-prime?素数かどうかを調べる
breakList searching
break!List searching
break-list-by-sequenceSelection and searching
break-list-by-sequence!Selection and searching
build-binary-heap12.8 data.heap - ヒープ
build-path12.23.2 パスネームユーティリティ
build-transliterator12.56 text.tr - 文字変換
byte-ready?6.22.7.1 データの読み込み
bytevectorバイトベクタユーティリティ
bytevector->generator9.10.1 ジェネレータの生成
bytevector-appendバイトベクタユーティリティ
bytevector-copyバイトベクタユーティリティ
bytevector-copy!バイトベクタユーティリティ
bytevector-lengthバイトベクタユーティリティ
bytevector-u8-refバイトベクタユーティリティ
bytevector-u8-set!バイトベクタユーティリティ
bytevector?バイトベクタユーティリティ

C
caaaar6.6.4 リストへのアクセスと変更
caaadr6.6.4 リストへのアクセスと変更
caaar6.6.4 リストへのアクセスと変更
caadar6.6.4 リストへのアクセスと変更
caaddr6.6.4 リストへのアクセスと変更
caadr6.6.4 リストへのアクセスと変更
caar6.6.4 リストへのアクセスと変更
cache-check!Implementing a cache algorithm
cache-clear!Common operations of caches
cache-compact-queue!Implementing a cache algorithm
cache-comparatorImplementing a cache algorithm
cache-evict!Common operations of caches
cache-lookup!Common operations of caches
cache-populate-queue!Implementing a cache algorithm
cache-register!Implementing a cache algorithm
cache-renumber-entries!Implementing a cache algorithm
cache-storageImplementing a cache algorithm
cache-through!Common operations of caches
cache-write!Common operations of caches
cadaar6.6.4 リストへのアクセスと変更
cadadr6.6.4 リストへのアクセスと変更
cadar6.6.4 リストへのアクセスと変更
caddar6.6.4 リストへのアクセスと変更
cadddr6.6.4 リストへのアクセスと変更
caddr6.6.4 リストへのアクセスと変更
cadr6.6.4 リストへのアクセスと変更
calculate-dominators12.60 util.dominator - 支配木
call-with-builder9.5.4 基礎的なイテレータ構築メソッド
call-with-cgi-script12.71 www.cgi.test - CGIのテスト
call-with-client-socket9.19.2 高レベルネットワーク手続き
call-with-consoleConsole control
call-with-current-continuation6.18.7 継続
call-with-ftp-connection12.31 rfc.ftp - FTPクライアント
call-with-input-conversion9.4.3 変換ポート
call-with-input-file6.22.4 ファイルポート
call-with-input-process9.24.4 Process ports
call-with-input-string6.22.5 文字列ポート
call-with-iterator9.5.4 基礎的なイテレータ構築メソッド
call-with-iterators9.5.4 基礎的なイテレータ構築メソッド
call-with-output-conversion9.4.3 変換ポート
call-with-output-file6.22.4 ファイルポート
call-with-output-process9.24.4 Process ports
call-with-output-string6.22.5 文字列ポート
call-with-port6.22.3 ポート共通の操作
call-with-process-io9.24.4 Process ports
call-with-string-io6.22.5 文字列ポート
call-with-values6.18.8 多値
call/cc6.18.7 継続
call/pc9.23 gauche.partcont - 部分継続
car6.6.4 リストへのアクセスと変更
car+cdrList selectors
car-sxpath12.45.2 SXPathクエリ言語
cartesian-product12.58 util.combinations - 組み合わせ
cartesian-product-right12.58 util.combinations - 組み合わせ
case4.5 条件式
case-lambda4.3 手続きを作る
cdaaar6.6.4 リストへのアクセスと変更
cdaadr6.6.4 リストへのアクセスと変更
cdaar6.6.4 リストへのアクセスと変更
cdadar6.6.4 リストへのアクセスと変更
cdaddr6.6.4 リストへのアクセスと変更
cdadr6.6.4 リストへのアクセスと変更
cdar6.6.4 リストへのアクセスと変更
cddaar6.6.4 リストへのアクセスと変更
cddadr6.6.4 リストへのアクセスと変更
cddar6.6.4 リストへのアクセスと変更
cdddar6.6.4 リストへのアクセスと変更
cddddr6.6.4 リストへのアクセスと変更
cdddr6.6.4 リストへのアクセスと変更
cddr6.6.4 リストへのアクセスと変更
cdr6.6.4 リストへのアクセスと変更
ceiling6.3.4 数値の演算
ceiling->exact6.3.4 数値の演算
ces-conversion-supported?9.4.1 サポートされる文字エンコーディング
ces-convert9.4.3 変換ポート
ces-equivalent?9.4.1 サポートされる文字エンコーディング
ces-guess-from-string9.4.2 文字エンコーディングの自動判定
ces-upper-compatible?9.4.1 サポートされる文字エンコーディング
cf$パラメータと定義
cf-arg-enableコマンドライン引数
cf-arg-varパラメータと定義
cf-arg-withコマンドライン引数
cf-check-header定義済みテスト
cf-check-headers定義済みテスト
cf-check-prog定義済みテスト
cf-defineパラメータと定義
cf-echoメッセージ
cf-feature-refコマンドライン引数
cf-have-subst?パラメータと定義
cf-help-stringコマンドライン引数
cf-includes-default定義済みテスト
cf-init初期化
cf-langコンパイラを走らせる
cf-lang-callコンパイラを走らせる
cf-lang-io-programコンパイラを走らせる
cf-lang-programコンパイラを走らせる
cf-make-gpd出力
cf-msg-checkingメッセージ
cf-msg-errorメッセージ
cf-msg-resultメッセージ
cf-msg-warnメッセージ
cf-output出力
cf-package-refコマンドライン引数
cf-path-prog定義済みテスト
cf-prog-cxx定義済みテスト
cf-refパラメータと定義
cf-show-variables出力
cf-substパラメータと定義
cf-try-compileコンパイラを走らせる
cf-try-compile-and-linkコンパイラを走らせる
cgen-add!Submitting code fragments for more than one parts
cgen-bodyFilling the content
cgen-box-expr9.3.3 SchemeとCの間の変換
cgen-cexpr9.3.2 Schemeリテラルを生成する
cgen-current-unitCreating a frame
cgen-declFilling the content
cgen-emit-bodySubmitting code fragments for more than one parts
cgen-emit-cCreating a frame
cgen-emit-declSubmitting code fragments for more than one parts
cgen-emit-hCreating a frame
cgen-emit-initSubmitting code fragments for more than one parts
cgen-emit-xtrnSubmitting code fragments for more than one parts
cgen-externFilling the content
cgen-initFilling the content
cgen-literal9.3.2 Schemeリテラルを生成する
cgen-pred-expr9.3.3 SchemeとCの間の変換
cgen-safe-commentFilling the content
cgen-safe-nameFilling the content
cgen-safe-name-friendlyFilling the content
cgen-safe-stringFilling the content
cgen-type-from-name9.3.3 SchemeとCの間の変換
cgen-unbox-expr9.3.3 SchemeとCの間の変換
cgen-unit-c-fileCreating a frame
cgen-unit-h-fileCreating a frame
cgen-unit-init-nameCreating a frame
cgen-with-cpp-conditionFilling the content
cgi-add-temporary-file便利な手続き
cgi-get-metavariableメタ変数
cgi-get-parameterパラメータの取得
cgi-header出力の生成
cgi-main便利な手続き
cgi-metavariablesメタ変数
cgi-output-character-encoding出力の生成
cgi-parse-parametersパラメータの取得
cgi-temporary-files便利な手続き
cgi-test-enviornment-ref12.71 www.cgi.test - CGIのテスト
change-classクラス変更プロトコル
change-classクラス変更プロトコル
change-object-classクラス変更プロトコル
char->integer6.10 文字
char->ucs6.10 文字
char-alphabetic?6.10 文字
char-ci-hash6.2.3 ハッシュ
char-ci<=?6.10 文字
char-ci<?6.10 文字
char-ci=?6.10 文字
char-ci>=?6.10 文字
char-ci>?6.10 文字
char-downcase6.10 文字
char-foldcase6.10 文字
char-general-category6.10 文字
char-hash6.2.3 ハッシュ
char-lower-case?6.10 文字
char-numeric?6.10 文字
char-ready?6.22.7.1 データの読み込み
char-set6.11 文字集合
char-set6.11 文字集合
char-set->list11.7.4 文字セットへの問い合わせ
char-set->string11.7.4 文字セットへの問い合わせ
char-set-adjoin11.7.5 文字セットに適用できる代数的関数
char-set-adjoin!11.7.5 文字セットに適用できる代数的関数
char-set-any11.7.4 文字セットへの問い合わせ
char-set-complement6.11 文字集合
char-set-complement!6.11 文字集合
char-set-contains?6.11 文字集合
char-set-copy6.11 文字集合
char-set-count11.7.4 文字セットへの問い合わせ
char-set-cursor11.7.3 文字セットのイテレーション
char-set-cursor-next11.7.3 文字セットのイテレーション
char-set-delete11.7.5 文字セットに適用できる代数的関数
char-set-delete!11.7.5 文字セットに適用できる代数的関数
char-set-diff+intersection11.7.5 文字セットに適用できる代数的関数
char-set-diff+intersection!11.7.5 文字セットに適用できる代数的関数
char-set-difference11.7.5 文字セットに適用できる代数的関数
char-set-difference!11.7.5 文字セットに適用できる代数的関数
char-set-every11.7.4 文字セットへの問い合わせ
char-set-filter11.7.1 文字セットの構築子
char-set-filter!11.7.1 文字セットの構築子
char-set-fold11.7.3 文字セットのイテレーション
char-set-for-each11.7.3 文字セットのイテレーション
char-set-hash11.7.2 文字セットの比較
char-set-intersection11.7.5 文字セットに適用できる代数的関数
char-set-intersection!11.7.5 文字セットに適用できる代数的関数
char-set-map11.7.3 文字セットのイテレーション
char-set-ref11.7.3 文字セットのイテレーション
char-set-size6.11 文字集合
char-set-unfold11.7.3 文字セットのイテレーション
char-set-unfold!11.7.3 文字セットのイテレーション
char-set-union11.7.5 文字セットに適用できる代数的関数
char-set-union!11.7.5 文字セットに適用できる代数的関数
char-set-xor11.7.5 文字セットに適用できる代数的関数
char-set-xor!11.7.5 文字セットに適用できる代数的関数
char-set<=11.7.2 文字セットの比較
char-set=11.7.2 文字セットの比較
char-set?6.11 文字集合
char-titlecase6.10 文字
char-upcase6.10 文字
char-upper-case?6.10 文字
char-whitespace?6.10 文字
char<=?6.10 文字
char<?6.10 文字
char=?6.10 文字
char>=?6.10 文字
char>?6.10 文字
char?6.10 文字
chars$プリミティブデータ型のジェネレータ
check-directory-tree12.23.1 ディレクトリユーティリティ
check-substring-spec11.6.14 低レベルな文字列に関する手続き
chready?Console control
circular-generator9.10.1 ジェネレータの生成
circular-listList constructors
circular-list?6.6.2 リストに関する述語
cise-ambient-copy9.3.4.3 CiSE procedures
cise-ambient-decl-strings9.3.4.3 CiSE procedures
cise-default-ambient9.3.4.3 CiSE procedures
cise-lookup-macro9.3.4.3 CiSE procedures
cise-register-macro!9.3.4.3 CiSE procedures
cise-render9.3.4.3 CiSE procedures
cise-render-rec9.3.4.3 CiSE procedures
cise-render-to-string9.3.4.3 CiSE procedures
cise-translate9.3.4.3 CiSE procedures
clamp6.3.4 数値の演算
class-direct-methods7.2.3 クラスオブジェクト
class-direct-slots7.2.3 クラスオブジェクト
class-direct-subclasses7.2.3 クラスオブジェクト
class-direct-supers7.2.3 クラスオブジェクト
class-name7.2.3 クラスオブジェクト
class-of6.1 型とクラス
class-post-initialize<class> 用の initialize メソッド
class-precedence-list7.2.3 クラスオブジェクト
class-slot-accessor7.2.3 クラスオブジェクト
class-slot-bound?特殊アクセサ
class-slot-definition7.2.3 クラスオブジェクト
class-slot-ref特殊アクセサ
class-slot-set!特殊アクセサ
class-slots7.2.3 クラスオブジェクト
clear-screenConsole control
clear-to-eolConsole control
clear-to-eosConsole control
close-input-port6.22.3 ポート共通の操作
close-output-port6.22.3 ポート共通の操作
close-port6.22.3 ポート共通の操作
code11.4 srfi-7 - 機能ベースプログラム設定言語
codepoints->grapheme-clusters9.34.2 Unicode text segmentation
codepoints->words9.34.2 Unicode text segmentation
codepoints-downcase9.34.3 Full string case conversion
codepoints-foldcase9.34.3 Full string case conversion
codepoints-titlecase9.34.3 Full string case conversion
codepoints-upcase9.34.3 Full string case conversion
coerce-to9.5.3 コレクションに対する様々な操作
combinations12.58 util.combinations - 組み合わせ
combinations*12.58 util.combinations - 組み合わせ
combinations*-for-each12.58 util.combinations - 組み合わせ
combinations-for-each12.58 util.combinations - 組み合わせ
combinations-of複合データ型のジェネレータ
combine-hash-value6.2.3 ハッシュ
command-line6.25.2 コマンドライン引数
common-prefixPrefix
common-prefix-toPrefix
comparator-check-type6.2.4.2 比較器にまつわる述語とアクセサ
comparator-compare6.2.4.2 比較器にまつわる述語とアクセサ
comparator-comparison-procedure6.2.4.2 比較器にまつわる述語とアクセサ
comparator-comparison-procedure?Basic comparator interface
comparator-equal?Basic comparator interface
comparator-equality-predicate6.2.4.2 比較器にまつわる述語とアクセサ
comparator-flavor6.2.4.2 比較器にまつわる述語とアクセサ
comparator-hash6.2.4.2 比較器にまつわる述語とアクセサ
comparator-hash-function6.2.4.2 比較器にまつわる述語とアクセサ
comparator-hash-function?Basic comparator interface
comparator-hashable?6.2.4.2 比較器にまつわる述語とアクセサ
comparator-maxMin/max comparison procedures
comparator-minMin/max comparison procedures
comparator-ordered?6.2.4.2 比較器にまつわる述語とアクセサ
comparator-ordering-predicate6.2.4.2 比較器にまつわる述語とアクセサ
comparator-register-default!6.2.4.3 用意されている比較器
comparator-test-type6.2.4.2 比較器にまつわる述語とアクセサ
comparator-type-test-procedure6.2.4.2 比較器にまつわる述語とアクセサ
comparator-type-test-procedureBasic comparator interface
comparator?6.2.4.2 比較器にまつわる述語とアクセサ
compare6.2.2 比較
complement6.18.3 コンビネータ
complete-sexp?Listener API
complex?6.3.2 数値に関する述語
compose6.18.3 コンビネータ
compute-cpl<class> 用の initialize メソッド
compute-get-n-set7.5.2 スロットアクセスのカスタマイズ
compute-get-n-set7.5.2 スロットアクセスのカスタマイズ
compute-slot-accessor7.5.2 スロットアクセスのカスタマイズ
compute-slot-accessor7.5.2 スロットアクセスのカスタマイズ
compute-slots7.5.2 スロットアクセスのカスタマイズ
compute-slots7.5.2 スロットアクセスのカスタマイズ
concatenate6.6.6 他のリスト手続き
concatenate!6.6.6 他のリスト手続き
cond4.5 条件式
cond-expandcond-expandマクロ
cond-list6.6.3 リストの作成
conditionコンディションAPI
condition-has-type?コンディションAPI
condition-refコンディションAPI
condition-type?コンディションAPI
condition-variable-broadcast!条件変数
condition-variable-name条件変数
condition-variable-signal!条件変数
condition-variable-specific条件変数
condition-variable-specific-set!条件変数
condition-variable?条件変数
condition?コンディションAPI
cons6.6.3 リストの作成
cons*6.6.3 リストの作成
console-device12.23.2 パスネームユーティリティ
construct-cookie-string12.30 rfc.cookie - HTTPクッキー
construct-cssConstructing CSS
construct-json12.36 rfc.json - JSONのパーズと構築
construct-json-string12.36 rfc.json - JSONのパーズと構築
continued-fraction6.3.4 数値の演算
copy-bit6.3.6 ビット演算
copy-bit11.15.3 Bit within word
copy-bit-field6.3.6 ビット演算
copy-bit-field11.15.4 Field of bits
copy-directory*12.23.1 ディレクトリユーティリティ
copy-file12.23.4 ファイル操作
copy-port6.22.3 ポート共通の操作
copy-queue12.11 data.queue - キュー
copy-time11.8.3 時間に関する手続き
cos6.3.4 数値の演算
cosh6.3.4 数値の演算
count6.6.5 リストをたどる手続き
count$6.18.3 コンビネータ
cpu-architecture11.20 srfi-112 - 実行環境の問い合わせ
crc32Miscellaneous API
create-directory*12.23.1 ディレクトリユーティリティ
create-directory-tree12.23.1 ディレクトリユーティリティ
csv-rows->tuples中レベルAPI
current-class-of特殊アクセサ
current-countryバンドル指定子
current-date11.8.2 時間に関する問い合わせ
current-directory12.23.1 ディレクトリユーティリティ
current-error-port6.22.3 ポート共通の操作
current-exception-handler下位レベルの例外処理機構
current-input-port6.22.3 ポート共通の操作
current-jiffy10.17 scheme.time - R7RS time
current-julian-day11.8.2 時間に関する問い合わせ
current-languageバンドル指定子
current-load-history6.23.1 Schemeファイルのロード
current-load-next6.23.1 Schemeファイルのロード
current-load-path6.23.1 Schemeファイルのロード
current-load-port6.23.1 Schemeファイルのロード
current-locale-detailsバンドル指定子
current-modified-julian-day11.8.2 時間に関する問い合わせ
current-module4.13.3 モジュールの定義と選択
current-output-port6.22.3 ポート共通の操作
current-second10.17 scheme.time - R7RS time
current-thread9.32.2 スレッド手続き
current-timeSRFI time
current-time11.8.2 時間に関する問い合わせ
cursor-down/scroll-upConsole control
cursor-up/scroll-downConsole control
cut4.3 手続きを作る
cute4.3 手続きを作る

D
d9.12 gauche.interactive - インタラクティブセッション
date->julian-day11.8.4 日付
date->modified-julian-day11.8.4 日付
date->rfc822-dateメッセージの構築
date->string11.8.5 日付の読み書き
date->time-monotonic11.8.4 日付
date->time-tai11.8.4 日付
date->time-utc11.8.4 日付
date-day11.8.4 日付
date-hour11.8.4 日付
date-minute11.8.4 日付
date-month11.8.4 日付
date-nanosecond11.8.4 日付
date-second11.8.4 日付
date-week-day11.8.4 日付
date-week-number11.8.4 日付
date-year11.8.4 日付
date-year-day11.8.4 日付
date-zone-offset11.8.4 日付
date?11.8.4 日付
dbi-closeデータベースへの接続
dbi-closeクエリの準備と発行
dbi-closeクエリの結果を見る
dbi-close実装するDBIメソッド
dbi-close実装するDBIメソッド
dbi-close実装するDBIメソッド
dbi-connectデータベースへの接続
dbi-doクエリの準備と発行
dbi-do実装するDBIメソッド
dbi-escape-sqlクエリの準備と発行
dbi-escape-sql実装するDBIメソッド
dbi-executeクエリの準備と発行
dbi-execute-using-connection実装するDBIメソッド
dbi-list-driversデータベースへの接続
dbi-make-connection実装するDBIメソッド
dbi-make-driverデータベースへの接続
dbi-open?データベースへの接続
dbi-open?クエリの準備と発行
dbi-open?クエリの結果を見る
dbi-open?実装するDBIメソッド
dbi-open?実装するDBIメソッド
dbi-open?実装するDBIメソッド
dbi-parse-dsnDBIのユーティリティ関数
dbi-prepareクエリの準備と発行
dbi-prepare実装するDBIメソッド
dbi-prepare-sqlDBIのユーティリティ関数
dbm-close12.17.1 DBMデータベースのオープンとクローズ
dbm-closed?12.17.1 DBMデータベースのオープンとクローズ
dbm-db-copy12.17.4 DBMデータベースインスタンスの管理
dbm-db-exists?12.17.4 DBMデータベースインスタンスの管理
dbm-db-move12.17.4 DBMデータベースインスタンスの管理
dbm-db-remove12.17.4 DBMデータベースインスタンスの管理
dbm-delete!12.17.2 DBMデータベースのアクセス
dbm-exists?12.17.2 DBMデータベースのアクセス
dbm-fold12.17.3 DBMデータベース上の繰り返し処理
dbm-for-each12.17.3 DBMデータベース上の繰り返し処理
dbm-get12.17.2 DBMデータベースのアクセス
dbm-map12.17.3 DBMデータベース上の繰り返し処理
dbm-open12.17.1 DBMデータベースのオープンとクローズ
dbm-open12.17.1 DBMデータベースのオープンとクローズ
dbm-put!12.17.2 DBMデータベースのアクセス
dbm-type->class12.17.1 DBMデータベースのオープンとクローズ
dcgettextgettext-互換の API
debug-funcall6.26.1 デバッグ補助
debug-label6.26.1 デバッグ補助
debug-print6.26.1 デバッグ補助
debug-print-width6.26.1 デバッグ補助
debug-source-info6.26.1 デバッグ補助
dec!4.4 代入
declare-bundle!バンドル準備
decode-float6.3.5 数値の変換
decompose-path12.23.2 パスネームユーティリティ
default-endian6.3.7 エンディアン
default-hash6.2.3 ハッシュ
default-sizer複合データ型のジェネレータ
define4.10 定義
define4.10 定義
define-cise-expr9.3.4.3 CiSE procedures
define-cise-macro9.3.4.3 CiSE procedures
define-cise-macro9.3.4.3 CiSE procedures
define-cise-stmt9.3.4.3 CiSE procedures
define-cise-toplevel9.3.4.3 CiSE procedures
define-class7.2.1 クラスの定義
define-condition-typeコンディションAPI
define-constant4.10 定義
define-constant4.10 定義
define-dict-interface9.8.1 ディクショナリのためのジェネリック関数
define-genericメソッドの定義
define-in-module4.10 定義
define-in-module4.10 定義
define-library10.3 R7RSライブラリ形式
define-macro5.3 伝統的なマクロ
define-macro5.3 伝統的なマクロ
define-methodメソッドの定義
define-module4.13.3 モジュールの定義と選択
define-reader-ctor6.22.7.3 読み込み時コンストラクタ
define-record-type9.25.2 Syntactic Layer
define-syntaxマクロ束縛
define-values4.10 定義
define-values4.10 定義
define-values4.10 定義
deflate-stringMiscellaneous API
deflating-port-full-flushOperations on inflating/deflating ports
delay6.19.1 Delayとforceとlazy
delay-force10.12 scheme.lazy - R7RS遅延評価
delete6.6.5 リストをたどる手続き
delete!6.6.5 リストをたどる手続き
delete$6.18.3 コンビネータ
delete-directory*12.23.1 ディレクトリユーティリティ
delete-duplicates6.6.5 リストをたどる手続き
delete-duplicates!6.6.5 リストをたどる手続き
delete-file10.10 scheme.file - R7RSファイルライブラリ
delete-file12.23.4 ファイル操作
delete-files12.23.4 ファイル操作
delete-hook!9.11 gauche.hook - フック
delete-keyword6.8 キーワード
delete-keyword!6.8 キーワード
delete-keywords6.8 キーワード
delete-keywords!6.8 キーワード
denominator6.3.4 数値の演算
dequeue!12.11 data.queue - キュー
dequeue-all!12.11 data.queue - キュー
dequeue/wait!12.11 data.queue - キュー
describe9.12 gauche.interactive - インタラクティブセッション
determinant9.1 gauche.array - 配列
determinant!9.1 gauche.array - 配列
dgettextgettext-互換の API
dict->alist9.8.1 ディクショナリのためのジェネリック関数
dict-clear!9.8.1 ディクショナリのためのジェネリック関数
dict-comparator9.8.1 ディクショナリのためのジェネリック関数
dict-delete!9.8.1 ディクショナリのためのジェネリック関数
dict-exists?9.8.1 ディクショナリのためのジェネリック関数
dict-fold9.8.1 ディクショナリのためのジェネリック関数
dict-fold-right9.8.1 ディクショナリのためのジェネリック関数
dict-for-each9.8.1 ディクショナリのためのジェネリック関数
dict-get9.8.1 ディクショナリのためのジェネリック関数
dict-keys9.8.1 ディクショナリのためのジェネリック関数
dict-map9.8.1 ディクショナリのためのジェネリック関数
dict-pop!9.8.1 ディクショナリのためのジェネリック関数
dict-push!9.8.1 ディクショナリのためのジェネリック関数
dict-put!9.8.1 ディクショナリのためのジェネリック関数
dict-update!9.8.1 ディクショナリのためのジェネリック関数
dict-values9.8.1 ディクショナリのためのジェネリック関数
diff12.50 text.diff - テキストストリームの相違点を計算する
diff-report12.50 text.diff - テキストストリームの相違点を計算する
digest12.59 util.digest - メッセージダイジェストフレームワーク
digest-final!12.59 util.digest - メッセージダイジェストフレームワーク
digest-hexify12.59 util.digest - メッセージダイジェストフレームワーク
digest-string12.59 util.digest - メッセージダイジェストフレームワーク
digest-update!12.59 util.digest - メッセージダイジェストフレームワーク
digit->integer6.10 文字
digit-value10.6 scheme.char - R7RS文字ライブラリ
directory-fold12.23.1 ディレクトリユーティリティ
directory-list12.23.1 ディレクトリユーティリティ
directory-list212.23.1 ディレクトリユーティリティ
disasm6.26.1 デバッグ補助
display6.22.8.3 オブジェクトの出力
div6.3.4 数値の演算
div-and-mod6.3.4 数値の演算
div06.3.4 数値の演算
div0-and-mod06.3.4 数値の演算
dl-distance12.63 util.levenshtein - Levenshtein編集距離
dl-distances12.63 util.levenshtein - Levenshtein編集距離
do4.8 繰り返し
do-ec内包表記マクロ
do-generator9.10.3 ジェネレータの消費
dolist4.8 繰り返し
dotimes4.8 繰り返し
dotted-list?6.6.2 リストに関する述語
drop6.6.4 リストへのアクセスと変更
drop*6.6.4 リストへのアクセスと変更
drop-right6.6.4 リストへのアクセスと変更
drop-right!6.6.4 リストへのアクセスと変更
drop-right*6.6.4 リストへのアクセスと変更
drop-whileList searching
dynamic-load6.23.2 ダイナミックライブラリのロード
dynamic-wind6.18.7 継続

E
ecase4.5 条件式
ed9.12 gauche.interactive - インタラクティブセッション
eighthList selectors
emergency-exit10.14 scheme.process-context - R7RSプロセスコンテキスト
encode-float6.3.5 数値の変換
end-of-char-set?11.7.3 文字セットのイテレーション
enqueue!12.11 data.queue - キュー
enqueue-unique!12.11 data.queue - キュー
enqueue/wait!12.11 data.queue - キュー
environment10.9 scheme.eval - R7RS eval
eof-object6.22.7.1 データの読み込み
eof-object?6.22.7.1 データの読み込み
eq-compare6.2.2 比較
eq-hash6.2.3 ハッシュ
eq?6.2.1 等価
equal?6.2.1 等価
eqv-hash6.2.3 ハッシュ
eqv?6.2.1 等価
er-macro-transformer5.2.2 Explcit-renamingマクロ変換器
errorエラーの通知
errorエラーの通知
error-object-irritants制御機能
error-object-message制御機能
error-object?制御機能
errorfエラーの通知
errorfエラーの通知
eval6.21 eval と repl
eval10.9 scheme.eval - R7RS eval
even?6.3.2 数値に関する述語
every6.6.5 リストをたどる手続き
every$6.18.3 コンビネータ
every-in-queue12.11 data.queue - キュー
every-pred6.18.3 コンビネータ
every?-ec内包表記マクロ
exact6.3.5 数値の変換
exact->inexact6.3.5 数値の変換
exact-integer-sqrt6.3.4 数値の演算
exact-integer?6.3.2 数値に関する述語
exact?6.3.2 数値に関する述語
exit6.25.1 プログラムの終了
exit-handler6.25.1 プログラムの終了
exp6.3.4 数値の演算
expand-path12.23.2 パスネームユーティリティ
export4.13.4 モジュールの使用
export-all4.13.4 モジュールの使用
expt6.3.4 数値の演算
expt-mod6.3.4 数値の演算
extend4.13.5 モジュールの継承
extract-conditionコンディションAPI

F
f16array9.1 gauche.array - 配列
f16vector9.35.1 ユニフォームベクタの基本操作
f16vector->list9.35.2 ユニフォームベクタの変換
f16vector->vector9.35.2 ユニフォームベクタの変換
f16vector-add9.35.3 ユニフォームベクタの数値演算
f16vector-add!9.35.3 ユニフォームベクタの数値演算
f16vector-append9.35.1 ユニフォームベクタの基本操作
f16vector-clamp!9.35.3 ユニフォームベクタの数値演算
f16vector-copy9.35.1 ユニフォームベクタの基本操作
f16vector-copy!9.35.1 ユニフォームベクタの基本操作
f16vector-div9.35.3 ユニフォームベクタの数値演算
f16vector-div!9.35.3 ユニフォームベクタの数値演算
f16vector-dot9.35.3 ユニフォームベクタの数値演算
f16vector-fill!9.35.1 ユニフォームベクタの基本操作
f16vector-length9.35.1 ユニフォームベクタの基本操作
f16vector-mul9.35.3 ユニフォームベクタの数値演算
f16vector-mul!9.35.3 ユニフォームベクタの数値演算
f16vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
f16vector-range-check9.35.3 ユニフォームベクタの数値演算
f16vector-ref9.35.1 ユニフォームベクタの基本操作
f16vector-set!9.35.1 ユニフォームベクタの基本操作
f16vector-sub9.35.3 ユニフォームベクタの数値演算
f16vector-sub!9.35.3 ユニフォームベクタの数値演算
f16vector?9.35.1 ユニフォームベクタの基本操作
f32array9.1 gauche.array - 配列
f32vector9.35.1 ユニフォームベクタの基本操作
f32vector->list9.35.2 ユニフォームベクタの変換
f32vector->vector9.35.2 ユニフォームベクタの変換
f32vector-add9.35.3 ユニフォームベクタの数値演算
f32vector-add!9.35.3 ユニフォームベクタの数値演算
f32vector-append9.35.1 ユニフォームベクタの基本操作
f32vector-clamp9.35.3 ユニフォームベクタの数値演算
f32vector-clamp!9.35.3 ユニフォームベクタの数値演算
f32vector-copy9.35.1 ユニフォームベクタの基本操作
f32vector-copy!9.35.1 ユニフォームベクタの基本操作
f32vector-div9.35.3 ユニフォームベクタの数値演算
f32vector-div!9.35.3 ユニフォームベクタの数値演算
f32vector-dot9.35.3 ユニフォームベクタの数値演算
f32vector-fill!9.35.1 ユニフォームベクタの基本操作
f32vector-length9.35.1 ユニフォームベクタの基本操作
f32vector-mul9.35.3 ユニフォームベクタの数値演算
f32vector-mul!9.35.3 ユニフォームベクタの数値演算
f32vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
f32vector-range-check9.35.3 ユニフォームベクタの数値演算
f32vector-ref9.35.1 ユニフォームベクタの基本操作
f32vector-set!9.35.1 ユニフォームベクタの基本操作
f32vector-sub9.35.3 ユニフォームベクタの数値演算
f32vector-sub!9.35.3 ユニフォームベクタの数値演算
f32vector?9.35.1 ユニフォームベクタの基本操作
f64array9.1 gauche.array - 配列
f64vector9.35.1 ユニフォームベクタの基本操作
f64vector->list9.35.2 ユニフォームベクタの変換
f64vector->vector9.35.2 ユニフォームベクタの変換
f64vector-add9.35.3 ユニフォームベクタの数値演算
f64vector-add!9.35.3 ユニフォームベクタの数値演算
f64vector-append9.35.1 ユニフォームベクタの基本操作
f64vector-clamp9.35.3 ユニフォームベクタの数値演算
f64vector-clamp!9.35.3 ユニフォームベクタの数値演算
f64vector-copy9.35.1 ユニフォームベクタの基本操作
f64vector-copy!9.35.1 ユニフォームベクタの基本操作
f64vector-div9.35.3 ユニフォームベクタの数値演算
f64vector-div!9.35.3 ユニフォームベクタの数値演算
f64vector-dot9.35.3 ユニフォームベクタの数値演算
f64vector-fill!9.35.1 ユニフォームベクタの基本操作
f64vector-length9.35.1 ユニフォームベクタの基本操作
f64vector-mul9.35.3 ユニフォームベクタの数値演算
f64vector-mul!9.35.3 ユニフォームベクタの数値演算
f64vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
f64vector-range-check9.35.3 ユニフォームベクタの数値演算
f64vector-ref9.35.1 ユニフォームベクタの基本操作
f64vector-set!9.35.1 ユニフォームベクタの基本操作
f64vector-sub9.35.3 ユニフォームベクタの数値演算
f64vector-sub!9.35.3 ユニフォームベクタの数値演算
f64vector?9.35.1 ユニフォームベクタの基本操作
feature-cond11.4 srfi-7 - 機能ベースプログラム設定言語
features入出力
fifthList selectors
file->byte-generator9.10.1 ジェネレータの生成
file->char-generator9.10.1 ジェネレータの生成
file->generator9.10.1 ジェネレータの生成
file->line-generator9.10.1 ジェネレータの生成
file->list12.23.4 ファイル操作
file->sexp-generator9.10.1 ジェネレータの生成
file->sexp-list12.23.4 ファイル操作
file->string12.23.4 ファイル操作
file->string-list12.23.4 ファイル操作
file-atime12.23.3 ファイル属性ユーティリティ
file-atime<=?12.23.3 ファイル属性ユーティリティ
file-atime<?12.23.3 ファイル属性ユーティリティ
file-atime=?12.23.3 ファイル属性ユーティリティ
file-atime>=?12.23.3 ファイル属性ユーティリティ
file-atime>?12.23.3 ファイル属性ユーティリティ
file-ctime12.23.3 ファイル属性ユーティリティ
file-ctime<=?12.23.3 ファイル属性ユーティリティ
file-ctime<?12.23.3 ファイル属性ユーティリティ
file-ctime=?12.23.3 ファイル属性ユーティリティ
file-ctime>=?12.23.3 ファイル属性ユーティリティ
file-ctime>?12.23.3 ファイル属性ユーティリティ
file-dev12.23.3 ファイル属性ユーティリティ
file-eq?12.23.3 ファイル属性ユーティリティ
file-equal?12.23.3 ファイル属性ユーティリティ
file-eqv?12.23.3 ファイル属性ユーティリティ
file-error?制御機能
file-exists?6.25.4.4 ファイルの状態
file-filter12.22 file.filter - ファイルのフィルタ
file-filter-fold12.22 file.filter - ファイルのフィルタ
file-filter-for-each12.22 file.filter - ファイルのフィルタ
file-filter-map12.22 file.filter - ファイルのフィルタ
file-gid12.23.3 ファイル属性ユーティリティ
file-ino12.23.3 ファイル属性ユーティリティ
file-is-directory?6.25.4.4 ファイルの状態
file-is-executable?12.23.3 ファイル属性ユーティリティ
file-is-readable?12.23.3 ファイル属性ユーティリティ
file-is-regular?6.25.4.4 ファイルの状態
file-is-symlink?12.23.3 ファイル属性ユーティリティ
file-is-writable?12.23.3 ファイル属性ユーティリティ
file-mode12.23.3 ファイル属性ユーティリティ
file-mtime12.23.3 ファイル属性ユーティリティ
file-mtime<=?12.23.3 ファイル属性ユーティリティ
file-mtime<?12.23.3 ファイル属性ユーティリティ
file-mtime=?12.23.3 ファイル属性ユーティリティ
file-mtime>=?12.23.3 ファイル属性ユーティリティ
file-mtime>?12.23.3 ファイル属性ユーティリティ
file-nlink12.23.3 ファイル属性ユーティリティ
file-perm12.23.3 ファイル属性ユーティリティ
file-rdev12.23.3 ファイル属性ユーティリティ
file-size12.23.3 ファイル属性ユーティリティ
file-type12.23.3 ファイル属性ユーティリティ
file-uid12.23.3 ファイル属性ユーティリティ
files11.4 srfi-7 - 機能ベースプログラム設定言語
filter6.6.5 リストをたどる手続き
filter9.5.2 コレクションからの選択と探索
filter!6.6.5 リストをたどる手続き
filter$6.18.3 コンビネータ
filter-map6.6.5 リストをたどる手続き
filter-to9.5.2 コレクションからの選択と探索
find6.6.5 リストをたどる手続き
find9.5.2 コレクションからの選択と探索
find$6.18.3 コンビネータ
find-file-in-paths12.23.2 パスネームユーティリティ
find-gauche-package-descriptionUtility procedures
find-in-queue12.11 data.queue - キュー
find-index9.28.3 シーケンス上のマップ
find-max9.5.2 コレクションからの選択と探索
find-min9.5.2 コレクションからの選択と探索
find-min&max9.5.2 コレクションからの選択と探索
find-module4.13.6 モジュールイントロスペクション
find-string-from-port?12.53 text.parse - 入力ストリームのパージング
find-tail6.6.5 リストをたどる手続き
find-tail$6.18.3 コンビネータ
find-with-index9.28.3 シーケンス上のマップ
finite?6.3.2 数値に関する述語
firstList selectors
first-ec内包表記マクロ
first-set-bit11.15.2 Integer properties
fixnum-width6.3.4 数値の演算
fixnum?6.3.2 数値に関する述語
fixnumsプリミティブデータ型のジェネレータ
floor6.3.4 数値の演算
floor->exact6.3.4 数値の演算
fluid-let4.6 変数束縛
flush6.22.8.5 低レベル出力
flush-all-ports6.22.8.5 低レベル出力
flush-output-port入出力
fmod6.3.5 数値の変換
fold6.6.5 リストをたどる手続き
fold9.5.1 コレクションに対するマッピング
fold$6.18.3 コンビネータ
fold$9.5.1 コレクションに対するマッピング
fold$9.5.1 コレクションに対するマッピング
fold-ec内包表記マクロ
fold-left6.6.5 リストをたどる手続き
fold-right6.6.5 リストをたどる手続き
fold-right9.28.3 シーケンス上のマップ
fold-right$6.18.3 コンビネータ
fold-with-index9.28.3 シーケンス上のマップ
fold29.5.1 コレクションに対するマッピング
fold39.5.1 コレクションに対するマッピング
fold3-ec内包表記マクロ
for-each6.6.5 リストをたどる手続き
for-each9.5.1 コレクションに対するマッピング
for-each$6.18.3 コンビネータ
for-each$9.5.1 コレクションに対するマッピング
for-each-with-index9.28.3 シーケンス上のマップ
force6.19.1 Delayとforceとlazy
format6.22.8.4 フォーマット出力
format6.22.8.4 フォーマット出力
format6.22.8.4 フォーマット出力
format6.22.8.4 フォーマット出力
format6.22.8.4 フォーマット出力
format拡張された format 手続き
fourthList selectors
frexp6.3.5 数値の変換
ftp-chdir12.31 rfc.ftp - FTPクライアント
ftp-current-directory12.31 rfc.ftp - FTPクライアント
ftp-get12.31 rfc.ftp - FTPクライアント
ftp-help12.31 rfc.ftp - FTPクライアント
ftp-list12.31 rfc.ftp - FTPクライアント
ftp-login12.31 rfc.ftp - FTPクライアント
ftp-ls12.31 rfc.ftp - FTPクライアント
ftp-mdtm12.31 rfc.ftp - FTPクライアント
ftp-mkdir12.31 rfc.ftp - FTPクライアント
ftp-mtime12.31 rfc.ftp - FTPクライアント
ftp-name-list12.31 rfc.ftp - FTPクライアント
ftp-noop12.31 rfc.ftp - FTPクライアント
ftp-passive?12.31 rfc.ftp - FTPクライアント
ftp-put12.31 rfc.ftp - FTPクライアント
ftp-put-unique12.31 rfc.ftp - FTPクライアント
ftp-quit12.31 rfc.ftp - FTPクライアント
ftp-remove12.31 rfc.ftp - FTPクライアント
ftp-rename12.31 rfc.ftp - FTPクライアント
ftp-rmdir12.31 rfc.ftp - FTPクライアント
ftp-site12.31 rfc.ftp - FTPクライアント
ftp-size12.31 rfc.ftp - FTPクライアント
ftp-stat12.31 rfc.ftp - FTPクライアント
ftp-system12.31 rfc.ftp - FTPクライアント
ftp-transfer-type12.31 rfc.ftp - FTPクライアント

G
gamma6.3.4 数値の演算
gappend9.10.2 ジェネレータの操作
gauche-architecture6.25.3 環境の問い合わせ
gauche-architecture-directory6.25.3 環境の問い合わせ
gauche-character-encoding6.10 文字
gauche-config9.6 gauche.config - コンフィグレーション情報
gauche-library-directory6.25.3 環境の問い合わせ
gauche-package-description-pathsUtility procedures
gauche-site-architecture-directory6.25.3 環境の問い合わせ
gauche-site-library-directory6.25.3 環境の問い合わせ
gauche-thread-type9.32 gauche.threads - スレッド
gauche-version6.25.3 環境の問い合わせ
gbuffer-filter9.10.2 ジェネレータの操作
gc6.25.12 ガベージコレクション
gc-stat6.25.12 ガベージコレクション
gcd6.3.4 数値の演算
gcombine9.10.2 ジェネレータの操作
gconcatenate9.10.2 ジェネレータの操作
gcons*9.10.2 ジェネレータの操作
gdbm-close12.19 dbm.gdbm - GDBMインタフェース
gdbm-closed?12.19 dbm.gdbm - GDBMインタフェース
gdbm-delete12.19 dbm.gdbm - GDBMインタフェース
gdbm-errno12.19 dbm.gdbm - GDBMインタフェース
gdbm-exists?12.19 dbm.gdbm - GDBMインタフェース
gdbm-fetch12.19 dbm.gdbm - GDBMインタフェース
gdbm-firstkey12.19 dbm.gdbm - GDBMインタフェース
gdbm-nextkey12.19 dbm.gdbm - GDBMインタフェース
gdbm-open12.19 dbm.gdbm - GDBMインタフェース
gdbm-reorganize12.19 dbm.gdbm - GDBMインタフェース
gdbm-setopt12.19 dbm.gdbm - GDBMインタフェース
gdbm-store12.19 dbm.gdbm - GDBMインタフェース
gdbm-strerror12.19 dbm.gdbm - GDBMインタフェース
gdbm-sync12.19 dbm.gdbm - GDBMインタフェース
gdbm-version12.19 dbm.gdbm - GDBMインタフェース
gdelete9.10.2 ジェネレータの操作
gdelete-neighbor-dups9.10.2 ジェネレータの操作
gdrop9.10.2 ジェネレータの操作
gdrop-while9.10.2 ジェネレータの操作
generate9.10.1 ジェネレータの生成
generatorSRFI-121 compatible procedures
generator->ideque12.9 data.ideque - 変更不可な両端キュー
generator->list9.10.3 ジェネレータの消費
generator->lseqプリミティブ
generator->lseqプリミティブ
generator->reverse-list9.10.3 ジェネレータの消費
generator->string9.10.3 ジェネレータの消費
generator->vector9.10.3 ジェネレータの消費
generator->vector!9.10.3 ジェネレータの消費
generator-any9.10.3 ジェネレータの消費
generator-count9.10.3 ジェネレータの消費
generator-every9.10.3 ジェネレータの消費
generator-find6.18.9 生成された値の畳み込み
generator-fold6.18.9 生成された値の畳み込み
generator-fold-right6.18.9 生成された値の畳み込み
generator-for-each6.18.9 生成された値の畳み込み
generator-map6.18.9 生成された値の畳み込み
generator-unfold9.10.3 ジェネレータの消費
gensym6.7 シンボル
get-environment-variable11.17 srfi-98 - 環境変数へのアクセス
get-environment-variables11.17 srfi-98 - 環境変数へのアクセス
get-f16ユニフォームベクタを用いたI/O
get-f16beユニフォームベクタを用いたI/O
get-f16leユニフォームベクタを用いたI/O
get-f32ユニフォームベクタを用いたI/O
get-f32beユニフォームベクタを用いたI/O
get-f32leユニフォームベクタを用いたI/O
get-f64ユニフォームベクタを用いたI/O
get-f64beユニフォームベクタを用いたI/O
get-f64leユニフォームベクタを用いたI/O
get-keyword6.8 キーワード
get-keyword*6.8 キーワード
get-optional6.18.4 省略可能引数のパージング
get-output-bytevector入出力
get-output-string6.22.5 文字列ポート
get-output-uvectorユニフォームベクタポート
get-remaining-input-generatorジェネレータポート
get-remaining-input-listリストポート
get-remaining-input-string6.22.5 文字列ポート
get-s16ユニフォームベクタを用いたI/O
get-s16beユニフォームベクタを用いたI/O
get-s16leユニフォームベクタを用いたI/O
get-s32ユニフォームベクタを用いたI/O
get-s32beユニフォームベクタを用いたI/O
get-s32leユニフォームベクタを用いたI/O
get-s64ユニフォームベクタを用いたI/O
get-s64beユニフォームベクタを用いたI/O
get-s64leユニフォームベクタを用いたI/O
get-s8ユニフォームベクタを用いたI/O
get-signal-handler6.25.7.3 シグナルの処理
get-signal-handler-mask6.25.7.3 シグナルの処理
get-signal-handlers6.25.7.3 シグナルの処理
get-signal-pending-limit6.25.7.3 シグナルの処理
get-u16ユニフォームベクタを用いたI/O
get-u16beユニフォームベクタを用いたI/O
get-u16leユニフォームベクタを用いたI/O
get-u32ユニフォームベクタを用いたI/O
get-u32beユニフォームベクタを用いたI/O
get-u32leユニフォームベクタを用いたI/O
get-u64ユニフォームベクタを用いたI/O
get-u64beユニフォームベクタを用いたI/O
get-u64leユニフォームベクタを用いたI/O
get-u8ユニフォームベクタを用いたI/O
getchConsole control
getter-with-setter4.4 代入
gettextgettext-互換の API
gfilter9.10.2 ジェネレータの操作
gfilter-map9.10.2 ジェネレータの操作
gflatten9.10.2 ジェネレータの操作
gindex9.10.2 ジェネレータの操作
giota9.10.1 ジェネレータの生成
glet*9.10.3 ジェネレータの消費
glet19.10.3 ジェネレータの消費
glob6.25.4.1 ディレクトリ
glob-fold6.25.4.1 ディレクトリ
global-variable-bound?4.13.6 モジュールイントロスペクション
global-variable-ref4.13.6 モジュールイントロスペクション
gmap9.10.2 ジェネレータの操作
gmap-accum9.10.2 ジェネレータの操作
gmerge9.10.2 ジェネレータの操作
grange9.10.1 ジェネレータの生成
greatest-fixnum6.3.4 数値の演算
gremove9.10.2 ジェネレータの操作
group-collection9.5.2 コレクションからの選択と探索
group-sequenceGrouping
grxmatch9.10.2 ジェネレータの操作
gselect9.10.2 ジェネレータの操作
gslices9.10.2 ジェネレータの操作
gstate-filter9.10.2 ジェネレータの操作
gtake9.10.2 ジェネレータの操作
gtake*9.10.2 ジェネレータの操作
gtake-while9.10.2 ジェネレータの操作
guard上位レベルの例外処理機構
gunfold9.10.1 ジェネレータの生成
gzip-decode-stringMiscellaneous API
gzip-encode-stringMiscellaneous API

H
has-setter?4.4 代入
has-windows-console?9.30.2 共通の高レベル端末制御
hash6.2.3 ハッシュ
hash11.16 srfi-69 - 基本的なハッシュテーブル
hash-bound6.2.3 ハッシュ
hash-by-identity11.16 srfi-69 - 基本的なハッシュテーブル
hash-salt6.2.3 ハッシュ
hash-table6.15 ハッシュテーブル
hash-table->alist6.15 ハッシュテーブル
hash-table-clear!6.15 ハッシュテーブル
hash-table-comparator6.15 ハッシュテーブル
hash-table-copy6.15 ハッシュテーブル
hash-table-delete!6.15 ハッシュテーブル
hash-table-equivalence-function11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-exists?6.15 ハッシュテーブル
hash-table-fold6.15 ハッシュテーブル
hash-table-for-each6.15 ハッシュテーブル
hash-table-get6.15 ハッシュテーブル
hash-table-hash-function11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-keys6.15 ハッシュテーブル
hash-table-map6.15 ハッシュテーブル
hash-table-merge!11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-num-entries6.15 ハッシュテーブル
hash-table-pop!6.15 ハッシュテーブル
hash-table-push!6.15 ハッシュテーブル
hash-table-put!6.15 ハッシュテーブル
hash-table-ref11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-ref/default11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-set!11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-size11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-type6.15 ハッシュテーブル
hash-table-update!6.15 ハッシュテーブル
hash-table-update!11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-update!/default11.16 srfi-69 - 基本的なハッシュテーブル
hash-table-values6.15 ハッシュテーブル
hash-table-walk11.16 srfi-69 - 基本的なハッシュテーブル
hash-table?6.15 ハッシュテーブル
hide-cursorConsole control
hmac-digest12.32 rfc.hmac - HMAC鍵付きハッシング
hmac-digest-string12.32 rfc.hmac - HMAC鍵付きハッシング
hmac-final!12.32 rfc.hmac - HMAC鍵付きハッシング
hmac-update!12.32 rfc.hmac - HMAC鍵付きハッシング
home-directory12.23.1 ディレクトリユーティリティ
hook->list9.11 gauche.hook - フック
hook-empty?9.11 gauche.hook - フック
hook?9.11 gauche.hook - フック
html-doctype12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html-escape12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html-escape-string12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:a12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:abbr12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:acronym12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:address12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:area12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:b12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:base12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:bdo12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:big12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:blockquote12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:body12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:br12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:button12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:caption12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:cite12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:code12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:col12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:colgroup12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:dd12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:del12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:dfn12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:div12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:dl12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:dt12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:em12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:fieldset12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:form12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:frame12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:frameset12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:h112.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:h212.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:h312.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:h412.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:h512.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:h612.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:head12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:hr12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:html12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:i12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:iframe12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:img12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:input12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:ins12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:kbd12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:label12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:legend12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:li12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:link12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:map12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:meta12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:noframes12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:noscript12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:object12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:ol12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:optgroup12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:option12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:p12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:param12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:pre12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:q12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:samp12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:script12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:select12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:small12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:span12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:strong12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:style12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:sub12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:sup12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:table12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:tbody12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:td12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:textarea12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:tfoot12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:th12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:thead12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:title12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:tr12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:tt12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:ul12.52 text.html-lite - シンプルなHTMLドキュメントの構築
html:var12.52 text.html-lite - シンプルなHTMLドキュメントの構築
http-compose-form-data12.33 rfc.http - HTTP
http-compose-query12.33 rfc.http - HTTP
http-default-redirect-handler12.33 rfc.http - HTTP
http-delete12.33 rfc.http - HTTP
http-get12.33 rfc.http - HTTP
http-head12.33 rfc.http - HTTP
http-post12.33 rfc.http - HTTP
http-proxy12.33 rfc.http - HTTP
http-put12.33 rfc.http - HTTP
http-secure-connection-available?セキュアな接続
http-status-code->description12.33 rfc.http - HTTP
http-user-agent12.33 rfc.http - HTTP

I
icmp-packet-code12.34 rfc.icmp - ICMPパケット
icmp-packet-ident12.34 rfc.icmp - ICMPパケット
icmp-packet-sequence12.34 rfc.icmp - ICMPパケット
icmp-packet-type12.34 rfc.icmp - ICMPパケット
icmp4-describe-packet12.34 rfc.icmp - ICMPパケット
icmp4-exceeded-code->string12.34 rfc.icmp - ICMPパケット
icmp4-fill-checksum!12.34 rfc.icmp - ICMPパケット
icmp4-fill-echo!12.34 rfc.icmp - ICMPパケット
icmp4-message-type->string12.34 rfc.icmp - ICMPパケット
icmp4-parameter-code->string12.34 rfc.icmp - ICMPパケット
icmp4-redirect-code->string12.34 rfc.icmp - ICMPパケット
icmp4-router-code->string12.34 rfc.icmp - ICMPパケット
icmp4-security-code->string12.34 rfc.icmp - ICMPパケット
icmp4-unreach-code->string12.34 rfc.icmp - ICMPパケット
icmp6-describe-packet12.34 rfc.icmp - ICMPパケット
icmp6-exceeded-code->string12.34 rfc.icmp - ICMPパケット
icmp6-fill-echo!12.34 rfc.icmp - ICMPパケット
icmp6-message-type->string12.34 rfc.icmp - ICMPパケット
icmp6-parameter-code->string12.34 rfc.icmp - ICMPパケット
icmp6-unreach-code->string12.34 rfc.icmp - ICMPパケット
identifier->symbol6.9 Identifier
identifier?6.9 Identifier
identity-array9.1 gauche.array - 配列
ideque12.9 data.ideque - 変更不可な両端キュー
ideque->generator12.9 data.ideque - 変更不可な両端キュー
ideque->list12.9 data.ideque - 変更不可な両端キュー
ideque-add-back12.9 data.ideque - 変更不可な両端キュー
ideque-add-front12.9 data.ideque - 変更不可な両端キュー
ideque-any12.9 data.ideque - 変更不可な両端キュー
ideque-append12.9 data.ideque - 変更不可な両端キュー
ideque-append-map12.9 data.ideque - 変更不可な両端キュー
ideque-bakc12.9 data.ideque - 変更不可な両端キュー
ideque-break12.9 data.ideque - 変更不可な両端キュー
ideque-drop12.9 data.ideque - 変更不可な両端キュー
ideque-drop-right12.9 data.ideque - 変更不可な両端キュー
ideque-drop-while12.9 data.ideque - 変更不可な両端キュー
ideque-drop-while-right12.9 data.ideque - 変更不可な両端キュー
ideque-empty?12.9 data.ideque - 変更不可な両端キュー
ideque-every12.9 data.ideque - 変更不可な両端キュー
ideque-filter12.9 data.ideque - 変更不可な両端キュー
ideque-filter-map12.9 data.ideque - 変更不可な両端キュー
ideque-find12.9 data.ideque - 変更不可な両端キュー
ideque-find-right12.9 data.ideque - 変更不可な両端キュー
ideque-fold12.9 data.ideque - 変更不可な両端キュー
ideque-fold-right12.9 data.ideque - 変更不可な両端キュー
ideque-for-each12.9 data.ideque - 変更不可な両端キュー
ideque-for-each-right12.9 data.ideque - 変更不可な両端キュー
ideque-front12.9 data.ideque - 変更不可な両端キュー
ideque-length12.9 data.ideque - 変更不可な両端キュー
ideque-map12.9 data.ideque - 変更不可な両端キュー
ideque-partition12.9 data.ideque - 変更不可な両端キュー
ideque-ref12.9 data.ideque - 変更不可な両端キュー
ideque-remove12.9 data.ideque - 変更不可な両端キュー
ideque-remove-back12.9 data.ideque - 変更不可な両端キュー
ideque-remove-front12.9 data.ideque - 変更不可な両端キュー
ideque-reverse12.9 data.ideque - 変更不可な両端キュー
ideque-span12.9 data.ideque - 変更不可な両端キュー
ideque-split-at12.9 data.ideque - 変更不可な両端キュー
ideque-tabulate12.9 data.ideque - 変更不可な両端キュー
ideque-take12.9 data.ideque - 変更不可な両端キュー
ideque-take-right12.9 data.ideque - 変更不可な両端キュー
ideque-take-while12.9 data.ideque - 変更不可な両端キュー
ideque-take-while-right12.9 data.ideque - 変更不可な両端キュー
ideque-unfold12.9 data.ideque - 変更不可な両端キュー
ideque-unfold-right12.9 data.ideque - 変更不可な両端キュー
ideque-zip12.9 data.ideque - 変更不可な両端キュー
ideque=12.9 data.ideque - 変更不可な両端キュー
if4.5 条件式
if4.5 条件式
ifControl qualifiers
if-car-sxpath12.45.2 SXPathクエリ言語
if-let14.6 変数束縛
if-let14.6 変数束縛
if-not=?Comparison syntax
if-sxpath12.45.2 SXPathクエリ言語
if3Comparison syntax
if<=?Comparison syntax
if<?Comparison syntax
if=?Comparison syntax
if>=?Comparison syntax
if>?Comparison syntax
imag-part6.3.5 数値の変換
imap-delete12.10 data.imap - 変更不可なマップ
imap-empty?12.10 data.imap - 変更不可なマップ
imap-exists?12.10 data.imap - 変更不可なマップ
imap-get12.10 data.imap - 変更不可なマップ
imap-max12.10 data.imap - 変更不可なマップ
imap-min12.10 data.imap - 変更不可なマップ
imap-put12.10 data.imap - 変更不可なマップ
imap?12.10 data.imap - 変更不可なマップ
implementation-name11.20 srfi-112 - 実行環境の問い合わせ
implementation-version11.20 srfi-112 - 実行環境の問い合わせ
import4.13.4 モジュールの使用
import10.2 3つのimport形式
in-closed-interval?Interval comparison predicates
in-closed-open-interval?Interval comparison predicates
in-open-closed-interval?Interval comparison predicates
in-open-interval?Interval comparison predicates
inc!4.4 代入
include4.11 インクルード
include-ci4.11 インクルード
inet-address->stringアドレスと文字列との変換
inet-checksum9.19.3 低レベルソケットインタフェース
inet-string->addressアドレスと文字列との変換
inet-string->address!アドレスと文字列との変換
inexact6.3.5 数値の変換
inexact->exact6.3.5 数値の変換
inexact?6.3.2 数値に関する述語
infinite?6.3.2 数値に関する述語
inflate-stringMiscellaneous API
inflate-syncOperations on inflating/deflating ports
info9.12 gauche.interactive - インタラクティブセッション
initialize7.3.1 インスタンスの作成
initialize7.3.1 インスタンスの作成
initialize<class> 用の initialize メソッド
input-port-open?入出力
input-port?6.22.3 ポート共通の操作
instance-of9.17 gauche.mop.singleton - シングルトン
int16sプリミティブデータ型のジェネレータ
int32sプリミティブデータ型のジェネレータ
int64sプリミティブデータ型のジェネレータ
int8sプリミティブデータ型のジェネレータ
integer->char6.10 文字
integer->digit6.10 文字
integer->list11.15.5 Bit as booleans
integer-length6.3.6 ビット演算
integer-length11.15.2 Integer properties
integer-range->char-set11.7.1 文字セットの構築子
integer-range->char-set!11.7.1 文字セットの構築子
integer-valued?6.3.2 数値に関する述語
integer?6.3.2 数値に関する述語
integers$プリミティブデータ型のジェネレータ
integers-between$プリミティブデータ型のジェネレータ
integers-geometric$非一様分布
integers-poisson$非一様分布
interaction-environment6.21 eval と repl
intersperse6.6.4 リストへのアクセスと変更
iota6.6.3 リストの作成
ip-destination-address12.35 rfc.ip - IPパケット
ip-header-length12.35 rfc.ip - IPパケット
ip-protocolProtocol
ip-protocol12.35 rfc.ip - IPパケット
ip-source-address12.35 rfc.ip - IPパケット
ip-version12.35 rfc.ip - IPパケット
is-a?6.1 型とクラス
isomorphic?12.61 util.isomorph - 同型判定
iterator->stream12.67 util.stream - ストリームライブラリ

J
jacobiその他の関数
jiffies-per-second10.17 scheme.time - R7RS time
job-acknowledge-time12.4 control.job - 制御モジュールのための汎用ジョブ記述子
job-finish-time12.4 control.job - 制御モジュールのための汎用ジョブ記述子
job-result12.4 control.job - 制御モジュールのための汎用ジョブ記述子
job-start-time12.4 control.job - 制御モジュールのための汎用ジョブ記述子
job-status12.4 control.job - 制御モジュールのための汎用ジョブ記述子
job-wait12.4 control.job - 制御モジュールのための汎用ジョブ記述子
job?12.4 control.job - 制御モジュールのための汎用ジョブ記述子
join-timeout-exception?9.32.4 スレッド例外
json-array-handler12.36 rfc.json - JSONのパーズと構築
json-object-handler12.36 rfc.json - JSONのパーズと構築
json-special-handler12.36 rfc.json - JSONのパーズと構築
julian-day->date11.8.4 日付
julian-day->time-monotonic11.8.4 日付
julian-day->time-tai11.8.4 日付
julian-day->time-utc11.8.4 日付

K
keyword->string6.8 キーワード
keyword?6.8 キーワード
kmp-step11.6.14 低レベルな文字列に関する手続き

L
l-distance12.63 util.levenshtein - Levenshtein編集距離
l-distances12.63 util.levenshtein - Levenshtein編集距離
lambda4.3 手続きを作る
lappend9.13 gauche.lazy - 遅延シーケンスユーティリティ
lappend-map9.13 gauche.lazy - 遅延シーケンスユーティリティ
last6.6.4 リストへのアクセスと変更
last-ec内包表記マクロ
last-pair6.6.4 リストへのアクセスと変更
lazy6.19.1 Delayとforceとlazy
lazy-size-of9.5.3 コレクションに対する様々な操作
lcm6.3.4 数値の演算
lconcatenate9.13 gauche.lazy - 遅延シーケンスユーティリティ
lconsプリミティブ
lcons*ユーティリティ
lcs12.62 util.lcs - 最長共通サブシーケンス
lcs-edit-list12.62 util.lcs - 最長共通サブシーケンス
lcs-fold12.62 util.lcs - 最長共通サブシーケンス
lcs-with-positions12.62 util.lcs - 最長共通サブシーケンス
ldexp6.3.5 数値の変換
least-fixnum6.3.4 数値の演算
legacy-hash6.2.3 ハッシュ
length6.6.4 リストへのアクセスと変更
length+6.6.4 リストへのアクセスと変更
length<=?6.6.4 リストへのアクセスと変更
length<?6.6.4 リストへのアクセスと変更
length=?6.6.4 リストへのアクセスと変更
length>=?6.6.4 リストへのアクセスと変更
length>?6.6.4 リストへのアクセスと変更
let4.6 変数束縛
let4.8 繰り返し
let11.3 srfi-5 - シグネチャとrest引数に互換性のあるlet形式
let11.3 srfi-5 - シグネチャとrest引数に互換性のあるlet形式
let11.3 srfi-5 - シグネチャとrest引数に互換性のあるlet形式
let*4.6 変数束縛
let*-values11.5 srfi-11 - Let-values
let-argsHigh-level API
let-keywords6.18.4 省略可能引数のパージング
let-keywords6.18.4 省略可能引数のパージング
let-keywords*6.18.4 省略可能引数のパージング
let-keywords*6.18.4 省略可能引数のパージング
let-optionals*6.18.4 省略可能引数のパージング
let-optionals*6.18.4 省略可能引数のパージング
let-string-start+end11.6.14 低レベルな文字列に関する手続き
let-syntaxマクロ束縛
let-values11.5 srfi-11 - Let-values
let/cc6.18.7 継続
let14.6 変数束縛
letrec4.6 変数束縛
letrec*4.6 変数束縛
letrec-syntaxマクロ束縛
lfilter9.13 gauche.lazy - 遅延シーケンスユーティリティ
lfilter-map9.13 gauche.lazy - 遅延シーケンスユーティリティ
lgamma6.3.4 数値の演算
library-exists?6.23.5 ライブラリの操作
library-fold6.23.5 ライブラリの操作
library-for-each6.23.5 ライブラリの操作
library-has-module?6.23.5 ライブラリの操作
library-map6.23.5 ライブラリの操作
linterweave9.13 gauche.lazy - 遅延シーケンスユーティリティ
liotaユーティリティ
list6.6.3 リストの作成
list*6.6.3 リストの作成
list->bagCopying and conversion
list->bag!Copying and conversion
list->char-set11.7.1 文字セットの構築子
list->char-set!11.7.1 文字セットの構築子
list->f16vector9.35.2 ユニフォームベクタの変換
list->f32vector9.35.2 ユニフォームベクタの変換
list->f64vector9.35.2 ユニフォームベクタの変換
list->generator9.10.1 ジェネレータの生成
list->ideque12.9 data.ideque - 変更不可な両端キュー
list->integer11.15.5 Bit as booleans
list->queue12.11 data.queue - キュー
list->s16vector9.35.2 ユニフォームベクタの変換
list->s32vector9.35.2 ユニフォームベクタの変換
list->s64vector9.35.2 ユニフォームベクタの変換
list->s8vector9.35.2 ユニフォームベクタの変換
list->setCopying and conversion
list->set!Copying and conversion
list->stream12.67 util.stream - ストリームライブラリ
list->string6.12.7 文字列を扱うその他の手続き
list->sys-fdset6.25.11 I/Oの多重化
list->u16vector9.35.2 ユニフォームベクタの変換
list->u32vector9.35.2 ユニフォームベクタの変換
list->u64vector9.35.2 ユニフォームベクタの変換
list->u8vector9.35.2 ユニフォームベクタの変換
list->vector6.14 ベクタ
list-copy6.6.3 リストの作成
list-ec内包表記マクロ
list-indexList searching
list-queue11.23 srfi-117 - リストを元にしたキュー
list-queue-add-back!11.23 srfi-117 - リストを元にしたキュー
list-queue-add-front!11.23 srfi-117 - リストを元にしたキュー
list-queue-append11.23 srfi-117 - リストを元にしたキュー
list-queue-append!11.23 srfi-117 - リストを元にしたキュー
list-queue-back11.23 srfi-117 - リストを元にしたキュー
list-queue-concatenate11.23 srfi-117 - リストを元にしたキュー
list-queue-copy11.23 srfi-117 - リストを元にしたキュー
list-queue-empty?11.23 srfi-117 - リストを元にしたキュー
list-queue-fist-last11.23 srfi-117 - リストを元にしたキュー
list-queue-for-each11.23 srfi-117 - リストを元にしたキュー
list-queue-front11.23 srfi-117 - リストを元にしたキュー
list-queue-list11.23 srfi-117 - リストを元にしたキュー
list-queue-map11.23 srfi-117 - リストを元にしたキュー
list-queue-map!11.23 srfi-117 - リストを元にしたキュー
list-queue-remove-all!11.23 srfi-117 - リストを元にしたキュー
list-queue-remove-back!11.23 srfi-117 - リストを元にしたキュー
list-queue-remove-front!11.23 srfi-117 - リストを元にしたキュー
list-queue-set-list!11.23 srfi-117 - リストを元にしたキュー
list-queue-unfold11.23 srfi-117 - リストを元にしたキュー
list-queue-unfold-right11.23 srfi-117 - リストを元にしたキュー
list-queue?11.23 srfi-117 - リストを元にしたキュー
list-ref6.6.4 リストへのアクセスと変更
list-set!6.6.4 リストへのアクセスと変更
list-tabulateList constructors
list-tail6.6.4 リストへのアクセスと変更
list=List predicates
list?6.6.2 リストに関する述語
listener-read-handlerListener API
listener-show-promptListener API
lists-of複合データ型のジェネレータ
lists-of複合データ型のジェネレータ
llist*ユーティリティ
lmap9.13 gauche.lazy - 遅延シーケンスユーティリティ
lmap-accum9.13 gauche.lazy - 遅延シーケンスユーティリティ
load6.23.1 Schemeファイルのロード
load10.13 scheme.load - R7RS load
load-bundle!バンドル準備
load-from-port6.23.1 Schemeファイルのロード
localized-template地域化されたメッセージの検索
log6.3.4 数値の演算
log6.3.4 数値の演算
log-default-drain9.15 gauche.logger - ユーザレベルのロギング
log-format9.15 gauche.logger - ユーザレベルのロギング
log-format9.15 gauche.logger - ユーザレベルのロギング
log-open9.15 gauche.logger - ユーザレベルのロギング
log2-binary-factors11.15.2 Integer properties
logand6.3.6 ビット演算
logand11.15.1 Bitwise operators
logbit?6.3.6 ビット演算
logbit?11.15.3 Bit within word
logcount6.3.6 ビット演算
logcount11.15.2 Integer properties
logior6.3.6 ビット演算
logior11.15.1 Bitwise operators
lognot6.3.6 ビット演算
lognot11.15.1 Bitwise operators
logtest6.3.6 ビット演算
logtest11.15.1 Bitwise operators
logxor6.3.6 ビット演算
logxor11.15.1 Bitwise operators
lrangeユーティリティ
lrxmatch9.13 gauche.lazy - 遅延シーケンスユーティリティ
lset-adjoin11.1.2 集合としてのリスト
lset-diff+intersection11.1.2 集合としてのリスト
lset-diff+intersection!11.1.2 集合としてのリスト
lset-difference11.1.2 集合としてのリスト
lset-difference!11.1.2 集合としてのリスト
lset-intersection11.1.2 集合としてのリスト
lset-intersection!11.1.2 集合としてのリスト
lset-union11.1.2 集合としてのリスト
lset-union!11.1.2 集合としてのリスト
lset-xor11.1.2 集合としてのリスト
lset-xor!11.1.2 集合としてのリスト
lset<=11.1.2 集合としてのリスト
lset=11.1.2 集合としてのリスト
lslices9.13 gauche.lazy - 遅延シーケンスユーティリティ
lstate-filter9.13 gauche.lazy - 遅延シーケンスユーティリティ
ltake9.13 gauche.lazy - 遅延シーケンスユーティリティ
ltake-while9.13 gauche.lazy - 遅延シーケンスユーティリティ
lunfold9.13 gauche.lazy - 遅延シーケンスユーティリティ

M
machine-name11.20 srfi-112 - 実行環境の問い合わせ
macroexpand5.4 マクロの展開
macroexpand-15.4 マクロの展開
macroexpand-all5.4 マクロの展開
magnitude6.3.5 数値の変換
make7.3.1 インスタンスの作成
make7.3.1 インスタンスの作成
make7.5.3 メソッドのインスタンシエーション
make-array9.1 gauche.array - 配列
make-bimap9.8.2 汎用ディクショナリ
make-binary-heap12.8 data.heap - ヒープ
make-bits-generatorSRFI-121 compatible procedures
make-byte-string6.12.3 文字列の構築子
make-bytevectorバイトベクタユーティリティ
make-bytevector-comparatorAuxiliary comparator constructors
make-car-comparatorAuxiliary comparator constructors
make-cdr-comparatorAuxiliary comparator constructors
make-client-socket9.19.2 高レベルネットワーク手続き
make-client-socketソケットオブジェクト
make-comparator6.2.4.1 比較器クラスとコンストラクタ
make-comparatorBasic comparator interface
make-comparator/compare6.2.4.1 比較器クラスとコンストラクタ
make-comparison<Comparison procedure constructors
make-comparison<=Comparison procedure constructors
make-comparison=/<Comparison procedure constructors
make-comparison=/>Comparison procedure constructors
make-comparison>Comparison procedure constructors
make-comparison>=Comparison procedure constructors
make-compound-conditionコンディションAPI
make-conditionコンディションAPI
make-condition-typeコンディションAPI
make-condition-variable条件変数
make-coroutine-generatorSRFI-121 compatible procedures
make-csv-header-parser中レベルAPI
make-csv-reader低レベルAPI
make-csv-record-parser中レベルAPI
make-csv-writer低レベルAPI
make-date11.8.4 日付
make-debug-comparatorAuxiliary comparator constructors
make-default-comparator6.2.4.4 比較器を組み合わせる
make-default-consoleコンソールオブジェクト
make-directory*12.23.1 ディレクトリユーティリティ
make-empty-attlist12.44.1 SSAXデータタイプ
make-eq-comparator6.2.4.4 比較器を組み合わせる
make-eqv-comparator6.2.4.4 比較器を組み合わせる
make-f16array9.1 gauche.array - 配列
make-f16vector9.35.1 ユニフォームベクタの基本操作
make-f32array9.1 gauche.array - 配列
make-f32vector9.35.1 ユニフォームベクタの基本操作
make-f64array9.1 gauche.array - 配列
make-f64vector9.35.1 ユニフォームベクタの基本操作
make-fifo-cachePredefined caches
make-for-each-generatorSRFI-121 compatible procedures
make-gauche-package-descriptionUtility procedures
make-gettext低水準の柔軟な API
make-glob-fs-fold6.25.4.1 ディレクトリ
make-grapheme-cluster-breaker9.34.2 Unicode text segmentation
make-grapheme-cluster-reader9.34.2 Unicode text segmentation
make-hash-table6.15 ハッシュテーブル
make-hash-table11.16 srfi-69 - 基本的なハッシュテーブル
make-hook9.11 gauche.hook - フック
make-ideque12.9 data.ideque - 変更不可な両端キュー
make-imap12.10 data.imap - 変更不可なマップ
make-imap12.10 data.imap - 変更不可なマップ
make-imap12.10 data.imap - 変更不可なマップ
make-improper-list-comparatorAuxiliary comparator constructors
make-inexact-real-comparatorAuxiliary comparator constructors
make-iota-generatorSRFI-121 compatible procedures
make-key-comparator6.2.4.4 比較器を組み合わせる
make-keyword6.8 キーワード
make-kmp-restart-vector11.6.14 低レベルな文字列に関する手続き
make-list6.6.3 リストの作成
make-list-comparatorAuxiliary comparator constructors
make-list-queue11.23 srfi-117 - リストを元にしたキュー
make-listwise-comparatorAuxiliary comparator constructors
make-lru-cachePredefined caches
make-module4.13.6 モジュールイントロスペクション
make-mtqueue12.11 data.queue - キュー
make-mutexMutex
make-option-parserLow-level API
make-overflow-doubler12.13 data.ring-buffer - リングバッファ
make-packer12.2 binary.pack - バイナリデータのパック
make-pair-comparatorAuxiliary comparator constructors
make-parameter9.21 gauche.parameter - パラメータ
make-polar6.3.5 数値の変換
make-promise10.12 scheme.lazy - R7RS遅延評価
make-queue12.11 data.queue - キュー
make-random-source11.9 srfi-27 - ランダムビットのソース
make-range-generatorSRFI-121 compatible procedures
make-rbtree8.3 廃止されたモジュール
make-record-type12.65 util.record - SLIB-互換のレコード型
make-rectangular6.3.5 数値の変換
make-refining-comparatorAuxiliary comparator constructors
make-reverse-comparator6.2.4.4 比較器を組み合わせる
make-reverse-comparatorAuxiliary comparator constructors
make-ring-buffer12.13 data.ring-buffer - リングバッファ
make-rtd9.25.4 Procedural layer
make-s16array9.1 gauche.array - 配列
make-s16vector9.35.1 ユニフォームベクタの基本操作
make-s32array9.1 gauche.array - 配列
make-s32vector9.35.1 ユニフォームベクタの基本操作
make-s64array9.1 gauche.array - 配列
make-s64vector9.35.1 ユニフォームベクタの基本操作
make-s8array9.1 gauche.array - 配列
make-s8vector9.35.1 ユニフォームベクタの基本操作
make-selecting-comparatorAuxiliary comparator constructors
make-server-socket9.19.2 高レベルネットワーク手続き
make-server-socketソケットオブジェクト
make-server-sockets9.19.2 高レベルネットワーク手続き
make-sockaddrsソケットアドレスオブジェクト
make-socket9.19.3 低レベルソケットインタフェース
make-sparse-matrix12.14.2 疎行列
make-sparse-table12.14.3 疎なテーブル
make-sparse-vector12.14.1 疎なベクタ
make-stream12.67 util.stream - ストリームライブラリ
make-string6.12.3 文字列の構築子
make-text-progress-bar12.54 text.progress - テキスト端末上で進捗を表示する
make-thread9.32.2 スレッド手続き
make-thread-pool12.5 control.thread-pool - スレッドプール
make-time11.8.3 時間に関する手続き
make-time-resultベンチマーク
make-tree-map6.16 ツリーマップ
make-tree-map6.16 ツリーマップ
make-trie12.15 data.trie - Trie
make-ttl-cachePredefined caches
make-ttlr-cachePredefined caches
make-tuple-comparator6.2.4.4 比較器を組み合わせる
make-u16array9.1 gauche.array - 配列
make-u16vector9.35.1 ユニフォームベクタの基本操作
make-u32array9.1 gauche.array - 配列
make-u32vector9.35.1 ユニフォームベクタの基本操作
make-u64array9.1 gauche.array - 配列
make-u64vector9.35.1 ユニフォームベクタの基本操作
make-u8array9.1 gauche.array - 配列
make-u8vector9.35.1 ユニフォームベクタの基本操作
make-unfold-generatorSRFI-121 compatible procedures
make-uvector9.35.1 ユニフォームベクタの基本操作
make-vector6.14 ベクタ
make-vector-comparatorAuxiliary comparator constructors
make-vectorwise-comparatorAuxiliary comparator constructors
make-weak-vector6.17 Weak ポインタ
make-word-breaker9.34.2 Unicode text segmentation
make-word-reader9.34.2 Unicode text segmentation
make-write-controls6.22.8.2 出力制御
make-xml-token12.44.1 SSAXデータタイプ
make<=?Comparison predicate constructors
make<?Comparison predicate constructors
make=?Comparison predicate constructors
make>=?Comparison predicate constructors
make>?Comparison predicate constructors
map6.6.5 リストをたどる手続き
map9.5.1 コレクションに対するマッピング
map!List fold, unfold & map
map$6.18.3 コンビネータ
map$9.5.1 コレクションに対するマッピング
map*6.6.5 リストをたどる手続き
map-accum9.5.1 コレクションに対するマッピング
map-in-orderList fold, unfold & map
map-to9.5.1 コレクションに対するマッピング
map-to-with-index9.28.3 シーケンス上のマップ
map-union12.45.1 SXPathの基本的なコンバータとアプリケータ
map-with-index9.28.3 シーケンス上のマップ
matchパターンマッチング API
match-defineパターンマッチング API
match-lambdaパターンマッチング API
match-lambda*パターンマッチング API
match-letパターンマッチング API
match-letパターンマッチング API
match-let*パターンマッチング API
match-let1パターンマッチング API
match-letrecパターンマッチング API
max6.3.3 数値の比較
max-ec内包表記マクロ
mc-factorize素因数分解
md5-digest12.37 rfc.md5 - MD5メッセージダイジェスト
md5-digest-string12.37 rfc.md5 - MD5メッセージダイジェスト
member6.6.6 他のリスト手続き
member$6.18.3 コンビネータ
memq6.6.6 他のリスト手続き
memv6.6.6 他のリスト手続き
merge6.24 ソートとマージ
merge!6.24 ソートとマージ
message-typeMessage type
method-more-specific?7.5.4 メソッド適用のカスタマイズ
miller-rabin-prime?素数かどうかを調べる
mime-body->fileStreaming parser
mime-body->stringStreaming parser
mime-compose-messageMessage composer
mime-compose-message-stringMessage composer
mime-compose-parametersUtilities for header fields
mime-decode-textUtilities for header fields
mime-decode-wordUtilities for header fields
mime-encode-textUtilities for header fields
mime-encode-wordUtilities for header fields
mime-make-boundaryMessage composer
mime-parse-content-dispositionUtilities for header fields
mime-parse-content-typeUtilities for header fields
mime-parse-messageStreaming parser
mime-parse-parametersUtilities for header fields
mime-parse-versionUtilities for header fields
mime-retrieve-bodyStreaming parser
min6.3.3 数値の比較
min&max6.3.3 数値の比較
min-ec内包表記マクロ
mod6.3.4 数値の演算
mod06.3.4 数値の演算
modf6.3.5 数値の変換
modified-julian-day->date11.8.4 日付
modified-julian-day->time-monotonic11.8.4 日付
modified-julian-day->time-tai11.8.4 日付
modified-julian-day->time-utc11.8.4 日付
modifier9.28.1 基本的なシーケンスのアクセサ
module-exports4.13.6 モジュールイントロスペクション
module-imports4.13.6 モジュールイントロスペクション
module-name4.13.6 モジュールイントロスペクション
module-name->path4.13.6 モジュールイントロスペクション
module-parents4.13.6 モジュールイントロスペクション
module-precedence-list4.13.6 モジュールイントロスペクション
module-reload-rules9.26 gauche.reload - モジュールの再ロード
module-table4.13.6 モジュールイントロスペクション
module?4.13.6 モジュールイントロスペクション
modulo6.3.4 数値の演算
move-cursor-toConsole control
move-file12.23.4 ファイル操作
mt-random-fill-f32vector!12.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-fill-f64vector!12.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-fill-u32vector!12.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-get-state12.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-integer12.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-real12.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-real012.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-set-seed!12.25 math.mt-random - Mersenne Twister乱数発生器
mt-random-set-state!12.25 math.mt-random - Mersenne Twister乱数発生器
mtqueue-max-length12.11 data.queue - キュー
mtqueue-num-waiting-readers12.11 data.queue - キュー
mtqueue-room12.11 data.queue - キュー
mtqueue?12.11 data.queue - キュー
mutex-lock!Mutex
mutex-lockerMutex
mutex-nameMutex
mutex-specificMutex
mutex-specific-set!Mutex
mutex-stateMutex
mutex-unlock!Mutex
mutex-unlockerMutex
mutex?Mutex

N
naive-factorize素因数分解
nan?6.3.2 数値に関する述語
native-endian6.3.7 エンディアン
ndbm-clear-error12.20 dbm.ndbm - NDBMインタフェース
ndbm-close12.20 dbm.ndbm - NDBMインタフェース
ndbm-closed?12.20 dbm.ndbm - NDBMインタフェース
ndbm-delete12.20 dbm.ndbm - NDBMインタフェース
ndbm-error12.20 dbm.ndbm - NDBMインタフェース
ndbm-fetch12.20 dbm.ndbm - NDBMインタフェース
ndbm-firstkey12.20 dbm.ndbm - NDBMインタフェース
ndbm-nextkey12.20 dbm.ndbm - NDBMインタフェース
ndbm-open12.20 dbm.ndbm - NDBMインタフェース
ndbm-store12.20 dbm.ndbm - NDBMインタフェース
negative?6.3.2 数値に関する述語
nestedControl qualifiers
newline6.22.8.3 オブジェクトの出力
next-methodジェネリック関数の適用
next-methodジェネリック関数の適用
next-token12.53 text.parse - 入力ストリームのパージング
next-token-of12.53 text.parse - 入力ストリームのパージング
ngettextgettext-互換の API
ninthList selectors
node-closure12.45.1 SXPathの基本的なコンバータとアプリケータ
node-eq?12.45.1 SXPathの基本的なコンバータとアプリケータ
node-equal?12.45.1 SXPathの基本的なコンバータとアプリケータ
node-join12.45.1 SXPathの基本的なコンバータとアプリケータ
node-or12.45.1 SXPathの基本的なコンバータとアプリケータ
node-pos12.45.1 SXPathの基本的なコンバータとアプリケータ
node-reduce12.45.1 SXPathの基本的なコンバータとアプリケータ
node-reverse12.45.1 SXPathの基本的なコンバータとアプリケータ
node-self12.45.1 SXPathの基本的なコンバータとアプリケータ
node-trace12.45.1 SXPathの基本的なコンバータとアプリケータ
nodeset?12.45.1 SXPathの基本的なコンバータとアプリケータ
not6.4 論理値
notControl qualifiers
not-pair?List predicates
ntype-names??12.45.1 SXPathの基本的なコンバータとアプリケータ
ntype-namespace-id??12.45.1 SXPathの基本的なコンバータとアプリケータ
ntype??12.45.1 SXPathの基本的なコンバータとアプリケータ
null-device12.23.2 パスネームユーティリティ
null-environment6.21 eval と repl
null-generator9.10.1 ジェネレータの生成
null-list?6.6.2 リストに関する述語
null?6.6.2 リストに関する述語
number->string6.3.5 数値の変換
number-hash6.2.3 ハッシュ
number?6.3.2 数値に関する述語
numerator6.3.4 数値の演算

O
object-apply6.18.6 適用可能なオブジェクト
object-compare6.2.2 比較
object-compare6.2.2 比較
object-equal?6.2.1 等価
object-equal?6.2.1 等価
object-equal?6.2.4.2 比較器にまつわる述語とアクセサ
object-hash6.2.3 ハッシュ
object-hash6.2.3 ハッシュ
object-hash6.2.3 ハッシュ
object-isomorphic?12.61 util.isomorph - 同型判定
odbm-close12.21 dbm.odbm - オリジナルのDBMインタフェース
odbm-delete12.21 dbm.odbm - オリジナルのDBMインタフェース
odbm-fetch12.21 dbm.odbm - オリジナルのDBMインタフェース
odbm-firstkey12.21 dbm.odbm - オリジナルのDBMインタフェース
odbm-init12.21 dbm.odbm - オリジナルのDBMインタフェース
odbm-nextkey12.21 dbm.odbm - オリジナルのDBMインタフェース
odbm-store12.21 dbm.odbm - オリジナルのDBMインタフェース
odd?6.3.2 数値に関する述語
open-binary-input-file10.10 scheme.file - R7RSファイルライブラリ
open-binary-output-file10.10 scheme.file - R7RSファイルライブラリ
open-coding-aware-port6.22.6 コーディング認識ポート
open-deflating-portCompression/decompression ports
open-inflating-portCompression/decompression ports
open-input-byte-generatorジェネレータポート
open-input-byte-listリストポート
open-input-bytevector入出力
open-input-char-generatorジェネレータポート
open-input-char-listリストポート
open-input-conversion-port9.4.3 変換ポート
open-input-fd-port6.22.4 ファイルポート
open-input-file6.22.4 ファイルポート
open-input-process-port9.24.4 Process ports
open-input-string6.22.5 文字列ポート
open-input-uvectorユニフォームベクタポート
open-output-bytevector入出力
open-output-conversion-port9.4.3 変換ポート
open-output-fd-port6.22.4 ファイルポート
open-output-file6.22.4 ファイルポート
open-output-process-port9.24.4 Process ports
open-output-string6.22.5 文字列ポート
open-output-uvectorユニフォームベクタポート
option11.11 srfi-37 - args-fold プログラム引数処理
option-names11.11 srfi-37 - args-fold プログラム引数処理
option-optional-arg?11.11 srfi-37 - args-fold プログラム引数処理
option-processor11.11 srfi-37 - args-fold プログラム引数処理
option-required-arg?11.11 srfi-37 - args-fold プログラム引数処理
option?11.11 srfi-37 - args-fold プログラム引数処理
or4.5 条件式
orControl qualifiers
os-name11.20 srfi-112 - 実行環境の問い合わせ
os-version11.20 srfi-112 - 実行環境の問い合わせ
output-port-open?入出力
output-port?6.22.3 ポート共通の操作

P
pa$6.18.3 コンビネータ
pack12.2 binary.pack - バイナリデータのパック
pair-foldList fold, unfold & map
pair-fold-rightList fold, unfold & map
pair-for-eachList fold, unfold & map
pair?6.6.2 リストに関する述語
pairs-of複合データ型のジェネレータ
parameter-observer-add!9.21 gauche.parameter - パラメータ
parameter-observer-delete!9.21 gauche.parameter - パラメータ
parameter-post-observers9.21 gauche.parameter - パラメータ
parameter-pre-observers9.21 gauche.parameter - パラメータ
parameterize9.21 gauche.parameter - パラメータ
parse-cookie-string12.30 rfc.cookie - HTTPクッキー
parse-cssParsing CSS
parse-css-fileParsing CSS
parse-css-selector-stringParsing CSS
parse-json12.36 rfc.json - JSONのパーズと構築
parse-json*12.36 rfc.json - JSONのパーズと構築
parse-json-string12.36 rfc.json - JSONのパーズと構築
parse-optionsLow-level API
partition9.5.2 コレクションからの選択と探索
partitionList partitioning
partition!List partitioning
partition$6.18.3 コンビネータ
partition-to9.5.2 コレクションからの選択と探索
path->gauche-package-descriptionUtility procedures
path->module-name4.13.6 モジュールイントロスペクション
path-extension12.23.2 パスネームユーティリティ
path-sans-extension12.23.2 パスネームユーティリティ
path-swap-extension12.23.2 パスネームユーティリティ
peek-byte6.22.7.1 データの読み込み
peek-char6.22.7.1 データの読み込み
peek-next-char12.53 text.parse - 入力ストリームのパージング
peek-u8入出力
permutations12.58 util.combinations - 組み合わせ
permutations*12.58 util.combinations - 組み合わせ
permutations*-for-each12.58 util.combinations - 組み合わせ
permutations-for-each12.58 util.combinations - 組み合わせ
permutations-of複合データ型のジェネレータ
permutePermutation and shuffling
permute!Permutation and shuffling
permute-toPermutation and shuffling
pop!4.4 代入
port->byte-generator9.10.1 ジェネレータの生成
port->byte-lseqユーティリティ
port->char-generator9.10.1 ジェネレータの生成
port->char-lseqユーティリティ
port->line-generator9.10.1 ジェネレータの生成
port->list6.22.7.4 入力ユーティリティ手続き
port->sexp-generator9.10.1 ジェネレータの生成
port->sexp-list6.22.7.4 入力ユーティリティ手続き
port->sexp-lseqユーティリティ
port->stream12.67 util.stream - ストリームライブラリ
port->string6.22.7.4 入力ユーティリティ手続き
port->string-list6.22.7.4 入力ユーティリティ手続き
port->string-lseqユーティリティ
port->uvector9.35.4 ユニフォームベクタのブロック入出力
port-buffering6.22.3 ポート共通の操作
port-closed?6.22.3 ポート共通の操作
port-current-line6.22.3 ポート共通の操作
port-fd-dup!6.22.4 ファイルポート
port-file-number6.22.3 ポート共通の操作
port-fold6.22.7.4 入力ユーティリティ手続き
port-fold-right6.22.7.4 入力ユーティリティ手続き
port-for-each6.22.7.4 入力ユーティリティ手続き
port-map6.22.7.4 入力ユーティリティ手続き
port-name6.22.3 ポート共通の操作
port-seek6.22.3 ポート共通の操作
port-tell6.22.3 ポート共通の操作
port-type6.22.3 ポート共通の操作
port?6.22.3 ポート共通の操作
portable-hash6.2.3 ハッシュ
positive?6.3.2 数値に関する述語
power-set12.58 util.combinations - 組み合わせ
power-set*12.58 util.combinations - 組み合わせ
power-set*-for-each12.58 util.combinations - 組み合わせ
power-set-binary12.58 util.combinations - 組み合わせ
power-set-for-each12.58 util.combinations - 組み合わせ
primes素数のシーケンス
print6.22.8.3 オブジェクトの出力
procedure-arity-includes?6.18.5 手続きのアリティ
procedure?6.18.1 Procedure class and applicability
process-alive?9.24.3 Process object
process-command9.24.3 Process object
process-continue9.24.3 Process object
process-error9.24.3 Process object
process-exit-status9.24.3 Process object
process-input9.24.3 Process object
process-kill9.24.3 Process object
process-list9.24.3 Process object
process-output9.24.3 Process object
process-output->string9.24.4 Process ports
process-output->string-list9.24.4 Process ports
process-pid9.24.3 Process object
process-send-signal9.24.3 Process object
process-stop9.24.3 Process object
process-wait9.24.3 Process object
process-wait-any9.24.3 Process object
process?9.24.3 Process object
product-ec内包表記マクロ
profiler-reset6.26.2 プロファイラAPI
profiler-show6.26.2 プロファイラAPI
profiler-start6.26.2 プロファイラAPI
profiler-stop6.26.2 プロファイラAPI
program11.4 srfi-7 - 機能ベースプログラム設定言語
promise?6.19.1 Delayとforceとlazy
proper-list?6.6.2 リストに関する述語
provide6.23.3 requireとprovide
provided?6.23.3 requireとprovide
pseudo-rtd9.25.5 Pseudo record types
push!4.4 代入
put-f16!ユニフォームベクタを用いたI/O
put-f16be!ユニフォームベクタを用いたI/O
put-f16le!ユニフォームベクタを用いたI/O
put-f32!ユニフォームベクタを用いたI/O
put-f32be!ユニフォームベクタを用いたI/O
put-f32le!ユニフォームベクタを用いたI/O
put-f64!ユニフォームベクタを用いたI/O
put-f64be!ユニフォームベクタを用いたI/O
put-f64le!ユニフォームベクタを用いたI/O
put-s16!ユニフォームベクタを用いたI/O
put-s16be!ユニフォームベクタを用いたI/O
put-s16le!ユニフォームベクタを用いたI/O
put-s32!ユニフォームベクタを用いたI/O
put-s32be!ユニフォームベクタを用いたI/O
put-s32le!ユニフォームベクタを用いたI/O
put-s64!ユニフォームベクタを用いたI/O
put-s64be!ユニフォームベクタを用いたI/O
put-s64le!ユニフォームベクタを用いたI/O
put-s8!ユニフォームベクタを用いたI/O
put-u16!ユニフォームベクタを用いたI/O
put-u16be!ユニフォームベクタを用いたI/O
put-u16le!ユニフォームベクタを用いたI/O
put-u32!ユニフォームベクタを用いたI/O
put-u32be!ユニフォームベクタを用いたI/O
put-u32le!ユニフォームベクタを用いたI/O
put-u64!ユニフォームベクタを用いたI/O
put-u64be!ユニフォームベクタを用いたI/O
put-u64le!ユニフォームベクタを用いたI/O
put-u8!ユニフォームベクタを用いたI/O
putchConsole control
putstrConsole control

Q
quasiquote4.9 準クオート(Quasiquote)
quasirename5.2.2 Explcit-renamingマクロ変換器
query-cursor-positionConsole control
query-screen-sizeConsole control
queue->list12.11 data.queue - キュー
queue-empty?12.11 data.queue - キュー
queue-front12.11 data.queue - キュー
queue-internal-list12.11 data.queue - キュー
queue-length12.11 data.queue - キュー
queue-pop!12.11 data.queue - キュー
queue-pop/wait!12.11 data.queue - キュー
queue-push!12.11 data.queue - キュー
queue-push-unique!12.11 data.queue - キュー
queue-push/wait!12.11 data.queue - キュー
queue-rear12.11 data.queue - キュー
queue?12.11 data.queue - キュー
quote4.2 リテラル
quoted-printable-decode12.39 rfc.quoted-printable - Quoted-printableエンコーディング
quoted-printable-decode-string12.39 rfc.quoted-printable - Quoted-printableエンコーディング
quoted-printable-encode12.39 rfc.quoted-printable - Quoted-printableエンコーディング
quoted-printable-encode-string12.39 rfc.quoted-printable - Quoted-printableエンコーディング
quotient6.3.4 数値の演算
quotient&remainder6.3.4 数値の演算

R
raise一般的な条件の通知
raise制御機能
raise-continuable制御機能
random-data-seedグローバルな状態
random-integer11.9 srfi-27 - ランダムビットのソース
random-real11.9 srfi-27 - ランダムビットのソース
random-source-make-integers11.9 srfi-27 - ランダムビットのソース
random-source-make-reals11.9 srfi-27 - ランダムビットのソース
random-source-pseudo-randomize!11.9 srfi-27 - ランダムビットのソース
random-source-randomize!11.9 srfi-27 - ランダムビットのソース
random-source-state-ref11.9 srfi-27 - ランダムビットのソース
random-source-state-set!11.9 srfi-27 - ランダムビットのソース
random-source?11.9 srfi-27 - ランダムビットのソース
rassoc6.6.7 連想リスト
rassoc-ref6.6.7 連想リスト
rassq6.6.7 連想リスト
rassq-ref6.6.7 連想リスト
rassv6.6.7 連想リスト
rassv-ref6.6.7 連想リスト
rational-valued?6.3.2 数値に関する述語
rational?6.3.2 数値に関する述語
rationalize6.3.4 数値の演算
rbtree->alist8.3 廃止されたモジュール
rbtree-copy8.3 廃止されたモジュール
rbtree-delete!8.3 廃止されたモジュール
rbtree-empty?8.3 廃止されたモジュール
rbtree-exists?8.3 廃止されたモジュール
rbtree-extract-max!8.3 廃止されたモジュール
rbtree-extract-min!8.3 廃止されたモジュール
rbtree-fold8.3 廃止されたモジュール
rbtree-fold-right8.3 廃止されたモジュール
rbtree-get8.3 廃止されたモジュール
rbtree-keys8.3 廃止されたモジュール
rbtree-max8.3 廃止されたモジュール
rbtree-min8.3 廃止されたモジュール
rbtree-num-entries8.3 廃止されたモジュール
rbtree-pop!8.3 廃止されたモジュール
rbtree-push!8.3 廃止されたモジュール
rbtree-put!8.3 廃止されたモジュール
rbtree-update!8.3 廃止されたモジュール
rbtree-values8.3 廃止されたモジュール
rbtree?8.3 廃止されたモジュール
re-distance12.63 util.levenshtein - Levenshtein編集距離
re-distances12.63 util.levenshtein - Levenshtein編集距離
read6.22.7.1 データの読み込み
read-ber-integerポートを用いたI/O
read-block6.22.7.1 データの読み込み
read-block!9.35.4 ユニフォームベクタのブロック入出力
read-byte6.22.7.1 データの読み込み
read-bytevector入出力
read-bytevector!入出力
read-char6.22.7.1 データの読み込み
read-error?制御機能
read-eval-print-loop6.21 eval と repl
read-f16ポートを用いたI/O
read-f32ポートを用いたI/O
read-f64ポートを用いたI/O
read-from-string6.22.5 文字列ポート
read-line6.22.7.1 データの読み込み
read-s16ポートを用いたI/O
read-s32ポートを用いたI/O
read-s64ポートを用いたI/O
read-s8ポートを用いたI/O
read-sintポートを用いたI/O
read-string6.22.7.1 データの読み込み
read-string12.53 text.parse - 入力ストリームのパージング
read-u16ポートを用いたI/O
read-u32ポートを用いたI/O
read-u64ポートを用いたI/O
read-u8入出力
read-u8ポートを用いたI/O
read-uintポートを用いたI/O
read-uvector9.35.4 ユニフォームベクタのブロック入出力
read-uvector!9.35.4 ユニフォームベクタのブロック入出力
read-with-shared-structure6.22.7.1 データの読み込み
read/ss6.22.7.1 データの読み込み
reader-lexical-mode6.22.7.2 リーダー字句モード
real->rational6.3.5 数値の変換
real-part6.3.5 数値の変換
real-valued?6.3.2 数値に関する述語
real?6.3.2 数値に関する述語
reals$プリミティブデータ型のジェネレータ
reals-between$プリミティブデータ型のジェネレータ
reals-exponential$非一様分布
reals-normal$非一様分布
rec4.6 変数束縛
rec4.6 変数束縛
receive4.6 変数束縛
record-accessor12.65 util.record - SLIB-互換のレコード型
record-constructor12.65 util.record - SLIB-互換のレコード型
record-modifier12.65 util.record - SLIB-互換のレコード型
record-predicate12.65 util.record - SLIB-互換のレコード型
record-rtd9.25.3 Inspection layer
record?9.25.3 Inspection layer
reduce6.6.5 リストをたどる手続き
reduce$6.18.3 コンビネータ
reduce-right6.6.5 リストをたどる手続き
reduce-right$6.18.3 コンビネータ
ref6.15 ハッシュテーブル
ref6.18.2 万能アクセサ
ref標準アクセサ
ref9.28.1 基本的なシーケンスのアクセサ
referencer9.28.1 基本的なシーケンスのアクセサ
regexpマッチを試みる
regexp->stringRegexpオブジェクトとrxmatchオブジェクト
regexp-ast6.13.3 正規表現の調査と合成
regexp-compile6.13.3 正規表現の調査と合成
regexp-named-groupsRegexpオブジェクトとrxmatchオブジェクト
regexp-num-groupsRegexpオブジェクトとrxmatchオブジェクト
regexp-optimize6.13.3 正規表現の調査と合成
regexp-parse6.13.3 正規表現の調査と合成
regexp-quote便利なユーティリティ
regexp-replace便利なユーティリティ
regexp-replace*便利なユーティリティ
regexp-replace-all便利なユーティリティ
regexp-replace-all*便利なユーティリティ
regexp-unparse6.13.3 正規表現の調査と合成
regexp?Regexpオブジェクトとrxmatchオブジェクト
regmatchマッチの結果を取り出す
regmatchマッチの結果を取り出す
regmatchマッチの結果を取り出す
relation-accessor基本となるクラスとメソッド
relation-coercer基本となるクラスとメソッド
relation-column-getter基本となるクラスとメソッド
relation-column-getters基本となるクラスとメソッド
relation-column-name?基本となるクラスとメソッド
relation-column-names基本となるクラスとメソッド
relation-column-setter基本となるクラスとメソッド
relation-column-setters基本となるクラスとメソッド
relation-deletable?基本となるクラスとメソッド
relation-delete!基本となるクラスとメソッド
relation-fold基本となるクラスとメソッド
relation-insert!基本となるクラスとメソッド
relation-insertable?基本となるクラスとメソッド
relation-modifier基本となるクラスとメソッド
relation-ref基本となるクラスとメソッド
relation-rows基本となるクラスとメソッド
relation-set!基本となるクラスとメソッド
relative-path?12.23.2 パスネームユーティリティ
relnum-compare9.36 gauche.version - バージョン番号の比較
reload9.26 gauche.reload - モジュールの再ロード
reload-modified-modules9.26 gauche.reload - モジュールの再ロード
reload-verbose9.26 gauche.reload - モジュールの再ロード
remainder6.3.4 数値の演算
remove6.6.5 リストをたどる手続き
remove9.5.2 コレクションからの選択と探索
remove!6.6.5 リストをたどる手続き
remove$6.18.3 コンビネータ
remove-directory*12.23.1 ディレクトリユーティリティ
remove-file12.23.4 ファイル操作
remove-files12.23.4 ファイル操作
remove-from-queue!12.11 data.queue - キュー
remove-hook!9.11 gauche.hook - フック
remove-to9.5.2 コレクションからの選択と探索
report-error処理されなかった例外のふるまい
report-time-resultsベンチマーク
require6.23.3 requireとprovide
require-extension11.14 srfi-55 - require の拡張
requires11.4 srfi-7 - 機能ベースプログラム設定言語
reset9.23 gauche.partcont - 部分継続
reset-character-attributeConsole control
reset-hook!9.11 gauche.hook - フック
reset-primes素数のシーケンス
reset-terminalConsole control
resolve-path12.23.2 パスネームユーティリティ
reverse6.6.6 他のリスト手続き
reverse!6.6.6 他のリスト手続き
reverse-bit-field11.15.4 Field of bits
reverse-bits->generator9.10.1 ジェネレータの生成
reverse-list->string11.6.3 文字列の構築子
reverse-list->vector6.14 ベクタ
reverse-vector->generator9.10.1 ジェネレータの生成
reverse-vector->listベクタの変換
rfc822-atom基本的なフィールドパーザ
rfc822-date->date特定フィールド用パーザ
rfc822-dot-atom基本的なフィールドパーザ
rfc822-field->tokens基本的なフィールドパーザ
rfc822-header->listメッセージヘッダのパーズ
rfc822-header-refメッセージヘッダのパーズ
rfc822-next-token基本的なフィールドパーザ
rfc822-parse-date特定フィールド用パーザ
rfc822-quoted-string基本的なフィールドパーザ
rfc822-read-headersメッセージヘッダのパーズ
rfc822-skip-cfws基本的なフィールドパーザ
rfc822-write-headersメッセージの構築
ring-buffer-add-back!12.13 data.ring-buffer - リングバッファ
ring-buffer-add-front!12.13 data.ring-buffer - リングバッファ
ring-buffer-back12.13 data.ring-buffer - リングバッファ
ring-buffer-capacity12.13 data.ring-buffer - リングバッファ
ring-buffer-empty?12.13 data.ring-buffer - リングバッファ
ring-buffer-front12.13 data.ring-buffer - リングバッファ
ring-buffer-full?12.13 data.ring-buffer - リングバッファ
ring-buffer-num-elements12.13 data.ring-buffer - リングバッファ
ring-buffer-ref12.13 data.ring-buffer - リングバッファ
ring-buffer-remove-back!12.13 data.ring-buffer - リングバッファ
ring-buffer-remove-front!12.13 data.ring-buffer - リングバッファ
ring-buffer-set!12.13 data.ring-buffer - リングバッファ
rlet14.6 変数束縛
rotate-bit-field11.15.4 Field of bits
round6.3.4 数値の演算
round->exact6.3.4 数値の演算
rtd-accessor9.25.4 Procedural layer
rtd-all-field-names9.25.3 Inspection layer
rtd-constructor9.25.4 Procedural layer
rtd-field-mutable?9.25.3 Inspection layer
rtd-field-names9.25.3 Inspection layer
rtd-mutator9.25.4 Procedural layer
rtd-name9.25.3 Inspection layer
rtd-parent9.25.3 Inspection layer
rtd-predicate9.25.4 Procedural layer
rtd?9.25.4 Procedural layer
run-cgi-script->header&body12.71 www.cgi.test - CGIのテスト
run-cgi-script->string12.71 www.cgi.test - CGIのテスト
run-cgi-script->string-list12.71 www.cgi.test - CGIのテスト
run-cgi-script->sxml12.71 www.cgi.test - CGIのテスト
run-hook9.11 gauche.hook - フック
run-process9.24.1 Running subprocess
run-process-pipeline9.24.2 Running multiple processes
rxmatchマッチを試みる
rxmatch->stringマッチの結果を取り出す
rxmatch-afterマッチの結果を取り出す
rxmatch-beforeマッチの結果を取り出す
rxmatch-case便利なユーティリティ
rxmatch-cond便利なユーティリティ
rxmatch-endマッチの結果を取り出す
rxmatch-if便利なユーティリティ
rxmatch-let便利なユーティリティ
rxmatch-named-groupsマッチの結果を取り出す
rxmatch-num-matchesマッチの結果を取り出す
rxmatch-positionsマッチの結果を取り出す
rxmatch-startマッチの結果を取り出す
rxmatch-substringマッチの結果を取り出す
rxmatch-substringsマッチの結果を取り出す

S
s16array9.1 gauche.array - 配列
s16vector9.35.1 ユニフォームベクタの基本操作
s16vector->list9.35.2 ユニフォームベクタの変換
s16vector->vector9.35.2 ユニフォームベクタの変換
s16vector-add9.35.3 ユニフォームベクタの数値演算
s16vector-add!9.35.3 ユニフォームベクタの数値演算
s16vector-and9.35.3 ユニフォームベクタの数値演算
s16vector-and!9.35.3 ユニフォームベクタの数値演算
s16vector-append9.35.1 ユニフォームベクタの基本操作
s16vector-clamp9.35.3 ユニフォームベクタの数値演算
s16vector-clamp!9.35.3 ユニフォームベクタの数値演算
s16vector-copy9.35.1 ユニフォームベクタの基本操作
s16vector-copy!9.35.1 ユニフォームベクタの基本操作
s16vector-dot9.35.3 ユニフォームベクタの数値演算
s16vector-fill!9.35.1 ユニフォームベクタの基本操作
s16vector-ior9.35.3 ユニフォームベクタの数値演算
s16vector-ior!9.35.3 ユニフォームベクタの数値演算
s16vector-length9.35.1 ユニフォームベクタの基本操作
s16vector-mul9.35.3 ユニフォームベクタの数値演算
s16vector-mul!9.35.3 ユニフォームベクタの数値演算
s16vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
s16vector-range-check9.35.3 ユニフォームベクタの数値演算
s16vector-ref9.35.1 ユニフォームベクタの基本操作
s16vector-set!9.35.1 ユニフォームベクタの基本操作
s16vector-sub9.35.3 ユニフォームベクタの数値演算
s16vector-sub!9.35.3 ユニフォームベクタの数値演算
s16vector-xor9.35.3 ユニフォームベクタの数値演算
s16vector-xor!9.35.3 ユニフォームベクタの数値演算
s16vector?9.35.1 ユニフォームベクタの基本操作
s32array9.1 gauche.array - 配列
s32vector9.35.1 ユニフォームベクタの基本操作
s32vector->list9.35.2 ユニフォームベクタの変換
s32vector->string9.35.2 ユニフォームベクタの変換
s32vector->vector9.35.2 ユニフォームベクタの変換
s32vector-add9.35.3 ユニフォームベクタの数値演算
s32vector-add!9.35.3 ユニフォームベクタの数値演算
s32vector-and9.35.3 ユニフォームベクタの数値演算
s32vector-and!9.35.3 ユニフォームベクタの数値演算
s32vector-append9.35.1 ユニフォームベクタの基本操作
s32vector-clamp9.35.3 ユニフォームベクタの数値演算
s32vector-clamp!9.35.3 ユニフォームベクタの数値演算
s32vector-copy9.35.1 ユニフォームベクタの基本操作
s32vector-copy!9.35.1 ユニフォームベクタの基本操作
s32vector-dot9.35.3 ユニフォームベクタの数値演算
s32vector-fill!9.35.1 ユニフォームベクタの基本操作
s32vector-ior9.35.3 ユニフォームベクタの数値演算
s32vector-ior!9.35.3 ユニフォームベクタの数値演算
s32vector-length9.35.1 ユニフォームベクタの基本操作
s32vector-mul9.35.3 ユニフォームベクタの数値演算
s32vector-mul!9.35.3 ユニフォームベクタの数値演算
s32vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
s32vector-range-check9.35.3 ユニフォームベクタの数値演算
s32vector-ref9.35.1 ユニフォームベクタの基本操作
s32vector-set!9.35.1 ユニフォームベクタの基本操作
s32vector-sub9.35.3 ユニフォームベクタの数値演算
s32vector-sub!9.35.3 ユニフォームベクタの数値演算
s32vector-xor9.35.3 ユニフォームベクタの数値演算
s32vector-xor!9.35.3 ユニフォームベクタの数値演算
s32vector?9.35.1 ユニフォームベクタの基本操作
s64array9.1 gauche.array - 配列
s64vector9.35.1 ユニフォームベクタの基本操作
s64vector->list9.35.2 ユニフォームベクタの変換
s64vector->vector9.35.2 ユニフォームベクタの変換
s64vector-add9.35.3 ユニフォームベクタの数値演算
s64vector-add!9.35.3 ユニフォームベクタの数値演算
s64vector-and9.35.3 ユニフォームベクタの数値演算
s64vector-and!9.35.3 ユニフォームベクタの数値演算
s64vector-append9.35.1 ユニフォームベクタの基本操作
s64vector-clamp9.35.3 ユニフォームベクタの数値演算
s64vector-clamp!9.35.3 ユニフォームベクタの数値演算
s64vector-copy9.35.1 ユニフォームベクタの基本操作
s64vector-copy!9.35.1 ユニフォームベクタの基本操作
s64vector-dot9.35.3 ユニフォームベクタの数値演算
s64vector-fill!9.35.1 ユニフォームベクタの基本操作
s64vector-ior9.35.3 ユニフォームベクタの数値演算
s64vector-ior!9.35.3 ユニフォームベクタの数値演算
s64vector-length9.35.1 ユニフォームベクタの基本操作
s64vector-mul9.35.3 ユニフォームベクタの数値演算
s64vector-mul!9.35.3 ユニフォームベクタの数値演算
s64vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
s64vector-range-check9.35.3 ユニフォームベクタの数値演算
s64vector-ref9.35.1 ユニフォームベクタの基本操作
s64vector-set!9.35.1 ユニフォームベクタの基本操作
s64vector-sub9.35.3 ユニフォームベクタの数値演算
s64vector-sub!9.35.3 ユニフォームベクタの数値演算
s64vector-xor9.35.3 ユニフォームベクタの数値演算
s64vector-xor!9.35.3 ユニフォームベクタの数値演算
s64vector?9.35.1 ユニフォームベクタの基本操作
s8array9.1 gauche.array - 配列
s8vector9.35.1 ユニフォームベクタの基本操作
s8vector->list9.35.2 ユニフォームベクタの変換
s8vector->string9.35.2 ユニフォームベクタの変換
s8vector->vector9.35.2 ユニフォームベクタの変換
s8vector-add9.35.3 ユニフォームベクタの数値演算
s8vector-add!9.35.3 ユニフォームベクタの数値演算
s8vector-and9.35.3 ユニフォームベクタの数値演算
s8vector-and!9.35.3 ユニフォームベクタの数値演算
s8vector-append9.35.1 ユニフォームベクタの基本操作
s8vector-clamp9.35.3 ユニフォームベクタの数値演算
s8vector-clamp!9.35.3 ユニフォームベクタの数値演算
s8vector-copy9.35.1 ユニフォームベクタの基本操作
s8vector-copy!9.35.1 ユニフォームベクタの基本操作
s8vector-dot9.35.3 ユニフォームベクタの数値演算
s8vector-fill!9.35.1 ユニフォームベクタの基本操作
s8vector-ior9.35.3 ユニフォームベクタの数値演算
s8vector-ior!9.35.3 ユニフォームベクタの数値演算
s8vector-length9.35.1 ユニフォームベクタの基本操作
s8vector-mul9.35.3 ユニフォームベクタの数値演算
s8vector-mul!9.35.3 ユニフォームベクタの数値演算
s8vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
s8vector-range-check9.35.3 ユニフォームベクタの数値演算
s8vector-ref9.35.1 ユニフォームベクタの基本操作
s8vector-set!9.35.1 ユニフォームベクタの基本操作
s8vector-sub9.35.3 ユニフォームベクタの数値演算
s8vector-sub!9.35.3 ユニフォームベクタの数値演算
s8vector-xor9.35.3 ユニフォームベクタの数値演算
s8vector-xor!9.35.3 ユニフォームベクタの数値演算
s8vector?9.35.1 ユニフォームベクタの基本操作
samples$プリミティブデータ型のジェネレータ
samples-from複合データ型のジェネレータ
save-bundle!バンドル準備
scheme-report-environment6.21 eval と repl
secondList selectors
seconds->timeSRFI time
select-kids12.45.1 SXPathの基本的なコンバータとアプリケータ
select-module4.13.3 モジュールの定義と選択
selector-add!9.27 gauche.selector - 簡単なディスパッチャ
selector-delete!9.27 gauche.selector - 簡単なディスパッチャ
selector-select9.27 gauche.selector - 簡単なディスパッチャ
sequence->kmp-stepperSelection and searching
sequence-containsSelection and searching
sequences-of複合データ型のジェネレータ
sequences-of複合データ型のジェネレータ
setConstructors
set!4.4 代入
set!4.4 代入
set!-values4.4 代入
set->bagCopying and conversion
set->bag!Copying and conversion
set->listCopying and conversion
set-adjoinUpdaters
set-adjoin!Updaters
set-any?The whole set
set-box!11.19 srfi-111 - ボックス
set-car!6.6.4 リストへのアクセスと変更
set-cdr!6.6.4 リストへのアクセスと変更
set-character-attributeConsole control
set-contains?Predicates
set-copyCopying and conversion
set-countThe whole set
set-deleteUpdaters
set-delete!Updaters
set-delete-allUpdaters
set-delete-all!Updaters
set-differenceSet theory operations
set-difference!Set theory operations
set-disjoint?Predicates
set-element-comparatorAccessors
set-empty?Predicates
set-every?The whole set
set-filterMapping and folding
set-filter!Mapping and folding
set-findThe whole set
set-foldMapping and folding
set-for-eachMapping and folding
set-intersectionSet theory operations
set-intersection!Set theory operations
set-mapMapping and folding
set-memberAccessors
set-partitionMapping and folding
set-partition!Mapping and folding
set-removeMapping and folding
set-remove!Mapping and folding
set-replaceUpdaters
set-replace!Updaters
set-search!Updaters
set-signal-handler!6.25.7.3 シグナルの処理
set-signal-pending-limit6.25.7.3 シグナルの処理
set-sizeThe whole set
set-time-nanosecond!11.8.3 時間に関する手続き
set-time-second!11.8.3 時間に関する手続き
set-time-type!11.8.3 時間に関する手続き
set-unfoldConstructors
set-unionSet theory operations
set-union!Set theory operations
set-xorSet theory operations
set-xor!Set theory operations
set<=?Subsets
set<?Subsets
set=?Subsets
set>=?Subsets
set>?Subsets
setter4.4 代入
seventhList selectors
sha1-digest12.40 rfc.sha - SHAメッセージダイジェスト
sha1-digest-string12.40 rfc.sha - SHAメッセージダイジェスト
sha224-digest12.40 rfc.sha - SHAメッセージダイジェスト
sha224-digest-string12.40 rfc.sha - SHAメッセージダイジェスト
sha256-digest12.40 rfc.sha - SHAメッセージダイジェスト
sha256-digest-string12.40 rfc.sha - SHAメッセージダイジェスト
sha384-digest12.40 rfc.sha - SHAメッセージダイジェスト
sha384-digest-string12.40 rfc.sha - SHAメッセージダイジェスト
sha512-digest12.40 rfc.sha - SHAメッセージダイジェスト
sha512-digest-string12.40 rfc.sha - SHAメッセージダイジェスト
shape9.1 gauche.array - 配列
shape-for-each9.1 gauche.array - 配列
share-array9.1 gauche.array - 配列
shell-escape-string9.24.4 Process ports
shell-tokenize-string9.24.4 Process ports
shift9.23 gauche.partcont - 部分継続
show-cursorConsole control
shufflePermutation and shuffling
shuffle!Permutation and shuffling
shuffle-toPermutation and shuffling
shutdown-methodShutdown method
simplify-path12.23.2 パスネームユーティリティ
sin6.3.4 数値の演算
sinh6.3.4 数値の演算
sixthList selectors
size-of9.5.3 コレクションに対する様々な操作
skip-until12.53 text.parse - 入力ストリームのパージング
skip-while12.53 text.parse - 入力ストリームのパージング
slices6.6.4 リストへのアクセスと変更
slot-bound-using-accessor?7.5.2 スロットアクセスのカスタマイズ
slot-bound-using-class?特殊アクセサ
slot-bound?標準アクセサ
slot-definition-accessor7.2.4 スロット定義オブジェクト
slot-definition-allocation7.2.4 スロット定義オブジェクト
slot-definition-getter7.2.4 スロット定義オブジェクト
slot-definition-name7.2.4 スロット定義オブジェクト
slot-definition-option7.2.4 スロット定義オブジェクト
slot-definition-options7.2.4 スロット定義オブジェクト
slot-definition-setter7.2.4 スロット定義オブジェクト
slot-exists?標準アクセサ
slot-initialize-using-accessor!7.5.2 スロットアクセスのカスタマイズ
slot-missingフォールバックメソッド
slot-missingフォールバックメソッド
slot-pop!標準アクセサ
slot-push!標準アクセサ
slot-ref標準アクセサ
slot-ref-using-accessor7.5.2 スロットアクセスのカスタマイズ
slot-ref-using-class特殊アクセサ
slot-set!標準アクセサ
slot-set-using-accessor!7.5.2 スロットアクセスのカスタマイズ
slot-set-using-class!特殊アクセサ
slot-unboundフォールバックメソッド
slot-unboundフォールバックメソッド
small-prime?素数かどうかを調べる
sockaddr-addrソケットアドレスオブジェクト
sockaddr-familyソケットアドレスオブジェクト
sockaddr-familyソケットアドレスオブジェクト
sockaddr-familyソケットアドレスオブジェクト
sockaddr-nameソケットアドレスオブジェクト
sockaddr-nameソケットアドレスオブジェクト
sockaddr-nameソケットアドレスオブジェクト
sockaddr-portソケットアドレスオブジェクト
socket-accept9.19.3 低レベルソケットインタフェース
socket-acceptCommunication
socket-address9.19.2 高レベルネットワーク手続き
socket-bind9.19.3 低レベルソケットインタフェース
socket-buildmsg9.19.3 低レベルソケットインタフェース
socket-close9.19.2 高レベルネットワーク手続き
socket-closeCommunication
socket-connect9.19.3 低レベルソケットインタフェース
socket-domainSocket domain
socket-fd9.19.3 低レベルソケットインタフェース
socket-getpeername9.19.3 低レベルソケットインタフェース
socket-getsockname9.19.3 低レベルソケットインタフェース
socket-getsockopt9.19.3 低レベルソケットインタフェース
socket-input-port9.19.2 高レベルネットワーク手続き
socket-input-portCommunication
socket-listen9.19.3 低レベルソケットインタフェース
socket-merge-flagsFlags
socket-output-port9.19.2 高レベルネットワーク手続き
socket-output-portCommunication
socket-purge-flagsFlags
socket-recv9.19.3 低レベルソケットインタフェース
socket-recvCommunication
socket-recv!9.19.3 低レベルソケットインタフェース
socket-recvfrom9.19.3 低レベルソケットインタフェース
socket-recvfrom!9.19.3 低レベルソケットインタフェース
socket-send9.19.3 低レベルソケットインタフェース
socket-sendCommunication
socket-sendmsg9.19.3 低レベルソケットインタフェース
socket-sendto9.19.3 低レベルソケットインタフェース
socket-setsockopt9.19.3 低レベルソケットインタフェース
socket-shutdown9.19.3 低レベルソケットインタフェース
socket-shutdownCommunication
socket-status9.19.3 低レベルソケットインタフェース
socket?ソケットオブジェクト
sort6.24 ソートとマージ
sort!6.24 ソートとマージ
sort-applicable-methods7.5.4 メソッド適用のカスタマイズ
sort-by6.24 ソートとマージ
sort-by!6.24 ソートとマージ
sorted?6.24 ソートとマージ
source-code6.26.1 デバッグ補助
source-location6.26.1 デバッグ補助
spanList searching
span!List searching
sparse-matrix-clear!12.14.2 疎行列
sparse-matrix-copy12.14.2 疎行列
sparse-matrix-delete!12.14.2 疎行列
sparse-matrix-exists?12.14.2 疎行列
sparse-matrix-fold12.14.2 疎行列
sparse-matrix-for-each12.14.2 疎行列
sparse-matrix-inc!12.14.2 疎行列
sparse-matrix-keys12.14.2 疎行列
sparse-matrix-map12.14.2 疎行列
sparse-matrix-num-entries12.14.2 疎行列
sparse-matrix-pop!12.14.2 疎行列
sparse-matrix-push!12.14.2 疎行列
sparse-matrix-ref12.14.2 疎行列
sparse-matrix-set!12.14.2 疎行列
sparse-matrix-update!12.14.2 疎行列
sparse-matrix-values12.14.2 疎行列
sparse-table-clear!12.14.3 疎なテーブル
sparse-table-comparator12.14.3 疎なテーブル
sparse-table-copy12.14.3 疎なテーブル
sparse-table-delete!12.14.3 疎なテーブル
sparse-table-exists?12.14.3 疎なテーブル
sparse-table-fold12.14.3 疎なテーブル
sparse-table-for-each12.14.3 疎なテーブル
sparse-table-keys12.14.3 疎なテーブル
sparse-table-map12.14.3 疎なテーブル
sparse-table-num-entries12.14.3 疎なテーブル
sparse-table-pop!12.14.3 疎なテーブル
sparse-table-push!12.14.3 疎なテーブル
sparse-table-ref12.14.3 疎なテーブル
sparse-table-set!12.14.3 疎なテーブル
sparse-table-update!12.14.3 疎なテーブル
sparse-table-values12.14.3 疎なテーブル
sparse-vector-clear!12.14.1 疎なベクタ
sparse-vector-copy12.14.1 疎なベクタ
sparse-vector-delete!12.14.1 疎なベクタ
sparse-vector-exists?12.14.1 疎なベクタ
sparse-vector-fold12.14.1 疎なベクタ
sparse-vector-for-each12.14.1 疎なベクタ
sparse-vector-inc!12.14.1 疎なベクタ
sparse-vector-keys12.14.1 疎なベクタ
sparse-vector-map12.14.1 疎なベクタ
sparse-vector-max-index-bits12.14.1 疎なベクタ
sparse-vector-num-entries12.14.1 疎なベクタ
sparse-vector-pop!12.14.1 疎なベクタ
sparse-vector-push!12.14.1 疎なベクタ
sparse-vector-ref12.14.1 疎なベクタ
sparse-vector-set!12.14.1 疎なベクタ
sparse-vector-update!12.14.1 疎なベクタ
sparse-vector-values12.14.1 疎なベクタ
split-at6.6.4 リストへのアクセスと変更
split-at!6.6.4 リストへのアクセスと変更
split-at*6.6.4 リストへのアクセスと変更
sql-tokenize12.55 text.sql - SQLのパーズと構築
sqrt6.3.4 数値の演算
srl:display-sxml12.47.2 カスタムSXML変換
srl:parameterizable12.47.2 カスタムSXML変換
srl:sxml->html12.47.1 簡単なSXML変換
srl:sxml->html-noindent12.47.1 簡単なSXML変換
srl:sxml->string12.47.2 カスタムSXML変換
srl:sxml->xml12.47.1 簡単なSXML変換
srl:sxml->xml-noindent12.47.1 簡単なSXML変換
ssax:assert-token12.44.3 SSAXの高レベルのパーザとスキャナ
ssax:complete-start-tag12.44.2 SSAXの低レベルパージングコード
ssax:handle-parsed-entity12.44.2 SSAXの低レベルパージングコード
ssax:make-elem-parser12.44.4 SSAXの高レベルのパーザ - XMLからSXMLへ
ssax:make-parser12.44.4 SSAXの高レベルのパーザ - XMLからSXMLへ
ssax:make-pi-parser12.44.4 SSAXの高レベルのパーザ - XMLからSXMLへ
ssax:ncname-starting-char?12.44.2 SSAXの低レベルパージングコード
ssax:read-attributes12.44.2 SSAXの低レベルパージングコード
ssax:read-cdata-body12.44.2 SSAXの低レベルパージングコード
ssax:read-char-data12.44.3 SSAXの高レベルのパーザとスキャナ
ssax:read-char-ref12.44.2 SSAXの低レベルパージングコード
ssax:read-external-id12.44.2 SSAXの低レベルパージングコード
ssax:read-markup-token12.44.2 SSAXの低レベルパージングコード
ssax:read-NCName12.44.2 SSAXの低レベルパージングコード
ssax:read-pi-body-as-string12.44.2 SSAXの低レベルパージングコード
ssax:read-QName12.44.2 SSAXの低レベルパージングコード
ssax:resolve-name12.44.2 SSAXの低レベルパージングコード
ssax:reverse-collect-str12.44.4 SSAXの高レベルのパーザ - XMLからSXMLへ
ssax:reverse-collect-str-drop-ws12.44.4 SSAXの高レベルのパーザ - XMLからSXMLへ
ssax:scan-Misc12.44.3 SSAXの高レベルのパーザとスキャナ
ssax:skip-internal-dtd12.44.2 SSAXの低レベルパージングコード
ssax:skip-pi12.44.2 SSAXの低レベルパージングコード
ssax:skip-S12.44.2 SSAXの低レベルパージングコード
ssax:uri-string->symbol12.44.2 SSAXの低レベルパージングコード
ssax:xml->sxml12.44.4 SSAXの高レベルのパーザ - XMLからSXMLへ
stable-sort6.24 ソートとマージ
stable-sort!6.24 ソートとマージ
stable-sort-by6.24 ソートとマージ
stable-sort-by!6.24 ソートとマージ
standard-error-port6.22.3 ポート共通の操作
standard-input-port6.22.3 ポート共通の操作
standard-output-port6.22.3 ポート共通の操作
stream12.67 util.stream - ストリームライブラリ
stream->list12.67 util.stream - ストリームライブラリ
stream->string12.67 util.stream - ストリームライブラリ
stream-any12.67 util.stream - ストリームライブラリ
stream-append12.67 util.stream - ストリームライブラリ
stream-break12.67 util.stream - ストリームライブラリ
stream-butlast12.67 util.stream - ストリームライブラリ
stream-butlast-n12.67 util.stream - ストリームライブラリ
stream-caaaar12.67 util.stream - ストリームライブラリ
stream-caaadr12.67 util.stream - ストリームライブラリ
stream-caaar12.67 util.stream - ストリームライブラリ
stream-caadar12.67 util.stream - ストリームライブラリ
stream-caaddr12.67 util.stream - ストリームライブラリ
stream-caadr12.67 util.stream - ストリームライブラリ
stream-caar12.67 util.stream - ストリームライブラリ
stream-cadaar12.67 util.stream - ストリームライブラリ
stream-cadadr12.67 util.stream - ストリームライブラリ
stream-cadar12.67 util.stream - ストリームライブラリ
stream-caddar12.67 util.stream - ストリームライブラリ
stream-cadddr12.67 util.stream - ストリームライブラリ
stream-caddr12.67 util.stream - ストリームライブラリ
stream-cadr12.67 util.stream - ストリームライブラリ
stream-car12.67 util.stream - ストリームライブラリ
stream-cdaaar12.67 util.stream - ストリームライブラリ
stream-cdaadr12.67 util.stream - ストリームライブラリ
stream-cdaar12.67 util.stream - ストリームライブラリ
stream-cdadar12.67 util.stream - ストリームライブラリ
stream-cdaddr12.67 util.stream - ストリームライブラリ
stream-cdadr12.67 util.stream - ストリームライブラリ
stream-cdar12.67 util.stream - ストリームライブラリ
stream-cddaar12.67 util.stream - ストリームライブラリ
stream-cddadr12.67 util.stream - ストリームライブラリ
stream-cddar12.67 util.stream - ストリームライブラリ
stream-cdddar12.67 util.stream - ストリームライブラリ
stream-cddddr12.67 util.stream - ストリームライブラリ
stream-cdddr12.67 util.stream - ストリームライブラリ
stream-cddr12.67 util.stream - ストリームライブラリ
stream-cdr12.67 util.stream - ストリームライブラリ
stream-concatenate12.67 util.stream - ストリームライブラリ
stream-cons12.67 util.stream - ストリームライブラリ
stream-cons*12.67 util.stream - ストリームライブラリ
stream-count12.67 util.stream - ストリームライブラリ
stream-delay12.67 util.stream - ストリームライブラリ
stream-delete12.67 util.stream - ストリームライブラリ
stream-delete-duplicates12.67 util.stream - ストリームライブラリ
stream-drop12.67 util.stream - ストリームライブラリ
stream-drop-safe12.67 util.stream - ストリームライブラリ
stream-drop-while12.67 util.stream - ストリームライブラリ
stream-eighth12.67 util.stream - ストリームライブラリ
stream-every12.67 util.stream - ストリームライブラリ
stream-fifth12.67 util.stream - ストリームライブラリ
stream-filter12.67 util.stream - ストリームライブラリ
stream-find12.67 util.stream - ストリームライブラリ
stream-find-tail12.67 util.stream - ストリームライブラリ
stream-first12.67 util.stream - ストリームライブラリ
stream-for-each12.67 util.stream - ストリームライブラリ
stream-format12.67 util.stream - ストリームライブラリ
stream-fourth12.67 util.stream - ストリームライブラリ
stream-grep12.67 util.stream - ストリームライブラリ
stream-index12.67 util.stream - ストリームライブラリ
stream-intersperse12.67 util.stream - ストリームライブラリ
stream-iota12.67 util.stream - ストリームライブラリ
stream-last12.67 util.stream - ストリームライブラリ
stream-last-n12.67 util.stream - ストリームライブラリ
stream-length12.67 util.stream - ストリームライブラリ
stream-length>=12.67 util.stream - ストリームライブラリ
stream-lines12.67 util.stream - ストリームライブラリ
stream-map12.67 util.stream - ストリームライブラリ
stream-member12.67 util.stream - ストリームライブラリ
stream-memq12.67 util.stream - ストリームライブラリ
stream-memv12.67 util.stream - ストリームライブラリ
stream-ninth12.67 util.stream - ストリームライブラリ
stream-null?12.67 util.stream - ストリームライブラリ
stream-pair?12.67 util.stream - ストリームライブラリ
stream-partition12.67 util.stream - ストリームライブラリ
stream-prefix=12.67 util.stream - ストリームライブラリ
stream-ref12.67 util.stream - ストリームライブラリ
stream-remove12.67 util.stream - ストリームライブラリ
stream-reverse12.67 util.stream - ストリームライブラリ
stream-second12.67 util.stream - ストリームライブラリ
stream-seventh12.67 util.stream - ストリームライブラリ
stream-sixth12.67 util.stream - ストリームライブラリ
stream-span12.67 util.stream - ストリームライブラリ
stream-split12.67 util.stream - ストリームライブラリ
stream-tabulate12.67 util.stream - ストリームライブラリ
stream-take12.67 util.stream - ストリームライブラリ
stream-take-safe12.67 util.stream - ストリームライブラリ
stream-take-while12.67 util.stream - ストリームライブラリ
stream-tenth12.67 util.stream - ストリームライブラリ
stream-third12.67 util.stream - ストリームライブラリ
stream-unfoldn12.67 util.stream - ストリームライブラリ
stream-xcons12.67 util.stream - ストリームライブラリ
stream=12.67 util.stream - ストリームライブラリ
stream?12.67 util.stream - ストリームライブラリ
string6.12.3 文字列の構築子
string->char-set11.7.1 文字セットの構築子
string->char-set!11.7.1 文字セットの構築子
string->date11.8.5 日付の読み書き
string->generator9.10.1 ジェネレータの生成
string->grapheme-clusters9.34.2 Unicode text segmentation
string->list6.12.7 文字列を扱うその他の手続き
string->number6.3.5 数値の変換
string->regexpRegexpオブジェクトとrxmatchオブジェクト
string->s32vector9.35.2 ユニフォームベクタの変換
string->s32vector!9.35.2 ユニフォームベクタの変換
string->s8vector9.35.2 ユニフォームベクタの変換
string->s8vector!9.35.2 ユニフォームベクタの変換
string->stream12.67 util.stream - ストリームライブラリ
string->symbol6.7 シンボル
string->u32vector9.35.2 ユニフォームベクタの変換
string->u32vector!9.35.2 ユニフォームベクタの変換
string->u8vector9.35.2 ユニフォームベクタの変換
string->u8vector!9.35.2 ユニフォームベクタの変換
string->uninterned-symbol6.7 シンボル
string->utf89.34.1 Unicode transfer encodings
string->vector6.14 ベクタ
string->words9.34.2 Unicode text segmentation
string-any11.6.2 文字列についての述語
string-append6.12.7 文字列を扱うその他の手続き
string-append!11.24 srfi-118 - 簡単な可変長文字列
string-append-ec内包表記マクロ
string-append/shared11.6.9 文字列の反転と追加
string-byte-ref6.12.5 文字列のアクセスと変更
string-byte-set!6.12.5 文字列のアクセスと変更
string-ci-hash6.2.3 ハッシュ
string-ci-hash11.16 srfi-69 - 基本的なハッシュテーブル
string-ci<11.6.5 文字列の色々な比較
string-ci<=11.6.5 文字列の色々な比較
string-ci<=?6.12.6 文字列の比較
string-ci<=?9.34.3 Full string case conversion
string-ci<>11.6.5 文字列の色々な比較
string-ci<?6.12.6 文字列の比較
string-ci<?9.34.3 Full string case conversion
string-ci=11.6.5 文字列の色々な比較
string-ci=?6.12.6 文字列の比較
string-ci=?9.34.3 Full string case conversion
string-ci>11.6.5 文字列の色々な比較
string-ci>=11.6.5 文字列の色々な比較
string-ci>=?6.12.6 文字列の比較
string-ci>=?9.34.3 Full string case conversion
string-ci>?6.12.6 文字列の比較
string-ci>?9.34.3 Full string case conversion
string-compare11.6.5 文字列の色々な比較
string-compare-ci11.6.5 文字列の色々な比較
string-concatenate11.6.9 文字列の反転と追加
string-concatenate-reverse11.6.9 文字列の反転と追加
string-concatenate-reverse/shared11.6.9 文字列の反転と追加
string-concatenate/shared11.6.9 文字列の反転と追加
string-contains11.6.7 文字列の探索
string-contains-ci11.6.7 文字列の探索
string-copy6.12.7 文字列を扱うその他の手続き
string-copy!11.6.4 文字列の選択
string-count11.6.7 文字列の探索
string-delete11.6.13 文字列のフィルタリング
string-downcase9.34.3 Full string case conversion
string-downcase11.6.8 文字列のケース(大文字小文字)マッピング
string-downcase!11.6.8 文字列のケース(大文字小文字)マッピング
string-drop11.6.4 文字列の選択
string-drop-right11.6.4 文字列の選択
string-ec内包表記マクロ
string-every11.6.2 文字列についての述語
string-fill!6.12.7 文字列を扱うその他の手続き
string-filter11.6.13 文字列のフィルタリング
string-fold11.6.10 文字列のマッピング
string-fold-right11.6.10 文字列のマッピング
string-foldcase9.34.3 Full string case conversion
string-for-each制御機能
string-for-each11.6.10 文字列のマッピング
string-for-each-index11.6.10 文字列のマッピング
string-hash6.2.3 ハッシュ
string-hash11.6.5 文字列の色々な比較
string-hash11.16 srfi-69 - 基本的なハッシュテーブル
string-hash-ci11.6.5 文字列の色々な比較
string-immutable?6.12.2 文字列に関する述語
string-incomplete->complete6.12.8 不完全文字列
string-incomplete?6.12.2 文字列に関する述語
string-index11.6.7 文字列の探索
string-index-right11.6.7 文字列の探索
string-join6.12.7 文字列を扱うその他の手続き
string-kmp-partial-search11.6.14 低レベルな文字列に関する手続き
string-length6.12.5 文字列のアクセスと変更
string-map制御機能
string-map11.6.10 文字列のマッピング
string-map!11.6.10 文字列のマッピング
string-null?11.6.2 文字列についての述語
string-pad11.6.4 文字列の選択
string-pad-right11.6.4 文字列の選択
string-parse-final-start+end11.6.14 低レベルな文字列に関する手続き
string-parse-start+end11.6.14 低レベルな文字列に関する手続き
string-prefix-ci?11.6.6 文字列のプリフィックスとサフィックス
string-prefix-length11.6.6 文字列のプリフィックスとサフィックス
string-prefix-length-ci11.6.6 文字列のプリフィックスとサフィックス
string-prefix?11.6.6 文字列のプリフィックスとサフィックス
string-ref6.12.5 文字列のアクセスと変更
string-replace11.6.12 他の文字列操作
string-replace!11.24 srfi-118 - 簡単な可変長文字列
string-reverse11.6.9 文字列の反転と追加
string-reverse!11.6.9 文字列の反転と追加
string-scan6.12.7 文字列を扱うその他の手続き
string-scan-right6.12.7 文字列を扱うその他の手続き
string-set!6.12.5 文字列のアクセスと変更
string-size6.12.5 文字列のアクセスと変更
string-skip11.6.7 文字列の探索
string-skip-right11.6.7 文字列の探索
string-split6.12.7 文字列を扱うその他の手続き
string-suffix-ci?11.6.6 文字列のプリフィックスとサフィックス
string-suffix-length11.6.6 文字列のプリフィックスとサフィックス
string-suffix-length-ci11.6.6 文字列のプリフィックスとサフィックス
string-suffix?11.6.6 文字列のプリフィックスとサフィックス
string-tabulate11.6.3 文字列の構築子
string-take11.6.4 文字列の選択
string-take-right11.6.4 文字列の選択
string-titlecase9.34.3 Full string case conversion
string-titlecase11.6.8 文字列のケース(大文字小文字)マッピング
string-titlecase!11.6.8 文字列のケース(大文字小文字)マッピング
string-tokenize11.6.12 他の文字列操作
string-tr12.56 text.tr - 文字変換
string-trim11.6.4 文字列の選択
string-trim-both11.6.4 文字列の選択
string-trim-right11.6.4 文字列の選択
string-unfold11.6.10 文字列のマッピング
string-unfold-right11.6.10 文字列のマッピング
string-upcase9.34.3 Full string case conversion
string-upcase11.6.8 文字列のケース(大文字小文字)マッピング
string-upcase!11.6.8 文字列のケース(大文字小文字)マッピング
string-xcopy!11.6.11 文字列のローテーション
string<11.6.5 文字列の色々な比較
string<=11.6.5 文字列の色々な比較
string<=?6.12.6 文字列の比較
string<>11.6.5 文字列の色々な比較
string<?6.12.6 文字列の比較
string=11.6.5 文字列の色々な比較
string=?6.12.6 文字列の比較
string>11.6.5 文字列の色々な比較
string>=11.6.5 文字列の色々な比較
string>=?6.12.6 文字列の比較
string>?6.12.6 文字列の比較
string?6.12.2 文字列に関する述語
strings-of複合データ型のジェネレータ
strings-of複合データ型のジェネレータ
strings-of複合データ型のジェネレータ
subseq9.28.2 シーケンスのスライス
substring6.12.7 文字列を扱うその他の手続き
substring-spec-ok?11.6.14 低レベルな文字列に関する手続き
substring/shared11.6.4 文字列の選択
subtract-duration11.8.3 時間に関する手続き
subtract-duration!11.8.3 時間に関する手続き
subtype?6.1 型とクラス
sum-ec内包表記マクロ
supported-character-encodings6.10 文字
sxml:add-attr12.46.3 SXMLのモディファイヤ
sxml:add-attr!12.46.3 SXMLのモディファイヤ
sxml:add-aux12.46.3 SXMLのモディファイヤ
sxml:add-aux!12.46.3 SXMLのモディファイヤ
sxml:add-parents12.46.4 SXPathの補助的ユーティリティ
sxml:ancestor12.45.3 SXPathの拡張
sxml:ancestor-or-self12.45.3 SXPathの拡張
sxml:attr12.46.2 SXMLへのアクセッサ
sxml:attr->html12.46.5 SXMLからマークアップへの変換
sxml:attr->xml12.46.5 SXMLからマークアップへの変換
sxml:attr-as-list12.46.2 SXMLへのアクセッサ
sxml:attr-list12.45.3 SXPathの拡張
sxml:attr-list-node12.46.2 SXMLへのアクセッサ
sxml:attr-list-u12.46.2 SXMLへのアクセッサ
sxml:attr-u12.46.2 SXMLへのアクセッサ
sxml:attribute12.45.3 SXPathの拡張
sxml:aux-as-list12.46.2 SXMLへのアクセッサ
sxml:aux-list12.46.2 SXMLへのアクセッサ
sxml:aux-list-node12.46.2 SXMLへのアクセッサ
sxml:aux-list-u12.46.2 SXMLへのアクセッサ
sxml:aux-node12.46.2 SXMLへのアクセッサ
sxml:aux-nodes12.46.2 SXMLへのアクセッサ
sxml:boolean12.45.3 SXPathの拡張
sxml:change-attr12.46.3 SXMLのモディファイヤ
sxml:change-attr!12.46.3 SXMLのモディファイヤ
sxml:change-attrlist12.46.3 SXMLのモディファイヤ
sxml:change-attrlist!12.46.3 SXMLのモディファイヤ
sxml:change-content12.46.3 SXMLのモディファイヤ
sxml:change-content!12.46.3 SXMLのモディファイヤ
sxml:change-name12.46.3 SXMLのモディファイヤ
sxml:change-name!12.46.3 SXMLのモディファイヤ
sxml:child12.45.3 SXPathの拡張
sxml:child-elements12.45.3 SXPathの拡張
sxml:child-nodes12.45.3 SXPathの拡張
sxml:clean12.46.3 SXMLのモディファイヤ
sxml:clean-feed12.46.5 SXMLからマークアップへの変換
sxml:content12.46.2 SXMLへのアクセッサ
sxml:content-raw12.46.2 SXMLへのアクセッサ
sxml:descendant12.45.3 SXPathの拡張
sxml:descendant-or-self12.45.3 SXPathの拡張
sxml:element-name12.46.2 SXMLへのアクセッサ
sxml:element?12.45.1 SXPathの基本的なコンバータとアプリケータ
sxml:empty-element?12.46.1 SXMLの述語
sxml:equal?12.45.3 SXPathの拡張
sxml:equality-cmp12.45.3 SXPathの拡張
sxml:filter12.45.1 SXPathの基本的なコンバータとアプリケータ
sxml:following12.45.3 SXPathの拡張
sxml:following-sibling12.45.3 SXPathの拡張
sxml:id12.45.3 SXPathの拡張
sxml:id-alist12.45.2 SXPathクエリ言語
sxml:invert12.45.1 SXPathの基本的なコンバータとアプリケータ
sxml:lookup12.46.4 SXPathの補助的ユーティリティ
sxml:minimized?12.46.1 SXMLの述語
sxml:name12.46.2 SXMLへのアクセッサ
sxml:name->ns-id12.46.2 SXMLへのアクセッサ
sxml:namespace12.45.3 SXPathの拡張
sxml:ncname12.46.2 SXMLへのアクセッサ
sxml:node-name12.46.2 SXMLへのアクセッサ
sxml:node-parent12.46.4 SXPathの補助的ユーティリティ
sxml:node?12.45.3 SXPathの拡張
sxml:non-terminated-html-tag?12.46.5 SXMLからマークアップへの変換
sxml:normalized?12.46.1 SXMLの述語
sxml:not-equal?12.45.3 SXPathの拡張
sxml:ns-id12.46.2 SXMLへのアクセッサ
sxml:ns-id->nodes12.46.2 SXMLへのアクセッサ
sxml:ns-id->uri12.46.2 SXMLへのアクセッサ
sxml:ns-list12.46.2 SXMLへのアクセッサ
sxml:ns-prefix12.46.2 SXMLへのアクセッサ
sxml:ns-uri12.46.2 SXMLへのアクセッサ
sxml:ns-uri->id12.46.2 SXMLへのアクセッサ
sxml:num-attr12.46.2 SXMLへのアクセッサ
sxml:number12.45.3 SXPathの拡張
sxml:parent12.45.3 SXPathの拡張
sxml:preceding12.45.3 SXPathの拡張
sxml:preceding-sibling12.45.3 SXPathの拡張
sxml:relational-cmp12.45.3 SXPathの拡張
sxml:set-attr12.46.3 SXMLのモディファイヤ
sxml:set-attr!12.46.3 SXMLのモディファイヤ
sxml:shallow-minimized?12.46.1 SXMLの述語
sxml:shallow-normalized?12.46.1 SXMLの述語
sxml:squeeze12.46.3 SXMLのモディファイヤ
sxml:squeeze!12.46.3 SXMLのモディファイヤ
sxml:string12.45.3 SXPathの拡張
sxml:string->html12.46.5 SXMLからマークアップへの変換
sxml:string->xml12.46.5 SXMLからマークアップへの変換
sxml:string-value12.45.3 SXPathの拡張
sxml:sxml->html12.46.5 SXMLからマークアップへの変換
sxml:sxml->xml12.46.5 SXMLからマークアップへの変換
sxpath12.45.2 SXPathクエリ言語
symbol->string6.7 シンボル
symbol-append6.7 シンボル
symbol-append6.7 シンボル
symbol-hash6.2.3 ハッシュ
symbol-interned?6.7 シンボル
symbol-sans-prefix6.7 シンボル
symbol=?6.7 シンボル
symbol?6.7 シンボル
syntax-error5.5 マクロユーティリティ
syntax-errorf5.5 マクロユーティリティ
syntax-rules5.2.1 Syntax-rules pattern langauge
syntax-rules5.2.1 Syntax-rules pattern langauge
sys-abort6.25.1 プログラムの終了
sys-access6.25.4.4 ファイルの状態
sys-alarm6.25.13 その他のシステムコール
sys-alloc-consoleAttaching and detaching
sys-asctimePOSIX time
sys-available-processors6.25.3 環境の問い合わせ
sys-basename6.25.4.3 パス名
sys-cfgetispeed9.30.1 POSIX termiosインタフェース
sys-cfgetospeed9.30.1 POSIX termiosインタフェース
sys-cfsetispeed9.30.1 POSIX termiosインタフェース
sys-cfsetospeed9.30.1 POSIX termiosインタフェース
sys-chdir6.25.4.5 他のファイル操作
sys-chmod6.25.4.4 ファイルの状態
sys-chown6.25.4.4 ファイルの状態
sys-clearenv6.25.3 環境の問い合わせ
sys-closelog9.29 gauche.syslog - Syslog
sys-create-console-screen-bufferScreen buffer
sys-cryptPassword encryption
sys-ctermid6.25.8 システムへの問い合わせ
sys-ctimePOSIX time
sys-difftimePOSIX time
sys-dirname6.25.4.3 パス名
sys-environ6.25.3 環境の問い合わせ
sys-environ->alist6.25.3 環境の問い合わせ
sys-errno->symbol6.25.8 システムへの問い合わせ
sys-execFork and exec
sys-exit6.25.1 プログラムの終了
sys-fchmod6.25.4.4 ファイルの状態
sys-fcntl9.9 gauche.fcntl - 低レベルファイル操作
sys-fdset6.25.11 I/Oの多重化
sys-fdset->list6.25.11 I/Oの多重化
sys-fdset-clear!6.25.11 I/Oの多重化
sys-fdset-copy!6.25.11 I/Oの多重化
sys-fdset-max-fd6.25.11 I/Oの多重化
sys-fdset-ref6.25.11 I/Oの多重化
sys-fdset-set!6.25.11 I/Oの多重化
sys-forkFork and exec
sys-fork-and-execFork and exec
sys-forkpty9.30.1 POSIX termiosインタフェース
sys-forkpty-and-exec9.30.1 POSIX termiosインタフェース
sys-free-consoleAttaching and detaching
sys-fstat6.25.4.4 ファイルの状態
sys-ftruncate6.25.4.5 他のファイル操作
sys-generate-console-ctrl-eventAttaching and detaching
sys-get-console-cpConsole codepage
sys-get-console-cursor-infoConsole codepage
sys-get-console-modeConsole mode
sys-get-console-output-cpConsole codepage
sys-get-console-screen-buffer-infoScreen buffer
sys-get-console-titleConsole input/output
sys-get-largest-console-window-sizeScreen buffer
sys-get-number-of-console-input-eventsConsole input/output
sys-get-number-of-console-mouse-buttonsConsole input/output
sys-get-osfhandle6.25.13 その他のシステムコール
sys-get-std-handleStandard handles
sys-getaddrinfo9.19.4 Netdbインタフェース
sys-getcwd6.25.8 システムへの問い合わせ
sys-getdomainname6.25.8 システムへの問い合わせ
sys-getegid6.25.8 システムへの問い合わせ
sys-getenv6.25.3 環境の問い合わせ
sys-geteuid6.25.8 システムへの問い合わせ
sys-getgid6.25.8 システムへの問い合わせ
sys-getgrgidUnix groups
sys-getgrnamUnix groups
sys-getgroups6.25.8 システムへの問い合わせ
sys-gethostbyaddr9.19.4 Netdbインタフェース
sys-gethostbyname9.19.4 Netdbインタフェース
sys-gethostname6.25.8 システムへの問い合わせ
sys-getlogin6.25.8 システムへの問い合わせ
sys-getpgid6.25.8 システムへの問い合わせ
sys-getpgrp6.25.8 システムへの問い合わせ
sys-getpid6.25.8 システムへの問い合わせ
sys-getppid6.25.8 システムへの問い合わせ
sys-getprotobyname9.19.4 Netdbインタフェース
sys-getprotobynumber9.19.4 Netdbインタフェース
sys-getpwnamUnix users
sys-getpwuidUnix users
sys-getrlimit6.25.8 システムへの問い合わせ
sys-getservbyname9.19.4 Netdbインタフェース
sys-getservbyport9.19.4 Netdbインタフェース
sys-gettimeofdayPOSIX time
sys-getuid6.25.8 システムへの問い合わせ
sys-gid->group-nameUnix groups
sys-glob6.25.4.1 ディレクトリ
sys-gmtimePOSIX time
sys-group-name->gidUnix groups
sys-htonl9.19.4 Netdbインタフェース
sys-htons9.19.4 Netdbインタフェース
sys-isatty6.25.4.5 他のファイル操作
sys-kill6.25.7.2 シグナルの送出
sys-link6.25.4.2 ディレクトリ操作
sys-localeconv6.25.6 ロケール
sys-localtimePOSIX time
sys-logmask9.29 gauche.syslog - Syslog
sys-lstat6.25.4.4 ファイルの状態
sys-message-box12.27.1 Windows dialogs
sys-mkdir6.25.4.2 ディレクトリ操作
sys-mkdtemp6.25.4.2 ディレクトリ操作
sys-mkfifo6.25.4.5 他のファイル操作
sys-mkstemp6.25.4.2 ディレクトリ操作
sys-mktimePOSIX time
sys-nanosleep6.25.13 その他のシステムコール
sys-normalize-pathname6.25.4.3 パス名
sys-ntohl9.19.4 Netdbインタフェース
sys-ntohs9.19.4 Netdbインタフェース
sys-openlog9.29 gauche.syslog - Syslog
sys-openpty9.30.1 POSIX termiosインタフェース
sys-pause6.25.13 その他のシステムコール
sys-peek-console-inputConsole input/output
sys-pipe6.25.4.5 他のファイル操作
sys-putenv6.25.3 環境の問い合わせ
sys-random6.25.13 その他のシステムコール
sys-read-consoleConsole input/output
sys-read-console-inputConsole input/output
sys-read-console-outputConsole input/output
sys-read-console-output-attributeConsole input/output
sys-read-console-output-characterConsole input/output
sys-readdir6.25.4.1 ディレクトリ
sys-readlink6.25.4.2 ディレクトリ操作
sys-realpath6.25.4.3 パス名
sys-remove6.25.4.2 ディレクトリ操作
sys-rename6.25.4.2 ディレクトリ操作
sys-rmdir6.25.4.2 ディレクトリ操作
sys-scroll-console-screen-bufferScreen buffer
sys-select6.25.11 I/Oの多重化
sys-select!6.25.11 I/Oの多重化
sys-set-console-active-screen-bufferScreen buffer
sys-set-console-cpConsole codepage
sys-set-console-cursor-infoConsole codepage
sys-set-console-cursor-positionConsole codepage
sys-set-console-modeConsole mode
sys-set-console-output-cpConsole codepage
sys-set-console-text-attributeConsole input/output
sys-set-console-window-infoConsole input/output
sys-set-screen-buffer-sizeScreen buffer
sys-set-std-handleStandard handles
sys-setenv6.25.3 環境の問い合わせ
sys-setgid6.25.8 システムへの問い合わせ
sys-setgroups6.25.8 システムへの問い合わせ
sys-setlocale6.25.6 ロケール
sys-setlogmask9.29 gauche.syslog - Syslog
sys-setpgid6.25.8 システムへの問い合わせ
sys-setrlimit6.25.8 システムへの問い合わせ
sys-setsid6.25.8 システムへの問い合わせ
sys-setuid6.25.8 システムへの問い合わせ
sys-sigmask6.25.7.4 シグナルのマスクと待機
sys-signal-name6.25.7.1 シグナルとシグナルセット
sys-sigset6.25.7.1 シグナルとシグナルセット
sys-sigset-add!6.25.7.1 シグナルとシグナルセット
sys-sigset-delete!6.25.7.1 シグナルとシグナルセット
sys-sigset-empty!6.25.7.1 シグナルとシグナルセット
sys-sigset-fill!6.25.7.1 シグナルとシグナルセット
sys-sigsuspend6.25.7.4 シグナルのマスクと待機
sys-sigwait6.25.7.4 シグナルのマスクと待機
sys-sleep6.25.13 その他のシステムコール
sys-srandom6.25.13 その他のシステムコール
sys-stat6.25.4.4 ファイルの状態
sys-stat->atime6.25.4.4 ファイルの状態
sys-stat->ctime6.25.4.4 ファイルの状態
sys-stat->dev6.25.4.4 ファイルの状態
sys-stat->file-type6.25.4.4 ファイルの状態
sys-stat->gid6.25.4.4 ファイルの状態
sys-stat->ino6.25.4.4 ファイルの状態
sys-stat->mode6.25.4.4 ファイルの状態
sys-stat->mtime6.25.4.4 ファイルの状態
sys-stat->nlink6.25.4.4 ファイルの状態
sys-stat->rdev6.25.4.4 ファイルの状態
sys-stat->size6.25.4.4 ファイルの状態
sys-stat->uid6.25.4.4 ファイルの状態
sys-strerror6.25.8 システムへの問い合わせ
sys-strftimePOSIX time
sys-symbol->errno6.25.8 システムへの問い合わせ
sys-symlink6.25.4.2 ディレクトリ操作
sys-syslog9.29 gauche.syslog - Syslog
sys-systemFork and exec
sys-tcdrain9.30.1 POSIX termiosインタフェース
sys-tcflow9.30.1 POSIX termiosインタフェース
sys-tcflush9.30.1 POSIX termiosインタフェース
sys-tcgetattr9.30.1 POSIX termiosインタフェース
sys-tcgetpgrp9.30.1 POSIX termiosインタフェース
sys-tcsendbreak9.30.1 POSIX termiosインタフェース
sys-tcsetattr9.30.1 POSIX termiosインタフェース
sys-tcsetpgrp9.30.1 POSIX termiosインタフェース
sys-timePOSIX time
sys-times6.25.8 システムへの問い合わせ
sys-tm->alistPOSIX time
sys-tmpdir6.25.4.3 パス名
sys-tmpnam6.25.4.2 ディレクトリ操作
sys-truncate6.25.4.5 他のファイル操作
sys-ttyname6.25.4.5 他のファイル操作
sys-uid->user-nameUnix users
sys-umask6.25.4.2 ディレクトリ操作
sys-uname6.25.8 システムへの問い合わせ
sys-unlink6.25.4.2 ディレクトリ操作
sys-unsetenv6.25.3 環境の問い合わせ
sys-user-name->uidUnix users
sys-utime6.25.4.4 ファイルの状態
sys-waitWait
sys-wait-exit-statusWait
sys-wait-exited?Wait
sys-wait-signaled?Wait
sys-wait-stopped?Wait
sys-wait-stopsigWait
sys-wait-termsigWait
sys-waitpidWait
sys-win-process-pidWindows特有のユーティリティ
sys-win-process?Windows特有のユーティリティ
sys-write-consoleConsole input/output
sys-write-console-output-characterConsole input/output

T
tabulate-array9.1 gauche.array - 配列
take6.6.4 リストへのアクセスと変更
take!6.6.4 リストへのアクセスと変更
take*6.6.4 リストへのアクセスと変更
take-after12.45.1 SXPathの基本的なコンバータとアプリケータ
take-right6.6.4 リストへのアクセスと変更
take-right*6.6.4 リストへのアクセスと変更
take-until12.45.1 SXPathの基本的なコンバータとアプリケータ
take-whileList searching
take-while!List searching
tan6.3.4 数値の演算
tanh6.3.4 数値の演算
temporary-directory12.23.1 ディレクトリユーティリティ
tenthList selectors
terminate-all!12.5 control.thread-pool - スレッドプール
terminated-thread-exception?9.32.4 スレッド例外
test個々のテスト
test*個々のテスト
test-check個々のテスト
test-endテストファイルの構造
test-error異常系をテストする
test-logテストファイルの構造
test-module準静的検査
test-none-of曖昧な結果をテストする
test-one-of曖昧な結果をテストする
test-record-fileテストファイルの構造
test-script準静的検査
test-sectionテストファイルの構造
test-startテストファイルの構造
test-summary-checkテストファイルの構造
textdomaingettext-互換の API
textual-port?入出力
thirdList selectors
thread-cont!9.32.2 スレッド手続き
thread-join!9.32.2 スレッド手続き
thread-name9.32.2 スレッド手続き
thread-pool-results12.5 control.thread-pool - スレッドプール
thread-pool-shut-down?12.5 control.thread-pool - スレッドプール
thread-sleep!9.32.2 スレッド手続き
thread-specific9.32.2 スレッド手続き
thread-specific-set!9.32.2 スレッド手続き
thread-start!9.32.2 スレッド手続き
thread-state9.32.2 スレッド手続き
thread-stop!9.32.2 スレッド手続き
thread-terminate!9.32.2 スレッド手続き
thread-yield!9.32.2 スレッド手続き
thread?9.32.2 スレッド手続き
timeインタラクティブな実行時間の計測
time->secondsSRFI time
time-counter-reset!より細かい計測
time-counter-start!より細かい計測
time-counter-stop!より細かい計測
time-counter-valueより細かい計測
time-difference11.8.3 時間に関する手続き
time-difference!11.8.3 時間に関する手続き
time-monotonic->date11.8.4 日付
time-monotonic->julian-day11.8.4 日付
time-monotonic->modified-julian-day11.8.4 日付
time-monotonic->time-tai11.8.4 日付
time-monotonic->time-tai!11.8.4 日付
time-monotonic->time-utc11.8.4 日付
time-monotonic->time-utc!11.8.4 日付
time-nanosecond11.8.3 時間に関する手続き
time-resolution11.8.2 時間に関する問い合わせ
time-result+ベンチマーク
time-result-ベンチマーク
time-result-countベンチマーク
time-result-realベンチマーク
time-result-sysベンチマーク
time-result-userベンチマーク
time-result?ベンチマーク
time-second11.8.3 時間に関する手続き
time-tai->date11.8.4 日付
time-tai->julian-day11.8.4 日付
time-tai->modified-julian-day11.8.4 日付
time-tai->time-monotonic11.8.4 日付
time-tai->time-monotonic!11.8.4 日付
time-tai->time-utc11.8.4 日付
time-tai->time-utc!11.8.4 日付
time-theseベンチマーク
time-these/reportベンチマーク
time-thisベンチマーク
time-type11.8.3 時間に関する手続き
time-utc->date11.8.4 日付
time-utc->julian-day11.8.4 日付
time-utc->modified-julian-day11.8.4 日付
time-utc->time-monotonic11.8.4 日付
time-utc->time-monotonic!11.8.4 日付
time-utc->time-tai11.8.4 日付
time-utc->time-tai!11.8.4 日付
time<=?11.8.3 時間に関する手続き
time<?11.8.3 時間に関する手続き
time=?11.8.3 時間に関する手続き
time>=?11.8.3 時間に関する手続き
time>?11.8.3 時間に関する手続き
time?SRFI time
topological-sort12.68 util.toposort - トポロジカルソート
totientその他の関数
touch-file12.23.4 ファイル操作
touch-files12.23.4 ファイル操作
tr12.56 text.tr - 文字変換
tree->string12.57 text.tree - 怠惰なテキスト構築
tree-map->alist6.16 ツリーマップ
tree-map->imap12.10 data.imap - 変更不可なマップ
tree-map-ceiling6.16 ツリーマップ
tree-map-ceiling-key6.16 ツリーマップ
tree-map-ceiling-value6.16 ツリーマップ
tree-map-clear!6.16 ツリーマップ
tree-map-comparator6.16 ツリーマップ
tree-map-copy6.16 ツリーマップ
tree-map-delete!6.16 ツリーマップ
tree-map-empty?6.16 ツリーマップ
tree-map-exists?6.16 ツリーマップ
tree-map-floor6.16 ツリーマップ
tree-map-floor-key6.16 ツリーマップ
tree-map-floor-value6.16 ツリーマップ
tree-map-fold6.16 ツリーマップ
tree-map-fold-right6.16 ツリーマップ
tree-map-for-each6.16 ツリーマップ
tree-map-get6.16 ツリーマップ
tree-map-keys6.16 ツリーマップ
tree-map-map6.16 ツリーマップ
tree-map-max6.16 ツリーマップ
tree-map-min6.16 ツリーマップ
tree-map-num-entries6.16 ツリーマップ
tree-map-pop!6.16 ツリーマップ
tree-map-pop-max!6.16 ツリーマップ
tree-map-pop-min!6.16 ツリーマップ
tree-map-predecessor6.16 ツリーマップ
tree-map-predecessor-key6.16 ツリーマップ
tree-map-predecessor-value6.16 ツリーマップ
tree-map-push!6.16 ツリーマップ
tree-map-put!6.16 ツリーマップ
tree-map-successor6.16 ツリーマップ
tree-map-successor-key6.16 ツリーマップ
tree-map-successor-value6.16 ツリーマップ
tree-map-update!6.16 ツリーマップ
tree-map-values6.16 ツリーマップ
trie12.15 data.trie - Trie
trie->hash-table12.15 data.trie - Trie
trie->list12.15 data.trie - Trie
trie-common-prefix12.15 data.trie - Trie
trie-common-prefix-fold12.15 data.trie - Trie
trie-common-prefix-for-each12.15 data.trie - Trie
trie-common-prefix-keys12.15 data.trie - Trie
trie-common-prefix-map12.15 data.trie - Trie
trie-common-prefix-values12.15 data.trie - Trie
trie-delete!12.15 data.trie - Trie
trie-exists?12.15 data.trie - Trie
trie-fold12.15 data.trie - Trie
trie-for-each12.15 data.trie - Trie
trie-get12.15 data.trie - Trie
trie-keys12.15 data.trie - Trie
trie-longest-match12.15 data.trie - Trie
trie-map12.15 data.trie - Trie
trie-num-entries12.15 data.trie - Trie
trie-partial-key?12.15 data.trie - Trie
trie-put!12.15 data.trie - Trie
trie-update!12.15 data.trie - Trie
trie-values12.15 data.trie - Trie
trie-with-keys12.15 data.trie - Trie
trie?12.15 data.trie - Trie
truncate6.3.4 数値の演算
truncate->exact6.3.4 数値の演算
tuples-of複合データ型のジェネレータ
twos-exponent6.3.6 ビット演算
twos-exponent-factor6.3.6 ビット演算

U
u16array9.1 gauche.array - 配列
u16vector9.35.1 ユニフォームベクタの基本操作
u16vector->list9.35.2 ユニフォームベクタの変換
u16vector->vector9.35.2 ユニフォームベクタの変換
u16vector-add9.35.3 ユニフォームベクタの数値演算
u16vector-add!9.35.3 ユニフォームベクタの数値演算
u16vector-and9.35.3 ユニフォームベクタの数値演算
u16vector-and!9.35.3 ユニフォームベクタの数値演算
u16vector-append9.35.1 ユニフォームベクタの基本操作
u16vector-clamp9.35.3 ユニフォームベクタの数値演算
u16vector-clamp!9.35.3 ユニフォームベクタの数値演算
u16vector-copy9.35.1 ユニフォームベクタの基本操作
u16vector-copy!9.35.1 ユニフォームベクタの基本操作
u16vector-dot9.35.3 ユニフォームベクタの数値演算
u16vector-fill!9.35.1 ユニフォームベクタの基本操作
u16vector-ior9.35.3 ユニフォームベクタの数値演算
u16vector-ior!9.35.3 ユニフォームベクタの数値演算
u16vector-length9.35.1 ユニフォームベクタの基本操作
u16vector-mul9.35.3 ユニフォームベクタの数値演算
u16vector-mul!9.35.3 ユニフォームベクタの数値演算
u16vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
u16vector-range-check9.35.3 ユニフォームベクタの数値演算
u16vector-ref9.35.1 ユニフォームベクタの基本操作
u16vector-set!9.35.1 ユニフォームベクタの基本操作
u16vector-sub9.35.3 ユニフォームベクタの数値演算
u16vector-sub!9.35.3 ユニフォームベクタの数値演算
u16vector-xor9.35.3 ユニフォームベクタの数値演算
u16vector-xor!9.35.3 ユニフォームベクタの数値演算
u16vector?9.35.1 ユニフォームベクタの基本操作
u32array9.1 gauche.array - 配列
u32vector9.35.1 ユニフォームベクタの基本操作
u32vector->list9.35.2 ユニフォームベクタの変換
u32vector->string9.35.2 ユニフォームベクタの変換
u32vector->vector9.35.2 ユニフォームベクタの変換
u32vector-add9.35.3 ユニフォームベクタの数値演算
u32vector-add!9.35.3 ユニフォームベクタの数値演算
u32vector-and9.35.3 ユニフォームベクタの数値演算
u32vector-and!9.35.3 ユニフォームベクタの数値演算
u32vector-append9.35.1 ユニフォームベクタの基本操作
u32vector-clamp9.35.3 ユニフォームベクタの数値演算
u32vector-clamp!9.35.3 ユニフォームベクタの数値演算
u32vector-copy9.35.1 ユニフォームベクタの基本操作
u32vector-copy!9.35.1 ユニフォームベクタの基本操作
u32vector-dot9.35.3 ユニフォームベクタの数値演算
u32vector-fill!9.35.1 ユニフォームベクタの基本操作
u32vector-ior9.35.3 ユニフォームベクタの数値演算
u32vector-ior!9.35.3 ユニフォームベクタの数値演算
u32vector-length9.35.1 ユニフォームベクタの基本操作
u32vector-mul9.35.3 ユニフォームベクタの数値演算
u32vector-mul!9.35.3 ユニフォームベクタの数値演算
u32vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
u32vector-range-check9.35.3 ユニフォームベクタの数値演算
u32vector-ref9.35.1 ユニフォームベクタの基本操作
u32vector-set!9.35.1 ユニフォームベクタの基本操作
u32vector-sub9.35.3 ユニフォームベクタの数値演算
u32vector-sub!9.35.3 ユニフォームベクタの数値演算
u32vector-xor9.35.3 ユニフォームベクタの数値演算
u32vector-xor!9.35.3 ユニフォームベクタの数値演算
u32vector?9.35.1 ユニフォームベクタの基本操作
u64array9.1 gauche.array - 配列
u64vector9.35.1 ユニフォームベクタの基本操作
u64vector->list9.35.2 ユニフォームベクタの変換
u64vector->vector9.35.2 ユニフォームベクタの変換
u64vector-add9.35.3 ユニフォームベクタの数値演算
u64vector-add!9.35.3 ユニフォームベクタの数値演算
u64vector-and9.35.3 ユニフォームベクタの数値演算
u64vector-and!9.35.3 ユニフォームベクタの数値演算
u64vector-append9.35.1 ユニフォームベクタの基本操作
u64vector-clamp9.35.3 ユニフォームベクタの数値演算
u64vector-clamp!9.35.3 ユニフォームベクタの数値演算
u64vector-copy9.35.1 ユニフォームベクタの基本操作
u64vector-copy!9.35.1 ユニフォームベクタの基本操作
u64vector-dot9.35.3 ユニフォームベクタの数値演算
u64vector-fill!9.35.1 ユニフォームベクタの基本操作
u64vector-ior9.35.3 ユニフォームベクタの数値演算
u64vector-ior!9.35.3 ユニフォームベクタの数値演算
u64vector-length9.35.1 ユニフォームベクタの基本操作
u64vector-mul9.35.3 ユニフォームベクタの数値演算
u64vector-mul!9.35.3 ユニフォームベクタの数値演算
u64vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
u64vector-range-check9.35.3 ユニフォームベクタの数値演算
u64vector-ref9.35.1 ユニフォームベクタの基本操作
u64vector-set!9.35.1 ユニフォームベクタの基本操作
u64vector-sub9.35.3 ユニフォームベクタの数値演算
u64vector-sub!9.35.3 ユニフォームベクタの数値演算
u64vector-xor9.35.3 ユニフォームベクタの数値演算
u64vector-xor!9.35.3 ユニフォームベクタの数値演算
u64vector?9.35.1 ユニフォームベクタの基本操作
u8-ready?入出力
u8array9.1 gauche.array - 配列
u8vector9.35.1 ユニフォームベクタの基本操作
u8vector->list9.35.2 ユニフォームベクタの変換
u8vector->string9.35.2 ユニフォームベクタの変換
u8vector->vector9.35.2 ユニフォームベクタの変換
u8vector-add9.35.3 ユニフォームベクタの数値演算
u8vector-add!9.35.3 ユニフォームベクタの数値演算
u8vector-and9.35.3 ユニフォームベクタの数値演算
u8vector-and!9.35.3 ユニフォームベクタの数値演算
u8vector-append9.35.1 ユニフォームベクタの基本操作
u8vector-clamp9.35.3 ユニフォームベクタの数値演算
u8vector-clamp!9.35.3 ユニフォームベクタの数値演算
u8vector-copy9.35.1 ユニフォームベクタの基本操作
u8vector-copy!9.35.1 ユニフォームベクタの基本操作
u8vector-dot9.35.3 ユニフォームベクタの数値演算
u8vector-fill!9.35.1 ユニフォームベクタの基本操作
u8vector-ior9.35.3 ユニフォームベクタの数値演算
u8vector-ior!9.35.3 ユニフォームベクタの数値演算
u8vector-length9.35.1 ユニフォームベクタの基本操作
u8vector-mul9.35.3 ユニフォームベクタの数値演算
u8vector-mul!9.35.3 ユニフォームベクタの数値演算
u8vector-multi-copy!9.35.1 ユニフォームベクタの基本操作
u8vector-range-check9.35.3 ユニフォームベクタの数値演算
u8vector-ref9.35.1 ユニフォームベクタの基本操作
u8vector-set!9.35.1 ユニフォームベクタの基本操作
u8vector-sub9.35.3 ユニフォームベクタの数値演算
u8vector-sub!9.35.3 ユニフォームベクタの数値演算
u8vector-xor9.35.3 ユニフォームベクタの数値演算
u8vector-xor!9.35.3 ユニフォームベクタの数値演算
u8vector?9.35.1 ユニフォームベクタの基本操作
ucs->char6.10 文字
ucs-range->char-set11.7.1 文字セットの構築子
ucs-range->char-set!11.7.1 文字セットの構築子
ucs4->utf169.34.1 Unicode transfer encodings
ucs4->utf89.34.1 Unicode transfer encodings
uint16sプリミティブデータ型のジェネレータ
uint32sプリミティブデータ型のジェネレータ
uint64sプリミティブデータ型のジェネレータ
uint8sプリミティブデータ型のジェネレータ
unbox11.19 srfi-111 - ボックス
uncaught-exception-reason9.32.4 スレッド例外
uncaught-exception?9.32.4 スレッド例外
undefined6.5 未定義値
undefined?6.5 未定義値
unfoldList fold, unfold & map
unfold-rightList fold, unfold & map
unify12.69 util.unification - ユニフィケーション
unify-merge12.69 util.unification - ユニフィケーション
unless4.5 条件式
unpack12.2 binary.pack - バイナリデータのパック
unpack-skip12.2 binary.pack - バイナリデータのパック
unquote4.9 準クオート(Quasiquote)
unquote-splicing4.9 準クオート(Quasiquote)
until4.8 繰り返し
until4.8 繰り返し
unwind-protect上位レベルの例外処理機構
unwrap-syntax5.5 マクロユーティリティ
unwrap-syntax6.9 Identifier
unzip1List miscellaneous routines
unzip2List miscellaneous routines
unzip3List miscellaneous routines
unzip4List miscellaneous routines
unzip5List miscellaneous routines
update!4.4 代入
uri-composeURIの構築
uri-compose-dataURIの構築
uri-decodeURIのエンコードとデコード
uri-decode-stringURIのエンコードとデコード
uri-decompose-authorityURIのパーズ
uri-decompose-dataURIのパーズ
uri-decompose-hierarchicalURIのパーズ
uri-encodeURIのエンコードとデコード
uri-encode-stringURIのエンコードとデコード
uri-mergeURIの構築
uri-parseURIのパーズ
uri-refURIのパーズ
uri-scheme&specificURIのパーズ
use4.13.4 モジュールの使用
utf16->ucs49.34.1 Unicode transfer encodings
utf16-length9.34.1 Unicode transfer encodings
utf8->string9.34.1 Unicode transfer encodings
utf8->ucs49.34.1 Unicode transfer encodings
utf8-length9.34.1 Unicode transfer encodings
uvector->generator9.10.1 ジェネレータの生成
uvector-alias9.35.2 ユニフォームベクタの変換
uvector-class-element-size9.35.1 ユニフォームベクタの基本操作
uvector-copy9.35.1 ユニフォームベクタの基本操作
uvector-copy!9.35.1 ユニフォームベクタの基本操作
uvector-length9.35.1 ユニフォームベクタの基本操作
uvector-ref9.35.1 ユニフォームベクタの基本操作
uvector-set!9.35.1 ユニフォームベクタの基本操作
uvector-size9.35.1 ユニフォームベクタの基本操作
uvector?9.35.1 ユニフォームベクタの基本操作

V
valid-version-spec?9.36 gauche.version - バージョン番号の比較
values6.18.8 多値
values->list6.18.8 多値
values-ref6.18.8 多値
vector6.14 ベクタ
vector->f16vector9.35.2 ユニフォームベクタの変換
vector->f32vector9.35.2 ユニフォームベクタの変換
vector->f64vector9.35.2 ユニフォームベクタの変換
vector->generator9.10.1 ジェネレータの生成
vector->list6.14 ベクタ
vector->s16vector9.35.2 ユニフォームベクタの変換
vector->s32vector9.35.2 ユニフォームベクタの変換
vector->s64vector9.35.2 ユニフォームベクタの変換
vector->s8vector9.35.2 ユニフォームベクタの変換
vector->string6.14 ベクタ
vector->u16vector9.35.2 ユニフォームベクタの変換
vector->u32vector9.35.2 ユニフォームベクタの変換
vector->u64vector9.35.2 ユニフォームベクタの変換
vector->u8vector9.35.2 ユニフォームベクタの変換
vector-anyベクタ上の検索
vector-append6.14 ベクタ
vector-append-subvectorsベクタ構成子
vector-binary-searchベクタ上の検索
vector-concatenateベクタ構成子
vector-copy6.14 ベクタ
vector-copy!6.14 ベクタ
vector-count11.13 srfi-43 - ベクタライブラリ(旧式)
vector-countベクタ上の繰り返し
vector-cumulateベクタ上の繰り返し
vector-ec内包表記マクロ
vector-empty?ベクタ上の述語
vector-everyベクタ上の検索
vector-fill!6.14 ベクタ
vector-fold11.13 srfi-43 - ベクタライブラリ(旧式)
vector-foldベクタ上の繰り返し
vector-fold-right11.13 srfi-43 - ベクタライブラリ(旧式)
vector-fold-rightベクタ上の繰り返し
vector-for-each6.14 ベクタ
vector-for-each11.13 srfi-43 - ベクタライブラリ(旧式)
vector-for-each-with-index6.14 ベクタ
vector-indexベクタ上の検索
vector-index-rightベクタ上の検索
vector-length6.14 ベクタ
vector-map6.14 ベクタ
vector-map11.13 srfi-43 - ベクタライブラリ(旧式)
vector-map!6.14 ベクタ
vector-map!11.13 srfi-43 - ベクタライブラリ(旧式)
vector-map-with-index6.14 ベクタ
vector-map-with-index!6.14 ベクタ
vector-of-length-ec内包表記マクロ
vector-partitionベクタ上の検索
vector-ref6.14 ベクタ
vector-reverse!ベクタの変更子
vector-reverse-copyベクタ構成子
vector-reverse-copy!ベクタの変更子
vector-set!6.14 ベクタ
vector-skipベクタ上の検索
vector-skip-rightベクタ上の検索
vector-swap!ベクタの変更子
vector-tabulate6.14 ベクタ
vector-unfoldベクタ構成子
vector-unfold!ベクタの変更子
vector-unfold-rightベクタ構成子
vector-unfold-right!ベクタの変更子
vector=ベクタ上の述語
vector?6.14 ベクタ
vectors-of複合データ型のジェネレータ
vectors-of複合データ型のジェネレータ
version-compare9.36 gauche.version - バージョン番号の比較
version-satisfy?9.36 gauche.version - バージョン番号の比較
version<=?9.36 gauche.version - バージョン番号の比較
version<?9.36 gauche.version - バージョン番号の比較
version=?9.36 gauche.version - バージョン番号の比較
version>=?9.36 gauche.version - バージョン番号の比較
version>?9.36 gauche.version - バージョン番号の比較
vt100-compatible?コンソールオブジェクト

W
wait-all12.5 control.thread-pool - スレッドプール
weak-vector-length6.17 Weak ポインタ
weak-vector-ref6.17 Weak ポインタ
weak-vector-set!6.17 Weak ポインタ
weighted-samples-from複合データ型のジェネレータ
when4.5 条件式
while4.8 繰り返し
while4.8 繰り返し
while4.8 繰り返し
with-builder9.5.4 基礎的なイテレータ構築メソッド
with-character-attributeConsole control
with-error-handler上位レベルの例外処理機構
with-error-to-port6.22.3 ポート共通の操作
with-exception-handler下位レベルの例外処理機構
with-input-conversion9.4.3 変換ポート
with-input-from-file6.22.4 ファイルポート
with-input-from-port6.22.3 ポート共通の操作
with-input-from-process9.24.4 Process ports
with-input-from-string6.22.5 文字列ポート
with-iterator9.5.4 基礎的なイテレータ構築メソッド
with-lock-file12.23.5 ロックファイル
with-locking-mutexMutex
with-module4.13.3 モジュールの定義と選択
with-output-conversion9.4.3 変換ポート
with-output-to-file6.22.4 ファイルポート
with-output-to-port6.22.3 ポート共通の操作
with-output-to-process9.24.4 Process ports
with-output-to-string6.22.5 文字列ポート
with-port-locking6.22.2 ポートとスレッド
with-ports6.22.3 ポート共通の操作
with-profiler6.26.2 プロファイラAPI
with-random-data-seedグローバルな状態
with-signal-handlers6.25.7.3 シグナルの処理
with-string-io6.22.5 文字列ポート
with-time-counterより細かい計測
without-echoing9.30.2 共通の高レベル端末制御
wrap-with-input-conversion9.4.3 変換ポート
wrap-with-output-conversion9.4.3 変換ポート
write6.22.8.3 オブジェクトの出力
write*6.22.8.3 オブジェクトの出力
write-ber-integerポートを用いたI/O
write-block9.35.4 ユニフォームベクタのブロック入出力
write-byte6.22.8.5 低レベル出力
write-bytevector入出力
write-char6.22.8.5 低レベル出力
write-controls-copy6.22.8.2 出力制御
write-f16ポートを用いたI/O
write-f32ポートを用いたI/O
write-f64ポートを用いたI/O
write-gauche-package-descriptionUtility procedures
write-object6.22.8.3 オブジェクトの出力
write-s16ポートを用いたI/O
write-s32ポートを用いたI/O
write-s64ポートを用いたI/O
write-s8ポートを用いたI/O
write-shared6.22.8.3 オブジェクトの出力
write-simple6.22.8.3 オブジェクトの出力
write-sintポートを用いたI/O
write-stream12.67 util.stream - ストリームライブラリ
write-string入出力
write-to-string6.22.5 文字列ポート
write-tree12.57 text.tree - 怠惰なテキスト構築
write-tree12.57 text.tree - 怠惰なテキスト構築
write-tree12.57 text.tree - 怠惰なテキスト構築
write-u16ポートを用いたI/O
write-u32ポートを用いたI/O
write-u64ポートを用いたI/O
write-u8入出力
write-u8ポートを用いたI/O
write-uintポートを用いたI/O
write-uvector9.35.4 ユニフォームベクタのブロック入出力
write-with-shared-structure6.22.8.3 オブジェクトの出力
write/ss6.22.8.3 オブジェクトの出力

X
x->generator9.10.1 ジェネレータの生成
x->integer6.3.5 数値の変換
x->lseq9.13 gauche.lazy - 遅延シーケンスユーティリティ
x->number6.3.5 数値の変換
x->string6.12.3 文字列の構築子
xconsList constructors
xml-token-head12.44.1 SSAXデータタイプ
xml-token-kind12.44.1 SSAXデータタイプ
xml-token?12.44.1 SSAXデータタイプ
xsubstring11.6.11 文字列のローテーション

Z
zero?6.3.2 数値に関する述語
zipList miscellaneous routines
zlib-versionMiscellaneous API
zstream-adler32Operations on inflating/deflating ports
zstream-data-typeOperations on inflating/deflating ports
zstream-dictionary-adler32Operations on inflating/deflating ports
zstream-params-set!Operations on inflating/deflating ports
zstream-total-inOperations on inflating/deflating ports
zstream-total-outOperations on inflating/deflating ports

移動:   $   %   (   *   +   -   .   /   :   <   =   >   ^   ~  
A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Z  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

D. Index - モジュール索引

移動:   B   C   D   F   G   K   M   N   O   R   S   T   U   W  
見出し一覧

B
binary.io12.1 binary.io - バイナリI/O
binary.pack12.2 binary.pack - バイナリデータのパック

C
compat.norational12.3 compat.norational - 有理数のない算術演算
control.job12.4 control.job - 制御モジュールのための汎用ジョブ記述子
control.thread-pool12.5 control.thread-pool - スレッドプール
crypt.bcrypt12.6 crypt.bcrypt - パスワードハッシュ

D
data.cache12.7 data.cache - キャッシュ
data.heap12.8 data.heap - ヒープ
data.ideque12.9 data.ideque - 変更不可な両端キュー
data.imap12.10 data.imap - 変更不可なマップ
data.queue12.11 data.queue - キュー
data.random12.12 data.random - ランダムデータの生成
data.ring-buffer12.13 data.ring-buffer - リングバッファ
data.sparse12.14 data.sparse - 疎なデータコンテナ
data.trie12.15 data.trie - Trie
dbi12.16 dbi - データベース非依存アクセス層
dbm12.17 dbm - 汎用DBMインタフェース
dbm.fsdbm12.18 dbm.fsdbm - ファイルシステムdbm
dbm.gdbm12.19 dbm.gdbm - GDBMインタフェース
dbm.ndbm12.20 dbm.ndbm - NDBMインタフェース
dbm.odbm12.21 dbm.odbm - オリジナルのDBMインタフェース

F
file.filter12.22 file.filter - ファイルのフィルタ
file.util12.23 file.util - ファイルシステムユーティリティ

G
gauche4.13.7 組み込みモジュール
gauche.array9.1 gauche.array - 配列
gauche.base9.2 gauche.base - Gauche組み込み関数のインポート
gauche.cgen9.3 gauche.cgen - Cコードの生成
gauche.charconv9.4 gauche.charconv - 文字コード変換
gauche.collection9.5 gauche.collection - コレクションフレームワーク
gauche.config9.6 gauche.config - コンフィグレーション情報
gauche.configure9.7 gauche.configure - ビルド用ファイルの生成
gauche.dictionary9.8 gauche.dictionary - ディクショナリフレームワーク
gauche.fcntl9.9 gauche.fcntl - 低レベルファイル操作
gauche.generator9.10 gauche.generator - ジェネレータ
gauche.hook9.11 gauche.hook - フック
gauche.interactive9.12 gauche.interactive - インタラクティブセッション
gauche.keyword4.13.7 組み込みモジュール
gauche.lazy9.13 gauche.lazy - 遅延シーケンスユーティリティ
gauche.listener9.14 gauche.listener - リスナー
gauche.logger9.15 gauche.logger - ユーザレベルのロギング
gauche.mop.propagate9.16 gauche.mop.propagate - スロットアクセスの伝播
gauche.mop.singleton9.17 gauche.mop.singleton - シングルトン
gauche.mop.validator9.18 gauche.mop.validator - Validator付きスロット
gauche.net9.19 gauche.net - ネットワーキング
gauche.package9.20 gauche.package - パッケージメタ情報
gauche.parameter9.21 gauche.parameter - パラメータ
gauche.parseopt9.22 gauche.parseopt - コマンドライン引数の解析
gauche.partcont9.23 gauche.partcont - 部分継続
gauche.process9.24 gauche.process - 高レベルプロセスインタフェース
gauche.record9.25 gauche.record - レコード型
gauche.reload9.26 gauche.reload - モジュールの再ロード
gauche.selector9.27 gauche.selector - 簡単なディスパッチャ
gauche.sequence9.28 gauche.sequence - シーケンスフレームワーク
gauche.syslog9.29 gauche.syslog - Syslog
gauche.termios9.30 gauche.termios - 端末の制御
gauche.test9.31 gauche.test - 単体テスト
gauche.threads9.32 gauche.threads - スレッド
gauche.time9.33 gauche.time - 時間の計測
gauche.unicode9.34 gauche.unicode - Unicodeユーティリティ
gauche.uvector9.35 gauche.uvector - ユニフォームベクタ
gauche.version9.36 gauche.version - バージョン番号の比較
gauche.vport9.37 gauche.vport - 仮想ポート

K
keyword4.13.7 組み込みモジュール

M
math.const12.24 math.const - 定数
math.mt-random12.25 math.mt-random - Mersenne Twister乱数発生器
math.prime12.26 math.prime - 素数

N
null4.13.7 組み込みモジュール

O
os.windows12.27 os.windows - Windowsのサポート

R
rfc.82212.28 rfc.822 - RFC822メッセージ形式
rfc.base6412.29 rfc.base64 - Base64エンコーディング
rfc.cookie12.30 rfc.cookie - HTTPクッキー
rfc.ftp12.31 rfc.ftp - FTPクライアント
rfc.hmac12.32 rfc.hmac - HMAC鍵付きハッシング
rfc.http12.33 rfc.http - HTTP
rfc.icmp12.34 rfc.icmp - ICMPパケット
rfc.ip12.35 rfc.ip - IPパケット
rfc.json12.36 rfc.json - JSONのパーズと構築
rfc.md512.37 rfc.md5 - MD5メッセージダイジェスト
rfc.mime12.38 rfc.mime - MIMEメッセージ処理
rfc.quoted-printable12.39 rfc.quoted-printable - Quoted-printableエンコーディング
rfc.sha12.40 rfc.sha - SHAメッセージダイジェスト
rfc.sha112.40 rfc.sha - SHAメッセージダイジェスト
rfc.uri12.41 rfc.uri - URIの解析と作成
rfc.zlib12.42 rfc.zlib - zlib圧縮ライブラリ

S
scheme4.13.7 組み込みモジュール
scheme.base10.4 scheme.base - R7RS基本ライブラリ
scheme.case-lambda10.5 scheme.case-lambda - R7RS case-lambda
scheme.char10.6 scheme.char - R7RS文字ライブラリ
scheme.complex10.7 scheme.compex - R7RS複素数
scheme.cxr10.8 scheme.cxr - R7RS cxrアクセサ
scheme.eval10.9 scheme.eval - R7RS eval
scheme.file10.10 scheme.file - R7RSファイルライブラリ
scheme.inexact10.11 scheme.inexact - R7RS不正確数
scheme.lazy10.12 scheme.lazy - R7RS遅延評価
scheme.load10.13 scheme.load - R7RS load
scheme.process-context10.14 scheme.process-context - R7RSプロセスコンテキスト
scheme.r5rs10.19 scheme.r5rs - R5RS互換性
scheme.read10.15 scheme.read - R7RS read
scheme.repl10.16 scheme.repl - R7RS repl
scheme.time10.17 scheme.time - R7RS time
scheme.write10.18 scheme.write - R7RS write
slib12.43 slib - SLIBインタフェース
srfi-111.1 srfi-1 - リストライブラリ
srfi-10611.18 srfi-106 - 基本的なソケットインタフェース
srfi-1111.5 srfi-11 - Let-values
srfi-11111.19 srfi-111 - ボックス
srfi-11211.20 srfi-112 - 実行環境の問い合わせ
srfi-11311.21 srfi-113 - setとbag
srfi-11411.22 srfi-114 - 比較器
srfi-11711.23 srfi-117 - リストを元にしたキュー
srfi-11811.24 srfi-118 - 簡単な可変長文字列
srfi-1311.6 srfi-13 - 文字列ライブラリ
srfi-13311.25 srfi-133 - ベクタライブラリ
srfi-1411.7 srfi-14 - 文字集合ライブラリ
srfi-1911.8 srfi-19 - 時間のデータ型と手続き
srfi-2711.9 srfi-27 - ランダムビットのソース
srfi-2911.10 srfi-29 - 地域化
srfi-29.bundle11.10 srfi-29 - 地域化
srfi-29.format11.10 srfi-29 - 地域化
srfi-3711.11 srfi-37 - args-fold プログラム引数処理
srfi-411.2 srfi-4 - 単一型のベクタ
srfi-4211.12 srfi-42 - 先行評価的内包表記
srfi-4311.13 srfi-43 - ベクタライブラリ(旧式)
srfi-511.3 srfi-5 - シグネチャとrest引数に互換性のあるlet形式
srfi-5511.14 srfi-55 - require の拡張
srfi-6011.15 srfi-60 - 整数に対するビット操作
srfi-6911.16 srfi-69 - 基本的なハッシュテーブル
srfi-711.4 srfi-7 - 機能ベースプログラム設定言語
srfi-9811.17 srfi-98 - 環境変数へのアクセス
sxml.serializer12.47 sxml.serializer - SXMLからXMLとXHTMLのシリアライゼーション
sxml.ssax12.44 sxml.ssax - 関数的なXMLパーザ
sxml.sxpath12.45 sxml.sxpath - SXMLクエリ言語
sxml.tools12.46 sxml.tools - SXML構造を操作する

T
text.console12.48 text.console - テキスト端末制御
text.csv12.49 text.csv - CSVテーブル
text.diff12.50 text.diff - テキストストリームの相違点を計算する
text.gettext12.51 text.gettext - 地域化メッセージ
text.html-lite12.52 text.html-lite - シンプルなHTMLドキュメントの構築
text.parse12.53 text.parse - 入力ストリームのパージング
text.progress12.54 text.progress - テキスト端末上で進捗を表示する
text.sql12.55 text.sql - SQLのパーズと構築
text.tr12.56 text.tr - 文字変換
text.tree12.57 text.tree - 怠惰なテキスト構築
text.unicode8.3 廃止されたモジュール

U
user4.13.7 組み込みモジュール
util.combinations12.58 util.combinations - 組み合わせ
util.digest12.59 util.digest - メッセージダイジェストフレームワーク
util.dominator12.60 util.dominator - 支配木
util.isomorph12.61 util.isomorph - 同型判定
util.lcs12.62 util.lcs - 最長共通サブシーケンス
util.levenshtein12.63 util.levenshtein - Levenshtein編集距離
util.list8.3 廃止されたモジュール
util.match12.64 util.match - パターンマッチング
util.queue8.3 廃止されたモジュール
util.rbtree8.3 廃止されたモジュール
util.record12.65 util.record - SLIB-互換のレコード型
util.relation12.66 util.relation - リレーションフレームワーク
util.sparse8.3 廃止されたモジュール
util.stream12.67 util.stream - ストリームライブラリ
util.toposort12.68 util.toposort - トポロジカルソート
util.trie8.3 廃止されたモジュール
util.unification12.69 util.unification - ユニフィケーション

W
www.cgi12.70 www.cgi - CGIユーティリティ
www.cgi.test12.71 www.cgi.test - CGIのテスト

移動:   B   C   D   F   G   K   M   N   O   R   S   T   U   W  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

E. Index - 字句構文索引

移動:   #   '   ,   [   \   `   |
見出し一覧

#
#!4.1.2 Hash-bangトークン
#"6.12.4 文字列の補間
#*6.12.1 文字列の表記
#,6.22.7.3 読み込み時コンストラクタ
#/6.13.1 正規表現の構文
#f169.35.1 ユニフォームベクタの基本操作
#f329.35.1 ユニフォームベクタの基本操作
#f649.35.1 ユニフォームベクタの基本操作
#s169.35.1 ユニフォームベクタの基本操作
#s329.35.1 ユニフォームベクタの基本操作
#s649.35.1 ユニフォームベクタの基本操作
#s89.35.1 ユニフォームベクタの基本操作
#u169.35.1 ユニフォームベクタの基本操作
#u329.35.1 ユニフォームベクタの基本操作
#u649.35.1 ユニフォームベクタの基本操作
#u89.35.1 ユニフォームベクタの基本操作
#[6.11 文字集合
#\6.10 文字
#`6.12.4 文字列の補間

'
'4.2 リテラル

,
,4.9 準クオート(Quasiquote)
,@4.9 準クオート(Quasiquote)

[
[4.1 字句構造

\
\x4.1 字句構造

`
`4.9 準クオート(Quasiquote)

|
|6.7 シンボル

移動:   #   '   ,   [   \   `   |

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

F. Index - クラス索引

For readability, the surrounding < and > are stripped off.

移動:   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W  
見出し一覧

A
abandoned-mutex-exception9.32.4 スレッド例外
array9.1 gauche.array - 配列
array-base9.1 gauche.array - 配列

B
bimap9.8.2 汎用ディクショナリ
binary-heap12.8 data.heap - ヒープ
boolean6.4 論理値
bottom6.1 型とクラス
buffered-input-port仮想バッファポート
buffered-output-port仮想バッファポート

C
cgen-nodeSubmitting code fragments for more than one parts
cgen-type9.3.3 SchemeとCの間の変換
cgen-unitCreating a frame
char6.10 文字
char-set6.11 文字集合
classクラス構造
complex6.3.1 数値クラス
compound-condition組み込みコンディションクラス
condition組み込みコンディションクラス
condition-meta組み込みコンディションクラス
condition-variable条件変数

D
date11.8.4 日付
dbi-connectionデータベースへの接続
dbi-driverデータベースへの接続
dbi-queryクエリの準備と発行
dbm12.17.1 DBMデータベースのオープンとクローズ
dbm-meta12.17.1 DBMデータベースのオープンとクローズ
deflating-portCompression/decompression ports

E
error組み込みコンディションクラス

F
f16array9.1 gauche.array - 配列
f16vector9.35.1 ユニフォームベクタの基本操作
f32array9.1 gauche.array - 配列
f32vector9.35.1 ユニフォームベクタの基本操作
f64array9.1 gauche.array - 配列
f64vector9.35.1 ユニフォームベクタの基本操作
fsdbm12.18 dbm.fsdbm - ファイルシステムdbm
ftp-connection12.31 rfc.ftp - FTPクライアント
ftp-error12.31 rfc.ftp - FTPクライアント

G
gdbm12.19 dbm.gdbm - GDBMインタフェース

H
hash-table6.15 ハッシュテーブル
hmac12.32 rfc.hmac - HMAC鍵付きハッシング
hook9.11 gauche.hook - フック
http-error12.33 rfc.http - HTTP

I
identifier6.9 Identifier
inflating-portCompression/decompression ports
integer6.3.1 数値クラス
io-closed-error組み込みコンディションクラス
io-error組み込みコンディションクラス
io-read-error組み込みコンディションクラス
io-unit-error組み込みコンディションクラス
io-write-error組み込みコンディションクラス

J
join-timeout-exception9.32.4 スレッド例外

K
keyword6.8 キーワード

L
list6.6.1 pairクラスとnullクラス
listenerListener API
log-drain9.15 gauche.logger - ユーザレベルのロギング

M
md512.37 rfc.md5 - MD5メッセージダイジェスト
mersenne-twister12.25 math.mt-random - Mersenne Twister乱数発生器
message-condition組み込みコンディションクラス
message-digest-algorithm12.59 util.digest - メッセージダイジェストフレームワーク
message-digest-algorithm-meta12.59 util.digest - メッセージダイジェストフレームワーク
mime-messageStreaming parser
module4.13.6 モジュールイントロスペクション
mtqueue12.11 data.queue - キュー
mutexMutex

N
ndbm12.20 dbm.ndbm - NDBMインタフェース
null6.6.1 pairクラスとnullクラス
number6.3.1 数値クラス

O
object6.1 型とクラス
object-set-relation具象クラス
odbm12.21 dbm.odbm - オリジナルのDBMインタフェース

P
pair6.6.1 pairクラスとnullクラス
parameter9.21 gauche.parameter - パラメータ
port6.22.1 ポート
port-error組み込みコンディションクラス
procedure6.18.1 Procedure class and applicability
process9.24.3 Process object
process-abnormal-exit9.24.3 Process object
process-time-counterより細かい計測
propagate-meta9.16 gauche.mop.propagate - スロットアクセスの伝播
propagate-mixin9.16 gauche.mop.propagate - スロットアクセスの伝播

Q
queue12.11 data.queue - キュー

R
rational6.3.1 数値クラス
rbtree8.3 廃止されたモジュール
read-error組み込みコンディションクラス
real6.3.1 数値クラス
real-time-counterより細かい計測
regexpRegexpオブジェクトとrxmatchオブジェクト
regmatchRegexpオブジェクトとrxmatchオブジェクト
relation基本となるクラスとメソッド

S
s16array9.1 gauche.array - 配列
s16vector9.35.1 ユニフォームベクタの基本操作
s32array9.1 gauche.array - 配列
s32vector9.35.1 ユニフォームベクタの基本操作
s64array9.1 gauche.array - 配列
s64vector9.35.1 ユニフォームベクタの基本操作
s8array9.1 gauche.array - 配列
s8vector9.35.1 ユニフォームベクタの基本操作
selector9.27 gauche.selector - 簡単なディスパッチャ
serious-compound-condition組み込みコンディションクラス
serious-condition組み込みコンディションクラス
sha112.40 rfc.sha - SHAメッセージダイジェスト
sha22412.40 rfc.sha - SHAメッセージダイジェスト
sha25612.40 rfc.sha - SHAメッセージダイジェスト
sha38412.40 rfc.sha - SHAメッセージダイジェスト
sha51212.40 rfc.sha - SHAメッセージダイジェスト
simple-relation具象クラス
singleton-meta9.17 gauche.mop.singleton - シングルトン
singleton-mixin9.17 gauche.mop.singleton - シングルトン
sockaddrソケットアドレスオブジェクト
sockaddr-inソケットアドレスオブジェクト
sockaddr-in6ソケットアドレスオブジェクト
sockaddr-unソケットアドレスオブジェクト
socket9.19.2 高レベルネットワーク手続き
sparse-f16matrix12.14.2 疎行列
sparse-f16vector12.14.1 疎なベクタ
sparse-f32matrix12.14.2 疎行列
sparse-f32vector12.14.1 疎なベクタ
sparse-f64matrix12.14.2 疎行列
sparse-f64vector12.14.1 疎なベクタ
sparse-matrix12.14.2 疎行列
sparse-matrix-base12.14.2 疎行列
sparse-s16matrix12.14.2 疎行列
sparse-s16vector12.14.1 疎なベクタ
sparse-s32matrix12.14.2 疎行列
sparse-s32vector12.14.1 疎なベクタ
sparse-s64matrix12.14.2 疎行列
sparse-s64vector12.14.1 疎なベクタ
sparse-s8matrix12.14.2 疎行列
sparse-s8vector12.14.1 疎なベクタ
sparse-table12.14.3 疎なテーブル
sparse-u16matrix12.14.2 疎行列
sparse-u16vector12.14.1 疎なベクタ
sparse-u32matrix12.14.2 疎行列
sparse-u32vector12.14.1 疎なベクタ
sparse-u64matrix12.14.2 疎行列
sparse-u64vector12.14.1 疎なベクタ
sparse-u8matrix12.14.2 疎行列
sparse-u8vector12.14.1 疎なベクタ
sparse-vector12.14.1 疎なベクタ
sparse-vector-base12.14.1 疎なベクタ
string6.12 文字列
symbol6.7 シンボル
sys-addrinfo9.19.4 Netdbインタフェース
sys-fdset6.25.11 I/Oの多重化
sys-flock9.9 gauche.fcntl - 低レベルファイル操作
sys-groupUnix groups
sys-hostent9.19.4 Netdbインタフェース
sys-passwdUnix users
sys-protoent9.19.4 Netdbインタフェース
sys-servent9.19.4 Netdbインタフェース
sys-sigset6.25.7.1 シグナルとシグナルセット
sys-stat6.25.4.4 ファイルの状態
sys-termios9.30.1 POSIX termiosインタフェース
sys-tmPOSIX time
system-error組み込みコンディションクラス
system-time-counterより細かい計測

T
terminated-thread-exception9.32.4 スレッド例外
thread9.32.2 スレッド手続き
thread-exception9.32.4 スレッド例外
thread-pool12.5 control.thread-pool - スレッドプール
timeSRFI time
timeSRFI time
time-counterより細かい計測
top6.1 型とクラス
tree-map6.16 ツリーマップ
trie12.15 data.trie - Trie

U
u16array9.1 gauche.array - 配列
u16vector9.35.1 ユニフォームベクタの基本操作
u32array9.1 gauche.array - 配列
u32vector9.35.1 ユニフォームベクタの基本操作
u64array9.1 gauche.array - 配列
u64vector9.35.1 ユニフォームベクタの基本操作
u8array9.1 gauche.array - 配列
u8vector9.35.1 ユニフォームベクタの基本操作
uncaught-exception9.32.4 スレッド例外
unhandled-signal-error組み込みコンディションクラス
user-time-counterより細かい計測

V
validator-meta9.18 gauche.mop.validator - Validator付きスロット
vector6.14 ベクタ
virtual-input-port完全仮想ポート
virtual-output-port完全仮想ポート
vt100コンソールオブジェクト

W
weak-vector6.17 Weak ポインタ
win:console-screen-buffer-infoScreen buffer
win:input-recordConsole input/output
windows-consoleコンソールオブジェクト

移動:   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W  

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

G. Index - 変数索引

移動:   &   *   1  
A   B   C   D   E   F   G   H   I   K   L   M   N   O   P   R   S   T   U   V   W   X   Y   Z  
見出し一覧

&
&condition組み込みコンディションクラス
&error組み込みコンディションクラス
&io-closed-error組み込みコンディションクラス
&io-error組み込みコンディションクラス
&io-port-error組み込みコンディションクラス
&io-read-error組み込みコンディションクラス
&io-write-error組み込みコンディションクラス
&read-error組み込みコンディションクラス
&serious組み込みコンディションクラス

*
*af-inet*Address family
*af-inet6*Address family
*af-unspec*Address family
*ai-addrconfig*Address info
*ai-all*Address info
*ai-canonname*Address info
*ai-numerichost*Address info
*ai-v4mapped*Address info
*argv*6.25.2 コマンドライン引数
*ipproto-ip*Protocol
*ipproto-tcp*Protocol
*ipproto-udp*Protocol
*load-path*6.23.1 Schemeファイルのロード
*msg-none*Message type
*msg-oob*Message type
*msg-peek*Message type
*msg-waitall*Message type
*primes*素数のシーケンス
*program-name*6.25.2 コマンドライン引数
*rfc2396-unreserved-char-set*URIのエンコードとデコード
*rfc3986-unreserved-char-set*URIのエンコードとデコード
*rfc822-atext-chars*基本的なフィールドパーザ
*rfc822-standard-tokenizers*基本的なフィールドパーザ
*shut-rd*Shutdown method
*shut-rdwr*Shutdown method
*shut-wr*Shutdown method
*small-prime-bound*素数かどうかを調べる
*sock-dgram*Socket domain
*sock-stream*Socket domain
*test-error*異常系をテストする
*test-report-error*異常系をテストする

1
1/pi12.24 math.const - 定数
180/pi12.24 math.const - 定数

A
accessorsクラス構造
addr9.19.4 Netdbインタフェース
addresses9.19.4 Netdbインタフェース
addrlen9.19.4 Netdbインタフェース
AF_INET9.19.3 低レベルソケットインタフェース
AF_INET69.19.3 低レベルソケットインタフェース
AF_UNIX9.19.3 低レベルソケットインタフェース
aliases9.19.4 Netdbインタフェース
aliases9.19.4 Netdbインタフェース
aliases9.19.4 Netdbインタフェース
atime6.25.4.4 ファイルの状態
attributesScreen buffer

B
BACKGROUND_BLUEScreen buffer
BACKGROUND_GREENScreen buffer
BACKGROUND_INTENSITYScreen buffer
BACKGROUND_REDScreen buffer
bag-comparatorComparators
boolean-comparator6.2.4.3 用意されている比較器
bsize12.19 dbm.gdbm - GDBMインタフェース
bytevector-comparator6.2.4.3 用意されている比較器

C
c-fileCreating a frame
categoryクラス構造
cc9.30.1 POSIX termiosインタフェース
cflag9.30.1 POSIX termiosインタフェース
char-ci-comparator6.2.4.3 用意されている比較器
char-comparator6.2.4.3 用意されている比較器
char-set:ascii11.7.6 定義済みの文字セット
char-set:blank11.7.6 定義済みの文字セット
char-set:digit11.7.6 定義済みの文字セット
char-set:empty11.7.6 定義済みの文字セット
char-set:full11.7.6 定義済みの文字セット
char-set:graphic11.7.6 定義済みの文字セット
char-set:hex-digit11.7.6 定義済みの文字セット
char-set:iso-control11.7.6 定義済みの文字セット
char-set:letter11.7.6 定義済みの文字セット
char-set:lower-case11.7.6 定義済みの文字セット
char-set:printing11.7.6 定義済みの文字セット
char-set:punctuation11.7.6 定義済みの文字セット
char-set:symbol11.7.6 定義済みの文字セット
char-set:title-case11.7.6 定義済みの文字セット
char-set:upper-case11.7.6 定義済みの文字セット
char-set:whitespace11.7.6 定義済みの文字セット
classUnix users
close完全仮想ポート
close完全仮想ポート
close仮想バッファポート
close仮想バッファポート
column組み込みコンディションクラス
complex-comparator6.2.4.3 用意されている比較器
connectionクエリの準備と発行
contentStreaming parser
countベンチマーク
cplクラス構造
ctime6.25.4.4 ファイルの状態
CTRL_BREAK_EVENTAttaching and detaching
CTRL_C_EVENTAttaching and detaching
cursor-position.xScreen buffer
cursor-position.yScreen buffer

D
day11.8.4 日付
default-comparator6.2.4.3 用意されている比較器
default-random-source11.9 srfi-27 - ランダムビットのソース
defined-modulesクラス構造
dev6.25.4.4 ファイルの状態
dirUnix users
direct-methodsクラス構造
direct-slotsクラス構造
direct-subclassesクラス構造
direct-supersクラス構造
driver-nameDBIのコンディション

E
e12.24 math.const - 定数
ENABLE_ECHO_INPUTConsole mode
ENABLE_LINE_INPUTConsole mode
ENABLE_MOUSE_INPUTConsole mode
ENABLE_PROCESSED_INPUTConsole mode
ENABLE_PROCESSED_OUTPUTConsole mode
ENABLE_WINDOW_INPUTConsole mode
ENABLE_WRAP_AT_EOL_OUTPUTConsole mode
environmentListener API
eq-comparator6.2.4.3 用意されている比較器
equal-comparator6.2.4.3 用意されている比較器
eqv-comparator6.2.4.3 用意されている比較器
errno組み込みコンディションクラス
error-handlerListener API
error-portListener API
evaluatorListener API
event-typeConsole input/output
exact-integer-comparator6.2.4.3 用意されている比較器

F
family9.19.4 Netdbインタフェース
fatal-handlerListener API
FD_CLOEXEC9.9 gauche.fcntl - 低レベルファイル操作
file-mode12.17.1 DBMデータベースのオープンとクローズ
filenum仮想バッファポート
filenum仮想バッファポート
FILE_SHARE_READScreen buffer
FILE_SHARE_WRITEScreen buffer
fill仮想バッファポート
finalizerListener API
flags9.19.4 Netdbインタフェース
flush完全仮想ポート
flush仮想バッファポート
focus.set-focusConsole input/output
FOREGROUND_BLUEScreen buffer
FOREGROUND_GREENScreen buffer
FOREGROUND_INTENSITYScreen buffer
FOREGROUND_REDScreen buffer
F_DUPFD9.9 gauche.fcntl - 低レベルファイル操作
F_GETFD9.9 gauche.fcntl - 低レベルファイル操作
F_GETFL9.9 gauche.fcntl - 低レベルファイル操作
F_GETLK9.9 gauche.fcntl - 低レベルファイル操作
F_GETOWN9.9 gauche.fcntl - 低レベルファイル操作
F_OK6.25.4.4 ファイルの状態
F_RDLCK9.9 gauche.fcntl - 低レベルファイル操作
F_SETFD9.9 gauche.fcntl - 低レベルファイル操作
F_SETFL9.9 gauche.fcntl - 低レベルファイル操作
F_SETLK9.9 gauche.fcntl - 低レベルファイル操作
F_SETLKW9.9 gauche.fcntl - 低レベルファイル操作
F_SETOWN9.9 gauche.fcntl - 低レベルファイル操作
F_UNLCK9.9 gauche.fcntl - 低レベルファイル操作
F_WRLCK9.9 gauche.fcntl - 低レベルファイル操作

G
GDBM_CACHESIZE12.19 dbm.gdbm - GDBMインタフェース
GDBM_CENTFREE12.19 dbm.gdbm - GDBMインタフェース
GDBM_COALESCEBLKS12.19 dbm.gdbm - GDBMインタフェース
GDBM_FAST12.19 dbm.gdbm - GDBMインタフェース
GDBM_FASTMODE12.19 dbm.gdbm - GDBMインタフェース
GDBM_INSERT12.19 dbm.gdbm - GDBMインタフェース
GDBM_NEWDB12.19 dbm.gdbm - GDBMインタフェース
GDBM_NOLOCK12.19 dbm.gdbm - GDBMインタフェース
GDBM_READER12.19 dbm.gdbm - GDBMインタフェース
GDBM_REPLACE12.19 dbm.gdbm - GDBMインタフェース
GDBM_SYNC12.19 dbm.gdbm - GDBMインタフェース
GDBM_SYNCMODE12.19 dbm.gdbm - GDBMインタフェース
GDBM_WRCREAT12.19 dbm.gdbm - GDBMインタフェース
GDBM_WRITER12.19 dbm.gdbm - GDBMインタフェース
gecosUnix users
GENERIC_READScreen buffer
GENERIC_WRITEScreen buffer
getb完全仮想ポート
getc完全仮想ポート
gets完全仮想ポート
gid6.25.4.4 ファイルの状態
gidUnix groups
gidUnix users

H
h-fileCreating a frame
headersStreaming parser
hmac-block-size12.59 util.digest - メッセージダイジェストフレームワーク
hourPOSIX time
hour11.8.4 日付

I
iflag9.30.1 POSIX termiosインタフェース
indexStreaming parser
init-epilogueCreating a frame
init-prologueCreating a frame
initargsクラス構造
ino6.25.4.4 ファイルの状態
input-delayコンソールオブジェクト
input-portListener API
integer-comparator6.2.4.3 用意されている比較器
iportコンソールオブジェクト
isdstPOSIX time

K
key-convert12.17.1 DBMデータベースのオープンとクローズ
key.ascii-charConsole input/output
key.control-key-stateConsole input/output
key.downConsole input/output
key.repeat-countConsole input/output
key.unicode-charConsole input/output
key.virtual-key-codeConsole input/output

L
LC_ALL6.25.6 ロケール
LC_COLLATE6.25.6 ロケール
LC_CTYPE6.25.6 ロケール
LC_MONETARY6.25.6 ロケール
LC_NUMERIC6.25.6 ロケール
LC_TIME6.25.6 ロケール
len9.9 gauche.fcntl - 低レベルファイル操作
length12.11 data.queue - キュー
lflag9.30.1 POSIX termiosインタフェース
line組み込みコンディションクラス
list-comparator6.2.4.3 用意されている比較器
lock-file-name12.23.5 ロックファイル
lock-policy9.15 gauche.logger - ユーザレベルのロギング
log-drain12.31 rfc.ftp - FTPクライアント

M
max-length12.11 data.queue - キュー
maximum-window-size.xScreen buffer
maximum-window-size.yScreen buffer
mdayPOSIX time
memUnix groups
menu.command-idConsole input/output
message組み込みコンディションクラス
minPOSIX time
minute11.8.4 日付
mode6.25.4.4 ファイルの状態
monPOSIX time
month11.8.4 日付
mouse.button-stateConsole input/output
mouse.event-flagsConsole input/output
mouse.xConsole input/output
mouse.yConsole input/output
MSG_CTRUNC9.19.3 低レベルソケットインタフェース
MSG_DONTROUTE9.19.3 低レベルソケットインタフェース
MSG_EOR9.19.3 低レベルソケットインタフェース
MSG_OOB9.19.3 低レベルソケットインタフェース
MSG_PEEK9.19.3 低レベルソケットインタフェース
MSG_TRUNC9.19.3 低レベルソケットインタフェース
MSG_WAITALL9.19.3 低レベルソケットインタフェース
mtime6.25.4.4 ファイルの状態
mutex9.32.4 スレッド例外

N
nameUnix groups
nameUnix users
nameクラス構造
nameCreating a frame
name9.19.4 Netdbインタフェース
name9.19.4 Netdbインタフェース
name9.19.4 Netdbインタフェース
name9.32.2 スレッド手続き
nameMutex
name条件変数
nanosecondSRFI time
nanosecond11.8.4 日付
nlink6.25.4.4 ファイルの状態
nolock12.19 dbm.gdbm - GDBMインタフェース
num-instance-slotsクラス構造
number-comparator6.2.4.3 用意されている比較器

O
object12.36 rfc.json - JSONのパーズと構築
oflag9.30.1 POSIX termiosインタフェース
oportコンソールオブジェクト
output-portListener API
O_ACCMODE9.9 gauche.fcntl - 低レベルファイル操作
O_APPEND9.9 gauche.fcntl - 低レベルファイル操作
O_CREAT9.9 gauche.fcntl - 低レベルファイル操作
O_EXCL9.9 gauche.fcntl - 低レベルファイル操作
O_NOCTTY9.9 gauche.fcntl - 低レベルファイル操作
O_NONBLOCK9.9 gauche.fcntl - 低レベルファイル操作
O_RDONLY9.9 gauche.fcntl - 低レベルファイル操作
O_RDWR9.9 gauche.fcntl - 低レベルファイル操作
O_TRUNC9.9 gauche.fcntl - 低レベルファイル操作
O_WRONLY9.9 gauche.fcntl - 低レベルファイル操作

P
pair-comparator6.2.4.3 用意されている比較器
parametersStreaming parser
parentStreaming parser
passive12.31 rfc.ftp - FTPクライアント
passwdUnix groups
passwdUnix users
path9.15 gauche.logger - ユーザレベルのロギング
path12.17.1 DBMデータベースのオープンとクローズ
perm6.25.4.4 ファイルの状態
PF_INET9.19.3 低レベルソケットインタフェース
PF_INET69.19.3 低レベルソケットインタフェース
PF_UNIX9.19.3 低レベルソケットインタフェース
pi12.24 math.const - 定数
pi/18012.24 math.const - 定数
pi/212.24 math.const - 定数
pi/412.24 math.const - 定数
pid9.9 gauche.fcntl - 低レベルファイル操作
pool12.5 control.thread-pool - スレッドプール
port組み込みコンディションクラス
port組み込みコンディションクラス
port9.19.4 Netdbインタフェース
position組み込みコンディションクラス
position12.36 rfc.json - JSONのパーズと構築
preambleCreating a frame
prefix9.15 gauche.logger - ユーザレベルのロギング
preparedクエリの準備と発行
printerListener API
process9.24.3 Process object
program-name9.15 gauche.logger - ユーザレベルのロギング
prompterListener API
proto9.19.4 Netdbインタフェース
proto9.19.4 Netdbインタフェース
protocol9.19.4 Netdbインタフェース
putb完全仮想ポート
putc完全仮想ポート
puts完全仮想ポート

R
RAND_MAX6.25.13 その他のシステムコール
rational-comparator6.2.4.3 用意されている比較器
rdev6.25.4.4 ファイルの状態
readerListener API
ready完全仮想ポート
ready仮想バッファポート
realベンチマーク
real-comparator6.2.4.3 用意されている比較器
reason9.32.4 スレッド例外
redefinedクラス構造
rw-mode12.17.1 DBMデータベースのオープンとクローズ
R_OK6.25.4.4 ファイルの状態

S
secPOSIX time
secondSRFI time
second11.8.4 日付
seek完全仮想ポート
seek完全仮想ポート
seek仮想バッファポート
seek仮想バッファポート
set-comparatorComparators
shellUnix users
SIGABRT6.25.7.1 シグナルとシグナルセット
SIGALRM6.25.7.1 シグナルとシグナルセット
SIGBUS6.25.7.1 シグナルとシグナルセット
SIGCHLD6.25.7.1 シグナルとシグナルセット
SIGCONT6.25.7.1 シグナルとシグナルセット
SIGFPE6.25.7.1 シグナルとシグナルセット
SIGHUP6.25.7.1 シグナルとシグナルセット
SIGILL6.25.7.1 シグナルとシグナルセット
SIGINT6.25.7.1 シグナルとシグナルセット
SIGIO6.25.7.1 シグナルとシグナルセット
SIGIOT6.25.7.1 シグナルとシグナルセット
SIGKILL6.25.7.1 シグナルとシグナルセット
signal組み込みコンディションクラス
SIGPIPE6.25.7.1 シグナルとシグナルセット
SIGPOLL6.25.7.1 シグナルとシグナルセット
SIGPROF6.25.7.1 シグナルとシグナルセット
SIGPWR6.25.7.1 シグナルとシグナルセット
SIGQUIT6.25.7.1 シグナルとシグナルセット
SIGSEGV6.25.7.1 シグナルとシグナルセット
SIGSTKFLT6.25.7.1 シグナルとシグナルセット
SIGSTOP6.25.7.1 シグナルとシグナルセット
SIGTERM6.25.7.1 シグナルとシグナルセット
SIGTRAP6.25.7.1 シグナルとシグナルセット
SIGTSTP6.25.7.1 シグナルとシグナルセット
SIGTTIN6.25.7.1 シグナルとシグナルセット
SIGTTOU6.25.7.1 シグナルとシグナルセット
SIGURG6.25.7.1 シグナルとシグナルセット
SIGUSR16.25.7.1 シグナルとシグナルセット
SIGUSR26.25.7.1 シグナルとシグナルセット
SIGVTALRM6.25.7.1 シグナルとシグナルセット
SIGWINCH6.25.7.1 シグナルとシグナルセット
SIGXCPU6.25.7.1 シグナルとシグナルセット
SIGXFSZ6.25.7.1 シグナルとシグナルセット
size6.25.4.4 ファイルの状態
size.xScreen buffer
size.yScreen buffer
slotsクラス構造
socktype9.19.4 Netdbインタフェース
SOCK_DGRAM9.19.3 低レベルソケットインタフェース
SOCK_RAW9.19.3 低レベルソケットインタフェース
SOCK_STREAM9.19.3 低レベルソケットインタフェース
SOL_IP9.19.3 低レベルソケットインタフェース
SOL_SOCKET9.19.3 低レベルソケットインタフェース
SOL_TCP9.19.3 低レベルソケットインタフェース
sourceStreaming parser
SO_BROADCAST9.19.3 低レベルソケットインタフェース
SO_ERROR9.19.3 低レベルソケットインタフェース
SO_KEEPALIVE9.19.3 低レベルソケットインタフェース
SO_OOBINLINE9.19.3 低レベルソケットインタフェース
SO_PRIORITY9.19.3 低レベルソケットインタフェース
SO_REUSEADDR9.19.3 低レベルソケットインタフェース
SO_TYPE9.19.3 低レベルソケットインタフェース
span組み込みコンディションクラス
specific9.32.2 スレッド手続き
specificMutex
specific条件変数
sql-string12.55 text.sql - SQLのパーズと構築
ssax:Prefix-XML12.44.2 SSAXの低レベルパージングコード
start9.9 gauche.fcntl - 低レベルファイル操作
stateMutex
STD_ERROR_HANDLEStandard handles
STD_INPUT_HANDLEStandard handles
STD_OUTPUT_HANDLEStandard handles
stream-null12.67 util.stream - ストリームライブラリ
string-ci-comparator6.2.4.3 用意されている比較器
string-comparator6.2.4.3 用意されている比較器
subtypeStreaming parser
sync12.19 dbm.gdbm - GDBMインタフェース
sysベンチマーク
syslog-facility9.15 gauche.logger - ユーザレベルのロギング
syslog-option9.15 gauche.logger - ユーザレベルのロギング
syslog-priority9.15 gauche.logger - ユーザレベルのロギング

T
TCIFLUSH9.30.1 POSIX termiosインタフェース
TCIOFF9.30.1 POSIX termiosインタフェース
TCIOFLUSH9.30.1 POSIX termiosインタフェース
TCION9.30.1 POSIX termiosインタフェース
TCOFLUSH9.30.1 POSIX termiosインタフェース
TCOOFF9.30.1 POSIX termiosインタフェース
TCOON9.30.1 POSIX termiosインタフェース
TCSADRAIN9.30.1 POSIX termiosインタフェース
TCSAFLUSH9.30.1 POSIX termiosインタフェース
TCSANOW9.30.1 POSIX termiosインタフェース
terminator9.32.4 スレッド例外
thread9.32.4 スレッド例外
time-duration11.8.1 時間のタイプ
time-monotonic11.8.1 時間のタイプ
time-process11.8.1 時間のタイプ
time-tai11.8.1 時間のタイプ
time-thread11.8.1 時間のタイプ
time-utc11.8.1 時間のタイプ
transfer-encodingStreaming parser
transfer-type12.31 rfc.ftp - FTPクライアント
type6.25.4.4 ファイルの状態
typeSRFI time
type9.9 gauche.fcntl - 低レベルファイル操作
typeStreaming parser

U
uid6.25.4.4 ファイルの状態
uidUnix users
userベンチマーク
uvector-comparator6.2.4.3 用意されている比較器

V
value-convert12.17.1 DBMデータベースのオープンとクローズ
vector-comparator6.2.4.3 用意されている比較器

W
wdayPOSIX time
whence9.9 gauche.fcntl - 低レベルファイル操作
window-buffer-size.xConsole input/output
window-buffer-size.yConsole input/output
window.bottomScreen buffer
window.leftScreen buffer
window.rightScreen buffer
window.topScreen buffer
W_OK6.25.4.4 ファイルの状態

X
X_OK6.25.4.4 ファイルの状態

Y
ydayPOSIX time
yearPOSIX time
year11.8.4 日付

Z
zone-offset11.8.4 日付
Z_ASCIIOperations on inflating/deflating ports
Z_BEST_COMPRESSIONCompression/decompression ports
Z_BEST_SPEEDCompression/decompression ports
Z_BINARYOperations on inflating/deflating ports
Z_DEFAULT_COMPRESSIONCompression/decompression ports
Z_DEFAULT_STRATEGYCompression/decompression ports
Z_FILTEREDCompression/decompression ports
Z_FIXEDCompression/decompression ports
Z_HUFFMAN_ONLYCompression/decompression ports
Z_NO_COMPRESSIONCompression/decompression ports
Z_RLECompression/decompression ports
Z_TEXTOperations on inflating/deflating ports
Z_UNKNOWNOperations on inflating/deflating ports

移動:   &   *   1  
A   B   C   D   E   F   G   H   I   K   L   M   N   O   P   R   S   T   U   V   W   X   Y   Z  

[冒頭] [目次] [見出し] [ ? ]

脚注

(1)

ある意味、 これは他のプログラミング言語でシリアライズやマーシャライズと呼ばれている操作に 似ています。一般のSchemeオブジェクトをディスクやネットワークにwriteして、 それをreadすれば、元のオブジェクトと等価なオブジェクトが得られるわけです。 Lisp族言語では、これはread/write invarianceと呼ばれ、組み込みの機能と なっています。但しオブジェクトによってはこの性質を持たないものもあり、 そういうオブジェクトを扱う時は専用のシリアライザを書く必要があります。


[冒頭] [目次] [見出し] [ ? ]

目次


[冒頭] [目次] [見出し] [ ? ]

この文書について

この文書は2016年11月月6日texi2html 1.82を用いて生成されました。

ナビゲーションパネル中のボタンには以下の意味があります。

ボタン 名称 移動先 1.2.3項からの移動先
[ < ] Back Previous section in reading order 1.2.2
[ > ] Forward Next section in reading order 1.2.4
[ << ] FastBack Beginning of this chapter or previous chapter 1
[上] Up Up section 1.2
[ >> ] FastForward Next chapter 2
[冒頭] 冒頭 Cover (top) of document  
[目次] 目次 Table of contents  
[見出し] 見出し 見出し  
[ ? ] About About (help)  

では、以下に示す構造を持つ文書の1.2.3項を現在位置に仮定しています。


この文書は2016年11月月6日texi2html 1.82を用いて生成されました。