Emacs Lisp 1 イントロダクション 2 Lispのデータ型 3 数値 4 文字列と文字 5 リスト 6 シーケンス、配列、ベクター 7 ハッシュテーブル 8 シンボル 9 評価 10 制御構造 11 変数 12 関数 13 マクロ 14 カスタマイゼーション設定 15 ロード 16 バイトコンパイル 17 Lispプログラムのデバッグ 18 Lispオブジェクトの読み取りとプリント 19 ミニバッファー 20 コマンドループ 21 キーマップ 22 メジャーモードとマイナーモード 23 ドキュメント 24 ファイル 25 バックアップと自動保存 26 バッファー 27 ウィンドウ 28 フレーム 29 ポジション 30 マーカー 31 テキスト 32 非ASCII文字 33 検索とマッチング 34 構文テーブル 35 abbrevとabbrev展開 36 プロセス 37 Emacsのディスプレー表示 38 オペレーティングシステムのインターフェース 39 配布用 Lispコードの準備 Appendix A Emacs 24 Antinews Appendix B GNU Free Documentation License Appendix C GNU General Public License Appendix D ヒントと規約 Appendix E GNU Emacsの内部 Appendix F 標準的なエラー Appendix G 標準的なキーマップ Appendix H 標準的なフック Index Emacs Lisp 1 イントロダクション 1.1 注意事項 1.2 Lispの歴史 1.3 表記について 1.3.1 用語について 1.3.2 ‘nil’と‘t’ 1.3.3 評価の表記 1.3.4 プリントの表記 1.3.5 エラーメッセージ 1.3.6 バッファーテキストの表記 1.3.7 説明のフォーマット 1.3.7.1 関数の説明例 1.3.7.2 変数の説明例 1.4 バージョンの情報 1.5 謝辞 2 Lispのデータ型 2.1 プリント表現と読み取り構文 2.2 コメント 2.3 プログラミングの型 2.3.1 整数型 2.3.2 浮動小数点数型 2.3.3 文字型 2.3.3.1 基本的な文字構文 2.3.3.2 一般的なエスケープ構文 2.3.3.3 コントロール文字構文 2.3.3.4 メタ文字構文 2.3.3.5 その他の文字修飾ビット 2.3.4 シンボル型 2.3.5 シーケンス型 2.3.6 コンスセルとリスト型 2.3.6.1 ボックスダイアグラムとしてのリストの描写 2.3.6.2 ドットペア表記 2.3.6.3 連想リスト型 2.3.7 配列型 2.3.8 文字列型 2.3.8.1 文字列の構文 2.3.8.2 文字列内の非ASCII文字 2.3.8.3 文字列内の非プリント文字 2.3.8.4 文字列内のテキストプロパティ 2.3.9 ベクター型 2.3.10 文字テーブル型 2.3.11 ブールベクター型 2.3.12 ハッシュテーブル型 2.3.13 関数型 2.3.14 マクロ型 2.3.15 プリミティブ関数型 2.3.16 バイトコード関数型 2.3.17 autoload型 2.3.18 Finalizer Type 2.4 編集用の型 2.4.1 バッファー型 2.4.2 マーカー型 2.4.3 ウィンドウ型 2.4.4 フレーム型 2.4.5 端末型 2.4.6 ウィンドウ構成型 2.4.7 フレーム構成型 2.4.8 プロセス型 2.4.9 ストリーム型 2.4.10 キーマップ型 2.4.11 オーバーレイ型 2.4.12 フォント型 2.5 循環オブジェクトの読み取り構文 2.6 型のための述語 2.7 同等性のための述語 3 数値 3.1 整数の基礎 3.2 浮動小数点数の基礎 3.3 数値のための述語 3.4 数値の比較 3.5 数値の変換 3.6 算術演算 3.7 丸め処理 3.8 ビット演算 on Integers 3.9 標準的な数学関数 3.10 乱数 4 文字列と文字 4.1 文字列と文字の基礎 4.2 文字列のための述語 4.3 文字列の作成 4.4 文字列の変更 4.5 文字および文字列の比較 4.6 文字および文字列の変換 4.7 文字列のフォーマット 4.8 Lispでの大文字小文字変換 4.9 caseテーブル 5 リスト 5.1 リストとコンスセル 5.2 リストのための述語 5.3 リスト要素へのアクセス 5.4 コンスセルおよびリストの構築 5.5 リスト変数の変更 5.6 既存のリスト構造の変更 5.6.1 ‘setcar’によるリスト要素の変更 5.6.2 リストのCDRの変更 5.6.3 リストを再配置する関数 5.7 集合としてのリストの使用 5.8 連想リスト 5.9 プロパティリスト 5.9.1 プロパティリストと連想リスト 5.9.2 プロパティリストと外部シンボル 6 シーケンス、配列、ベクター 6.1 シーケンス 6.2 配列 6.3 配列を操作する関数 6.4 ベクター 6.5 ベクターのための関数 6.6 文字テーブル 6.7 ブールベクター 6.8 オブジェクト用固定長リングの管理 7 ハッシュテーブル 7.1 ハッシュテーブルの作成 7.2 ハッシュテーブルへのアクセス 7.3 ハッシュの比較の定義 7.4 ハッシュテーブルのためのその他関数 8 シンボル 8.1 シンボルの構成要素 8.2 シンボルの定義 8.3 シンボルの作成とintern 8.4 シンボルのプロパティ 8.4.1 シンボルのプロパティへのアクセス 8.4.2 シンボルの標準的なプロパティ 9 評価 9.1 評価の概要 9.2 フォームの種類 9.2.1 自己評価を行うフォーム 9.2.2 シンボルのフォーム 9.2.3 リストフォームの分類 9.2.4 シンボル関数インダイレクション 9.2.5 関数フォームの評価 9.2.6 Lispマクロの評価 9.2.7 スペシャルフォーム 9.2.8 自動ロード 9.3 クォート 9.4 バッククォート 9.5 eval 10 制御構造 10.1 順序 10.2 条件 10.2.1 パターンマッチングによるcase文 10.3 条件の組み合わせ 10.4 繰り返し 10.5 Generators 10.6 非ローカル脱出 10.6.1 明示的な非ローカル脱出: ‘catch’と‘throw’ 10.6.2 ‘catch’と‘thrown’の例 10.6.3 エラー 10.6.3.1 エラーをシグナルする方法 10.6.3.2 Emacsがエラーを処理する方法 10.6.3.3 エラーを処理するコードの記述 10.6.3.4 エラーシンボルとエラー条件 10.6.4 非ローカル脱出のクリーンアップ 11 変数 11.1 グローバル変数 11.2 Variables that Never Change 11.3 ローカル変数 11.4 変数がvoidのとき 11.5 グローバル変数の定義 11.6 堅牢な変数定義のためのヒント 11.7 変数の値へのアクセス 11.8 変数の値のセット 11.9 変数のバインディングのスコーピングルール 11.9.1 ダイナミックバインディング 11.9.2 ダイナミックバインディングの正しい使用 11.9.3 レキシカルバインディング 11.9.4 レキシカルバインディングの使用 11.10 バッファーローカル変数 11.10.1 バッファーローカル変数の概要 11.10.2 バッファーローカルなバインディングの作成と削除 11.10.3 バッファーローカル変数のデフォルト値 11.11 ファイルローカル変数 11.12 ディレクトリーローカル変数 11.13 変数のエイリアス 11.14 値を制限された変数 11.15 ジェネリック変数 11.15.1 ‘setf’マクロ 11.15.2 新たな‘setf’フォーム 12 関数 12.1 関数とは? 12.2 ラムダ式 12.2.1 ラムダ式の構成要素 12.2.2 単純なラムダ式の例 12.2.3 引数リストのその他機能 12.2.4 関数のドキュメント文字列 12.3 関数の命名 12.4 関数の定義 12.5 関数の呼び出し 12.6 関数のマッピング 12.7 無名関数 12.8 Generic Functions 12.9 関数セルの内容へのアクセス 12.10 クロージャー 12.11 Emacs Lisp関数にたいするアドバイス 12.11.1 アドバイスを操作するためのプリミティブ 12.11.2 名前つき関数にたいするアドバイス 12.11.3 アドバイスの構築方法 12.11.4 古いdefadviceを使用するコードの改良 12.12 関数を陳腐と宣言する 12.13 インライン関数Inline Functions 12.14 ‘declare’フォーム 12.15 コンパイラーへの定義済み関数の指示 12.16 安全に関数を呼び出せるかどうかの判断 12.17 関数に関するその他トピック 13 マクロ 13.1 単純なマクロの例 13.2 マクロ呼び出しの展開 13.3 マクロとバイトコンパイル 13.4 マクロの定義 13.5 マクロ使用に関する一般的な問題 13.5.1 タイミング間違い 13.5.2 マクロ引数の多重評価 13.5.3 マクロ展開でのローカル変数 13.5.4 展開におけるマクロ引数の評価 13.5.5 マクロが展開される回数は? 13.6 マクロのインデント 14 カスタマイゼーション設定 14.1 一般的なキーワードアイテム 14.2 カスタマイゼーショングループの定義 14.3 カスタマイゼーション変数の定義 14.4 カスタマイゼーション型 14.4.1 単純型 14.4.2 複合型 14.4.3 リストへのスプライス 14.4.4 型キーワード 14.4.5 新たな型の定義 14.5 カスタマイゼーションの適用 14.6 Customテーマ 15 ロード 15.1 プログラムがロードを行う方法 15.2 ロードでの拡張子 15.3 ライブラリー検索 15.4 非ASCII文字のロード 15.5 autoload 15.6 多重ロード 15.7 名前つき機能 15.8 どのファイルで特定のシンボルが定義されているか 15.9 アンロード 15.10 ロードのためのフック 15.11 Emacs Dynamic Modules 16 バイトコンパイル 16.1 バイトコンパイル済みコードのパフォーマンス 16.2 バイトコンパイル関数 16.3 ドキュメント文字列とコンパイル 16.4 個別関数のダイナミックロード 16.5 コンパイル中の評価 16.6 コンパイラーのエラー 16.7 バイトコード関数オブジェクト 16.8 逆アセンブルされたバイトコード 17 Lispプログラムのデバッグ 17.1 Lispデバッガ 17.1.1 エラーによるデバッガへのエンター 17.1.2 無限ループのデバッグ 17.1.3 関数呼び出しによるデバッガへのエンター 17.1.4 明示的なデバッガへのエントリー 17.1.5 デバッガの使用 17.1.6 デバッガのコマンド 17.1.7 デバッガの呼び出し 17.1.8 デバッガの内部 17.2 Edebug 17.2.1 Edebugの使用 17.2.2 Edebugのためのインストルメント 17.2.3 Edebugの実行モード 17.2.4 ジャンプ 17.2.5 その他のEdebugコマンド 17.2.6 ブレーク 17.2.6.1 Edebugのブレークポイント 17.2.6.2 グローバルなブレーク条件 17.2.6.3 ソースブレークポイント 17.2.7 エラーのトラップ 17.2.8 Edebugのビュー 17.2.9 評価 17.2.10 評価 List Buffer 17.2.11 Edebugでのプリント 17.2.12 トレースバッファー 17.2.13 カバレッジテスト 17.2.14 コンテキスト外部 17.2.14.1 停止するかどうかのチェック 17.2.14.2 Edebugの表示の更新 17.2.14.3 Edebugの再帰編集 17.2.15 Edebugとマクロ 17.2.15.1 マクロ呼び出しのインストルメント 17.2.15.2 仕様リスト 17.2.15.3 仕様でのバックトレース 17.2.15.4 仕様の例 17.2.16 Edebugのオプション 17.3 無効なLisp構文のデバッグ 17.3.1 過剰な開カッコ 17.3.2 過剰な閉カッコ 17.4 カバレッジテスト 17.5 プロファイリング 18 Lispオブジェクトの読み取りとプリント 18.1 読み取りとプリントの概念 18.2 入力ストリーム 18.3 入力関数 18.4 出力ストリーム 18.5 出力関数 18.6 出力に影響する変数 19 ミニバッファー 19.1 ミニバッファーの概念 19.2 ミニバッファーでのテキスト文字列の読み取り 19.3 ミニバッファーでのLispオブジェクトの読み取り 19.4 ミニバッファーのヒストリー 19.5 入力の初期値 19.6 補完 19.6.1 基本的な補完関数 19.6.2 補完とミニバッファー 19.6.3 補完を行うミニバッファーコマンド 19.6.4 高レベルの補完関数 19.6.5 ファイル名の読み取り 19.6.6 補完変数 19.6.7 プログラムされた補完 19.6.8 通常バッファーでの補完 19.7 Yes-or-Noによる問い合わせ 19.8 複数のY-or-Nの問い合わせ 19.9 パスワードの読み取り 19.10 ミニバッファーのコマンド 19.11 ミニバッファーのウィンドウ 19.12 ミニバッファーのコンテンツ 19.13 再帰的なミニバッファー 19.14 ミニバッファー、その他の事項 20 コマンドループ 20.1 コマンドループの概要 20.2 コマンドの定義 20.2.1 ‘interactive’の使用 20.2.2 ‘interactive’にたいするコード文字 20.2.3 ‘interactive’の使用例 20.2.4 コマンド候補からの選択 20.3 interactiveな呼び出し 20.4 interactiveな呼び出しの区別 20.5 コマンドループからの情報 20.6 コマンド後のポイントの調整 20.7 入力イベント 20.7.1 キーボードイベント 20.7.2 ファンクションキー 20.7.3 マウスイベント 20.7.4 クリックイベント 20.7.5 ドラッグイベント 20.7.6 ボタンダウンイベント 20.7.7 リピートイベント 20.7.8 モーションイベント 20.7.9 フォーカスイベント 20.7.10 その他のシステムイベント 20.7.11 イベントの例 20.7.12 イベントの分類 20.7.13 マウスイベントへのアクセス 20.7.14 スクロールバーイベントへのアクセス 20.7.15 文字列内へのキーボードイベントの配置 20.8 入力の読み取り 20.8.1 キーシーケンス入力 20.8.2 単一イベントの読み取り 20.8.3 入力イベントの変更と変換 20.8.4 入力メソッドの呼び出し 20.8.5 クォートされた文字の入力 20.8.6 その他のイベント入力の機能 20.9 スペシャルイベント 20.10 時間の経過や入力の待機 20.11 quit 20.12 プレフィクスコマンド引数 20.13 再帰編集 20.14 コマンドの無効化 20.15 コマンドのヒストリー 20.16 キーボードマクロ 21 キーマップ 21.1 キーシーケンス 21.2 キーマップの基礎 21.3 キーマップのフォーマット 21.4 キーマップの作成 21.5 継承とキーマップ 21.6 プレフィクスキー 21.7 アクティブなキーマップ 21.8 アクティブなキーマップの検索 21.9 アクティブなキーマップの制御 21.10 キーの照合 21.11 キー照合のための関数 21.12 キーバインディングの変更 21.13 コマンドのリマップ 21.14 イベントシーケンス変換のためのキーマップ 21.14.1 通常のキーマップとの対話 21.15 キーのバインドのためのコマンド 21.16 キーマップのスキャン 21.17 メニューキーアップ 21.17.1 メニューの定義 21.17.1.1 単純なメニューアイテム 21.17.1.2 拡張メニューアイテム 21.17.1.3 メニューセパレーター 21.17.1.4 メニューアイテムのエイリアス 21.17.2 メニューとマウス 21.17.3 メニューとキーボード 21.17.4 メニューの例 21.17.5 メニューバー Bar 21.17.6 ツールバー 21.17.7 メニューの変更 21.17.8 easy-menu 22 メジャーモードとマイナーモード 22.1 フック 22.1.1 フックの実行 22.1.2 フックのセットSetting Hooks 22.2 メジャーモード 22.2.1 メジャーモードの慣習 22.2.2 Emacsがメジャーモードを選択する方法 22.2.3 メジャーモードでのヘルプ入手 22.2.4 派生モードの定義 22.2.5 基本的なメジャーモード 22.2.6 モードフック 22.2.7 Tabulated Listモード 22.2.8 ジェネリックモード 22.2.9 メジャーモードの例 22.3 マイナーモード 22.3.1 マイナーモード記述の規約 22.3.2 キーマップとマイナーモード 22.3.3 マイナーモードの定義 22.4 モードラインのフォーマット 22.4.1 モードラインの基礎 22.4.2 モードラインのデータ構造 22.4.3 モードライン制御のトップレベル 22.4.4 モードラインで使用される変数 22.4.5 モードラインでの‘%’構造 22.4.6 モードラインでのプロパティ 22.4.7 ウィンドウのヘッダーライン 22.4.8 モードラインのフォーマットのエミュレート 22.5 Imenu 22.6 Font Lockモード 22.6.1 Font Lockの基礎 22.6.2 検索ベースのフォント化 22.6.3 検索ベースのフォント化のカスタマイズ 22.6.4 Font Lockのその他の変数 22.6.5 Font Lockのレベル 22.6.6 事前計算されたフォント化 22.6.7 Font Lockのためのフェイス 22.6.8 構文的なFont Lock 22.6.9 複数行のFont Lock構造 22.6.9.1 複数行のFont Lock 22.6.9.2 バッファー変更後のリージョンのフォント化 22.7 コードの自動インデント 22.7.1 SMIE: 無邪気なインデントエンジン 22.7.1.1 SMIEのセットアップと機能 22.7.1.2 演算子順位文法 22.7.1.3 言語の文法の定義 22.7.1.4 トークンの定義 22.7.1.5 非力なパーサーの使用 22.7.1.6 インデントルールの指定 22.7.1.7 インデントルールにたいするヘルパー関数 22.7.1.8 インデントルールの例 22.7.1.9 インデントのカスタマイズ 22.8 Desktop Saveモード 23 ドキュメント 23.1 ドキュメントの基礎 23.2 ドキュメント文字列へのアクセス 23.3 ドキュメント内でのキーバインディングの置き換え 23.4 ヘルプメッセージの文字記述 23.5 ヘルプ関数 24 ファイル 24.1 ファイルのvisit 24.1.1 ファイルをvisitする関数 24.1.2 visitのためのサブルーチン 24.2 バッファーの保存 24.3 ファイルの読み込み 24.4 ファイルの書き込み 24.5 ファイルのロック 24.6 ファイルの情報 24.6.1 アクセシビリティのテスト 24.6.2 ファイル種別の区別 24.6.3 本当の名前 24.6.4 ファイルの属性 24.6.5 拡張されたファイル属性 24.6.6 標準的な場所へのファイルの配置 24.7 ファイルの名前と属性の変更 24.8 ファイルの名前 24.8.1 ファイル名の構成要素 24.8.2 絶対ファイル名と相対ファイル名 24.8.3 ディレクトリーの名前 24.8.4 ファイル名を展開する関数 24.8.5 一意なファイル名の生成 24.8.6 ファイル名の補完 24.8.7 標準的なファイル名 24.9 ディレクトリーのコンテンツ 24.10 ディレクトリーの作成・コピー・削除 24.11 特定のファイル名の“Magic”の作成 24.12 ファイルのフォーマット変換 24.12.1 概要 24.12.2 ラウンドトリップ仕様 24.12.3 漸次仕様 25 バックアップと自動保存 25.1 ファイルのバックアップ 25.1.1 バックアップファイルの作成 25.1.2 リネームかコピーのどちらでバックアップするか? 25.1.3 番号つきバックアップファイルの作成と削除 25.1.4 バックアップファイルの命名 25.2 自動保存 25.3 リバート 26 バッファー 26.1 バッファーの基礎 26.2 カレントバッファー 26.3 バッファーの名前 26.4 バッファーのファイル名 26.5 バッファーの変更 26.6 バッファーの変更 Time 26.7 読み取り専用のバッファー 26.8 バッファーリスト 26.9 バッファーの作成 26.10 バッファーのkill 26.11 インダイレクトバッファー 26.12 2つのバッファー間でのテキストの交換 26.13 バッファーのギャップ 27 ウィンドウ 27.1 Emacsウィンドウの基本概念 27.2 ウィンドウとフレーム 27.3 ウィンドウのサイズ 27.4 ウィンドウのリサイズ 27.5 Preserving Window Sizes 27.6 ウィンドウの分割 27.7 ウィンドウの削除 27.8 ウィンドウの再結合 27.9 ウィンドウの選択 27.10 ウィンドウのサイクル順 27.11 バッファーとウィンドウ 27.12 ウィンドウ内のバッファーへの切り替え 27.13 表示するウィンドウの選択 27.14 ‘display-buffer’にたいするアクション関数 27.15 バッファー表示の追加オプション 27.16 ウィンドウのヒストリー 27.17 専用のウィンドウ 27.18 ウィンドウのquit 27.19 ウィンドウとポイント 27.20 ウィンドウの開始位置と終了位置 27.21 テキスト的なスクロール 27.22 割り合いによる垂直スクロール 27.23 水平スクロール 27.24 座標とウィンドウ 27.25 ウィンドウの構成 27.26 ウィンドウのパラメーター 27.27 ウィンドウのスクロールと変更のためのフック 28 フレーム 28.1 フレームの作成 28.2 複数の端末 28.3 Frame Geometry 28.3.1 Frame Layout 28.3.2 Frame Font 28.3.3 Size and Position 28.3.4 Implied Frame Resizing 28.4 フレームのパラメーター 28.4.1 フレームパラメーターへのアクセス 28.4.2 フレームの初期パラメーター 28.4.3 ウィンドウフレームパラメーター 28.4.3.1 基本パラメーター 28.4.3.2 位置のパラメーター 28.4.3.3 サイズのパラメーター 28.4.3.4 レイアウトのパラメーター 28.4.3.5 バッファーのパラメーター 28.4.3.6 ウィンドウ管理のパラメーター 28.4.3.7 カーソルのパラメーター 28.4.3.8 フォントとカラーのパラメーター 28.4.4 ジオメトリー 28.5 端末のパラメーター 28.6 フレームのタイトル 28.7 フレームの削除 28.8 すべてのフレームを探す 28.9 ミニバッファーとフレーム 28.10 入力のフォーカス 28.11 フレームの可視性 28.12 フレームを前面や背面に移動する 28.13 フレーム構成 28.14 マウスの追跡 28.15 マウスの位置 28.16 ポップアップメニュー 28.17 ダイアログボックス 28.18 ポインターの形状 28.19 ウィンドウシステムによる選択 28.20 ドラッグアンドドロップ 28.21 カラー名 28.22 テキスト端末のカラー 28.23 Xリソース 28.24 ディスプレー機能のテスト 29 ポジション 29.1 ポイント 29.2 モーション 29.2.1 文字単位の移動 29.2.2 単語単位の移動 29.2.3 バッファー終端への移動 29.2.4 テキスト行単位の移動 29.2.5 スクリーン行単位の移動 29.2.6 バランスのとれたカッコを越えた移動 29.2.7 文字のスキップ 29.3 エクスカーション 29.4 ナローイング 30 マーカー 30.1 マーカーの概要 30.2 マーカーのための述語 30.3 マーカーを作成する関数 30.4 マーカーからの情報 30.5 Marker 挿入タイプ 30.6 マーカー位置の移動 30.7 マーク 30.8 リージョン 31 テキスト 31.1 ポイント周辺のテキストを調べる 31.2 バッファーのコンテンツを調べる 31.3 テキストの比較 31.4 テキストの挿入 31.5 ユーザーレベルの挿入コマンド 31.6 テキストの削除 31.7 ユーザーレベルの削除コマンド 31.8 killリング 31.8.1 killリングの概念 31.8.2 killリングのための関数 31.8.3 yank 31.8.4 yankのための関数 31.8.5 低レベルのkillリング 31.8.6 killリングの内部 31.9 アンドゥ 31.10 アンドゥリストの保守 31.11 fill 31.12 fillのマージン 31.13 Adaptive Fillモード 31.14 オートfill 31.15 テキストのソート 31.16 列を数える 31.17 インデント 31.17.1 インデント用のプリミティブ 31.17.2 メジャーモードが制御するインデント 31.17.3 リージョン全体のインデント 31.17.4 前行に相対的なインデント 31.17.5 Adjustable Tab Stops 31.17.6 インデントにもとづくモーションコマンド 31.18 大文字小文字の変更 31.19 テキストのプロパティ 31.19.1 テキストプロパティを調べる 31.19.2 テキストプロパティの変更 31.19.3 テキストプロパティの検索関数 31.19.4 特殊な意味をもつプロパティ 31.19.5 Formatted Text Properties 31.19.6 テキストプロパティの粘着性 31.19.7 テキストプロパティのlazyな計算 31.19.8 クリック可能なテキストの定義 31.19.9 フィールドの定義と使用 31.19.10 なぜテキストプロパティはインターバルではないのか 31.20 文字コードの置き換え 31.21 レジスター 31.22 テキストの交換 31.23 圧縮されたデータの処理 31.24 Base 64エンコーディング 31.25 チェックサムとハッシュ 31.26 HTMLとXMLの解析 31.26.1 Document Object Model 31.27 グループのアトミックな変更 31.28 フックの変更 32 非ASCII文字 32.1 テキストの表現方法 32.2 マルチバイト文字の無効化 32.3 テキスト表現の変換 32.4 表現の選択 32.5 文字コード 32.6 文字のプロパティ 32.7 文字セット 32.8 文字セットのスキャン 32.9 文字の変換 32.10 コーディングシステム 32.10.1 コーディングシステムの基本概念 32.10.2 エンコーディングとI/O 32.10.3 Lispでのコーディングシステム 32.10.4 ユーザー選択のコーディングシステム 32.10.5 デフォルトのコーディングシステム 32.10.6 単一の操作にたいするコーディングシステムの指定 32.10.7 明示的なエンコードとデコード 32.10.8 端末I/Oのエンコーディング 32.11 入力メソッド 32.12 locale 33 検索とマッチング 33.1 文字列の検索 33.2 検索と大文字小文字 33.3 正規表現 33.3.1 正規表現の構文 33.3.1.1 正規表現内の特殊文字 33.3.1.2 文字クラス 33.3.1.3 正規表現内のバッククラッシュ構造 33.3.2 正規表現の複雑な例 33.3.3 正規表現の関数 33.4 正規表現の検索 33.5 POSIX正規表現の検索 33.6 マッチデータ 33.6.1 マッチしたテキストの置換 33.6.2 単純なマッチデータへのアクセス 33.6.3 マッチデータ全体へのアクセス 33.6.4 マッチデータの保存とリストア 33.7 検索と置換 33.8 編集で使用される標準的な正規表現 34 構文テーブル 34.1 構文テーブルの概念 34.2 構文記述子 34.2.1 構文クラスのテーブル 34.2.2 構文フラグ 34.3 構文テーブルの関数 34.4 構文プロパティ 34.5 モーションと構文 34.6 式のパース 34.6.1 パースにもとづくモーションコマンド 34.6.2 ある位置のパース状態を調べる 34.6.3 パーサー状態 34.6.4 低レベルのパース 34.6.5 パースを制御するためのパラメーター 34.7 構文テーブルの内部 34.8 カテゴリー 35 abbrevとabbrev展開 35.1 abbrevテーブル 35.2 abbrevの定義 35.3 ファイルへのabbrevの保存 35.4 略語の照会と展開 35.5 標準的なabbrevテーブル 35.6 abbrevプロパティー 35.7 abbrevテーブルのプロパティー 36 プロセス 36.1 サブプロセスを作成する関数 36.2 shell引数 36.3 同期プロセスの作成 36.4 非同期プロセスの作成 36.5 プロセスの削除 36.6 プロセスの情報 36.7 プロセスへの入力の送信 36.8 プロセスへのシグナルの送信 36.9 プロセスからの出力の受信 36.9.1 プロセスのバッファー 36.9.2 プロセスのフィルター関数 36.9.3 プロセス出力のデコード 36.9.4 プロセスからの出力を受け入れる 36.10 センチネル: プロセス状態の変更の検知 36.11 exit前の問い合わせ 36.12 別のプセスへのアクセス 36.13 トランザクションキュー 36.14 ネットワーク接続 36.15 ネットワークサーバー 36.16 データグラム 36.17 低レベルのネットワークアクセス 36.17.1 ‘make-network-process’ 36.17.2 ネットワークのオプション 36.17.3 ネットワーク機能の可用性のテスト 36.18 その他のネットワーク機能 36.19 シリアルポートとの対話 36.20 バイト配列のpackとunpack 36.20.1 データレイアウトの記述 36.20.2 バイトのunpackとpackのための関数 36.20.3 バイトのunpackとpackの例 37 Emacsのディスプレー表示 37.1 スクリーンのリフレッシュ 37.2 強制的な再表示 37.3 切り詰め 37.4 エコーエリア 37.4.1 エコーエリアへのメッセージの表示 37.4.2 処理の進捗レポート 37.4.3 ‘*Messages*’へのメッセージのロギング 37.4.4 エコーエリアのカスタマイズ 37.5 警告のレポート 37.5.1 警告の基礎 37.5.2 警告のための変数 37.5.3 警告のためのオプション 37.5.4 遅延された警告 37.6 不可視のテキスト 37.7 選択的な表示 37.8 一時的な表示 37.9 オーバーレイ 37.9.1 オーバーレイの管理 37.9.2 オーバーレイのプロパティ 37.9.3 オーバーレイにたいする検索 37.10 表示されるテキストのサイズ 37.11 行の高さ 37.12 フェイス 37.12.1 フェイスの属性 37.12.2 フェイスの定義 37.12.3 フェイス属性のための関数 37.12.4 フェイスの表示 37.12.5 フェイスのリマップ 37.12.6 フェイスを処理するための関数 37.12.7 フェイスの自動割り当て 37.12.8 基本的なフェイス 37.12.9 フォントの選択 37.12.10 フォントの照会 37.12.11 フォントセット 37.12.12 低レベルのフォント表現 37.13 フリンジ 37.13.1 フリンジのサイズと位置 37.13.2 フリンジのインジケーター 37.13.3 フリンジのカーソルFringe Cursors 37.13.4 フリンジのビットマップ 37.13.5 フリンジビットマップのカスタマイズ 37.13.6 オーバーレイ矢印 37.14 スクロールバー 37.15 ウィンドウディバイダー 37.16 ‘display’プロパティ 37.16.1 テキストを置換するディスプレー仕様 37.16.2 スペースの指定 37.16.3 スペースにたいするピクセル指定 37.16.4 その他のディスプレー仕様 37.16.5 マージン内への表示 37.17 イメージ 37.17.1 イメージのフォーマット 37.17.2 イメージのディスクリプタ 37.17.3 XBMイメージ 37.17.4 XPMイメージ 37.17.5 PostScriptイメージ 37.17.6 ImageMagickイメージ 37.17.7 その他のイメージタイプ 37.17.8 イメージの定義 37.17.9 イメージの表示 37.17.10 マルチフレームのイメージ 37.17.11 イメージキャッシュ 37.18 Embedded Native Widgets 37.19 ボタン 37.19.1 ボタンのプロパティ 37.19.2 ボタンのタイプ 37.19.3 ボタンの作成 37.19.4 ボタンの操作 37.19.5 ボタンのためのバッファーコマンド 37.20 抽象的なディスプレー 37.20.1 抽象ディスプレーの関数 37.20.2 抽象ディスプレーの例 37.21 カッコの点滅 37.22 文字の表示 37.22.1 通常の表示の慣習 37.22.2 ディスプレーテーブル 37.22.3 アクティブなディスプレーテーブル 37.22.4 グリフ 37.22.5 グリフ文字の表示 37.23 ビープ 37.24 ウィンドウシステム 37.25 Tooltips 37.26 双方向テキストの表示 38 オペレーティングシステムのインターフェース 38.1 Emacsのスタートアップ 38.1.1 要約: スタートアップ時のアクション順序 38.1.2 initファイル 38.1.3 端末固有の初期化 38.1.4 コマンドライン引数 38.2 Emacsからの脱出 38.2.1 Emacsのkill 38.2.2 Emacsのサスペンド 38.3 オペレーティングシステムの環境 38.4 ユーザーの識別 38.5 時刻 38.6 Time Zone Rules 38.7 時刻の変換 38.8 時刻のパースとフォーマット 38.9 プロセッサーの実行時間 38.10 時間の計算 38.11 遅延実行のためのタイマー 38.12 アイドルタイマー 38.13 端末の入力 38.13.1 入力のモード 38.13.2 入力の記録 38.14 端末の出力 38.15 サウンドの出力 38.16 X11キーシンボルの処理 38.17 batchモード 38.18 セッションマネージャー 38.19 デスクトップ通知 38.20 ファイル変更による通知 38.21 動的にロードされるライブラリー 38.22 Security Considerations 39 配布用 Lispコードの準備 39.1 パッケージ化の基礎 39.2 単純なパッケージ 39.3 複数ファイルのパッケージ 39.4 パッケージアーカイブの作成と保守 Appendix A Emacs 24 Antinews A.1 Old Lisp Features in Emacs 24 Appendix B GNU Free Documentation License Appendix C GNU General Public License Appendix D ヒントと規約 D.1 Emacs Lispコーディングの慣習 D.2 キーバインディングの慣習 D.3 Emacsプログラミングのヒント D.4 コンパイル済みコードを高速化ためのヒント D.5 コンパイラー警告を回避するためのヒント D.6 ドキュメント文字列のヒント D.7 コメント記述のヒント D.8 Emacsライブラリーのヘッダーの慣習 Appendix E GNU Emacsの内部 E.1 Emacsのビルド E.2 純粋ストレージ E.3 ガーベージコレクション E.4 Stack-allocated Objects E.5 メモリー使用量 E.6 C方言 E.7 Emacsプリミティブの記述 E.8 オブジェクトの内部 E.8.1 バッファーの内部 E.8.2 ウィンドウの内部 E.8.3 プロセスの内部 E.9 Cの整数型 Appendix F 標準的なエラー Appendix G 標準的なキーマップ Appendix H 標準的なフック Index Emacs Lisp ********** This is the ‘GNU Emacs Lisp Reference Manual’ corresponding to Emacs version 25.1. Copyright © 1990–1996, 1998–2016 Free Software Foundation, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with the Invariant Sections being “GNU General Public License,” with the Front-Cover Texts being “A GNU Manual,” and with the Back-Cover Texts as in (a) below. A copy of the license is included in the section entitled “GNU Free Documentation License.” (a) The FSF’s Back-Cover Text is: “You have the freedom to copy and modify this GNU manual. Buying copies from the FSF supports it in developing GNU and promoting software freedom.” 1 イントロダクション ******************** GNU Emacsテキストエディターのほとんどの部分は、Emacs Lispと呼ばれるプロ グラミング言語で記述されています。新しいコードをEmacs Lispで記述して、こ のエディターの拡張としてインストールできます。しかしEmacs Lispは、単なる 拡張言語を越える言語であり、それ自体で完全なコンピュータープログラミング 言語です。他のプログラミング言語で行なうすべてのことに、この言語を使用で きます。 Emacs Lispはエディターの中で使用するようにデザインされているので、テ キストのスキャンやパースのための特別な機能をもち、同様にファイル、バッフ ァー、ディスプレー、サブプロセスを処理する機能をもちます。Emacs Lispは編 集機能と密に統合されています。つまり編集コマンドはLispプログラムから簡単 に呼び出せる関数で、カスタマイズのためのパラメーターは普通のLisp変数です 。 このマニュアルはEmacs Lispの完全な記述を試みます。初心者のためのEmacs Lispのイントロダクションは、Free Software Foundationからも出版されている 、Bob Chassellの‘An Introduction to Emacs Lisp Programming’を参照してく ださい。このマニュアルは、Emacsを使用した編集に熟知していることを前提と しています。これの基本的な情報については、‘The GNU Emacs Manual’を参照し てください。 おおまかに言うと、前の方のチャプターでは多くのプログラミング言語の機 能にたいして、Emacs Lispでの対応する機能を説明し、後の方のチャプターでは Emacs Lispに特異な機能や、編集に特化した関連する機能を説明します。 これは Emacs 25.1に対応した‘GNU Emacs Lisp Reference Manual’です。 1.1 注意事項 ============ このマニュアルは幾多のドラフトを経てきました。ほとんど完璧ではありますが 、不備がないとも言えません。(ほとんどの特定のモードのように)それらが副次 的であるとか、まだ記述されていないという理由により、カバーされていないト ピックもあります。わたしたちがそれらを完璧に扱うことはできないので、いく つかの部分は意図的に省略しました。 このマニュアルは、それがカバーしている事柄については完全に正しくある べきあり、故に特定の説明テキスト、チャプターやセクションの順番にたいして の批判にオープンであるべきです。判りにくかったり、このマニュアルでカバー されていない何かを学ぶためにソースを見たり実地から学ぶ必要があるなら、こ のマニュアルはおそらく訂正されるべきなのかもしれません。どうかわたしたち にそれを教えてください。 このマニュアルを使用するときは、間違いを見つけたらすぐに訂正を送って ください。関数または関数グループの単純な現実例を考えたときは、ぜひそれを 記述して送ってください。それが妥当ならコメントでノード名と関数名や変数名 を参照してください。あなたが訂正を求めるエディションのバージョンも示して ください。 ‘M-x report-emacs-bug’を使用して、コメントや訂正を送ってください。 1.2 Lispの歴史 ============== Lisp(LISt Processing language: リスト処理言語)は、MIT(Massachusetts Institute of Technology: マサチューセッツ工科大学)で、AI(artificial intelligence: 人工知能)の研究のために、1950年代末に最初に開発されました 。Lisp言語の強力なパワーは、編集コマンドの記述のような、他の目的にも適っ ています。 長年の間に何ダースものLisp実装が構築されてきて、それらのそれぞれに特 異な点があります。これらの多くは、1960年代にMITのProject MACで記述された 、Maclispに影響を受けています。最終的に、Maclisp後裔の実装者は共同して、 Common Lispと呼ばれる標準のLispシステムを開発しました。その間にMITの Gerry SussmanとGuy Steeleにより、簡潔ながらとても強力なLisp方言の、 Schemeが開発されました。 GNU Emacs LispはMaclispから多く、Common Lispから少し影響を受けていま す。Common Lispを知っている場合、多くの類似点に気づくでしょう。しかし Common Lispの多くの機能は、GNU Emacsが要求するメモリー量を削減するために 、省略または単純化されています。ときには劇的に単純化されているために、 Common Lispユーザーは混乱するかもしれません。わたしたちは時折GNU Emacs LispがCommon Lispと異なるか示すでしょう。Common Lispを知らない場合、それ について心配する必要はありません。このマニュアルは、それ自体で自己完結し ています。 ‘cl-lib’ライブラリーを通じて、Common Lispをかなりエミュレートできます 。*note Overview: (cl)Top.を参照してください。 Emacs LispはSchemeの影響は受けていません。しかしGNUプロジェクトには Guileと呼ばれるScheme実装があります。拡張が必要な新しいGNUソフトウェアー では、Guileを使用します。 1.3 表記について ================ このセクションでは、このマニュアルで使用する表記規約を説明します。あなた はこのセクションをスキップして、後から参照したいと思うかもしれません。 1.3.1 用語について ------------------ このマニュアルでは、“Lispリーダー”および“Lispプリンター”という用語で、 Lispのテキスト表現を実際のLispオブジェクトに変換したり、その逆を行なう Lispルーチンを参照します。詳細については、*note Printed Representation::を参照してください。あなた、つまりこのマニュアルを読んで いる人のことはプログラマーと考えて“あなた”と呼びます。“ユーザー”とは、あ なたの記述したものも含めて、Lispプログラムを使用する人を指します。 Lispコードの例は、‘(list 1 2 3)’のようなフォーマットです。メタ構文変 数(metasyntactic variables)を表す名前や、説明されている関数の引数名前は 、FIRST-NUMBERのようにフォーマットされています。 1.3.2 ‘nil’と‘t’ ---------------- Emacs Lispでは、シンボル‘nil’には3つの異なる意味があります。1つ目は ‘nil’という名前のシンボル、2つ目は論理値のFALSE、3つ目は空リスト — つま り要素が0のリストです。変数として使用した場合、‘nil’は常に値‘nil’をもち ます。 Lispリーダーに関する限り、‘()’と‘nil’は同一です。これらは同じオブジェ クト、つまりシンボル‘nil’を意味します。このシンボルを異なる方法で記述す るのは、完全に人間の読み手を意図したものです。Lispリーダーが‘()’か ‘nil’のどちらかを読み取った後は、プログラマーが実際にどちらの表現で記述 したかを判断する方法はありません。 このマニュアルでは、空リストを意味することを強調したいときは‘()’と記 述し、論理値のFALSEを意味することを強調したいときは‘nil’と記述します。こ の慣習はLispプログラムで使用してもよいでしょう。 (cons 'foo ()) ; 空リストを強調 (setq foo-flag nil) ; 論理値のFALSEを強調 論理値が期待されているコンテキストでは、非‘nil’はTRUEと判断されます。 しかし論理値のTRUEを表す好ましい方法は‘t’です。TRUEを表す値を選択する必 要があり、他に選択の根拠がない場合は‘t’を使用してください。シンボル‘t’は 、常に値‘t’をもちます。 Emacs Lispでの‘nil’と‘t’は、常に自分自身を評価する特別なシンボルです 。そのためプログラムでこれらを定数として使用する場合、クォートする必要は ありません。これらの値の変更を試みると、結果は‘setting-constant’エラーと なります。*note Constant Variables::を参照してください。 -- Function: booleanp object OBJECTが2つの正規のブーリーン値(‘t’か‘nil’)のいずれかなら、非 ‘nil’をリターンする。 1.3.3 評価の表記 ---------------- 評価できるLisp式のことを“フォーム(form)”と呼びます。フォームの評価により 、これは結果として常にLispオブジェクトを生成します。このマニュアルの例で は、これを‘⇒’で表します: (car '(1 2)) ⇒ 1 これは“‘(car '(1 2))’を評価すると、1になる”と読むことができます。 フォームがマクロ呼び出しの場合、それは評価されるための新たなLispフォ ームに展開されます。展開された結果は‘↦’で表します。わたしたちは展開され たフォームの評価し結果を示すこともあれば、示さない場合もあります。 (third '(a b c)) ↦ (car (cdr (cdr '(a b c)))) ⇒ c 1つのフォームを説明するために、同じ結果を生成する別のフォームを示すこ ともあります。完全に等価な2つのフォームは、‘≡’で表します。 (make-sparse-keymap) ≡ (list 'keymap) 1.3.4 プリントの表記 -------------------- このマニュアルの例の多くは、それらが評価されるときにテキストをプリントし ます。(‘*scratch*’バッファーのような)Lisp Interactionバッファーでコード 例を実行する場合、プリントされるテキストはそのバッファーに挿入されます。 (関数‘eval-region’での評価のように)他の方法でコード例を実行する場合、プ リントされるテキストはエコーエリアに表示されます。 このマニュアルの例はプリントされるテキストがどこに出力されるかに関わ らず、それを‘⊣’で表します。フォームを評価することにより戻される値は、 ‘⇒’とともに後続の行で示します。 (progn (prin1 'foo) (princ "\n") (prin1 'bar)) ⊣ foo ⊣ bar ⇒ bar 1.3.5 エラーメッセージ ---------------------- エラーをシグナルする例もあります。これは通常、エコーエリアにエラーメッセ ージを表示します。エラーメッセージの行は‘error→’で始まります。‘error→’自 体は、エコーエリアに表示されないことに注意してください。 (+ 23 'x) error→ Wrong type argument: number-or-marker-p, x 1.3.6 バッファーテキストの表記 ------------------------------ バッファー内容の変更を説明する例もあます。それらの例では、そのテキストの before(以前)とafter(以後)のバージョンを示します。それらの例では、バッフ ァー内容の該当する部分を、ダッシュを用いた2行の破線(バッファー名を含む )で示します。さらに、‘★’はポイントの位置を表します(もちろんポイントのシ ンボルはバッファーのテキストの一部ではなく、ポイントが現在配されている 2つの文字の_間_の位置を表す)。 ---------- Buffer: foo ---------- This is the ★contents of foo. ---------- Buffer: foo ---------- (insert "changed ") ⇒ nil ---------- Buffer: foo ---------- This is the changed ★contents of foo. ---------- Buffer: foo ---------- 1.3.7 説明のフォーマット ------------------------ このマニュアルでは関数(function)、変数(variable)、コマンド(command)、ユ ーザーオプション(user option)、スペシャルフォーム(special form)を、統一 されたフォーマットで記述します。記述の最初の行には、そのアイテムの名前と 、もしあれば引数(argument)が続きます。 そのアイテムの属するカテゴリー (function、variableなど)は、行の先頭に表示します。 それ以降の行は説明行 で、例を含む場合もあります。 1.3.7.1 関数の説明例 .................... 関数の記述では、関数の名前が最初に記述されます。同じ行に引数の名前のリス トが続きます。引数の値を参照するために、引数の名前は記述の本文にも使用さ れます。 引数リストの中にキーワード‘&optional’がある場合、その後の引数が省略可 能であることを示します(省略された引数のデフォルトは‘nil’)。その関数を呼 び出すときは、‘&optional’を記述しないでください。 キーワード‘&rest’(これの後には1つの引数名を続けなければならない)は、 その後に任意の引数を続けることができることを表します。‘&rest’の後に記述 された引数名の値には、その関数に渡された残りのすべての引数がリストとして セットされます。この関数を呼び出すときは、‘&rest’を記述しないでください 。 以下は‘foo’という架空の関数(function)の説明です: -- Function: foo integer1 &optional integer2 &rest integers 関数‘foo’はINTEGER2からINTEGER1を減じてから、その結果に残りすべての 引数を加える。INTEGER2が与えられなかった場合、デフォルトして数値 19が使用される。 (foo 1 5 3 9) ⇒ 16 (foo 5) ⇒ 14 より一般的には、 (foo W X Y...) ≡ (+ (- X W) Y...) 慣例として引数の名前には、(たとえばINTEGER、INTEGER1、BUFFERのような )期待されるタイプ名が含まれます。(BUFFERSのような)複数形のタイプは、しば しばその型のオブジェクトのリストを意味します。OBJECTのような引き数名は、 それが任意の型であることを表します(Emacsオブジェクトタイプのリストは *note Lisp Data Types::を参照)。他の名前をもつ引数(たとえばNEW-FILE)はそ の関数に固有の引数で、関数がドキュメント文字列をもつ場合、引数のタイプは その中で説明されるべきです(*note Documentation::を参照)。 ‘&optional’や‘&rest’により修飾される引数のより完全な説明は、*note Lambda Expressions::を参照してください。 コマンド(command)、マクロ(macro)、スペシャルフォーム(special form)の 説明も同じフォーマットですが、‘Function’が‘Command’、‘Macro’、‘Special Form’に置き換えられます。コマンドはとは単に、インタラクティブ (interactive: 対話的)に呼び出すことができる関数です。マクロは関数とは違 う方法(引数は評価されない)で引数を処理しますが、同じ方法で記述します。 マクロとスペシャルフォームにたいする説明には、特定のオプション引数や 繰り替えされる引数のために、より複雑な表記が使用されます。なぜなら引数リ ストが、より複雑な方法で別の引数に分離されるからです。‘[OPTIONAL-ARG]’は OPTIONAL-ARGがオプションであることを意味し、‘REPEATED-ARGS...’は0個以上 の引数を表します。カッコ(parentheses)は、複数の引数をリスト構造の追加レ ベルにグループ化するのに使用されます。以下は例です: -- Special Form: count-loop (var [from to [inc]]) body... この架空のスペシャルフォームは、 BODYフォームを実行してから変数 VARをインクリメントするループを実装します。最初の繰り返しでは変数は 値FROMをもちます。以降の繰り返しでは、変数は1(INCが与えられた場合は INC)増分されます。VARがTOに等しい場合、BODYを実行する前にループを exitします。以下は例です: (count-loop (i 0 10) (prin1 i) (princ " ") (prin1 (aref vector i)) (terpri)) FROMとTOが省略された場合、ループを実行する前にVARに‘nil’がバインド され、繰り返しの先頭においてVARが非‘nil’の場合は、ループをexitしま す。以下は例です: (count-loop (done) (if (pending) (fixit) (setq done t))) このスペシャルフォームでは、引数FROMとTOはオプションですが、両方を 指定するか未指定にするかのいずれかでなければなりません。これらの引 数が与えられた場合には、オプションでINCも同様に指定することができま す。これらの引数は、フォームのすべての残りの要素を含むBODYと区別す るために、引数VARとともにリストにグループ化されます。 1.3.7.2 変数の説明例 .................... “変数(variable)”とは、オブジェクトに“バインド(bind)”される名前です(“セッ ト(set)”とも言う)。変数がバインドされたオブジェクトのことを“値(value)”と 呼びます。このような場合には、その変数が値をもつという言い方もします。ほ とんどすべての変数はユーザーがセットすることができますが、特にユーザーが 変更できる特定の変数も存在し、これらは“ユーザーオプション(user options)”と呼ばれます。通常の変数およびユーザーオプションは、関数と同様 のフォーマットを使用して説明されますが、それらには引数がありません。 以下は架空の変数‘electric-future-map’の説明です。 -- Variable: electric-future-map この変数の値はElectric Command Futureモードで使用される完全なキーマ ップである。このマップ内の関数により、まだ実行を考えていないコマン ドの編集が可能になる。 ユーザーオプションも同じフォーマットをもちますが、‘Variable’が‘User Option’に置き換えられます。 1.4 バージョンの情報 ==================== 以下の機能は、使用しているEmacsに関する情報を提供します。 -- Command: emacs-version &optional here この関数は実行しているEmacsのバージョンを説明する文字列をreturnすす 。これはバグレポートにこの文字列を含めるときに有用である。 (emacs-version) ⇒ "GNU Emacs 24.5.1 (x86_64-unknown-linux-gnu, GTK+ Version 3.16) of 2015-06-01" HEREが非‘nil’ならテキストをバッファーのポイントの前に挿入して、 ‘nil’をリターンする。この関数がインタラクティブに呼び出すと、同じ情 報をエコーエリアに出力する。プレフィクス引数を与えると、HEREが非 ‘nil’になる。 -- Variable: emacs-build-time この変数の値はEmacsがビルドされた日時を示す。値は‘current-time’と同 様の、4つの整数からなるリストである(*note Time of Day::を参照)。 emacs-build-time ⇒ (20614 63694 515336 438000) -- Variable: emacs-version この変数の値は実行中のEmacsのバージョンであり、‘"23.1.1"’のような文 字列。この文字列の最後の数字は、実際にはEmacsリリースのバージョン番 号の一部ではなく、任意のディレクトリーにおいてEmacsがビルドされる度 に増分される。‘"22.0.91.1"’のように4つの数字から構成される値は、そ れがリリースではないテストバージョンであることを示す。 -- Variable: emacs-major-version Emacsのメジャーバージョン番号を示す整数。Emacs 23.1では値は23。 -- Variable: emacs-minor-version Emacsのマイナーバージョン番号を示す整数。Emacs 23.1では値は1。 1.5 謝辞 ======== このマニュアルは当初、Robert Krawitz、Bil Lewis、Dan LaLiberte、 Richard M. Stallman、Chris Welty、およびGNUマニュアルグループのボランテ ィアにより、数年を費やして記述されました。Robert J. Chassellはこのマニュ アルのレビューと編集をDefense Advanced Research Projects Agency、ARPA Order 6082のサポートのもとに手助けしてくれ、Computational Logic, Incの Warren A. Hunt, Jr.によりアレンジされました。それ以降も追加のセクション がMiles Bader、Lars Brinkhoff、Chong Yidong、Kenichi Handa、Lute Kamstra、Juri Linkov、Glenn Morris、Thien-Thi Nguyen、Dan Nicolaescu、 Martin Rudalics、Kim F. Storm、Luc Teirlinck、Eli Zaretskii、およびその 他の人たちにより記述されました。 Drew Adams、Juanma Barranquero、Karl Berry、Jim Blandy、Bard Bloom、 Stephane Boucher、David Boyes、Alan Carroll、Richard Davis、Lawrence R. Dodd、Peter Doornbosch、David A. Duff、Chris Eich、Beverly Erlebacher、 David Eckelkamp、Ralf Fassel、Eirik Fuller、Stephen Gildea、Bob Glickstein、Eric Hanchrow、Jesper Harder、George Hartzell、Nathan Hess、 Masayuki Ida、Dan Jacobson、Jak Kirman、Bob Knighten、Frederick M. Korz、 Joe Lammens、Glenn M. Lewis、K. Richard Magill、Brian Marick、Roland McGrath、Stefan Monnier、Skip Montanaro、John Gardiner Myers、Thomas A. Peterson、Francesco Potortì、Friedrich Pukelsheim、Arnold D. Robbins、 Raul Rockwell、Jason Rumney、Per Starbäck、Shinichirou Sugou、Kimmo Suominen、Edward Tharp、Bill Trost、Rickard Westman、Jean White、Eduard Wiebe、Matthew Wilding、Carl Witty、Dale Worley、Rusty Wright、David D. Zuhnにより訂正が提供されました。 より完全な貢献者のリストは、Emacsソースリポジトリーの関連する変更ログ エントリーを参照してください。 2 Lispのデータ型 **************** Lispの“オブジェクト(object)”とは、Lispプログラムから操作されるデータです 。“型(type)”や“データ型(data type)”とは、可能なオブジェクトの集合を意味 します。 すべてのオブジェクトは少なくとも1つの型に属します。同じ型のオブジェク トは同様な構造をもち、通常は同じコンテキストで使用されます。型を重複して もつことができ、オブジェクトは複数の型に属することができます。その結果と して、あるオブジェクトが特定の型に属するかどうかを尋ねることはできますが 、オブジェクトが_その_型だけに属するかどうかは決定できません。 Emacsにはいくつかの基本オブジェクト型が組み込まれています。これらの型 は他のすべての型を構成するもとであり、“プリミティブ型(primitive types: 基 本型)”と呼ばれます。すべてのオブジェクトはただ1つのプリミティブ型に属し ます。これらの型には“整数(integer)”、“浮動小数点数(float)”、“コンス (cons)”、“シンボル(symbol)”、“文字列(string)”、“ベクター(vector)”、“ハッ シュテーブル(hash-table)”、“サブルーチン(subr)”、“バイトコード関数 (byte-code function)”、および“buffer”のような編集に関連した特別な型が含 まれます(*note Editing Types::を参照)。 プリミティブ型にはそれぞれ、オブジェクトがその型のメンバーかどうかの チェックを行なうために、それぞれ対応するLisp関数があります。 他の多くの言語とは異なり、Lispのオブジェクトは“自己記述 (self-typing)”的です。オブジェクトのプリミティブ型は、オブジェクト自体に 暗に含まれます。たとえばオブジェクトがベクターなら、それを数字として扱う ことはできません。Lispはベクターが数字でないことを知っているのです。 多くの言語では、プログラマーは各変数にたいしてデータ型を宣言しなけれ ばならず、コンパイラーは型を知っていますが、データの中に型はありません。 Emacs Lispには、このような型宣言はありません。Lisp変数は任意の型の値をも つことができ、変数に保存した値と型を記憶します(実際には特定の型の値だけ をもつことができる少数のEmacs Lisp変数がある。*note Variables with Restricted Values::を参照されたい)。 このチャプターでは、GNU Emacs Lispの各標準型の意味、プリント表現 (printed representation)、入力構文(read syntax)を説明します。これらのデ ータ型を使用する方法についての詳細は、以降のチャプターを参照してください 。 2.1 プリント表現と読み取り構文 ============================== オブジェクトのプリント表現(printed representation)とは、オブジェクトにた いしてLispプリンター(関数‘prin1’)が生成する出力のフォーマットです。すべ てのデータ型は一意なプリント表現をもちます。オブジェクトの“入力構文(read syntax)”とは、オブジェクトにたいしてLispリーダー(関数‘read’)が受け取る入 力のフォーマットです。これは一意である必要はありません。多くの種類のオブ ジェクトが複数の構文をもちます。*note Read and Print::を参照してください 。 ほとんどの場合、オブジェクトのプリント表現が、入力構文としても使用さ れます。しかしLispプログラム内の定数とすることに意味が無いいくつかの型に は、入力構文がありません。これらのオブジェクトは“ハッシュ表記(hash notation)”でプリントされ、‘#<’、説明的な文字列(典型的には型名にオブジェ クトの名前を続けたもの)、‘>’で構成される文字列です。たとえば: (current-buffer) ⇒ # ハッシュ表記は読み取ることができないので、Lispリーダーは‘#<’に遭遇すると 常にエラー‘invalid-read-syntax’をシグナルします。 他の言語では式はテキストであり、これ以外の形式はありません。Lispでは 式は第一にまずLispオブジェクトであって、オブジェクトの入力構文であるテキ ストは副次的なものに過ぎません。たいていこの違いを強調する必要はありませ んが、このことを心に留めておかないとたまに混乱することがあるでしょう。 インタラクティブに式を評価するとき、Lispインタープリターは最初にそれ のテキスト表現を読み取り、Lispオブジェクトを生成してからそのオブジェクト を評価します(*note Evaluation::を参照)。しかし評価と読み取りは別の処理で す。読み取りによりテキストにより表現されたLispオブジェクトを読み取り、 Lispオブジェクトがリターンされます。後でオブジェクトは評価されるかもしれ ないし、評価されないかもしれません。オブジェクトを読み取るための基本的な 関数‘read’の説明は、*note Input Functions::を参照してください。 2.2 コメント ============ “コメント(comment)”はプログラム中に記述されたテキストであり、そのプログ ラムを読む人間ためだけに存在するもので、プログラムの意味には何の影響もも ちません。Lispではそれが文字列や文字定数にある場合をのぞき、セミコロン (‘;’)でコメントが開始されます。行の終端までがコメントになります。Lispリ ーダーはコメントを破棄します。コメントはLispシステム内でプログラムを表す Lispオブジェクトの一部にはなりません。 ‘#@COUNT’構成は、次のCOUNT個の文字をスキップします。これはプログラム により生成されたバイナリーデータを含むコメントにたいして有用です。Emacs Lispバイトコンパイラーは出力ファイルにこれを使用します(*note Byte Compilation::を参照)。しかしソースファイル用ではありません。 コメントのフォーマットにたいする慣例は、*note Comment Tips::を参照し てください。 2.3 プログラミングの型 ====================== Emacs Lispには2種類の一般的な型があります。1つはLispプログラミングに関わ るもので、もう1つは編集に関わるものです。前者はさまざまな形で多くの Lisp実装に存在します。後者はEmacs Lispに固有です。 2.3.1 整数型 ------------ 整数の値の範囲はマシンに依存します、最小のレンジは−536,870,912から 536,870,911(30ビットでは −2**29 から 2**29 − 1) ですが、多くのマシンはこ れより広い範囲を提供します。Emacs Lispの数学関数は整数のオーバーフローを チェックしません。したがってEmacsのh整数が30ビットの場合、‘(1+ 536870911)’は−536,870,912になります。 整数にたいする入力構文は、(10を基数とする)数字のシーケンスで、オプシ ョンで先頭に符号、最後にピリオドがつきます。Lispインタープリターにより生 成されるプリント表現には、先頭の ‘+’や最後の‘.’はありません。 -1 ; 整数の−1 1 ; 整数の1 1. ; これも整数の1 +1 ; これも整数の1 特別な例外として、数字シーケンスが有効なオブジェクトとしては大きすたり小 さすぎる整数を指定する場合、Lispリーダーはそれを浮動小数点数(*note Floating-Point Type::を参照)として読み取ります。たとえば、Emacsの整数が 30ビットの場合、‘536870912’は浮動小数点数の‘536870912.0’として読み取られ ます。 詳細は*note Numbers::を参照してください。 2.3.2 浮動小数点数型 -------------------- 浮動小数点数は、コンピューターにおける科学表記に相当するものです。浮動小 数点数を10の指数をともなう有理数として考えることができます。正確な有効桁 数と可能な指数はマシン固有です。Emacsは値の保存にCデータ型の‘double’を使 用し、内部的には10の指数ではなく、2の指数として記録します。 浮動小数点数のプリント表現には、(後に最低1つの数字をともなう)小数点と 、指数のどちらか一方、または両方が必要です。たとえば‘1500.0’、‘+15e2’、 ‘15.0e+2’、‘+1500000e-3’、‘.15e4’は、いずれも浮動小数点数の1500を記述し 、これらはすべて等価です。 詳細は*note Numbers::を参照してください。 2.3.3 文字型 ------------ Emacs Lispでの“文字(character)”は、整数以外の何者でもありません。言い換 えると、文字は文字コードで表現されます。たとえば文字‘A’は、整数の65とし て表現されます。 プログラムで文字を個別に使用するのは稀であり、文字のシーケンスとして 構成される_文字列(strings)_として扱われるのがより一般的です。*note String Type::を参照してください。 文字列やバッファーの中の文字は、現在のところ0から4194303の範囲 — つま り22ビットに制限されています(*note Character Codes::を参照)。0から127の コードはASCIIコードで、残りは非ASCIIです(*note Non-ASCII Characters::を 参照)。キーボード入力を表す文字はコントロール(Control)、メタ(Meta)、シフ ト(Shift)などの修飾キーをエンコードするために、より広い範囲をもちます。 文字から可読なテキスト記述を生成する、メッセージ用の特別な関数が存在 します。*note Describing Characters::を参照してください。 2.3.3.1 基本的な文字構文 ........................ 文字は実際には整数なので、文字のプリント表現は10進数です。文字にたいする 入力構文も利用可能ですが、Lispプログラムでこの方法により文字を記述するの は、明解なプログラミングではありません。文字にたいしては、Emacs Lispが提 供する、特別な入力構文を_常に_使用するべきです。これらの構文フォーマット はクエスチョンマークで開始されます。 英数字にたいする通常の入力構文は、クエスチョンマークと、その後にその 文字を記述します。したがって文字‘A’は‘?A’、文字‘B’は‘?B’、文字‘a’は ‘?a’となります。 たとえば: ?Q ⇒ 81 ?q ⇒ 113 区切り文字(punctuation characters)にも同じ構文を使用できますが、 Lispコードを編集するためのEmacsコマンドが混乱しないように、‘\’を追加する のがよい場合がしばしばあります。たとえば開カッコを記述するために‘?\(’と 記述します。その文字が‘\’の場合、それをクォートするために、‘?\\’のように 2つ目の‘\’を_使用しなければなりません_。 Ctrl+g(control-g)、バックスペース(backspace)、タブ(tab)、改行 (newline)、垂直タブ(vertical tab)、フォームフィード(formfeed)、スペース (space)、キャリッジリターン(return)、デリート(del)、エスケープ(escape)は それぞれ‘?\a’、‘?\b’、‘?\t’、‘?\n’、‘?\v’、‘?\f’、‘?\s’、‘?\r’、‘?\d’、 ‘?\e’と表すことができます(後にダッシュのついた‘?\s’は違う意味をもつ — こ れは後続の文字にたいしてスーパーによる修飾を適用する)。したがって、 ?\a ⇒ 7 ; control-g、‘C-g’ ?\b ⇒ 8 ; バックスペース、、‘C-h’ ?\t ⇒ 9 ; タブ、、‘C-i’ ?\n ⇒ 10 ; 改行、‘C-j’ ?\v ⇒ 11 ; 垂直タブ、‘C-k’ ?\f ⇒ 12 ; フォームフィード文字、‘C-l’ ?\r ⇒ 13 ; キャリッジリターン、、‘C-m’ ?\e ⇒ 27 ; エスケープ文字、、‘C-[’ ?\s ⇒ 32 ; スペース文字、 ?\\ ⇒ 92 ; バックスラッシュ文字、‘\’ ?\d ⇒ 127 ; デリート文字、 バックスラッシュがエスケープ文字の役割を果たすので、これらのバックス ラッシュで始まるシーケンスは“エスケープシーケンス(escape sequences)”とも 呼ばれます。この用語法は文字とは関係ありません。‘\s’は文字定数とし ての使用を意図しており、文字定数の内部では単にスペースを記述します。 エスケープという特別な意味を与えずに、任意の文字の前にバックスラッシ ュの使用することは許されており、害もありません。したがって‘?\+’は‘?+’と 等価です。ほとんどの文字の前にバックスラッシュを追加することに理由はあり ません。しかしLispコードを編集するEmacsコマンドが混乱するのを避けるため に、文字‘()\|;'`"#.,’の前にはバックスラッシュを追加するべきです。スペー ス、タブ、改行、フォームフィードのような空白文字の前にもバックスラッシュ を追加できます。しかしタブやスペースspaceのような実際の空白文字のかわり に、‘\t’や‘\s’のような可読性のあるエスケープシーケンスを使用するほうが明 解です(スペースを後にともなうバックスラッシュを記述する場合、後続のテキ ストと区別するために、文字定数の後に余分なスペースを記述すること)。 2.3.3.2 一般的なエスケープ構文 .............................. 特に重要なコントロール文字にたいする特別なエスケープシーケンスに加えて、 Emacsは非ASCIIテキスト文字の指定に使用できる、何種類かのエスケープ構文を 提供します。 最初に、文字をUnicodeの値で指定することができます。‘?\uNNNN’は Unicodeのコードポイント‘U+NNNN’の文字を表します。ここでNNNNは、(慣例によ り)正確に4桁の16進数です。バックスラッシュは、後続の文字がエスケープシー ケンスを形成することを示し、‘u’はUnicodeエスケープシーケンスを指定します 。 ‘U+FFFF’より大きなコードポイントをもつUnicode文字を指定するために、若 干異なる構文が存在します。‘?\U00NNNNNN’はコードポイント‘U+NNNNNN’の文字 を表します。ここでNNNNNNは6桁の16進数です。Unicode標準は‘U+10FFFF’までの コードポイントだけを定義するので、これより大きいコードポイントを指定する とEmacsはエラーをシグナルします。 次に文字を16進の文字コードで指定できます。16進エスケープシーケンスは バックスラッシュ、‘x’、および16進の文字コードにより構成されます。したが って‘?\x41’は文字‘A’、‘?\x1’は文字‘C-a’、‘?\xe0’は文字‘à’(グレイブアクセ ントつきの‘a’)になります。任意の16進数を使用できるので、この方法で任意の 文字を表すことができます。 最後に、8進の文字コードにより文字を指定できます。8進エスケープシーケ ンスは、3桁までの8進数字をともなうバックスラッシュにより形成されます。し たがって‘?\101’は文字‘A’、‘?\001’は文字‘C-a’、‘?\002’は文字‘C-b’を表しま す。この方法で指定できるのは、8進コード777までの文字だけです。 これらのエスケープシーケンスは文字列内でも使用されます。*note Non-ASCII in Strings::を参照してください。 2.3.3.3 コントロール文字構文 ............................ 他の入力構文を使用してコントロール文字を表すことができます。これは後にバ ックスラッシュ、カレット、対応する非コントロール文字(大文字か小文字)をと もなうクエスチョンマークから構成されます。たとえば‘?\^I’と‘?\^i’はどちら も、値が9である文字‘C-i’の有効な入力構文です。 ‘^’のかわりに‘C-’を使用することもできます。したがって‘?\C-i’は ‘?\^I’や‘?\^i’と等価です。 ?\^I ⇒ 9 ?\C-I ⇒ 9 文字列やバッファーの中ではASCIIのコントロール文字だけが許されますが、 キーボード入力にたいしては‘C-’により任意の文字をコントロール文字にするこ とができます。これらの非ASCIIのコントロール文字にたいするコントロール文 字には 非コントロール文字にたいするコードと同様に、2**26 ビットが含まれ ます。通常のテキスト端末には非ASCIIコントロール文字を生成する方法があり ませんが、Xやその他のウィンドウシステムを使用すれば簡単に生成することが できます。 歴史的な理由により、Emacsは文字を‘?’のコントロール文字として扱い ます: ?\^? ⇒ 127 ?\C-? ⇒ 127 結果として、Xでは有意な入力文字である‘Control-?’文字を、‘\C-’を使用して 表現することは今のところできません。さまざまなLispファイルがこの方法で を参照するので、これを変更するのは簡単ではないのです。 コントロール文字の表現はファイルや文字列内で見ることができますが、わ たしたちは‘^’構文を推奨します。キーボード入力にたいするコントロール文字 に好ましいのは、‘C-’構文です。どちらを使用するかはプログラムの意味に影響 しませんが、プログラムを読む人の理解を助けるでしょう。 2.3.3.4 メタ文字構文 .................... “メタ文字(meta character)”とは、修飾キーとともにタイプされた文字で す。そのような文字を表す整数には 2**27 のビットがセットされています。基 本的な文字コードの広い範囲を利用可能にするために、メタやその他の修飾にた いしては上位ビットを使用します。 文字列では、メタ文字を示すASCII文字に、 2**7 ビットが付加されます。し たがって文字列に含めることができるメタ文字のコードは1から255の範囲となり 、メタ文字は通常のASCII文字のメタ修飾されたバージョンとなります。文字列 内での処理の詳細については、*note Strings of Events::を参照してく ださい。 メタ文字の入力構文には‘\M-’を使用します。たとえば‘?\M-A’は‘M-A’を意味 します。8進文字コード(以下参照)や、‘\C-’、その他の文字にたいする他の構文 とともに‘\M-’を使用できます。したがって、‘M-A’は‘?\M-A’や‘?\M-\101’と記 述できます。同様に‘C-M-b’は‘?\M-\C-b’、‘?\C-\M-b’、‘?\M-\002’と記述する ことができます。 2.3.3.5 その他の文字修飾ビット .............................. グラフィック文字(graphic character)のcaseは文字コードで示されます。たと えばASCIIでは、文字‘a’と文字‘A’は区別されます。しかしASCIIにはコントロー ル文字が大文字なのか小文字なのかを表現する方法がありません。コントロール 文字がタイプされたときシフトキーが使用されたかを示すために、Emacsは 2**25 のビットを使用します。この区別はX端末や、その他の特別な端末を使用 しているときだけ可能です。通常のテキスト端末は、これらの違いを報告しませ ん。シフトをあらわすビットのためのLisp構文は‘\S-’です。したがって ‘?\C-\S-o’や‘?\C-\S-O’は、Shift+Ctrl+o文字を表します。 Xウィンドウシステムは文字にセットするために、他にも3つ修飾ビット — “ハ イパー(hyper)”、“スーパー(super)”、“アルト(alt)”を定義します。これらのビ ットにたいする構文は、‘\H-’、‘\s-’、‘\A-’です(これらのプレフィクスでは、 caseは意味がある)。したがって‘?\H-\M-\A-x’は‘Alt-Hyper-Meta-x’を表します (‘-’が後にない‘\s’はスペース文字を表すことに注意)。 数値としてはビット値 2**22はアルト、2**23はスーパー、2**24はハイパーです。 2.3.4 シンボル型 ---------------- GNU Emacs Lispでの“シンボル(symbol)”とは、名前をもつオブジェクトです。シ ンボル名は、そのシンボルのプリント表現としての役割があります。Lispの通常 の使用では、1つのobarray(*note Creating Symbols::を参照)により、シンボル 名は一意です — 2つのシンボルが同じ名前をもつことはありません。 シンボルは変数や関数名としての役割、プロパティーリストを保持する役割 をもつことができます。データ構造内にそのようなシンボルが存在することが確 実に認識できるように、他のすべてのLispオブジェクトから区別するためだけの 役割をもつ場合もあります。与えられたコンテキストにおいて、通常はこれらの うちの1つの使用だけが意図されます。しかし3つすべての方法で、1つのシンボ ルを独立して使用することもできます。 名前がコロン(‘:’)で始まるシンボルは“キーワードシンボル(keyword symbol)”と呼ばれます。これらのシンボルは自動的に定数として振る舞い、通常 は未知のシンボルといくつかの特定の候補を比較することだけに使用されます。 *note Constant Variables::を参照してください。 シンボル名にはどんな文字でも含めることができます。ほとんどのシンボル 名は英字、数字、‘-+=*/’などの区切り文字で記述されます。このような名前に は特別な区切り文字は必要ありません。名前が数字のように見えない限り、名前 にはどのような文字も使用できます(名前が数字のように見える場合は、名前の 先頭に‘\’を記述して強制的にシンボルとして解釈させる)。文字 ‘_~!@$%^&:<>{}?’はあまり使用されませんが、これらも特別な句読点文字を必要 としません。他の文字も、バックスラッシュでエスケープすることにより、シン ボル名に含めることができます。しかし文字列内でのバックスラッシュの使用と は対照的に、シンボル名でのバックスラッシュは、バックスラッシュの後の1文 字をエスケープするだけです。たとえば文字列内では、‘\t’はタブ文字を表しま す。しかしシンボル名の中では、‘\t’は英字‘t’をクォートするに過ぎません。 名前にタブ文字をもつシンボルを記述するには、(バックスラッシュを前置した )実際のタブを使用しなければなりません。しかし、そのようなことを行なうこ とは稀です。 Common Lispに関する注意:Common Lispでは明示的にエスケープされない限 り、小文字は常に大文字に“フォールド(folded)”される。Emacs Lispでは 大文字と小文字は区別される。 以下はシンボル名の例です。4つ目の例の中の‘+’は、シンボルが数字として 読み取られるのを防ぐためにエスケープされていることに注意してください。 6つ目の例では、名前の残りの部分により数字としては不正なのでエスケープの 必要はありません。 foo ; ‘foo’という名前のシンボル FOO ; ‘foo’とは別の、‘FOO’という名前のシンボル 1+ ; ‘1+’という名前のシンボル ; (整数の‘+1’ではない) \+1 ; ‘+1’という名前のシンボル ; (判読しにくい名前) \(*\ 1\ 2\) ; ‘(* 1 2)’という名前のシンボル(悪い名前) +-*/_~!@$%^&=:<>{} ; ‘+-*/_~!@$%^&=:<>{}’という名前のシンボル ; これらの文字のエスケープは不要 シンボル名がプリント表現としての役割をもつというルールの例外として、 ‘##’があります。これは、名前が空文字列のinternされたシンボルのプリント表 現です。さらに‘#:FOO’は、internされていないFOOという名前のシンボルにたい するプリント表現です(通常、Lispリーダーはすべてのシンボルをinternする。 *note Creating Symbols::を参照されたい)。 2.3.5 シーケンス型 ------------------ “シーケンス(sequence)”とは、要素の順序セットを表現するLispオブジェクトで す。Emacs Lispには2種類のシーケンス — “リスト(lists)”と“配列(arrays)”が あります。 リストはもっとも一般的に使用されるシーケンスです。リストは任意の型の 要素を保持でき、要素の追加と削除により簡単に長さを変更できます。リストに ついては、次のサブセクションを参照してください。 配列は固定長のシーケンスです。配列はさらに文字列(strings)、ベクター (vectors)、文字テーブル(char-tables)、ブールベクター(bool-vectors)に細分 されます。ベクターは任意の型の要素を保持できますが、文字列の要素は文字で なければならず、ブールベクターの要素は‘t’か‘nil’でなければなりません。文 字テーブルはベクターと似ていますが、有効な文字によりインデックスづけされ る点が異なります。文字列内の文字は、バッファー内の文字のようにテキストプ ロパティーをもつことができます(*note Text Properties::を参照)。しかしベ クターはその要素が文字のときでも、テキストプロパティーをサポートしません 。 リスト、文字列、およびその他の配列型も、重要な類似点を共有します。た とえば、それらはすべて長さLをもち、要素は0からL−1でインデックスづけされ ます。いくつかの関数はシーケンス関数と呼ばれ、これらは任意の種類のシーケ ンスを許容します。たとえば、関数‘length’は、任意の種類のシーケンスの長さ を報告します。*note Sequences Arrays Vectors::を参照してください。 シーケンスは読み取りにより常に新たに作成されるやめ、同じシーケンスを 2回読み取るのは一般的に不可能です。シーケンスにたいする入力構文を2回読み 取った場合には、内容が等しい2つのシーケンスを得ます。これには1つ例外があ ります。空リスト‘()’は、常に同じオブジェクト‘nil’を表します。 2.3.6 コンスセルとリスト型 -------------------------- “コンスセル(cons cell)”はCARスロット、CDRスロットと呼ばれる2つのスロット から構成されるオブジェクトです。それぞれのスロットはには、任意のLispオブ ジェクトを“保持”できます。そのときCARスロットに保持されるオブジェクトが 何であれ、わたしたちは“このコンスセルのCAR”のような言い方をします。これ はCDRの場合も同様です。 “リスト(list)”はコンスセルの連続するシリーズで、各コンスセルのCDRスロ ットは次のコンスセル、または空リストを保持します。空リストは実際にはシン ボル‘nil’です。詳細については、*note Lists::を参照してください。ほとんど のコンスセルはリストの一部として使用されるので、わたしたちはコンスセルに より構成される任意の構造を、“リスト構造(list structure)”という用語で参照 します。 Cプログラマーにたいする注意: Lispのリストはコンスセルにより構築され る“リンクリスト(linked list)”として機能する。Lispではポインターは暗 黙的なので、コンスセルのスロットが値を“保持(hold)”するのか、それと も値を“指す(point)”のかは区別しない。 コンスセルはLispの中核なので、コンスセルではないオブジェクトにたいす る用語もあります。これらのオブジェクトは“アトム(atoms)”と呼ばれます。 リストにたいする入力構文とプリント表現は同じで左カッコ、任意の数の要 素、右カコから構成されます。以下はリストの例です: (A 2 "A") ; 3要素のリスト () ; 要素がないリスト(空リスト) nil ; 要素がないリスト(空リスト) ("A ()") ; 1要素のリスト: 文字列‘"A ()"’ (A ()) ; 2要素のリスト: ‘A’と空リスト (A nil) ; 同上 ((A B C)) ; 1要素のリスト ; (この要素は、3要素のリスト) 読み取りではカッコの内側はリストの要素になります。つまりコンスセルは 各要素から作成されます。コンスセルのCARスロットは要素を保持し、CDRスロッ トはリスト内の次のコンスセル(このコンスセルはリスト内の次の要素をする)を 参照します。最後のコンスセルのCDRスロットは‘nil’を保持するようにセットさ れます。 CARやCDRという名称はLispの歴史に由来します。オリジナルのLisp実装は IBM 704コンピューターで実行されていました。ワードを2つの部分、つまり “address”と呼ばれる部分と、“decrement”と呼ばれる部分に分割していて、その 際CARはaddress部から内容を取り出す命令で、CDRはdecrement部から内容を取り 出す命令でした。これとは対照的に“cons cells”は、これらを作成する関数 ‘cons’から命名されました。この関数は関数の目的、すなわちセルを作る (construction of cells)という目的から命名されました。 2.3.6.1 ボックスダイアグラムとしてのリストの描写 ................................................ コンスセルを表現するドミノのような1対のボックスによる図で、リストを説明 することができます(Lispリーダーがこのような図を読み取ることはできない。 人間とコンピューターが理解できるテキスト表記と異なり、ボックス図は人間だ けが理解できる)。この図は3要素のリスト‘(rose violet buttercup)’を表した ものです: --- --- --- --- --- --- | | |--> | | |--> | | |--> nil --- --- --- --- --- --- | | | | | | --> rose --> violet --> buttercup この図では、ボックスは任意のLispオブジェクトへの参照を保持できるスロ ットを表します。ボックスのペアーはコンスセルを表します。矢印はLispオブジ ェクト(アトム、または他のコンスセル)への参照を表します。 この例では、1番目のボックスは1番目のコンスセルで、それのCARは ‘rose’(シンボル)を参照または保持します。2番目のボックスは1番目のコンスセ ルのCDRを保持し、次のボックスペアすなわち2番目のコンスセルを参照します。 2番目のコンスセルのCARは‘violet’で、CDRは3番目のコンスセルです。(最後の )3番目のコンスセルのCDRは‘nil’です。 同じリスト‘(rose violet buttercup)’を、違うやり方で描いた別の図で表し てみましょう: --------------- ---------------- ------------------- | car | cdr | | car | cdr | | car | cdr | | rose | o-------->| violet | o-------->| buttercup | nil | | | | | | | | | | --------------- ---------------- ------------------- 要素がないリストは“空リスト(empty list)”で、これはシンボル‘nil’と同じ です。言い換えると‘nil’はシンボルであり、かつリストでもあります。 以下はリスト‘(A ())’、または等価な‘(A nil)’をボックスと矢印で描いたも のです: --- --- --- --- | | |--> | | |--> nil --- --- --- --- | | | | --> A --> nil 以下はもっと複雑な例です。これは1番目の要素が2要素のリストであるよう な、3要素のリスト‘((pine needles) oak maple)’を表します: --- --- --- --- --- --- | | |--> | | |--> | | |--> nil --- --- --- --- --- --- | | | | | | | --> oak --> maple | | --- --- --- --- --> | | |--> | | |--> nil --- --- --- --- | | | | --> pine --> needles 同じリストを2番目のボックス表記で表すと、以下のようになります: -------------- -------------- -------------- | car | cdr | | car | cdr | | car | cdr | | o | o------->| oak | o------->| maple | nil | | | | | | | | | | | -- | --------- -------------- -------------- | | | -------------- ---------------- | | car | cdr | | car | cdr | ------>| pine | o------->| needles | nil | | | | | | | -------------- ---------------- 2.3.6.2 ドットペア表記 ...................... “ドットペア表記(dotted pair notation)”は、CARとCDRが明示的に表されたコン スセルの一般的な構文です。この構文では‘(A . B)’がCARがオブジェクトA、 CDRがオブジェクトBという意味になります。CDRがリストである必要がないので 、ドットペア表記はより一般的なリスト構文です。しかしリスト構文が機能する ような場合には、より扱いにくくなります。ドットペア表記では、リスト‘(1 2 3)’は‘(1 . (2 . (3 . nil)))’と記述されます。‘nil’で終端されたリストにた いしては、どちらの表記法も使用できますが、リスト表記の方が通常は明解で便 利です。リストをプリントする場合には、コンスセルのCDRがリストでないとき だけドットペア表記が使用されます。 以下はボックスを使用してドットペア表記を表した例です。これはペア ‘(rose . violet)’を表します: --- --- | | |--> violet --- --- | | --> rose 最後のCDRが非‘nil’のコンスセルのチェーンを表すので、ドットペア表記と リスト表記を組み合わせることができます。リストの最後の要素の後にドットを 記述して、その後に最後のコンスセルのCDRを記述します。たとえば‘(rose violet . buttercup)’は、‘(rose . (violet . buttercup))’と等価です。オブ ジェクトは以下のようになります: --- --- --- --- | | |--> | | |--> buttercup --- --- --- --- | | | | --> rose --> violet 構文‘(rose . violet . buttercup)’は無効です。なぜならこれは何も意味し ていないからです。何か意味があるとしても、‘violet’のためにCDRがすでに使 用されているコンスセルのCDRに、‘buttercup’を置くということになります。 リスト‘(rose violet)’は‘(rose . (violet))’と等価であり、以下のように なります: --- --- --- --- | | |--> | | |--> nil --- --- --- --- | | | | --> rose --> violet 同様に3要素のリスト‘(rose violet buttercup)’は、‘(rose . (violet . (buttercup)))’と等価です。 これは以下のようになります: --- --- --- --- --- --- | | |--> | | |--> | | |--> nil --- --- --- --- --- --- | | | | | | --> rose --> violet --> buttercup 2.3.6.3 連想リスト型 .................... “連想リスト(association list)”または“alist”は、要素がコンスセルであるよ うに特別に構成されたリストです。各要素においては、CARが“キー(key)”で、 CDRが“連想値(associated value)”であると考えます(連想値がCDRのCARに保存さ れる場合もある)。リストの先頭への連想値の追加と削除は簡単なので、連想リ ストはスタック(stack)にしばしば使用されます。 たとえば、 (setq alist-of-colors '((rose . red) (lily . white) (buttercup . yellow))) これは変数‘alist-of-colors’に3要素のalistをセットします。最初の要素では 、‘rose’がキーで‘red’が値になります。 alistとalist関数についての詳細な説明は*note Association Lists::を参照 してください。(多くのキーの操作をより高速に行なう)テーブルを照合する他の 手段については*note Hash Tables::を参照してください。 2.3.7 配列型 ------------ “配列(array)”は、他のLispオブジェクトを保持または参照する任意の数のスロ ットから構成され、メモリーの連続ブロックに配列されます。配列の任意の要素 へのアクセス時間は大体同じです。対照的にリストの要素にたいするアクセスは 、リスト内でのその要素の位置に比例した時間を要します(リストの最後の要素 にアクセスするにはリストの最初の要素にアクセスするより長い時間が必要)。 Emacsは文字列(strings)、ベクター(vectors)、ブールベクター (bool-vectors)、文字テーブル(char-tables)という4種の配列を定義します。 文字列は文字の配列であり、ベクターは任意のオブジェクトの配列です。ブ ールベクターは‘t’か‘nil’だけを保持できます。この種の配列は、もっとも大き い整数までの任意の長さをもつことができます。文字テーブルは、任意の有効な 文字コードによりインデックスづけされる疎な配列であり、任意のオブジェクト を保持することができます。 配列の最初の要素はインデックス0、2番目の要素はインデックス1、...とな ります。これは“0基準(zero-origin)”のインデックスづけと呼ばれます。たとえ ば、4要素の配列はインデックス0、1、2、3をもちます。利用できる最大のイン デックス値は、配列の長さより1つ小さくなります。▼一度配列が作成されると、 長さは固定されます。 Emacs Lispのすべての配列は、1次元です(他のほとんどのプログラミング言 語は多次元配列をサポートするが、これらは必須ではない。ネストされた1次元 配列により同じ効果を得ることが可能)。各種の配列は独自の入力構文をもちま す。詳細は以降のセクションを参照してください。 配列型はシーケンス型のサブセットであり文字列型、ベクター型、ブールベ クター型、文字テーブル型が含まれます。 2.3.8 文字列型 -------------- “文字列(string)”とは文字の配列です。Emacsがテキストエディターであること から予想できるように、文字列はたとえばLispシンボルの名前、ユーザーへのメ ッセージ、バッファーから抽出されたテキストの表現など多くの目的のために使 用されます。Lispの文字列は定数です。文字列を評価すると、それと同じ文字列 がリターンされます。 文字列を操作する関数については*note Strings and Characters::を参照し てください。 2.3.8.1 文字列の構文 .................... 文字列にたいする入力構文は‘"like this"’のようにダブルクォート、任意個数 の文字、もう1つのダブルクォートから構成されます。文字列内にダブルクォー トを含める場合は、それの前にバックスラッシュを記述します。したがって ‘"\""’は1つのダブルクォート文字だけを含む文字列です。同様にバックスラッ シュを含める場合は、‘"this \\ is a single embedded backslash"’のように、 それの前にもう1つのバックスラッシュを記述します。 文字列にたいする入力構文では、改行(newline)は特別ではありません。ダブ ルクォートの間に改行を記述すれば、その改行は文字列内の文字となります。し かしエスケープされた改行 — 前に‘\’をともなう改行 — は文字列の一部とはな りません。同様にエスケープされたスペース‘\ ’も無視されます。 "It is useful to include newlines in documentation strings, but the newline is \ ignored if escaped." ⇒ "It is useful to include newlines in documentation strings, but the newline is ignored if escaped." 2.3.8.2 文字列内の非ASCII文字 ............................. Emacdの文字列内の非ASCII文字にたいしては2つのテキスト表現 — マルチバイト (multibyte)とユニバイト(unibyte)があります(*note Text Representations::を 参照)。大まかに言うとユニバイト文字列にはraw(生)バイトが保存され、マルチ バイト文字列には人間が読めるテキストが保存されます。ユニバイト文字列内の 各文字はバイトであり、値は0から255となります。対照的にマルチバイト文字列 内の各文字は、0から4194303の値をもつかもしれません(*note Character Type::を参照)。いずれも127より上の文字は非ASCIIです。 文字をリテラルとして記述することにより、文字列に非ASCII文字を含めるこ とができます。マルチバイトのバッファーや文字列、あるいはマルチバイトとし てvisitされたファイル等、マルチバイトのソースから文字列定数を読み込む場 合、Emacsは非ASCII文字をマルチバイト文字として読み取り、その文字列を自動 的にマルチバイト文字列にします。ユニバイトのソースから文字列定数を読み込 む場合、Emacsは非ASCII文字をユニバイト文字として読み取り、その文字列をユ ニバイト文字列にします。 マルチバイト文字列内にリテラルとして文字を記述するかわりに、エスケー プシーケンスを使用して文字コードとして記述できます。エスケープシーケンス についての詳細は、*note General Escape Syntax::を参照してください。 文字列定数内でUnicodeスタイルのエスケープシーケンス‘\uNNNN’または ‘\U00NNNNNN’を使用する場合、(たとえASCII文字であっても)Emacsは自動的に文 字列をマルチバイトとみなします。 文字列定数内で16進エスケープシーケンス(‘\xN’)と8進エスケープシーケン ス(‘\N’)を使用することもできます。*しかし注意してください: *文字列定数が 16進または8進のエスケープシーケンスを含み、それらのエスケープシーケンス すべてがユニバイト文字(256より小)を指定していて、その文字列内に他にリテ ラルの非ASCII文字またはUnicodeスタイルのエスケープシーケンスが存在しない 場合、Emacsは自動的に文字列をユニバイト文字列とみなします。つまり文字列 内のすべての非ASCII文字は8ビットのrawバイトとみなされます。 16進および8進のエスケープシーケンスでは、エスケープされた文字コードに 可変個の数字が含まれるかもしれないので、それに続く文字で16進および8進と して有効ではない最初の文字は、そのエスケープシーケンスを終了させます。文 字列内の次の文字が16進または8進として解釈できる文字の場合は、‘\ ’(バッ クスラッシュとスペース)を記述して、エスケープシーケンスを終了できます。 たとえば‘\xe0\ ’はgrave accentつきの‘a’という1文字を表します。文字列内の ‘\ ’はバックスラッシュ改行と同様です。これは文字列内の文字とはなりませ んが、先行する16進エスケープを終了します。 2.3.8.3 文字列内の非プリント文字 ................................ リテラル文字と同様に、文字列定数内でバックスラッシュによるエスケープシー ケンスを使用できます(ただし文字定数を開始するクエスチョンマークは使用し ない)。たとえば非プリント文字のタブと‘C-a’を含む文字列は、‘"\t, \C-a"’の ように、それらの間にカンマとスペースを記述します。文字にたいする入力構文 については*note Character Type::を参照してください。 しかしバックスラッシュによるエスケープシーケンスとともに記述できるす べての文字が、文字列内で有効というわけではありません。文字列が保持できる コントロール文字はASCIIコントロール文字だけです。ASCIIコントロール文字で は、文字列のcaseは区別されません。 正確に言うと、文字列はメタ文字を保持できません。しかし文字列がキーシ ーケンスとして使用される場合には、文字列内でメタ修飾されたASCII文字を表 現するための方法を提供する特別な慣習があります。文字列定数内でメタ文字を 示すために‘\M-’構文を使用した場合、これは文字列内の文字の 2**7 のビット をセットします。その文字列が‘define-key’または‘lookup-key’で使用される場 合、この数字コードは等価なメタ文字に変換されます。*note Character Type::を参照してください。 文字列はハイパー(hyper)、スーパー(super)、アルト(alt)で修飾された文字 を保持できません。 2.3.8.4 文字列内のテキストプロパティ .................................... 文字列にはその文字自身に加えて、文字のプロパティーも保持することができま す。これにより特別なことをしなくても、文字列とバッファーとの間でテキスト をコピーするプログラムが、テキストプロパティーをコピーすることが可能にな ります。テキストプロパティーが何を意味するかについては*note Text Properties::を参照してください。テキストプロパティーをもつ文字列は、特別 な入力構文とプリント構文を使用します。 #("CHARACTERS" PROPERTY-DATA...) ここでPROPERTY-DATAは,3個でグループ化された0個以上の要素から構成されます : BEG END PLIST 要素BEGおよびENDは整数で、文字列内のインデックスの範囲を指定します。 PLISTはその範囲にたいするプロパティーリストです。たとえば、 #("foo bar" 0 3 (face bold) 3 4 nil 4 7 (face italic)) これはテキスト内容が‘foo bar’で、最初の3文字は‘face’プロパティーに値 ‘bold’をもち、最後の3文字は‘face’プロパティーに値‘italic’をもつことを表 します(4番目の文字にはテキストプロパティーはないので、プロパティーリスト は‘nil’。実際には範囲の中の指定されていない文字はデフォルトではプロパテ ィーをもたないので、範囲のプロパティーリストを‘nil’と指定する必要ない)。 2.3.9 ベクター型 ---------------- “ベクター(vector)”は任意の型の要素からなる1次元の配列です。ベクター内の 任意の要素へのアクセスに要す時間は一定です(リストの場合では要素へのアク セスに要す時間は、リストの先頭からその要素までの距離に比例する)。 ベクターのプリント表現は左角カッコ(left square bracket)、要素、右角カ ッコ(right square bracket)から構成されます。これは入力構文でもあります。 数字や文字列と同様にベクターは評価において定数と判断されます。 [1 "two" (three)] ; 3要素のベクター ⇒ [1 "two" (three)] ベクターに作用する関数については*note Vectors::を参照してください。 2.3.10 文字テーブル型 --------------------- “文字テーブル(char-table)”は任意の型の要素をもつ1次元の配列であり、文字 コードによりインデックスづけされます。文字テーブルは、文字コードに情報を 割り当てることを必要とする多くの処理を簡単にするための、特別な追加の機能 をもちます — たとえば文字テーブルは継承する親、デフォルト値、特別な目的 のために使用する余分なスロットをいくつかもつことができます。文字テーブル は文字セット全体にたいして1つの値を指定することもできます。 文字テーブルのプリント表現はベクターと似ていますが、最初に余分な ‘#^’があります(1)。 文字テーブルを操作する特別な関数については*note Char-Tables::を参照し てください。文字テーブルの使用には以下が含まれます: • caseテーブル(*note Case Tables::を参照)。 • 文字カテゴリーテーブル(*note Categories::を参照)。 • ディスプレーテーブル(*note Display Tables::を参照)。 • 構文テーブル(*note Syntax Tables::を参照)。 ---------- Footnotes ---------- (1) 副文字テーブル(sub-char-tables)に使用される‘#^^’を目にすることが あるかもしれません。 2.3.11 ブールベクター型 ----------------------- “ブールベクター(bool-vector)”は、要素が‘t’か‘nil’のいずれかでなければな らない1次元の配列です。 ブールベクターのプリント表現は文字列と似ていますが、後に長さを記述し た‘#&’で始まります。これに続く文字列定数は、ビットマップとして実際に内容 を指定するブールベクターです — 文字列定数内のそれぞれの“文字”は8ビットを 含み、これはブールベクターの次の8要素を指定します(1は‘t’、0は‘nil’です )。文字の最下位ビットブールベクターの最下位のインデックスに対応します。 (make-bool-vector 3 t) ⇒ #&3"^G" (make-bool-vector 3 nil) ⇒ #&3"^@" ‘C-g’の2進コードは111、‘C-@’はコード0の文字なのでこの結果は理にかなって います。 長さが8の倍数でなければプリント表現には余分な要素が表示されますが、こ れらの余分な要素に意味はありません。たとえば以下の例では、最初の3ビット だけが使用されるので2つのブールベクターは等価です: (equal #&3"\377" #&3"\007") ⇒ t 2.3.12 ハッシュテーブル型 ------------------------- ハッシュテーブルは非常に高速な照合テーブルの一種で、キーを対応する値にマ ップするalistと似ていますがより高速です。ハッシュテーブルのプリント表現 では、以下のようにハッシュテーブルのプロパティーと内容を指定します: (make-hash-table) ⇒ #s(hash-table size 65 test eql rehash-size 1.5 rehash-threshold 0.8 data ()) ハッシュテーブルについての詳細は*note Hash Tables::を参照してください。 2.3.13 関数型 ------------- 他のプログラミング言語の関数と同様、Lisp関数は実行可能なコードです。他の 言語と異なり、Lispの関数はLispオブジェクトでもあります。Lispのコンパイル されていない関数はラムダ式 — つまり1番目の要素がシンボル‘lambda’であるリ ストです(*note Lambda Expressions::を参照)。 ほとんどのプログラミング言語では名前のない関数はありません。Lispでは 関数に本質的な名前はありません。名前がなくてもラムダ式を関数として呼び出 すことができます。これを強調するために、わたしたちはこれを“無名関数 (anonymous function)”とも呼びます(*note Anonymous Functions::を参照)。 Lispの名前つき関数は関数セルに有効な関数がセットされた単なるシンボルです (*note Defining Functions::を参照)。 ほとんどの場合、関数はLispプログラム内のLisp式の名前が記述されたとこ ろで呼び出されます。しかし実行時に関数オブジェクトを構築または取得してか ら、プリミティブ関数‘funcall’および‘apply’により呼び出すことができます。 *note Calling Functions::を参照してください。 2.3.14 マクロ型 --------------- “Lispマクロ(Lisp macro)”はLisp言語を拡張するユーザー定義の構成です。これ はオブジェクトとしてではなく関数のように表現されますが、引数の渡し方の意 味が異なります。Lispマクロの形式はリストです。これは最初の要素が ‘macro’で、CDRがLisp関数オブジェクト(‘lambda’シンボルを含む)であるような リストです。 Lispマクロオブジェクトは通常、ビルトインの‘defmacro’関数で定義されま すが、‘macro’で始まる任意のリストもEmacsにとってはマクロです。マクロを記 述する方法の説明は*note Macros::を参照してください。 *警告*: Lispマクロとキーボードマクロ(*note Keyboard Macros::を参照)は 完全に別の物である。修飾なしで“マクロ”という単語を使用したときは、キーボ ードマクロではなくLispマクロのことを指す。 2.3.15 プリミティブ関数型 ------------------------- “プリミティブ関数(primitive function)”とは、Cプログラミング言語で記述さ れたLispから呼び出せる関数です。プリミティブ関数は“subrs”や“ビルトイン関 数(built-in functions)”とも呼ばれます(単語“subr”は“サブルーチン (subroutine)”が由来)。ほとんどのプリミティブ関数ハ、呼び出されたときニす べての引数を評価します。すべての引数を評価しないプリミティブ関数は“スペ シャルフォーム(special form)”と呼ばれます(*note Special Forms::を参照)。 呼び出す側からすれば、その関数がプリミティブ関数かどうかは問題になり ません。しかしプリミティブ関数をLispで記述された関数で再定義した場合に問 題になります。理由はそのプリミティブ関数がCコードから直接呼び出されてい るかもしれないからです。Lispから再定義した関数を呼び出すと新しい定義を使 用するでしょうが、Cコードから呼び出すとビルトインの定義が使用されるでし ょう。したがって、*プリミティブ関数の再定義はしないでください*。 “関数(function)”という用語で、LispやCで記述されたすべてのEmacs関数を 参照します。Lispで記述された関数についての情報は*note Function Type::を 参照してください。 プリミティブ関数に入力構文はなく、サブルーチン名とともにハッシュ表記 でプリントします。 (symbol-function 'car) ; そのシンボルの関数セルに ; アクセスする ⇒ # (subrp (symbol-function 'car)) ; これはプリミティブ関数? ⇒ t ; そのとおり 2.3.16 バイトコード関数型 ------------------------- “バイトコード関数オブジェクト(byte-code function objects)”は、Lispコード をバイトコンパイルすることにより生成されます(*note Byte Compilation::を 参照)。バイトコード関数オブジェクトは、内部的にはベクターによく似ていま す。しかしバイトコード関数オブジェクトが関数呼び出しのように見える場合、 評価プロセスによりこのデータ型は特別に処理されます。*note Byte-Code Objects::を参照してください。 バイトコード関数オブジェクトのプリント表現と入力構文はベクターのもの と似ていますが、開き角カッコ‘[’の前に‘#’があります。 2.3.17 autoload型 ----------------- “autoloadオブジェクト(autoload object)”は、最初の要素がシンボル ‘autoload’のリストです。これはシンボルの関数定義として保存され、実際の定 義にたいする代替としての役割をもちます。autoloadオブジェクトは、必要な時 にロードされるLispコードファイルの中で実際の定義を見つけることができるこ とを宣言します。これにはファイル名と、それに加えて実際の定義についての他 のいくつかの情報が含まれます。 ファイルのロード後、そのシンボルはautoloadオブジェクトではない新しい 関数定義をもつはずです。新しい定義は、最初からそこにあったかのように呼び 出されます。ユーザーの観点からは関数呼び出しは期待された動作、つまりロー ドされたファイル内の関数定義を使用します。 autoloadオブジェクトは通常、シンボルの関数セルにオブジェクトを保存す る関数‘autoload’により作成されます。詳細は*note Autoload::を参照してくだ さい。 2.3.18 Finalizer Type --------------------- “ファイナライザーオブジェクト(finalizer object)”は、オブジェクトがもはや 必要なくなった後のLispコードのクリーンアップを助けます。ファイナライザー は、Lisp関数オブジェクトを保持します。ガーベージコレクションのオアス後に ファイナライザーオブジェクトが到達不能になったとき、Emacsはそのファイナ ライザーに関連付けられた関数オブジェクトを呼び出します。ファイナライザー の到達可否の判定時、もしかしてファイナライザーオブジェクト自身が参照を離 さないのではないかと心配することなくファイナライザーを使用できるように、 Emacsはファイナラーオブジェト自身からの参照は勘定しません。 ファイナラーザー内でのエラーは‘*Messages*’にプリントされます。その関 数が失敗しても、Emacsは与えられたファイナライザーオブジェクトに関連付け られた関数を正確に1回実行します。 -- Function: make-finalizer function FUNCTIONを実行するファイナライザーを作成する。FUNCTIONはガーベージ コレクション後、リターンされたファイナライザーオブジェクトが到達不 能になったときに実行される。そのファイナライザーオブジェクトがファ イナライザーオブジェクトからの参照を通じてのみ到達可能なら、 FUNCTIONの実行是非の判断時の目的にたいして、それは到達可能とみなさ れない。FUNCTIONはファイナライザーオブジェクトごとに1回実行される。 2.4 編集用の型 ============== 前セクションの型は一般的なプログラミング目的のために使用され、これらの型 のほとんどはLisp方言のほとんどで一般的です。Emacs Lispは編集に関する目的 のために、いくつかの追加のデータ型を提供します。 2.4.1 バッファー型 ------------------ “バッファー(buffer)”とは、編集されるテキストを保持するオブジェクトです (*note Buffers::を参照)。ほとんどのバッファーはディスクファイル(*note Files::を参照)の内容を保持するので編集できますが、他の目的のために使用さ れるものもいくつかあります。ほとんどのバッファーはユーザーにより閲覧され ることも意図しているので、いつかはウィンドウ内(*note Windows::を参照)に 表示されます。しかしバッファーはウィンドウに表示される必要はありません。 バッファーはそれぞれ、“ポイント(point)”と呼ばれる位置指定をもちます (*note Positions::を参照)。ほとんどの編集コマンドは、カレントバッファー 内のポイントに隣接する内容を処理します。常に1つのバッファーが“カレントバ ッファー(current buffer)”です。 バッファーの内容は文字列によく似ていますが、バッファーはEmacs Lispの 文字列と同じようには使用されず、利用可能な操作は異なります。文字列にテキ ストを挿入するためには部分文字列の結合が必要で、結果は完全に新しい文字列 オブジェクトなのるのにたいして、バッファーでは既存のバッファーに効率的に テキストを挿入してバッファーの内容を変更できます。 標準的なEmacs関数の多くは、カレントバッファー内の文字を操作したりテス トするためのものです。このマニュアルはこれらの関数の説明のために、1つの チャプターを設けています(*note Text::を参照)。 他のデータ構造のいくつかは、各バッファーに関連付けられています: • ローカル構文テーブル(*note Syntax Tables::を参照)。 • ローカルキーマップ(*note Keymaps::を参照)。 • バッファーローカルな変数バインディングのリスト(*note Buffer-Local Variables::を参照)。 • オーバーレイ(*note Overlays::を参照)。 • バッファー内のテキストにたいするテキストプロパティー(*note Text Properties::を参照)。 ローカルキーマップと変数リストは、グローバルなバインディングや値を個別に オーバーライドするためのエントリーを含みます。これらは実際にプログラムを 変更することなく、異なるバッファーでプログラムの振る舞いをカスタマイズす るために使用されます。 バッファーは“インダイレクト(indirect: 間接)” — つまり他のバッファーと テキストを共有するがそれぞれ別に表示する — かもしれません。*note Indirect Buffers::を参照してください。 バッファーに入力構文はありません。バッファーはバッファー名を含むハッ シュ表記でプリントされます。 (current-buffer) ⇒ # 2.4.2 マーカー型 ---------------- “マーカー(marker)”は特定のバッファー内の位置を表します。したがってマーカ ーには2つの内容 — 1つはバッファー、もう1つは位置 — をもちます。バッファ ーのテキストの変更では、マーカーが常にバッファー内の同じ2つの文字の間に 位置することを確実にするために、必要に応じて自動的に位置の値が再配置され ます。 マーカーは入力構文をもちません。マーカーはカレントの文字位置とそのバ ッファー名を与える、ハッシュ表記でプリントされます。 (point-marker) ⇒ # マーカーのテスト、作成、コピー、移動の方法についての情報は*note Markers::を参照してください。 2.4.3 ウィンドウ型 ------------------ “ウィンドウ(window)”はEmacsがバッファーを表示するために使用する端末スク リーンの部分を記述します。すべてのウィンドウは関連付けられた1つのバッフ ァーをもち、バッファーの内容はそのウィンドウに表示されます。それとは対照 的に、あるバッファーは1つのウィンドウに表示されるか表示されないか、それ とも複数のウィンドウに表示されるかもしれません。 同時に複数のウィンドウが存在するかもしれませんが、常に1つのウィンドウ が“選択されたウィンドウ(selected window)”になります。Emacsがコマンドにた いして準備できているときは、(通常は)カーソルが表示されるウィンドウが選択 されたウィンドウです。選択されたウィンドウは、通常はカレントバッファー (*note Current Buffer::を参照)を表示しますがこれは必須ではありません。 スクリーン上でウィンドウはフレームにグループ化されます。ウィンドウは それぞれ、ただ1つのフレームだけに属します。*note Frame Type::を参照して ください。 ウィンドウは入力構文をもちません。ウィンドウはウィンドウ番号と表示さ れているバッファー名を与える、ハッシュ表記でプリントされます。与えられた ウィンドウに表示されるバッファーは頻繁に変更されるかもしれないので、一意 にウィンドウを識別するためにウィンドウ番号が存在します。 (selected-window) ⇒ # ウィンドウに作用する関数の説明は*note Windows::を参照してください。 2.4.4 フレーム型 ---------------- “フレーム(frame)”とは1つ以上のEmacsウィンドウを含むスクリーン領域です。 スクリーン領域を参照するためにEmacsが使用するLispオブジェクトを指す場合 にも“フレーム”という用語を使用します。 フレームは入力構文をもちません。フレームはフレームのタイトルとメモリ ー内のアドレス(フレームを一意に識別するのに有用)を与えるハッシュ表記でプ リントされます。 (selected-frame) ⇒ # フレームに作用する関数の説明は*note Frames::を参照してください。 2.4.5 端末型 ------------ “端末(terminal)”は1つ以上のEmacsフレーム(*note Frame Type::を参照)を表示 する能力があるデバイスです。 端末は入力構文をもちません。端末はその端末の順序番号とTTYデバイスファ イル名を与える、ハッシュ表記でプリントされます。 (get-device-terminal nil) ⇒ # 2.4.6 ウィンドウ構成型 ---------------------- “ウィンドウ構成(window configuration)”はフレーム内のウィンドウの位置とサ イズ、内容についての情報を保持します。これにより後で同じウィンドウ配置を 再作成できます。 ウィンドウ構成は入力構文をもちません。ウィンドウ構成のプリント表現は ‘#’のようになります。ウィンドウ構成に関連するいく つかの関数の説明は*note Window Configurations::を参照してください。 2.4.7 フレーム構成型 -------------------- “フレーム構成(frame configuration)”はすべてのフレーム内のウィンドウの位 置とサイズ、内容についての情報を保持します。これは基本型ではありません — 実際のところ、これはCARが‘frame-configuration’でCDRがalistであるようなリ ストです。それぞれのalist要素は、その要素のCARに示される1つのフレームを 記述します。 フレーム構成に関連するいくつかの関数の説明は*note Frame Configurations::を参照してください。 2.4.8 プロセス型 ---------------- “プロセス(process)”という単語は、通常は実行中のプログラムを意味します。 Emacs自身はこの種のプロセス内で実行されます。しかしEmacs Lispでは、プロ セスとはEmacsプロセスにより作成されたサブプロセスを表すLispオブジェクト です。シェル、GDB、ftp、コンパイラーなどのプログラムは、Emacsのサブプロ セスとして実行されEmacsの能力を拡張します。さらに操作を行なうために、 EmacsサブプロセスはEmacsからテキスト入力を受け取り、テキスト出力を Emacsにリターンします。Emacsがサブプロセスにシグナルを送ることもできます 。 プロセスオブジェクトは入力構文をもちません。プロセスオブジェクトはプ ロセス名を与えるハッシュ表記でプリントされます。 (process-list) ⇒ (#) プロセスの作成、削除、プロセスに関する情報のリターン、入力やシグナル の送信、出力の受信を行なう関数についての情報は*note Processes::を参照し てください。 2.4.9 ストリーム型 ------------------ “ストリーム(stream)”とは、文字のソースまたはシンクとして — つまり入力と して文字を供給したり、出力として文字を受け入れるために使用できるオブジェ クトです。多くの異なるタイプ — マーカー、バッファー、文字列、関数をこの 方法で使用できます。ほとんどの場合、入力ストリーム(文字列ソース)はキーボ ード、バッファー、ファイルから文字を受け取り、出力ストリーム(文字シンク )は文字を‘*Help*’バッファーのようなバッファーやエコーエリアに文字を送り ます。 オブジェクト‘nil’は、他の意味に加えてストリームとして使用されることが あります。‘nil’は変数‘standard-input’や‘standard-output’の値を表します。 オブジェクト‘t’も入力としてミニバッファー(*note Minibuffers::を参照)、出 力としてエコーエリア(*note The Echo Area::を参照)の使用を指定するストリ ームになります。 ストリームは特別なプリント表現や入力構文をもたず、それが何であれそれ らの基本型としてプリントされます。 パース関数およびプリント関数を含む、ストリームに関連した関数の説明は *note Read and Print::を参照してください。 2.4.10 キーマップ型 ------------------- “キーマップ(keymap)”はユーザーがタイプした文字をコマンドにマップします。 このマップはユーザーのコマンド入力が実行される方法を制御します。キーマッ プは、実際にはCARがシンボル‘keymap’であるようなリストです。 キーマップの作成、プレフィクスキーの処理、ローカルキーマップやグロー バルキーマップ、キーバインドの変更についての情報は*note Keymaps::を参照 してください。 2.4.11 オーバーレイ型 --------------------- “オーバーレイ(overlay)”はバッファーの一部に適用するプロパティーを指定し ます。それぞれのオーバーレイはバッファーの指定された範囲に適用され、プロ パティーリスト(プロパティー名と値が交互に記述された要素のリスト)を含みま す。オーバーレイプロパティーは、バッファーの指定された一部を、一時的に異 なるスタイルで表示するために使用されます。オーバーレイは入力構文をもたず 、バッファー名と範囲の位置を与えるハッシュ表記でプリントされます。 オーバーレイを作成したり使用する方法についての情報は*note Overlays::を 参照してください。 2.4.12 フォント型 ----------------- “font”はグラフィカルな端末上のテキストを表示する方法を指定します。実際に は異なる3つのフォント型 — “フォントオブジェクト(font objects)”、“フォン トスペック(font specs)”、“フォントエンティティー(font entities)” — が存 在します。これらは入力構文をもちません。これらのプリント構文は ‘#’、‘#’、‘#’のようになります。これ らのLispオブジェクトの説明は*note Low-Level Font::を参照してください。 2.5 循環オブジェクトの読み取り構文 ================================== 複雑なLispオブジェクトでの共有された構造や循環する構造を表すために、リー ダー構成‘#N=’と‘#N#’を使用することができます。 後でオブジェクトを参照するには、オブジェクトの前で‘#N=’を使用します。 その後で、他の場所にある同じオブジェクトを参照するために、‘#N#’を使用す ることができます。ここでNは任意の整数です。たとえば以下は、1番目の要素が 3番目の要素にも繰り替えされるリストを作成する方法です: (#1=(a) b #1#) これは、以下のような通常の構文とは異なります ((a) b (a)) これは1番目の要素と3番目の要素がそっくりなリストですが、これらは同じ Lispオブジェクトではありません。以下で違いを見ることができます: (prog1 nil (setq x '(#1=(a) b #1#))) (eq (nth 0 x) (nth 2 x)) ⇒ t (setq x '((a) b (a))) (eq (nth 0 x) (nth 2 x)) ⇒ nil “要素”として自身を含むような循環する構造を作成するために、同じ構文を 使用できます。以下は例です: #1=(a #1#) これは2番目の要素がそのリスト自身であるリストを作成します。これが実際に うまくいくのか、以下で確認できます: (prog1 nil (setq x '#1=(a #1#))) (eq x (cadr x)) ⇒ t 変数‘print-circle’を非‘nil’値にバインドした場合、Lispプリンターは、循 環および共有されるLispオブジェクトを記録するこの構文を生成することができ ます。*note Output Variables::を参照してください。 2.6 型のための述語 ================== 関数が呼び出されたとき、Emacs Lispインタープリター自身はその関数に渡され た実際の引数の型チェックは行ないません。それが行なえないのは、Lispにおけ る関数の引数は他のプログラミング言語のようなデータ型宣言をもたないからで す。したがって実際の引数が、その関数が使用できる型に属するかどうかをテス トするのは、それぞれの関数に任されています。 すべてのビルトイン関数は適切なときに実際の引数の型チェックを行い、引 数の型が違う場合は‘wrong-type-argument’エラーをシグナルします。たとえば 以下は、‘+’の引数に‘+’が扱うことができない引数を渡したとき何が起こるかの 例です: (+ 2 'a) error→ Wrong type argument: number-or-marker-p, a 異なる型にたいして異なる処理をプログラムに行なわせる場合は、明示的に 型チェックを行なわなければなりません。オブジェクトの型をチェックするもっ とも一般的な方法は“型述語(type predicate)”関数の呼び出しです。Emacsはそ れぞれの型にたいする型述語をもち、組み合わされた型にたいする述語もありま す。 型述語関数は1つの引数をとり、その引数が適切な型であれば‘t’、そうでな ければ‘nil’をリターンします。述語関数にたいする一般的なLisp慣習にしたが い、ほとんどの型述語の名前は‘p’で終わります。 以下はリストにたいしてチェックを行なう述語‘listp’と、シンボルにたいし てチェックを行なう述語‘symbolp’の例です。 (defun add-on (x) (cond ((symbolp x) ;; XがシンボルならLISTにputする (setq list (cons x list))) ((listp x) ;; Xがリストならその要素をLISTに追加 (setq list (append x list))) (t ;; シンボルとリストだけを処理する (error "Invalid argument %s in add-on" x)))) 以下のテーブルは事前定義された型述語(アルファベット順)と、さらに情報 を得るためのリファレンスです。 ‘atom’ *note atom: List-related Predicates.を参照のこと。 ‘arrayp’ *note arrayp: Array Functions.を参照のこと。 ‘bool-vector-p’ *note bool-vector-p: Bool-Vectors.を参照のこと。 ‘bufferp’ *note bufferp: Buffer Basics.を参照のこと。 ‘byte-code-function-p’ *note byte-code-function-p: Byte-Code Type.を参照のこと。 ‘case-table-p’ *note case-table-p: Case Tables.を参照のこと。 ‘char-or-string-p’ *note char-or-string-p: Predicates for Strings.を参照のこと。 ‘char-table-p’ *note char-table-p: Char-Tables.を参照のこと。 ‘commandp’ *note commandp: Interactive Call.を参照のこと。 ‘consp’ *note consp: List-related Predicates.を参照のこと。 ‘custom-variable-p’ *note custom-variable-p: Variable Definitions.を参照のこと。 ‘floatp’ *note floatp: Predicates on Numbers.を参照のこと。 ‘fontp’ *note Low-Level Font::を参照のこと。 ‘frame-configuration-p’ *note frame-configuration-p: Frame Configurations.を参照のこと。 ‘frame-live-p’ *note frame-live-p: Deleting Frames.を参照のこと。 ‘framep’ *note framep: Frames.を参照のこと。 ‘functionp’ *note functionp: Functions.を参照のこと。 ‘hash-table-p’ *note hash-table-p: Other Hash.を参照のこと。 ‘integer-or-marker-p’ *note integer-or-marker-p: Predicates on Markers.を参照のこと。 ‘integerp’ *note integerp: Predicates on Numbers.を参照のこと。 ‘keymapp’ *note keymapp: Creating Keymaps.を参照のこと。 ‘keywordp’ *note Constant Variables::を参照のこと。 ‘listp’ *note listp: List-related Predicates.を参照のこと。 ‘markerp’ *note markerp: Predicates on Markers.を参照のこと。 ‘wholenump’ *note wholenump: Predicates on Numbers.を参照のこと。 ‘nlistp’ *note nlistp: List-related Predicates.を参照のこと。 ‘numberp’ *note numberp: Predicates on Numbers.を参照のこと。 ‘number-or-marker-p’ *note number-or-marker-p: Predicates on Markers.を参照のこと。 ‘overlayp’ *note overlayp: Overlays.を参照のこと。 ‘processp’ *note processp: Processes.を参照のこと。 ‘sequencep’ *note sequencep: Sequence Functions.を参照のこと。 ‘stringp’ *note stringp: Predicates for Strings.を参照のこと。 ‘subrp’ *note subrp: Function Cells.を参照のこと。 ‘symbolp’ *note symbolp: Symbols.を参照のこと。 ‘syntax-table-p’ *note syntax-table-p: Syntax Tables.を参照のこと。 ‘vectorp’ *note vectorp: Vectors.を参照のこと。 ‘window-configuration-p’ *note window-configuration-p: Window Configurations.を参照のこと。 ‘window-live-p’ *note window-live-p: Deleting Windows.を参照のこと。 ‘windowp’ *note windowp: Basic Windows.を参照のこと。 ‘booleanp’ *note booleanp: nil and t.を参照のこと。 ‘string-or-null-p’ *note string-or-null-p: Predicates for Strings.を参照のこと。 あるオブジェクトがどの型かチェックするもっとも一般的な方法は、関数 ‘type-of’の呼び出しです。オブジェクトは、ただ1つだけの基本型に属すること を思い出してください。‘type-of’は、それがどの型かを告げます(*note Lisp Data Types::を参照)。しかし‘type-of’は基本型以外の型については何も知りま せん。ほとんどの場合では、‘type-of’より型述語を使用するほうが便利でしょ う。 -- Function: type-of object この関数はOBJECTの基本型を名前とするシンボルをリターンする。リター ン値はシンボル‘bool-vector’、‘buffer’、‘char-table’、 ‘compiled-function’、‘cons’、‘finalizer’、‘float’、‘font-entity’、 ‘font-object’、‘font-spec’、‘frame’、‘hash-table’、‘integer’、 ‘marker’、‘overlay’、‘process’、‘string’、‘subr’、‘symbol’、 ‘vector’、‘window’、‘window-configuration’のいずれか。 (type-of 1) ⇒ integer (type-of 'nil) ⇒ symbol (type-of '()) ; ‘()’は‘nil’です。 ⇒ symbol (type-of '(x)) ⇒ cons 2.7 同等性のための述語 ====================== ここでは2つのオブジェクトの同一性をテストする関数を説明します。(たとえば 文字列などの)特定の型のオブジェクト同士で内容の同一性をテストするのは、 別の関数を使用します。これらの述語にたいしては、そのデータ型を説明する適 切なチャプターを参照してください。 -- Function: eq object1 object2 この関数はOBJECT1とOBJECT2が同じオブジェクトなら‘t’、それ以外は ‘nil’をリターンする。 OBJECT1とOBJECT2が同じ値をもつ整数なら、これらは同じオブジェクトと 判断される(‘eq’は‘t’をリターンする)。OBJECT1とOBJECT2が同じ名前のシ ンボルなら、通常は同じオブジェクトであるが例外もある。*note Creating Symbols::を参照のこと。(リストやベクター、文字列などの)他 の型にたいしては、同じ内容(または要素)の2つの引数が両者‘eq’である必 要はない。これらが同じオブジェクトの場合だけ‘eq’であり、その場合は 一方の内容を変更するともう一方の内容にも同じ変更が反映される。 (eq 'foo 'foo) ⇒ t (eq 456 456) ⇒ t (eq "asdf" "asdf") ⇒ nil (eq "" "") ⇒ t ;; この例外は省スペースのためにEmacs Lispが ;; マルチバイトの空文字列を1つだけ作成するため (eq '(1 (2 (3))) '(1 (2 (3)))) ⇒ nil (setq foo '(1 (2 (3)))) ⇒ (1 (2 (3))) (eq foo foo) ⇒ t (eq foo '(1 (2 (3)))) ⇒ nil (eq [(1 2) 3] [(1 2) 3]) ⇒ nil (eq (point-marker) (point-marker)) ⇒ nil ‘make-symbol’関数はinternされていないシンボルをリターンする。これは Lisp式内でその名前を記述したシンボルとは区別される。同じ名前の異な るシンボルは‘eq’ではない。*note Creating Symbols::を参照のこと。 (eq (make-symbol "foo") 'foo) ⇒ nil -- Function: equal object1 object2 この関数はOBJECT1とOBJECT2が同じ構成要素をもつなら‘t’、それ以外は ‘nil’をリターンする。‘eq’が引数が同じオブジェクトなのかテストするの にたいして、‘equal’は同一でない引数の内部を調べて、それらの要素また は内容が同一化をテストする。したがって2つのオブジェクトが‘eq’ならば それらは‘equal’だが、その逆は常に真ではない。 (equal 'foo 'foo) ⇒ t (equal 456 456) ⇒ t (equal "asdf" "asdf") ⇒ t (eq "asdf" "asdf") ⇒ nil (equal '(1 (2 (3))) '(1 (2 (3)))) ⇒ t (eq '(1 (2 (3))) '(1 (2 (3)))) ⇒ nil (equal [(1 2) 3] [(1 2) 3]) ⇒ t (eq [(1 2) 3] [(1 2) 3]) ⇒ nil (equal (point-marker) (point-marker)) ⇒ t (eq (point-marker) (point-marker)) ⇒ nil 文字列の比較はcaseを区別するがテキストプロパティーは考慮しない — こ れは文字列内の文字だけを比較する。*note Text Properties::を参照のこ と。テキストプロパティーも比較する場合には、 ‘equal-including-properties’を使用すること。技術的な理由によりユニ バイト文字列とマルチバイト文字列は、それらが同じ文字シーケンスを含 みすべてのコードが0から127(ASCII)、または160から255(‘8ビットグラフ ィック’)の場合に限り‘equal’となる(*note Text Representations::を参 照)。 (equal "asdf" "ASDF") ⇒ nil しかし2つの別のバッファーは、それらのテキスト内容が同じでも ‘equal’と判断されることはない。 ‘equal’のテストは再帰的に実装されています。たとえば2つのコンスセルXと Yを与えると、‘(equal X Y)’は、以下の式の両方が‘t’をリターンする場合だけ ‘t’をリターンします: (equal (car X) (car Y)) (equal (cdr X) (cdr Y)) これは再帰処理なので循環するリストがあると無限再帰となる(エラーとなる )。 -- Function: equal-including-properties object1 object2 この関数はすべてのケースにおいて‘equal’と同様に振る舞うが、2つの文 字列が‘equal’になるためには、それらが同じテキストプロパティーをもつ 必要がある。 (equal "asdf" (propertize "asdf" 'asdf t)) ⇒ t (equal-including-properties "asdf" (propertize "asdf" 'asdf t)) ⇒ nil 3 数値 ****** GNU Emacsは2つの数値データ型 — “整数(integers)”と“浮動小数点数 (floating-point numbers)”をサポートします。整数は−3、0、7、13、511などの 整数です。浮動小数点数は−4.5、0.0、2.71828などの小数部をもちます。これら は指数記数法でも表現できます — ‘1.5e2’は‘150.0’と同じです。ここで‘e2’は 10の2乗を表し、それに1.5を乗じるという意味です。整数計算はオーバーフロー するときもありますが正確です。浮動小数点数の計算にでは、数値は固定された 精度をもつので、しばしば丸め誤差(rounding errors)が発生します。 3.1 整数の基礎 ============== 整数の値の範囲はマシンに依存します。最小の範囲は−536,870,912から 536,870,911(30ビット長の −2**29 から 2**29 − 1) ですが、多くのマシンはこ れより広い範囲を提供します。このチャプターの例の多くは、最小の整数が30ビ ット長であると仮定します。 Lispリーダーは、数字のシーケンス(オプションで最初の符号記号と最後のピ リオドをともなう)として整数を読み取ります。Emacsの範囲を超える整数は浮動 小数点数として扱われます。 1 ; 整数1 1. ; 整数1 +1 ; これも整数1 -1 ; 整数−1 9000000000000000000 ; 浮動小数点数9e18 0 ; 整数0 -0 ; 整数0 基数が10以外の整数の構文では‘#’の後に基数を指定する文字 — 2進は‘b’、 8進は‘o’、16進は‘x’、‘RADIXr’は基数RADIX — を記述します。基数を指定する 文字のcaseは区別されません。したがって‘#bINTEGER’はINTEGERを2進として読 み取り、‘#RADIXrINTEGER’はINTEGERを基数RADIXとして読み取ります。RADIXに 指定できる値は2から36です。たとえば: #b101100 ⇒ 44 #o54 ⇒ 44 #x2c ⇒ 44 #24r1k ⇒ 44 整数にたいして処理を行なうさまざまな関数、特にビット演算(*note Bitwise Operations::を参照)を理解するためには、数を2進形式で見ることが助 けになることがよくあります。 30ビットの2進では10進数の整数5は以下のようになります: 0000...000101 (全部で30ビット) (‘...’は30ビットのワードを満たすのに充分なビットを意味しており、この場合 の‘...’は12個の0ビットを意味する。以下の例でも2進の整数を読みやすくする ために、‘...’の表記を使用している。) 整数の−1は以下のようになります: 1111...111111 (全部で30ビット) −1は30個の1で表現されます(“2の補数”表記と呼ばれる)。 −1から4を減じることで負の整数−5が得られます。10進の整数4は2進では 100です。したがって−5は以下のようになります: 1111...111011 (全部で30ビット) この実装では、0ビットの2進の最大は10進の536,870,911です。これは2進で は以下のようになります: 0111...111111 (全部で30ビット) 算術関数は整数が範囲外かどうかをチェックしないので、536,870,911に1を 加えるとその値は負の整数−536,870,912になります: (+ 1 536870911) ⇒ -536870912 ⇒ 1000...000000 (全部で30ビット) このチャプターで説明する多くの関数は、数字の位置として引数にマーカー (*note Markers::を参照)を受け取ります。そのような関数にたいする実際の引 数は数字かマーカーなので、わたしたちはこれらの引数にNUMBER-OR-MARKERとい う名前を与えることがあります。引数の値がマーカーならマーカーの位置が使用 され、マーカーのバッファーは無視されます。 -- Variable: most-positive-fixnum この変数の値はEmacs Lispが扱える整数の最大値。典型的な値は32ビット では 2**29 − 1 、64ビットでは 2**61 − 1 。 -- Variable: most-negative-fixnum この変数の値はEmacs Lispが扱える最小の整数。これは負の整数になる。 典型的な値は32ビットでは −2**29 、64ビットでは −2**61、 。 Emacs Lispでは、テキスト文字は整数により表現されます。0から ‘(max-char)’までの整数は、有効な文字として判断されます。*note Character Codes::を参照してください。 3.2 浮動小数点数の基礎 ====================== 浮動小数点数は整数ではない数を表現するのに有用です。浮動小数点数の範囲は 、使用しているマシンでのCデータ型の‘double’と同じ範囲です。Emacsで現在サ ポートされているすべてのコンピューターでは、これは倍精度のIEEE浮動小数点 数です。 浮動小数点数にたいする入力構文は、小数点と指数のどちらか1つ、または両 方が必要とします。オプションの符号(‘+’か‘-’)は、その数字と指数の前に記述 します。たとえば‘1500.0’、‘+15e2’、‘15.0e+2’、‘+1500000e-3’、‘.15e4’は値 が1500の浮動小数点数を記述する5つの方法です。これらはすべて等価です。 Common Lispと同様、Emacs Lispは浮動小数点数の小数点の後に少なくとも1つの 数字を必要とします。‘1500.’は整数であって浮動小数点数ではありません。 Emacs Lispは‘equal’と‘=’に関して、‘-0.0’を通常の0と数学的に同じものと して扱います。これは、(他の処理がこれらを区別にしても‘-0.0’と‘0.0’は数学 的に等しいとする)IEEE浮動小数点数規格にしたがっています。 IEEE浮動小数点数規格は浮動小数点数として、正の無限大と負の無限大をサ ポートします。この規格はNaNまたは“not a number(数字ではない)”と呼ばれる 値クラスも提供します。正しい答えが存在しないような場合に、数学関数はこの ような値をリターンします。たとえば‘(/ 0.0 0.0)’はNaNをリターンします。実 用に際し、たとえNaN値に符号がついていたとしても、Emacs Lispでは異なる NaN値に有意な差はありません。 以下は、これらの特別な浮動小数点数にたいする入力構文です: infinity ‘1.0e+INF’と‘-1.0e+INF’ not-a-number ‘0.0e+NaN’と‘-0.0e+NaN’ 以下の関数は浮動小数点数を扱うために特化したものです: -- Function: isnan x この述語は浮動小数引数がNaNなら‘t’、それ以外は‘nil’をリターンする。 -- Function: frexp x この関数はコンスセル‘(S . E)’をリターンする。ここでSとEは、浮動小数 点数の仮数(浮動小数点数を2の指数表現したときの仮数)と指数である。 Xが有限ならSは0.5以上1.0未満の浮動小数点数、Eは整数で、 X = S * 2**Eとなる。 Xが0または無限ならSはXと等しくなる。XがNaNならSもNaN。 Xが0ならEは0。 -- Function: ldexp s e 数値の仮数Sと整数の指数Eを与えられると、この関数は浮動小数点数 S * 2**Eをリターンする。 -- Function: copysign x1 x2 この関数はX2の符号をX1の値にコピーして結果をリターンする。X1とX2は 浮動小数でなければならない。 -- Function: logb x この関数はXの2進指数をリターンする。より正確には、これは|x|の2を底 とする対数を整数に切り下げた値。 (logb 10) ⇒ 3 (logb 10.0e20) ⇒ 69 3.3 数値のための述語 ==================== このセクションの関数は数値や、特定の数値型にたいしてテストを行ないます。 関数‘integerp’と‘floatp’は、引数として任意のLispオブジェクト型をとること ができます(でなければ、あまり使用する機会ない)。しかし述語‘zerop’は引数 として数値を要求します。*note Predicates on Markers::の ‘integer-or-marker-p’、‘number-or-marker-p’も参照してください。 -- Function: floatp object この述語は引数が浮動小数かどうかをテストしてもしそうなら‘t’、それ以 外は‘nil’をリターンする。 -- Function: integerp object この述語は引数が整数かどうかをテストしてもしそうなら‘t’、それ以外は ‘nil’をリターンする。 -- Function: numberp object この述語は引数が数(整数か浮動小数)かどうかをテストしてもしそうなら ‘t’、それ以外は‘nil’をリターンする。 -- Function: natnump object この述語は引数が正の整数かどうかをテストしてもしそうなら‘t’、それ以 外は‘nil’をリターンする(名前は“natural numberl: 自然数”が由来)。0は 整数と判断される。 ‘wholenump’は‘natnump’のシノニム。 -- Function: zerop number この述語は引数が0かどうかをテストしてもしそうなら‘t’、それ以外は ‘nil’をリターンする。引数は数でなければならない。 ‘(zerop x)’は‘(= x 0)’と等価。 3.4 数値の比較 ============== 数が数値的に等しいかのテストには、‘eq’ではなく通常は‘=’を使用するべきで す。同じ数値をもつ多くの浮動小数オブジェクトが存在するかもしれません。こ れらを比較するのに‘eq’を使用する場合、これは2つの値が同じオブジェクトか どうかをテストすることになります。対照的に‘=’はオブジェクトの数値的な値 だけを比較します。 Emacs Lispでは、それぞれの整数は一意なLispオブジェクトです。したがっ て整数に関しては‘eq’は‘=’と同じです。未知の整数の値の比較に、‘eq’を使用 する方が便利な場合があります。なぜなら未知の値が数でない場合でも‘eq’はエ ラーを報告しないからです。対照的に引数が数でもマーカーでもない場合、 ‘=’はエラーをシグナルします。しかし整数の比較においてさえ、使用できる場 合は‘=’を使用するのがよいプログラミング習慣です。 数の比較において、2つの数が同じデータ型(どちらも整数か浮動小数)では、 同じ値の場合は等しい数として扱う‘equal’のほうが便利なときもあります。対 照的に‘=’は整数と浮動小数点数を等しい数と扱うことができます。*note Equality Predicates::を参照してください。 他の欠点もあります。浮動小数演算は正確ではないので、浮動小数値を比較 するのが悪いアイデアとなるときがよくあります。通常は近似的に等しいことを テストするほうがよいでしょう。以下はこれを行なう関数です: (defvar fuzz-factor 1.0e-6) (defun approx-equal (x y) (or (= x y) (< (/ (abs (- x y)) (max (abs x) (abs y))) fuzz-factor))) Common Lispに関する注意: Common Lispは複数ワード整数を実装していて 、2つの別の整数オブジェクトが同じ数値的な値をもつことができるので、 Common Lispでの数の比較はには常に‘=’が要求されます。Emacs Lispの整 数は範囲が制限されているため、与えられた値に対応する整数オブジェク トは1つだけです。 -- Function: = number-or-marker &rest number-or-markers この関数はすべての引数が数値的に等しいかどうかをテストしてもしそう なら‘t’、それ以外は‘nil’をリターンする。 -- Function: eql value1 value2 この関数は‘eq’と同様に振る舞うが引数が両方とも数のときを除く。これ は数を型と数値的な値により比較するので、‘(eql 1.0 1)’は‘nil’をリタ ーンするが、‘(eql 1.0 1.0)’と‘(eql 1 1)’は‘t’をリターンする。 -- Function: /= number-or-marker1 number-or-marker2 この関数は引数が数値的に等しいかどうかをテストして、もし異なる場合 は‘t’、等しい場合は‘nil’をリターンする。 -- Function: < number-or-marker &rest number-or-markers この関数は、各引数それぞれを後の引数より小さいかどうかをテストして もしそうなら‘t’、それ以外は‘nil’をリターンする。 -- Function: <= number-or-marker &rest number-or-markers この関数は、各引数それぞれが後の引数以下かどうかをテストしてもしそ うなら‘t’、それ以外は‘nil’をリターンする。 -- Function: > number-or-marker &rest number-or-markers この関数は、各引数それぞれが後の引数より大きいかどうかをテストして もしそうなら‘t’、それ以外は‘nil’をリターンする。 -- Function: >= number-or-marker &rest number-or-markers この関数は、各引数それぞれが後の引数以上かどうかをテストしてもしそ うなら‘t’、それ以外は‘nil’をリターンする。 -- Function: max number-or-marker &rest numbers-or-markers この関数は引数の最大をリターンする。引数のどれかが浮動小数なら、た とえ最大が整数であっても浮動小数として値がリターンする。 (max 20) ⇒ 20 (max 1 2.5) ⇒ 2.5 (max 1 3 2.5) ⇒ 3.0 -- Function: min number-or-marker &rest numbers-or-markers この関数は引数の最小をリターンする。引数のどれかが浮動小数なら、た とえ最小が整数であっても浮動小数として値がリターンする。 (min -4 1) ⇒ -4 -- Function: abs number この関数はNUMBERの絶対値をリターンする。 3.5 数値の変換 ============== 整数を浮動少数の変換には関数‘float’を使用します。 -- Function: float number これは浮動小数点数に変換されたNUMBERをリターンする。すでにNUMBERが 浮動小数点数なら‘float’はそれを変更せずにリターンする。 浮動小数点数を整数に変換する関数が4つあります。これらは浮動小数点数を 丸める方法が異なります。これらはすべて引数NUMBER、およびオプション引数と してDIVISORを受け取ります。引数は両方とも整数か浮動小数点数です。 DIVISORが‘nil’のこともあります。DIVISORが‘nil’または省略された場合、これ らの関数はNUMBERを整数に変換するか、それが既に整数の場合は変更せずにリタ ーンします。DIVISORが非‘nil’なら、これらの関数はNUMBERをDIVISORで除して 結果を整数に変換します。DIVISORが(整数か浮動小数かに関わらず)0の場合、 Emacsは‘arith-error’エラーをシグナルします。 -- Function: truncate number &optional divisor これは0に向かって丸めることにより整数に変換したNUMBERをリターンする 。 (truncate 1.2) ⇒ 1 (truncate 1.7) ⇒ 1 (truncate -1.2) ⇒ -1 (truncate -1.7) ⇒ -1 -- Function: floor number &optional divisor これは下方(負の無限大に向かって)に丸めることにより整数に変換した NUMBERをリターンする。 DIVISORが指定された場合には、‘mod’に相当する種類の除算演算を使用し て下方に丸めを行う。 (floor 1.2) ⇒ 1 (floor 1.7) ⇒ 1 (floor -1.2) ⇒ -2 (floor -1.7) ⇒ -2 (floor 5.99 3) ⇒ 1 -- Function: ceiling number &optional divisor これは上方(正の無限大に向かって)に丸めることにより整数に変換した NUMBERをリターンする。 (ceiling 1.2) ⇒ 2 (ceiling 1.7) ⇒ 2 (ceiling -1.2) ⇒ -1 (ceiling -1.7) ⇒ -1 -- Function: round number &optional divisor これはもっとも近い整数に向かって丸めることにより、整数に変換した NUMBERをリターンする。2つの整数から等距離にある値の丸めでは、偶数の 整数をリターンする。 (round 1.2) ⇒ 1 (round 1.7) ⇒ 2 (round -1.2) ⇒ -1 (round -1.7) ⇒ -2 3.6 算術演算 ============ Emacs Lispは伝統的な4つの算術演算(加減乗除)、同様に剰余とmodulusの関数、 および1の加算と減算を行う関数を提供します。‘%’を除き、これらの各関数は引 き数として整数か浮動小数を受け取り、浮動小数の引数がある場合は浮動小数点 数をリターンします。 Emacs Lispの算術関数は整数のオーバーフローをチェックしません。したが って‘(1+ 536870911)’は−536870912に評価されるかもしれず、それはハードウェ アーに依存します。 -- Function: 1+ number-or-marker この関数はNUMBER-OR-MARKER + 1をリターンする。例えば、 (setq foo 4) ⇒ 4 (1+ foo) ⇒ 5 この関数はCの演算子‘++’とは異なり、変数をインクリメントしない。この 関数は和を計算するだけである。したがって以下を続けて評価すると、 foo ⇒ 4 変数をインクリメントしたい場合は、以下のように‘setq’を使用しなけれ ばならない: (setq foo (1+ foo)) ⇒ 5 -- Function: 1- number-or-marker この関数はNUMBER-OR-MARKER − 1をリターンする。 -- Function: + &rest numbers-or-markers この関数は引数すべてを加算する。引数を与えないと‘+’は0をリターンす る。 (+) ⇒ 0 (+ 1) ⇒ 1 (+ 1 2 3 4) ⇒ 10 -- Function: - &optional number-or-marker &rest more-numbers-or-markers ‘-’関数は2つの目的 — 符号反転と減算 — をもつ。‘-’に1つの引数を与え ると、値は引数の符号を反転したものになる。複数の引数の場合は、 NUMBER-OR-MARKERからMORE-NUMBERS-OR-MARKERSまでの各値を蓄積的に減算 する。引数がなければ結果は0。 (- 10 1 2 3 4) ⇒ 0 (- 10) ⇒ -10 (-) ⇒ 0 -- Function: * &rest numbers-or-markers この関数はすべての引数を乗じて積をリターンする。引数がなかれば‘*’は 1をリターンする。 (*) ⇒ 1 (* 1) ⇒ 1 (* 1 2 3 4) ⇒ 24 -- Function: / number &rest divisors DIVISORSが1つ以上ならこの関数はDIVISORS内の除数で順にNUMBERを除して 、その商をリターンする。DIVISORSがなければ、この関数は1/NUMBER、つ まりNUMBERの逆数をリターンする。各引数には数かマーカーを指定できる 。 すべての引数が整数なら、結果は各除算の後に商を0へ向かって丸めること により得られる整数となる。 (/ 6 2) ⇒ 3 (/ 5 2) ⇒ 2 (/ 5.0 2) ⇒ 2.5 (/ 5 2.0) ⇒ 2.5 (/ 5.0 2.0) ⇒ 2.5 (/ 4.0) ⇒ 0.25 (/ 4) ⇒ 0 (/ 25 3 2) ⇒ 4 (/ -17 6) ⇒ -2 整数を整数0で除するとEmacsは‘arith-error’エラー(*note Errors::を参 照)をシグナルする。浮動小数点数の除算では、非0の数を0で除することで 正の無限大または負の無限大を得る(*note Float Basics::を参照)。 -- Function: % dividend divisor この関数はDIVIDENDをDIVISORで除した後、その剰余を整数でリターンする 。引数は整数かマーカーでなければならない。 任意の2つの整数DIVIDENDとDIVISORにたいして、 (+ (% DIVIDEND DIVISOR) (* (/ DIVIDEND DIVISOR) DIVISOR)) は、DIVISORが非0なら常にDIVIDENDと等しくなる。 (% 9 4) ⇒ 1 (% -9 4) ⇒ -1 (% 9 -4) ⇒ 1 (% -9 -4) ⇒ -1 -- Function: mod dividend divisor この関数はDIVIDENDのDIVISORにたいするmodulo、言い換えるとDIVIDENDを DIVISORで除した後の剰余(ただし符号はDIVISORと同じ)をリターンする。 引数は数かマーカーでなければならない。 ‘%’とは異なり‘mod’は浮動小数の引数を許す。これは商を整数に下方(負の 無限大に向かって)へ丸めて剰余を計算するのにこの商を使用する。 ‘mod’はDIVISORが0のとき、両方の引数が整数なら‘arith-error’エラーを シグナルし、それ以外はNaNをリターンする。 (mod 9 4) ⇒ 1 (mod -9 4) ⇒ 3 (mod 9 -4) ⇒ -3 (mod -9 -4) ⇒ -1 (mod 5.5 2.5) ⇒ .5 任意の2つの数DIVIDENDとDIVISORにたいして、 (+ (mod DIVIDEND DIVISOR) (* (floor DIVIDEND DIVISOR) DIVISOR)) は常にDIVIDENDになる(ただし引数のどちらかが浮動小数なら、丸め誤差の 範囲内で等しく、かつDIVIDENDが整数でDIVISORが0なら‘arith-error’とな る)。‘floor’については、*note Numeric Conversions::を参照のこと。 3.7 丸め処理 ============ 関数‘ffloor’、‘fceiling’、‘fround’、‘ftruncate’は浮動小数の引数をとり、 値が近くの整数であるような浮動少数をリターンします。‘ffloor’は一番近い下 方の整数、‘fceiling’は一番近い上方の整数、‘ftruncate’は0に向かう方向で一 番近い整数、‘fround’は一番近い整数をリターンします。 -- Function: ffloor float この関数はFLOATを次に小さい整数値に丸めて、その値を浮動小数点数とし てリターンする。 -- Function: fceiling float この関数はFLOATを次に大きい整数値に丸めて、その値を浮動小数点数とし てリターンする。 -- Function: ftruncate float この関数はFLOATを0方向の整数値に丸めて、その値を浮動小数点数として リターンする。 -- Function: fround float この関数はFLOATを一番近い整数値に丸めて、その値を浮動小数点数として リターンする。2つの整数値との距離が等しい値にたいする丸めでは、偶数 の整数をリターンする。 3.8 ビット演算 on Integers ========================== コンピューターの中では、整数は“ビット(bit: 0か1の数字)”のシーケンスであ る2進数で表されます。ビット演算は、そのようなシーケンスの中の個々のビッ トに作用します。たとえば“シフト(shifting)”はシーケンス全体を1つ以上左ま たは右に移動して、移動されたのと同じパターンを再現します。 Emacs Lispのビット演算は整数だけに適用されます。 -- Function: lsh integer1 count ‘lsh’は“logical shift”の略で、INTEGER1のビットを左にCOUNT個シフトす る。COUNTが負なら右にシフトし、シフトにより空きになったビットには 0がセットされる。COUNTが負なら‘lsh’は左端(最上位)に0をシフトするの で、INTEGER1が負の場合でも正の結果が生成される。これと対照的なのが 以下で説明する‘ash’である。 以下は‘lsh’でビットパターンの位置を1つ左にシフトする例である。ここ では下位8ビットの2進パターンだけを表示しており、残りのビットはすべ て0である。 (lsh 5 1) ⇒ 10 ;; 10進の5が10進の10になる 00000101 ⇒ 00001010 (lsh 7 1) ⇒ 14 ;; 10進の7は10進の14になる 00000111 ⇒ 00001110 この例が示すように、ビットパターンを左に1シフトすると、生成される数 は元の数の2倍になる。 ビットパターンを左に2シフトすると、以下の結果が生成される(8ビット 2進数): (lsh 3 2) ⇒ 12 ;; 10進の3が10進の12になる 00000011 ⇒ 00001100 一方、右に1シフトすると以下のようになる: (lsh 6 -1) ⇒ 3 ;; 10進の6は10進の3になる 00000110 ⇒ 00000011 (lsh 5 -1) ⇒ 2 ;; 10進の5は10進の2になる 00000101 ⇒ 00000010 例で明らかなように右に1シフトすることにより、正の整数の値が2で除さ れ下方に丸められる。 関数‘lsh’は他のEmacs Lisp算術関数と同様、オーバーフローをチェックし ないので、左にシフトすることにより上位ビットが捨てられ、その数の符 号が変化するかもしれない。たとえば30ビットの実装では、536,870,911を 左にシフトすると−2が生成されます。 (lsh 536870911 1) ; 左シフト ⇒ -2 2進ではこの引数は以下のようになる: ;; 10進の536,870,911 0111...111111 (全部で30ビット) これを左にシフトすると以下のようになる: ;; 10進の−2 1111...111110 (全部で30ビット) -- Function: ash integer1 count ‘ash’ (“算術シフト(arithmetic shift)”)は、INTEGER1の中のビット位置 を左にCOUNTシフトする。COUNTが負なら右にシフトする。 ‘ash’は‘lsh’と同じ結果を与えるが、例外はINTEGER1とCOUNTがいずれも負 の場合である。この場合、‘lsh’は左にできる空きビットに0、‘ash’は1を 置く。 したがって‘ash’でビットパターンの位置を右に1シフトすると以下のよう になる: (ash -6 -1) ⇒ -3 ;; 10進の−6は10進の−3になる 1111...111010 (30 bits total) ⇒ 1111...111101 (30 bits total) 対照的に、‘lsh’でビットパターンの位置を1右にシフトすると以下のよう になる: (lsh -6 -1) ⇒ 536870909 ;; 10進の−6は10進の536,870,909になる 1111...111010 (30 bits total) ⇒ 0111...111101 (30 bits total) 他にも例を示す: ; 30ビットの2進数 (lsh 5 2) ; 5 = 0000...000101 ⇒ 20 ; = 0000...010100 (ash 5 2) ⇒ 20 (lsh -5 2) ; -5 = 1111...111011 ⇒ -20 ; = 1111...101100 (ash -5 2) ⇒ -20 (lsh 5 -2) ; 5 = 0000...000101 ⇒ 1 ; = 0000...000001 (ash 5 -2) ⇒ 1 (lsh -5 -2) ; -5 = 1111...111011 ⇒ 268435454 ; = 0011...111110 (ash -5 -2) ; -5 = 1111...111011 ⇒ -2 ; = 1111...111110 -- Function: logand &rest ints-or-markers この関数は引数のビットのANDをリターンする。すべての引数のN番目のビ ットが1の場合に限り、結果のN番目のビットが1となる。 たとえば13と12では、4ビット2進数を使用すると1101と1100のビットANDは 1100を生成する。この2進数ではいずれも左の2ビットがセット(つまり1)さ れているので、リターンされる値の左2ビットがセットされる。しかし右の 2ビットにたいしては少なくとも1つの引数でそのビットが0なので、リター ンされる値の右2ビットは0になる。 したがって、 (logand 13 12) ⇒ 12 ‘logand’に何も引数も渡さなければ、値−1がリターンされる。−1を2進数で 表すとすべてのビットが1なので、−1は‘logand’にたいする単位元 (identity element)である。 ; 30ビット2進数 (logand 14 13) ; 14 = 0000...001110 ; 13 = 0000...001101 ⇒ 12 ; 12 = 0000...001100 (logand 14 13 4) ; 14 = 0000...001110 ; 13 = 0000...001101 ; 4 = 0000...000100 ⇒ 4 ; 4 = 0000...000100 (logand) ⇒ -1 ; -1 = 1111...111111 -- Function: logior &rest ints-or-markers この関数は、引数のビット単位の包含的ORをリターンする。少なくとも1つ の引数でN番目のビットが1なら、結果のN番目のビットが1になる。引数を 与えなければ、結果はこの処理にたいする単位元である0となる。 ‘logior’に渡す引数が1つだけならその引数がリターンされる。 ; 30ビット2進数 (logior 12 5) ; 12 = 0000...001100 ; 5 = 0000...000101 ⇒ 13 ; 13 = 0000...001101 (logior 12 5 7) ; 12 = 0000...001100 ; 5 = 0000...000101 ; 7 = 0000...000111 ⇒ 15 ; 15 = 0000...001111 -- Function: logxor &rest ints-or-markers この関数は、引数のビット単位の排他的ORをリターンする。N番目のビット が1であるような引数の数が奇数個の場合のみ、結果のN番目のビットが1と なる。引数を与えなければ、結果はこの処理の単位元である0となる。 ‘logxor’に渡す引数が1つだけならその引数がリターンされる。 ; 30ビット2進数 (logxor 12 5) ; 12 = 0000...001100 ; 5 = 0000...000101 ⇒ 9 ; 9 = 0000...001001 (logxor 12 5 7) ; 12 = 0000...001100 ; 5 = 0000...000101 ; 7 = 0000...000111 ⇒ 14 ; 14 = 0000...001110 -- Function: lognot integer この関数は引数のビット単位の補数(bitwise complement)をリターンする 。INTEGERのN番目のビットが0の場合に限り、結果のN番目のビットが1にな り、その逆も成り立つ。 (lognot 5) ⇒ -6 ;; 5 = 0000...000101 (全部で30ビット) ;; becomes ;; -6 = 1111...111010 (全部で30ビット) 3.9 標準的な数学関数 ==================== 以下の数学的関数は、引数として整数と同様に浮動小数点数も受け入れます。 -- Function: sin arg -- Function: cos arg -- Function: tan arg これらは三角関数であり、引数ARGはラジアン単位。 -- Function: asin arg ‘(asin ARG)’の値は、sinの値がARGとなるような −pi/2 から pi/2 (両端 を含む)の数である。ARGが範囲外([−1, 1]の外)なら、‘asin’はNaNをリタ ーンする。 -- Function: acos arg ‘(acos ARG)’の値は、cosの値がARGとなるような、0から pi (両端を含む )の数である。argが範囲外([-1, 1]の外)なら‘acos’はNaNをリターンする 。 -- Function: atan y &optional x ‘(atan Y)’の値は、tanの値がYとなるような、 −pi/2 から pi/2 (両端を 含まず)の数である。オプションの第2引数Xが与えられると、‘(atan y x)’の値はベクトル‘[X, Y]’と‘X’軸が成す角度のラジアン値となる。 -- Function: exp arg これは指数関数である。この関数はeの指数ARGをリターンする。 -- Function: log arg &optional base この関数は底をBASEとするARGの対数をリターンする。BASEを指定しなけれ ば、自然底(natural base)eが使用される。ARGかBASEが負なら、‘log’は NaNをリターンする。 -- Function: expt x y この関数はXにYを乗じてリターンする。引数が両方とも整数でYが正なら結 果は整数になる。この場合オーバーフローによる切り捨てが発生するので 注意しされたい。Xが有限の負数でYが有限の非整数なら、‘expt’はNaNをリ ターンする。 -- Function: sqrt arg これはARGの平方根をリターンする。ARGが有限で0より小さければ、 ‘sqrt’はNaNをリターンする。 加えて、Emacsは以下の数学的な定数を定義します: -- Variable: float-e 自然対数e(2.71828...) -- Variable: float-pi 円周率pi(3.14159...) 3.10 乱数 ========= 決定論的なコンピュータープログラムでは真の乱数を生成することはできません 。しかしほとんどの目的には、“疑似乱数(pseudo-random numbers)”で充分です 。一連の疑似乱数は決定論的な手法により生成されます。真の乱数ではありませ んが、それらにはランダム列を模する特別な性質があります。たとえば疑似ラン ダム系では、すべての可能な値は均等に発生します。 疑似乱数は“シード値(seed value)”から生成されます。与えられた任意のシ ードから開始することにより、‘random’関数は常に同じ数列を生成します。デフ ォルトでは、Emacsは開始時に乱数シードを初期化することにより、それぞれの Emacsの実行において、‘random’の値シーケンスは(ほとんど確実に)異なります 。 再現可能な乱数シーケンスが欲しい場合もあります。たとえば乱数シーケン スに依存するプログラムをデバッグする場合、プログラムの各実行において同じ 挙動を得ることが助けになります。再現可能なシーケンスを作成するには、 ‘(random "")’を実行します。これは特定のEmacsの実行可能ファイルにたいして 、シードに定数値をセットします(しかしこの実行可能ファイルは、その他の Emacsビルドと異なるものになるであろう)。シード値として、他のさまざまな文 字列を使用することができます。 -- Function: random &optional limit この関数は疑似乱数の整数をリターンする。繰り返し呼び出すと一連の疑 似乱数の整数をリターンする。 LIMITが正なら、値は負ではないLIMIT未満の値から選択される。それ以外 なら値は‘most-negative-fixnum’から‘most-positive-fixnum’の間の、 Lispで表現可能な任意の整数(*note Integer Basics::を参照)となるだろ う。 LIMITが‘t’なら、あたかもEmacsが再起動されたかのように、通常はシステ ムのエントロピーから新たなシードが選択されることを意味する。エント ロピープールを欠くシステムでは、カレント時刻のような若干揮発性が低 い乱数からシードが選択される。 LIMITが文字列なら、その文字列定数にもとづいた新しいシードを選択する ことを意味する。 4 文字列と文字 ************** Emacs Lispの文字列は、文字列の順序列(ordered sequence)を含む配列です。文 字列はシンボル、バッファー、ファイルの名前に使用されます。その他にもユー ザーにたいしてメッセージを送ったりバッファー間でコピーする文字列を保持し たり等、多くの目的に使用されます。文字列は特に重要なので、Emacs Lispは特 別には文字列を操作するために多くの関数があります。Emacs Lispプログラムで は個々の文字より文字列を多用します。 キーボードの文字イベントの文字列にたいする特別な考慮は、*note Strings of Events::を参照してください。 4.1 文字列と文字の基礎 ====================== 文字(character)とは、テキスト内の1つの文字を表すLispオブジェクトです。 Emacs Lispでは文字は単なる整数です。ある整数が文字か文字でないかを区別す るのは、それが使用される方法だけです。Emacsでの文字表現についての詳細は *note Character Codes::を参照してください。 文字列(string)とは固定された文字シーケンスです。これは“配列(array)”と 呼ばれるシーケンス型であり、配列長が固定で一度作成したら変更できないこと を意味します(*note Sequences Arrays Vectors::を参照)。Cとは異なり、Emacs Lispの文字列は文字コードを判断することにより_終端されません_。 文字列は配列であるということは同様にシーケンスでもあるので、*note Sequences Arrays Vectors::にドキュメントされている一般的な配列関数やシー ケンス関数で文字列を処理できます。たとえば文字列内の特定の文字にアクセス したり変更することができます。しかし表示された文字列の幅を計算するために 、‘length’を_使用するべきではない_ことに注意してください。かわりに ‘string-width’を使用してください(*note Size of Displayed Text::を参照)。 Emacs文字列での非ASCIIにたいすテキスト表現は2つ — ユニバイト (unibyte)とマルチバイト(multibyte)があります。ほとんどのLispプログラミン グでは、これら2つの表現を気にする必要はありません。詳細は*note Text Representations::を参照してください。 キーシーケンスがユニバイト文字列で表されることがあります。ユニバイト 文字列がキーシーケンスの場合、範囲128から255までの文字列要素は範囲128か ら255の文字コードではなく、メタ文字(これは非常に大きな整数である)を表し ます。文字列はハイパー(hyper)、スーパー(super)、アルト(alt)で修飾された 文字を保持できません。文字列はASCIIコントロール文字を保持できますが、そ れは他のコントロール文字です。文字列はASCIIコントロール文字のcaseを区別 できません。そのような文字をシーケンスに保存したい場合は、文字列ではなく ベクターを使用しなければなりません。キーボード入力文字についての情報は *note Character Type::を参照してください。 文字列は正規表現を保持するために便利です。‘string-match’ (*note Regexp Search::を参照)を使用して、文字列にたいして正規表現をマッチするこ ともできます。関数‘match-string’ (*note Simple Match Data::を参照)と ‘replace-match’ (*note Replacing Match::を参照)は、文字列にたいして正規 表現をマッチした後に、文字列を分解・変更するのに便利です。 バッファーのように、文字列は文字列内の文字自身とその文字にたいするテ キストプロパティーを含みます。*note Text Properties::を参照してください 。文字列からバッファーや他の文字列にテキストをコピーする、すべてのLispプ リミティブ(Lisp primitives)はコピーされる文字のプロパティーもコピーしま す。 文字列の表示やバッファーにコピーする関数についての情報は*note Text::を 参照してください。文字または文字列の構文についての情報は、*note Character Type::と*note String Type::を参照してください。異なるテキスト 表現間で変換したり、文字コードをエンコード、デコードする関数については *note Non-ASCII Characters::を参照してください。 4.2 文字列のための述語 ====================== 一般的なシーケンスや配列にたいする述語についての情報は、*note Sequences Arrays Vectors::と*note Arrays::を参照してください。 -- Function: stringp object この関数はOBJECTが文字列なら‘t’、それ以外は‘nil’をリターンする。 -- Function: string-or-null-p object この関数はOBJECTが文字列か‘nil’なら‘t’、それ以外は‘nil’をリターンす る。 -- Function: char-or-string-p object この関数はOBJECTが文字列か文字(たとえば整数)なら‘t’、それ以外は ‘nil’をリターンする。 4.3 文字列の作成 ================ 以下の関数は新たに文字列を作成したり、文字列同士の結合による文字列の作成 や、文字列の一部から文字列を作成する関数です。 -- Function: make-string count character この関数はCHARACTERをCOUNT回繰り返すことにより作成された文字列をリ ターンする。COUNTが負ならエラーをシグナルする。 (make-string 5 ?x) ⇒ "xxxxx" (make-string 0 ?x) ⇒ "" この関数に対応する他の関数には‘make-vector’ (*note Vectors::を参照 )や‘make-list’ (*note Building Lists::を参照)が含まれる。 -- Function: string &rest characters この関数は文字CHARACTERSを含む文字列をリターンする。 (string ?a ?b ?c) ⇒ "abc" -- Function: substring string &optional start end この関数はSTRINGから、インデックスSTARTの文字(その文字を含む)と ENDの文字(その文字は含まない)の間の範囲の文字で構成される、新しい文 字列をリターンする。文字列の最初の文字はインデックス0。引数が1つな ら、この関数は単にSTRINGをコピーする。 (substring "abcdefg" 0 3) ⇒ "abc" 上記の例では‘a’のインデックスは0、‘b’のインデックスは1、‘c’のインデ ックスは2となる。インデックス3 — この文字列の4番目の文字 — は、コピ ーされる部分文字列の文字位置までをマークする。したがって文字列 ‘"abcdefg"’から‘abc’がコピーされる。 負の数は文字列の最後から数えることを意味するので、−1は文字列の最後 の文字のインデックスである。たとえば: (substring "abcdefg" -3 -1) ⇒ "ef" この例では‘e’のインデックスは−3、‘f’のインデックスは−2、‘g’のインデ ックスは−1。つまり‘e’と‘f’が含まれ、‘g’は含まれない。 ENDに‘nil’を使用した場合、それは文字列の長さを意味する。したがって 、 (substring "abcdefg" -3 nil) ⇒ "efg" 引数ENDを省略した場合、それは‘nil’を指定したのと同じである。 ‘(substring STRING 0)’はSTRINGのすべてをコピーしてリターンする。 (substring "abcdefg" 0) ⇒ "abcdefg" しかしこの目的のためには‘copy-sequence’を推奨する(*note Sequence Functions::を参照)。 STRINGからコピーされた文字がテキストプロパティーをもつなら、そのプ ロパティーは新しい文字列へもコピーされる。*note Text Properties::を 参照のこと。 ‘substring’の最初の引数にはベクターも指定できる。たとえば: (substring [a b (c) "d"] 1 3) ⇒ [b (c)] STARTが整数でない、またはENDが整数でも‘nil’でもななければ、 ‘wrong-type-argument’エラーがシグナルされる。STARTがENDの後の文字を 指す、またはSTRINGにたいして範囲外の整数をいずれかに指定すると、 ‘args-out-of-range’エラーがシグナルされる。 この関数に対応するのは‘buffer-substring’ (*note Buffer Contents::を 参照)で、これはカレントバッファー内のテキストの一部を含む文字列をリ ターンする。文字列の先頭はインデックス0だが、バッファーの先頭はイン デックス1である。 -- Function: substring-no-properties string &optional start end これは‘substring’と同じように機能するが、値のすべてのテキストプロパ ティーを破棄する。STARTを省略したり‘nil’を指定することができ、その 場合は0と等価だる。したがって‘(substring-no-properties STRING)’は、 すべてのテキストプロパティーが削除されたSTRINGのコピーをリターンす る。 -- Function: concat &rest sequences この関数は渡された引数内の文字からなる、新しい文字列をリターンする (もしあればテキストプロパティーも)。引数には文字列、数のリスト、数 のベクターを指定できる。引数は変更されない。‘concat’に引数を指定し なければ空文字列をリターンする。 (concat "abc" "-def") ⇒ "abc-def" (concat "abc" (list 120 121) [122]) ⇒ "abcxyz" ;; ‘nil’hあ空のシーケンス。 (concat "abc" nil "-def") ⇒ "abc-def" (concat "The " "quick brown " "fox.") ⇒ "The quick brown fox." (concat) ⇒ "" この関数は常に、任意の既存文字列にたいして‘eq’ではない、新しい文字 列を構築するが、結果が空文字列の時を除く(スペース省略のために Emacsは空のマルチバイト文字列を1つだけ作成する)。 他の結合関数(concatenation functions)についての情報は*note Mapping Functions::の‘mapconcat’、*note Vector Functions::の‘vconcat’、 *note Building Lists::の‘append’を参照のこと。シェルコマンドで使用 される文字列の中に、個々のコマンドライン引数を結合するには、*note combine-and-quote-strings: Shell Arguments.を参照されたい。 -- Function: split-string string &optional separators omit-nulls trim この関数は正規表現SEPARATORS(*note Regular Expressions::を参照)にも とづいて、STRINGを部分文字列に分解する。SEPARATORSにたいする各マッ チは分割位置を定義する。分割位置の間にある部分文字列をリストにまと めてリターンする。 OMIT-NULLSが‘nil’(または省略)なら、連続する2つのSEPARATORSへのマッ チか、STRINGの最初か最後にマッチしたときの空文字列が結果に含まれる 。OMIT-NULLSが‘t’なら、これらの空文字列は結果から除外される。 SEPARATORSが‘nil’(または省略)なら、デフォルトは ‘split-string-default-separators’の値となる。 特別なケースとしてSEPARATORSが‘nil’(または省略)なら、常に結果から空 文字列が除外される。したがって: (split-string " two words ") ⇒ ("two" "words") 有用性はほとんどないであろう‘("" "two" "words" "")’という結果とはな らない。このような結果が必要ならSEPARATORSに明示的な値を使用するこ と (split-string " two words " split-string-default-separators) ⇒ ("" "two" "words" "") 他にも例を示す: (split-string "Soup is good food" "o") ⇒ ("S" "up is g" "" "d f" "" "d") (split-string "Soup is good food" "o" t) ⇒ ("S" "up is g" "d f" "d") (split-string "Soup is good food" "o+") ⇒ ("S" "up is g" "d f" "d") 空のマッチはカウントされます。例外は、空でないマッチを使用すること により、すでに文字列の最後に到達しているとき、またはSTRINGが空の時 で、この場合‘split-string’は最後の空マッチを探しません。 (split-string "aooob" "o*") ⇒ ("" "a" "" "b" "") (split-string "ooaboo" "o*") ⇒ ("" "" "a" "b" "") (split-string "" "") ⇒ ("") しかしSEPARATORSが空文字列にマッチできるとき、通常はOMIT-NULLSを ‘t’にすれば、前の3つの例の不明瞭さはほとんど発生しない: (split-string "Soup is good food" "o*" t) ⇒ ("S" "u" "p" " " "i" "s" " " "g" "d" " " "f" "d") (split-string "Nice doggy!" "" t) ⇒ ("N" "i" "c" "e" " " "d" "o" "g" "g" "y" "!") (split-string "" "" t) ⇒ nil 空でないマッチより空のマッチを優先するような、一部の“非貪欲 (non-greedy)”な値をSEPARATORSに指定することにより、幾分奇妙(ではあ るが予見可能)な振る舞いが発生することがある。繰り返しになるが、その ような値は実際には稀である: (split-string "ooo" "o*" t) ⇒ nil (split-string "ooo" "\\|o+" t) ⇒ ("o" "o" "o") オプションの引数TRIMが非‘nil’なら、その値は各部分文字列の最初と最後 からトリム(trim: 除去)するテキストにマッチする正規表現を指定する。 トリムによりその部分文字列が空になるようなら、それは空文字列として 扱われる。 文字列を分割して‘call-process’や‘start-process’に適するような、個々 のコマンドライン引数のリストにする必要がある場合は、*note split-string-and-unquote: Shell Arguments.を参照されたい。 -- Variable: split-string-default-separators ‘split-string’のSEPARATORSにたいするデフォルト値。通常の値は ‘"[ \f\t\n\r\v]+"’。 4.4 文字列の変更 ================ 既存の文字列の内容を変更するもっとも基本的な方法は、‘aset’ (*note Array Functions::を参照)を使用する方法です。‘(aset STRING IDX CHAR)’は、 STRINGのインデックスIDXに、CHARを格納します。それぞれの文字は1文字以上を 占有しますが、すでにインデックスの場所にある文字のバイト数がCHARが要する バイト数と異なる場合、‘aset’はエラーをシグナルします。 より強力な関数は‘store-substring’です: -- Function: store-substring string idx obj この関数はインデックスIDXで開始される位置にOBJを格納することにより 、文字列STRINGの内容の一部を変更する。OBJは文字、または(STRINGより 小さい)文字列です。 既存の文字列の長さを変更するのは不可能なので、STRINGの実際の長さに OBJが収まらない、またはSTRINGのその位置に現在ある文字のバイト数が新 しい文字に必要なバイト数と異なる場合はエラーになる。 パスワードを含む文字列をクリアーするときには‘clear-string’を使用しま す: -- Function: clear-string string これはSTRINGをユニバイト文字列にして、内容を0にクリアーする。これに よりSTRINGの長さも変更されるだろう。 4.5 文字および文字列の比較 ========================== -- Function: char-equal character1 character2 この関数は引数が同じ文字を表すなら‘t’、それ以外は‘nil’をリターンす る。‘case-fold-search’が非‘nil’なら、この関数はcaseの違いを無視する 。 (char-equal ?x ?x) ⇒ t (let ((case-fold-search nil)) (char-equal ?x ?X)) ⇒ nil -- Function: string= string1 string2 この関数は、2つの文字列の文字が正確にマッチすれば‘t’をリターンする 。引数にはシンボルも指定でき、この場合はそのシンボル名が使用される 。‘case-fold-search’とは無関係にcaseは常に意味をもつ。 この関数は、‘equal’で2つの文字列を比較するのと等価である(*note Equality Predicates::を参照)。特に、2つの文字列のテキストプロパティ ーは無視されます。テキストプロパティーだけが異なる文字列を区別する 必要があるなら‘equal-including-properties’を使用すること。しかし ‘equal’とは異なり、いずれかの引数が文字列でもシンボルでもなければ、 ‘string=’はエラーをシグナルする。 (string= "abc" "abc") ⇒ t (string= "abc" "ABC") ⇒ nil (string= "ab" "ABC") ⇒ nil 技術的な理由によりユニバイト文字列とマルチバイト文字列が‘equal’にな るのは、それらが同じ文字コードのシーケンスを含み、それらすべてのコ ードが0から127(ASCII)、または160から255(‘eight-bit-graphic’)のとき だけである。しかしユニバイト文字列をマルチバイト文字列に変更する際 、コードが160から255の範囲にあるすべての文字はより高いコードに変換 され、ASCII文字は変換されないまま残る。したがってユニバイト文字列と それを変換したマルチバイト文字列は、その文字列のすべてがASCIIのとき だけ‘equal’となる。もしマルチバイト文字列中で文字コード160から255の 文字があったとしても、それは完全に正しいとは言えない。結果として、 すべてがASCIIではないユニバイト文字列とマルチバイト文字列が ‘equal’という状況は、もしかしたらEmacs Lispプロプラマーが直面するか もしれない、とても稀で記述的に不可解な状況だといえよう。*note Text Representations::を参照されたい。 -- Function: string-equal string1 string2 ‘string-equal’は‘string=’の別名である。 -- Function: string-collate-equalp string1 string2 &optional locale ignore-case この関数は照合ルール(collation rules)にもとづいてSTRING1とSTRING2が 等しければ‘t’をリターンする。照合ルールはSTRING1とSTRING2に含まれる 文字の辞書順だけではなく、それらの文字間の関係に関する他のルールに より判断される。これは通常は、Emacs実行中のLOCALE環境により決定され る。 たとえば異なるコーディングポイントでも、Unicodeのグレイブアクセント 文字のように同じ意味なら等しいとみなされる。 (string-collate-equalp (string ?\uFF40) (string ?\u1FEF)) ⇒ t オプション引数LOCALE(文字列)は、照合用のカレントlocale識別子 (current locale identifier)をオーバーライドする。値はシステムに依存 する。たとえばPOSIXシステムでは‘"en_US.UTF-8"’、MS-Windowsシステム では‘"enu_USA.1252"’のLOCALEが適用できるだろう。 IGNORE-CASEが非‘nil’なら、それらは比較前に小文字に変換される。 MS-WindowsシステムでUnicode互換の照合をエミュレートする場合、 MS-Windowsではlocaleのコードセット部分を‘"UTF-8"’にできないので、 ‘w32-collate-ignore-punctuation’に非‘nil’値をバインドすること。 あるlocale環境をシステムがサポートしなれければ、この関数は ‘string-equal’と同様に振る舞う。 一般的にファイルシステムは照合ルールが実装するような文字列の言語学 的な等価性を尊重しないので、この関数をファイル名の等価性の比較に_使 用しないこと_。 -- Function: string-prefix-p string1 string2 &optional ignore-case この関数はSTRING1がSTRING2のプレフィクス(たとえばSTRING2がSTRING1で 始まる)なら、非‘nil’をリターンする。オプションの引数IGNORE-CASEが非 ‘nil’ばら、比較においてcaseの違いは無視される。 -- Function: string-suffix-p suffix string &optional ignore-case この関数はSUFFIXがSTRINGのサフィックス(たとえばSTRINGがSUFFIXで終わ る)なら、非‘nil’をリターンする。オプションの引数IGNORE-CASEが非 ‘nil’なら、比較においてcaseの違いは無視される。 -- Function: string< string1 string2 この関数は2つの文字列を1文字ずつ比較する。この関数は同時に2つの文字 列をスキャンして、対応する文字同士がマッチしない最初のペアを探す。 2つの文字列内で小さいほうの文字がSTRING1の文字ならSTRING1が小さいこ とになり、この関数は‘t’をリターンする。小さいほうの文字がSTRING2の 文字ならSTRING1が大きいことになり、この関数は‘nil’をリターンする。 2つの文字列が完全にマッチしたら値は‘nil’になる。 文字のペアーは文字コードで比較されル。ASCII文字セットでは英小文字は 英大文字より高い数値をもつことに留意されたい。数字と区切り文字の多 くは英大文字より低い数値をもつ。ASCII文字は任意の非ASCII文字より小 さくなる。ユニバイトの非ASCII文字は、任意のマルチバイト非ASCII文字 より常に小さくなります(*note Text Representations::を参照)。 (string< "abc" "abd") ⇒ t (string< "abd" "abc") ⇒ nil (string< "123" "abc") ⇒ t 文字列の長さが異なり、STRING1の長さまでマッチする場合、結果は‘t’に なる。STRING2の長さまでマッチする場合、結果は‘nil’になる。文字を含 まない文字列は、他の任意の文字列より小さくなる。 (string< "" "abc") ⇒ t (string< "ab" "abc") ⇒ t (string< "abc" "") ⇒ nil (string< "abc" "ab") ⇒ nil (string< "" "") ⇒ nil 引数としてシンボルを指定することもでき、この場合はシンボルのプリン ト名が比較される。 -- Function: string-lessp string1 string2 ‘string-lessp’は‘string<’の別名である。 -- Function: string-greaterp string1 string2 この関数は逆順でSTRING1とSTRING2を比較した結果をリタンーする。つま りこれは‘(string-lessp STRING2 STRING1)’を呼び出すのと等価である。 -- Function: string-collate-lessp string1 string2 &optional locale ignore-case この関数は照合順でSTRING1がSTRING2より小さければ‘t’をリターンする。 照合順はSTRING1とSTRING2に含まれる文字の辞書順だけではなく、それら の文字間の関係に関するルールによっても判断される。これは通常は Emacs実行中のLOCALE環境により決定される。 たとえばソートでは区切り文字と空白文字は無視されるだろう(*note Sequence Functions::を参照)。 (sort '("11" "12" "1 1" "1 2" "1.1" "1.2") 'string-collate-lessp) ⇒ ("11" "1 1" "1.1" "12" "1 2" "1.2") Cygwinではlocaleと無関係に区切り文字と空白文字が無視されることが決 してないように、この振る舞いはシステム依存である。 オプション引数LOCALE(文字列)は、照合用のカレントlocale識別子 (current locale identifier)をオーバーライドする。値はシステムに依存 する。たとえばPOSIXシステムでは‘"en_US.UTF-8"’、MS-Windowsシステム では‘"enu_USA.1252"’のLOCALEが適用できるだろう。LOCALEの値を ‘"POSIX"’か‘"C"’にすると、‘string-collate-lessp’は‘string-lessp’と 同様に振る舞う。 (sort '("11" "12" "1 1" "1 2" "1.1" "1.2") (lambda (s1 s2) (string-collate-lessp s1 s2 "POSIX"))) ⇒ ("1 1" "1 2" "1.1" "1.2" "11" "12") IGNORE-CASEが非‘nil’なら、それらは比較前に小文字に変換される。 MS-WindowsシステムでUnicode互換の照合をエミュレートする場合、 MS-Windowsではlocaleのコードセット部分を‘"UTF-8"’にできないので、 ‘w32-collate-ignore-punctuation’に非‘nil’値をバインドすること。 locale環境をサポートしないシステムでは、この関数は‘string-lessp’と 同様に振る舞う。 -- Function: string-prefix-p string1 string2 &optional ignore-case この関数はSTRING1がSTRING2のプレフィクス(たとえばSTRING2がSTRING1で 始まる)なら、非‘nil’をリターンする。オプションの引数IGNORE-CASEが非 ‘nil’ばら、比較においてcaseの違いは無視される。 -- Function: string-suffix-p suffix string &optional ignore-case この関数はSUFFIXがSTRINGのサフィックス(たとえばSTRINGがSUFFIXで終わ る)なら、非‘nil’をリターンする。オプションの引数IGNORE-CASEが非 ‘nil’なら、比較においてcaseの違いは無視される。 -- Function: compare-strings string1 start1 end1 string2 start2 end2 &optional ignore-case この関数はSTRING1の指定部分をとSTRING2指定部分を比較する。STRING1の 指定部分とは、インデックスSTART1(その文字を含む)から、インデックス END1(その文字を含まない)まで。START1に‘nil’を指定すると文字列の最初 という意味になり、END1に‘nil’を指定すると文字列の長さを意味する。同 様にSTRING2の指定部分とはインデックスSTART2からインデックスEND2まで 。 文字列は文字列内の文字の数値により比較される。たとえばSTR1とSTR2は 、最初に異なる文字でSTR1の文字の数値が小さければ小さいと判断される 。IGNORE-CASEが非‘nil’なら比較を行なう前に大文字に変換される。比較 用にユニバイト文字列はマルチバイト文字列に変換されるので(*note Text Representations::を参照)、ユニバイト文字列とそれを変換したマルチバ イト文字列は常に等しくなる。 2つの文字列の指定部分がマッチした場合、値は‘t’になる。それ以外なら 値は整数で、何文字が一致してどちらの文字が小さいかを示す。この値の 絶対値は、2つの文字列の先頭から一致した文字数に1加えた値になる。 STRING1(または指定部分)のほうが小さければ符号は負になる。 -- Function: assoc-string key alist &optional case-fold この関数は‘assoc’と同様に機能するが、KEYは文字列かシンボルでなけれ ばならず、比較は‘compare-strings’を使用して行なわれる。テストする前 にシンボルは文字列に変換される。CASE-FOLDが非‘nil’なら、KEYと ALISTの要素は比較前に大文字に変換される。‘assoc’とは異なり、この関 数はコンスではない文字列またはシンボルのalist要素もマッチできる。特 にALISTは実際のalistではなく、文字列またはリストでも可。*note Association Lists::を参照のこと。 バッファー内のテキストを比較する方法として、*note Comparing Text::の 関数‘compare-buffer-substrings’も参照してください。文字列にたいして正規 表現のマッチを行なう関数‘string-match’も、ある種の文字列比較に使用するこ とができます。*note Regexp Search::を参照してください。 4.6 文字および文字列の変換 ========================== このセクションでは文字、文字列、整数の間で変換を行なう関数を説明します。 ‘format’ (*note Formatting Strings::を参照)と‘prin1-to-string’ (*note Output Functions::を参照)もLispオブジェクトを文字列に変換できます。 ‘read-from-string’ (*note Input Functions::を参照)は、Lispオブジェクトの 文字列表現をオブジェクトに“変換”できます。関数‘string-to-multibyte’と ‘string-to-unibyte’は、テキスト表現を文字列に変換します(*note Converting Representations::を参照)。 テキスト文字と一般的なインプットイベントにたいするテキスト記述を生成 する関数(‘single-key-description’と‘text-char-description’)については、 *note Documentation::を参照してください。これらの関数は主にヘルプメッセ ージを作成するために使用されます。 -- Function: number-to-string number この関数はNUMBERの10進プリント表現からなる文字列をリターンする。引 数が負ならリターン値はマイナス記号から開始される。 (number-to-string 256) ⇒ "256" (number-to-string -23) ⇒ "-23" (number-to-string -23.5) ⇒ "-23.5" ‘int-to-string’はこの関数にたいする半ば廃れたエイリアスである。 *note Formatting Strings::の関数‘format’も参照されたい。 -- Function: string-to-number string &optional base この関数はSTRING内の文字の数値的な値をリターンする。BASEが非‘nil’な ら値は2以上16以下でなければならず、整数はその基数に変換される。 BASEが‘nil’なら基数に10が使用される。浮動少数点数の変換は基数が10の ときだけ機能する。わたしたちは浮動小数点数にたいして他の基数を実装 しない。なぜならこれには多くの作業を要し、その割にその機能が有用に は思えないからだ。STRINGが整数のように見えるが、その値がLispの整数 に収まらないほど大きな値なら、‘string-to-number’は浮動小数点数の結 果をリターンする。 パースではSTRINGの先頭にあるスペースとタブはスキップして、与えられ た基数で数字として解釈できるところまでSTRINGを読み取る(スペースとタ ブだけではなく先頭にある他の空白文字を無視するシステムもある)。 STRINGを数字として解釈できなければこの関数は0をリターンする。 (string-to-number "256") ⇒ 256 (string-to-number "25 is a perfect square.") ⇒ 25 (string-to-number "X256") ⇒ 0 (string-to-number "-4.5") ⇒ -4.5 (string-to-number "1e5") ⇒ 100000.0 ‘string-to-int’はこの関数にたいする半ば廃れたエイリアスである。 -- Function: char-to-string character この関数は1つの文字CHARACTERを含む新しい文字列をリターンする。関数 ‘string’のほうがより一般的であり、この関数は半ば廃れている。*note Creating Strings::を参照のこと。 -- Function: string-to-char string この関数はSTRINGの最初の文字をリターンする。これはほとんど‘(aref string 0)’と同じで、例外は文字列が空のときに0をリターンすること(文 字列の最初の文字がASCIIコード0のヌル文字のときも0をリターンする)。 この関数は残すのに充分なほど有用と思えないければ、将来削除されるか もしれない。 以下は文字列へ/からの変換に使用できるその他の関数です: ‘concat’ この関数はベクターまたはリストから文字列に変換する。*note Creating Strings::を参照のこと。 ‘vconcat’ この関数は文字列をベクターに変換する。*note Vector Functions::を参 照のこと。 ‘append’ この関数は文字列をリストに変換する。*note Building Lists::を参照の こと。 ‘byte-to-string’ この関数は文字データのバイトをユニバイト文字列に変換する。*note Converting Representations::を参照のこと。 4.7 文字列のフォーマット ======================== “フォーマット(formatting)”とは、定数文字列内のなまざまな場所を計算された 値で置き換えることにより、文字列を構築することを意味します。この定数文字 列は他の値がプリントされる方法、同様にどこに表示するかを制御します。これ は“フォーマット文字列(format string)”と呼ばれます。 表示されるメッセージを計算するためにフォーマットが便利なことがしばし ばあります。実際に関数‘message’と‘error’は、ここで説明する機能と同じフォ ーマットを提供します。これらの関数と‘format-message’の違いはフォーマット された結果を使用する方法だけです。 -- Function: format string &rest objects この関数はSTRINGをコピーしてから、対応するOBJECTSをエンコードする、 そのコピー内の任意のフォーマット仕様(format specification)を置換し て作成される、新しい文字列をリターンする。引数OBJECTSはフォーマット される計算された値。 (もしあれば)STRING内のフォーマット仕様以外の文字は、テキストプロパ ティーを含めて出力に直接コピーされる。 -- Function: format-message string &rest objects この関数は‘format’と同様に機能するが、‘text-quoting-style’の各値に 応じてSTRING内のすべてのcurved single quotes文字も変換して、グレイ ブアクセント(`)とアポストロフィー(')があたかもcurved single quotes文 字であるかのように扱う点が異なる。 グレイブアクセントとアポストロフィーでクォートされたフォーマット `like this'は、通常はcurved quotesされた‘like this’を生成する。対照 的にアポストロフィーだけでクォートされた'like this'は、通常の英文ス タイルであるは2つのclosing curved quotesでクォートされた’like this’を生成する。変数‘text-quoting-style’が生成されるクォートに影響 を与える方法については、*note Keys in Documentation::を参照のこと。 フォーマット仕様(format specification)は‘%’で始まる文字シーケンスです 。したがってSTRING内に‘%d’がると‘format’はそれを、フォーマットされる値の 1つ(引数OBJECTSのうちの1つ)にたいするプリント表現で置き換えます。たとえ ば: (format "The value of fill-column is %d." fill-column) ⇒ "The value of fill-column is 72." ‘format’は文字‘%’をフォーマット仕様と解釈するので、_決して_最初の引数 に不定な文字列(arbitrary string)を渡すべきではありません。これは特に何ら かのLispコードga生成siた文字列の場合に当てはまります。その文字列が決して 文字‘%’を含まないと_確信_できないならば、以下で説明するように最初の引数 に‘"%s"’を渡して、不定な文字列を2番目の引数として渡します: (format "%s" ARBITRARY-STRING) STRINGに複数のフォーマット仕様が含まれる場合、フォーマット仕様は OBJECTSから連続して値を引き当てます。つまり、STRING内の1番目のフォーマッ ト仕様は1番目の値、2番目のフォーマット仕様は2番目の値、...を使用します。 余分なフォーマット仕様(対応する値がない場合)にはエラーとなります。フォー マットされる値が余分にある場合は無視されます。 ある種のフォーマット仕様は特定の型の値を要求します。その要求に適合し ない値を与えた場合にはエラーがシグナルされます。 以下は有効なフォーマット仕様のテーブルです: ‘%s’ フォーマット仕様を、クォートなしのオブジェクトのプリント表現で置き 換える(つまり‘prin1’ではなく‘princ’を使用して置き換える。*note Output Functions::を参照されたい)。したがって文字列は‘"’文字なしの 文字列内容だけが表示され、シンボルは‘\’文字なしで表される。 オブジェクトが文字列なら文字列のプロパティーは出力にコピーされる。 ‘%s’のテキストプロパティー自身もコピーされるが、オブジェクトのテキ ストプロパティーが優先される。 ‘%S’ フォーマット仕様を、クォートありのオブジェクトのプリント表現で置き 換える(つまり‘prin1’を使用して変換する。*note Output Functions::を 参照されたい)。したがって文字列は‘"’文字で囲まれ、必要となる特別文 字の前に‘\’文字が表示される。 ‘%o’ フォーマット仕様を8進表現の整数で置き換える。 ‘%d’ フォーマット仕様を10進表現の整数で置き換える。 ‘%x’ ‘%X’ フォーマット仕様を16進表現の整数で置き換える。‘%x’なら小文字、 ‘%X’なら大文字が使用される。 ‘%c’ フォーマット仕様を与えられた値の文字で置き換える。 ‘%e’ フォーマット仕様を浮動小数点数の指数表現で置き換える。 ‘%f’ フォーマット仕様を浮動小数点数にたいする10進少数表記で置き換える。 ‘%g’ フォーマット仕様を指数か10進少数のいずれか短いほうの表記を使用した 浮動小数点数で置き換える。 ‘%%’ フォーマット仕様を1つの‘%’で置き換える。このフォーマット仕様は値を 使用しない。たとえば‘(format "%% %d" 30)’は‘"% 30"’をリターンする。 他のフォーマット文字は‘Invalid format operation’エラーとなります。 以下は典型的な‘text-quoting-style’のセッティングを想定した場合の例で す: (format "The octal value of %d is %o, and the hex value is %x." 18 18 18) ⇒ "The octal value of 18 is 22, and the hex value is 12." (format-message "The name of this buffer is ‘%s’." (buffer-name)) ⇒ "The name of this buffer is ‘strings.texi’." (format-message "The buffer object prints as `%s'." (current-buffer)) ⇒ "The buffer object prints as ‘strings.texi’." フォーマット仕様は“フィールド幅(width)”をもつことができます。これは ‘%’とフォーマット仕様文字(specification character)の間の10進の数字です。 そのオブジェクトのプリント表現がこのフィールド幅より少ない文字で構成され る場合、‘format’はパディングによりフィールド幅に拡張します。フォーマット 仕様‘%%’ではフィールド幅の指定は無視されます。フィールド幅指定子により行 なわれるパディングは、通常は左側にスペースを挿入します。 (format "%5d is padded on the left with spaces" 123) ⇒ " 123 is padded on the left with spaces" フィールド幅が小さすぎる場合でも‘format’はオブジェクトのプリント表現を切 り詰めません。したがって情報を失う危険を犯すことなく、フィールドの最小幅 を指定することができます。以下の2つの例では‘%7s’は最小幅に7を指定します 。1番目の例では‘%7s’に挿入される文字列は3文字だけなので、4つのブランクス ペースによりパディングされます。2番目の例では文字列‘"specification"’は 13文字ですが切り詰めはされません。 (format "The word '%7s' has %d letters in it." "foo" (length "foo")) ⇒ "The word ' foo' has 3 letters in it." (format "The word '%7s' has %d letters in it." "specification" (length "specification")) ⇒ "The word 'specification' has 13 letters in it." ‘%’の直後、オプションのフィールド幅指定子の前に“フラグ文字(flag characters)”を置くこともできます。 フラグ‘+’は正の数の前にプラス符号を挿入するので、数には常に符号がつき ます。フラグとしてスペースを指定すると、正数の前に1つのスペースが挿入さ れます(それ以外は、正数は最初の数字から開始される)。これらのフラグは、確 実に正数と負数が同じ列数を使用させるために有用です。これらは‘%d’、‘%e’、 ‘%f’、‘%g’以外では無視され、両方が指定された場合は‘+’が優先されます。 フラグ‘#’は代替形式(alternate form)を指定します。これは使用するフォー マットに依存します。‘%o’にたいしては結果を‘0’で開始させます。‘%x’と ‘%X’にたいしては結果のプレフィクスは‘0x’または‘0X’になります。‘%e’、 ‘%f’、‘%g’にたいしての‘#’フラグは、少数部が0のときにも小数点が含まれるこ とを意味します。 フラグ‘0’はスペースの代わりに文字‘0’でパディングします。このフラグは ‘%s’、‘%S’、‘%c’のような非数値のフォーマット仕様文字では無視されます。こ れらのフォーマット仕様文字で‘0’フラグを指定できますが、それでも_スペース _でパディングされます。 フラグ‘-’はフィールド幅指定子により挿入されるパディングに作用し、もし パディングがあるなら左側ではなく右側にパディングされます。‘-’と‘0’の両方 が指定されると‘0’フラグは無視されます。 (format "%06d is padded on the left with zeros" 123) ⇒ "000123 is padded on the left with zeros" (format "'%-6d' is padded on the right" 123) ⇒ "'123 ' is padded on the right" (format "The word '%-7s' actually has %d letters in it." "foo" (length "foo")) ⇒ "The word 'foo ' actually has 3 letters in it." すべてのフォーマット仕様文字には、その文字の前(フィールド幅がある場合 はその後)に、オプションで“精度(precision)”を指定できます。精度は小数点 ‘.’と、その後に桁文字列(digit-string)を指定します。浮動少数点数のフォー マット仕様(‘%e’、‘%f’、‘%g’)では、精度は表示する小数点以下の桁数を指定し ます。0の場合は小数点も省略されます。‘%s’と‘%S’にたいしては、文字列を精 度で指定された幅に切り詰めます。したがって‘%.3s’では、OBJECTにたいするプ リント表現の最初の3文字だけが表示されます。他のフォーマット仕様文字にた いしては、精度の効果はローカルライブラリーの‘printf’関数ファミリーが生成 する効果となります。 4.8 Lispでの大文字小文字変換 ============================ case変換関数(character case functions)は、1つの文字または文字列中の大文 字小文字を変換します。関数は通常、アルファベット文字(英字‘A’から‘Z’と ‘a’から‘z’、同様に非ASCIIの英字)だけを変換し、それ以外の文字は変換しませ ん。caseテーブル(case table。*note Case Tables::を参照されたい)で指定す ることにより、caseの変換に異なるマッピングを指定できます。 これらの関数は引数として渡された文字列は変更しません。 以下の例では文字‘X’と‘x’を使用します。これらのASCIIコードは88と120で す。 -- Function: downcase string-or-char この関数はSTRING-OR-CHAR(文字か文字列)を小文字に変換する。 STRING-OR-CHARが文字列なら、この関数は引数の大文字を小文字に変換し た新しい文字列をリターンする。STRING-OR-CHARが文字なら、この関数は 対応する小文字(整数)をリターンする。元の文字が小文字か非英字ならリ ターン値は元の文字と同じ。 (downcase "The cat in the hat") ⇒ "the cat in the hat" (downcase ?X) ⇒ 120 -- Function: upcase string-or-char この関数はSTRING-OR-CHAR(文字か文字列)を大文字に変換する。 STRING-OR-CHARが文字列なら、この関数は引数の小文字を大文字に変換し た新しい文字列をリターンする。STRING-OR-CHARが文字なら、この関数は 対応する大文字(整数)をリターンする。元の文字が大文字か非英字ならリ ターン値は元の文字と同じ。 (upcase "The cat in the hat") ⇒ "THE CAT IN THE HAT" (upcase ?x) ⇒ 88 -- Function: capitalize string-or-char この関数は文字列や文字をキャピタライズ(capitalize: 先頭が大文字で残 りは小文字)する。この関数はSTRING-OR-CHARが文字列なら STRING-OR-CHARの各単語をキャピタライズした新たなコピーをリターンす る。これは各単語の最初の文字が大文字に変換され、残りは小文字に変換 されることを意味する。 単語の定義はカレント構文テーブル(current syntax table)の単語構成構 文クラス(word constituent syntax class)に割り当てられた、連続する文 字の任意シーケンスである(*note Syntax Class Table::を参照)。 STRING-OR-CHARが文字ならこの関数は‘upcase’と同じことを行なう。 (capitalize "The cat in the hat") ⇒ "The Cat In The Hat" (capitalize "THE 77TH-HATTED CAT") ⇒ "The 77th-Hatted Cat" (capitalize ?x) ⇒ 88 -- Function: upcase-initials string-or-char この関数はSTRING-OR-CHARが文字列なら、STRING-OR-CHARの中の単語の頭 文字をキャピタライズして、頭文字以外の文字は変更しない。この関数は STRING-OR-CHARの各単語の頭文字が大文字に変換された新しいコピーをリ ターンする。 単語の定義はカレント構文テーブル(current syntax table)の単語構成構 文クラス(word constituent syntax class)に割り当てられた、連続する文 字の任意シーケンスである(*note Syntax Class Table::を参照)。 ‘upcase-initials’の引数が文字なら、‘upcase-initials’の結果は ‘upcase’と同じ。 (upcase-initials "The CAT in the hAt") ⇒ "The CAT In The HAt" 文字列を比較する関数(caseの違いを無視するものや、オプションでcaseの違 いを無視できるもの)については、*note Text Comparison::を参照されたい。 4.9 caseテーブル ================ 特別な“caseテーブル(case table)”をインストールすることにより、caseの変換 をカスタマイズできます。caseテーブルは大文字と小文字の間のマッピングを指 定します。caseテーブルはLispオブジェクトにたいするcase変換関数(前のセク ションを参照)と、バッファー内のテキストに適用される関数の両方に影響しま す。それぞれのバッファーにはcaseテーブルがあります。新しいバッファーの caseテーブルを初期化するために使用される、標準のcaseテーブル(standard case table)もあります。 caseテーブルは、サブタイプが‘case-table’の文字テーブル(char-table。 *note Char-Tables::を参照)です。この文字テーブルはそれぞれの文字を対応す る小文字にマップします。caseテーブルは、関連するテーブルを保持する3つの 余分なスロットをもちます: UPCASE upcase(大文字)テーブルはそれぞれの文字を対応する大文字にマップする 。 CANONICALIZE canonicalize(正準化)テーブルは、caseに関連する文字セットのすべてを 、その文字セットの特別なメンバーにマップする。 EQUIVALENCES equivalence(同値)テーブルは、大の字小文字に関連した文字セットのそれ ぞれを、そのセットの次の文字にマップする。 単純な例では、小文字へのマッピングを指定することだけが必要です。3つの 関連するテーブルは、このマッピングから自動的に計算されます。 大文字と小文字が1対1で対応しない言語もいくつかあります。これらの言語 では、2つの異なる小文字が同じ大文字にマップされます。このような場合、大 文字と小文字の両方にたいするマップを指定する必要があります。 追加のCANONICALIZEテーブルは、それぞれの文字を正準化された等価文字に マップします。caseに関連する任意の2文字は、同じ正準等価文字(canonical equivalent character)をもちます。たとえば‘a’と‘A’はcase変換に関係がある ので、これらの文字は同じ正準等価文字(両方の文字が‘a’、または両方の文字が ‘A’)をもつべきです。 追加のEQUIVALENCESテーブルは、等価クラスの文字(同じ正準等価文字をもつ 文字)それぞれを循環的にマップします(通常のASCIIでは、これは‘a’を‘A’に ‘A’を‘a’にマップし、他の等価文字セットにたいしても同様にマップする)。 caseテーブルを構築する際は、CANONICALIZEに‘nil’を指定できます。この場 合、Emacsは大文字と小文字のマッピングでこのスロットを充填します。 EQUIVALENCESにたいして‘nil’を指定することもできます。この場合、Emacsは CANONICALIZEからこのスロットを充填します。実際に使用されるcaseテーブルで は、これらのコンポーネントは非‘nil’です。CANONICALIZEを指定せずに EQUIVALENCESを指定しないでください。 以下はcaseテーブルに作用する関数です: -- Function: case-table-p object この述語は、OBJECTが有効なcaseテーブルなら非‘nil’をリターンする。 -- Function: set-standard-case-table table この関数はTABLEを標準caseテーブルにして、これ以降に作成される任意の バッファーにたいしてこのテーブルが使用されるようにする。 -- Function: standard-case-table これは標準caseテーブル(standard case table)をリターンする。 -- Function: current-case-table この関数はカレントバッファーのcaseテーブルをリターンする。 -- Function: set-case-table table これはカレントバッファーのcaseテーブルをTABLEにセットする。 -- Macro: with-case-table table body... ‘with-case-table’マクロはカレントcaseテーブルを保存してから、 TABLEをカレントcaseテーブルにセットし、その後にBODYフォームを評価し てから、最後にcaseテーブルをリストアします。リターン値は、BODYの最 後のフォームの値です。‘throw’かエラー(*note Nonlocal Exits::を参照 )により異常終了した場合でも、caseテーブルはリストアされます。 ASCII文字のcase変換を変更する言語環境(language environment)がいくつか あります。たとえばトルコ語の言語環境では、ASCIIの大文字‘I’にたいする小文 字は、トルコ語のドットがないi(‘ı’)です。これは(ASCIIベースのネットワーク プロトコル実装のような)ASCIIの通常のcase変換を要求するコードに干渉する可 能性があります。このような場合には、変数ASCII-CASE-TABLEにたいして ‘with-case-table’マクロを使用してください。これにより変更されていない ASCII文字セットのcaseテーブルが保存されます。 -- Variable: ascii-case-table ASCII文字セットにたいするcaseテーブル。すべての言語環境セッティング において、これを変更するべきではない。 以下の3つの関数は、非ASCII文字セットを定義するパッケージにたいして便 利なサブルーチンです。これらはCASE-TABLEに指定されたcaseテーブルを変更し ます。これは標準構文テーブルも変更します。*note Syntax Tables::を参照し てください。通常これらの関数は、標準caseテーブルを変更するために使用され ます。 -- Function: set-case-syntax-pair uc lc case-table この関数は対応する文字のペアー(一方は大文字でもう一方は小文字)を指 定する。 -- Function: set-case-syntax-delims l r case-table この関数は文字LとRを、case不変区切り(case-invariant delimiter)のマ ッチングペアーとする。 -- Function: set-case-syntax char syntax case-table この関数はCHARを構文SYNTAXのcase不変(case-invariant)とする。 -- Command: describe-buffer-case-table このコマンドはカレントバッファーのcaseテーブルの内容にたいする説明 を表示する。 5 リスト ******** “リスト(list)”は0個以上の要素(任意のLispオブジェクト)のシーケンスを表し ます。リストとベクターの重要な違いは、2つ以上のリストが構造の一部を共有 できることです。加えて、リスト全体をコピーすることなく要素の挿入と削除が できます。 5.1 リストとコンスセル ====================== Lispでのリストは基本データ型ではありません。リストは“コンスセル(cons cells)”から構築されます(*note Cons Cell Type::を参照)。コンスセルは順序 つきペアを表現するデータオブジェクトです。つまりコンスセルは2つのスロッ トをもち、それぞれのスロットはLispオブジェクトを“保持(holds)”または“参照 (refers to)”します。1つのスロットはCAR、もう1つはCDRです(これらの名前は 歴史的なものである。*note Cons Cell Type::を参照されたい)。CDRは “could-er(クダー)”と発音します。 わたしたちは、コンスセルのCARスロットに現在保持されているオブジェクト が何であれ、“このコンスセルのCARは、...”のような言い方をします。これは CDRの場合でも同様です。 リストとは互いに連なる(chained together)一連のコンスセルであり、各セ ルは次のセルを参照します。リストの各要素にたいして1つのコンスセルがあり ます。慣例によりコンスセルのCARはリストの要素を保持し、CDRはリストをチェ ーンするのに使用されます(CARとCDRの間の非対称性は完全に慣例的なものであ る。コンスセルのレベルではCARスロットとCDRスロットは同じようなプロパティ ーをもつ)。したがって、リスト内の各コンスセルのCDRスロットは次のコンスセ ルを参照します。 これも慣例的なものですが、リスト内の最後のコンスセルのCDRは‘nil’です 。わたしたちはこのような‘nil’で終端された構造を、“真リスト(true list)”と 呼びます。Emacs Lispではシンボル‘nil’はシンボルであり、かつ要素なしのリ ストでもあります。便宜上、シンボル‘nil’はそのCDR(とCAR)に‘nil’をもつと考 えます。 したがって真リストのCDRは、常に真リストです。空でない真リストのCDRは 、1番目の要素以外を含む真リストです。 リストの最後のコンスセルのCDRが‘nil’以外の何らかの値の場合、このリス トのプリント表現はドットペア表記(dotted pair notation。*note Dotted Pair Notation::を参照のこと)を使用するので、わたしたちはこの構造を“ドットリス ト(dotted list)”と呼びます。他の可能性もあります。あるコンスセルのCDRが 、そのリストのそれより前にある要素を指すかもしれません。わたしたちは、こ の構造を“循環リスト(circular list)”と呼びます。 ある目的においてはそのリストが真リストか、循環リストなのか、ドットリ ストなのかが問題にならない場合もあります。そのプログラムがリストを充分に 辿って最後のコンスセルのCDRを確認しようとしないなら、これは問題になりま せん。しかしリストを処理する関数のいくつかは真リストを要求し、ドットリス トの場合はエラーをシグナルします。リストの最後を探そうと試みる関数のほと んどは、循環リストを与えると無限ループに突入します。 ほとんどのコンスセルはリストの一部として使用されるので、わたしたちは コンスセルで構成される任意の構造を“リスト構造(list structure)”と呼びます 。 5.2 リストのための述語 ====================== 以下の述語はあるLispオブジェクトがアトムか、コンスセルか、リストなのか、 またはオブジェクトが‘nil’かどうかテストします(これらの述語の多くは他の述 語で定義することもできるが、多用されるので個別に定義する価値がある)。 -- Function: consp object この関数はOBJECTがコンスセルなら‘t’、それ以外は‘nil’をリターンする 。たとえ‘nil’が_リスト_であっても、コンスセルではない。 -- Function: atom object この関数はOBJECTがアトムなら‘t’、それ以外は‘nil’をリターンする。シ ンボル‘nil’はアトムであり、かつリストでもある。そのようなLispオブジ ェクトは‘nil’だけである。 (atom OBJECT) ≡ (not (consp OBJECT)) -- Function: listp object この関数はOBJECTがコンスセルか‘nil’なら‘t’、それ以外は‘nil’をリター ンする。 (listp '(1)) ⇒ t (listp '()) ⇒ t -- Function: nlistp object この関数は‘listp’の反対である。OBJECTがリストでなければ‘t’、それ以 外は‘nil’をリターンする。 (listp OBJECT) ≡ (not (nlistp OBJECT)) -- Function: null object この関数はOBJECTが‘nil’なら‘t’、それ以外は‘nil’をリターンする。この 関数は‘not’と等価だが、明解にするためにOBJECTをリストだと考えるとき は‘null’、真偽値だと考えるときは‘not’を使用すること(*note Combining Conditions::の‘not’を参照)。 (null '(1)) ⇒ nil (null '()) ⇒ t 5.3 リスト要素へのアクセス ========================== -- Function: car cons-cell この関数はコンスセルCONS-CELLの1番目のスロットが参照する値をリター ンする。言い換えるとこの関数はCONS-CELLのCARをリターンする。 特別なケースとしてCONS-CELLが‘nil’の場合、この関数は‘nil’をリターン する。したがってリストはすべて引数として有効である。引数がコンスセ ルでも‘nil’でもなければエラーがシグナルされる。 (car '(a b c)) ⇒ a (car '()) ⇒ nil -- Function: cdr cons-cell この関数はコンスセルCONS-CELLの2番目のスロットにより参照される値を リターンする。言い換えるとこの関数はCONS-CELLのCDRをリターンする。 特別なケースとしてCONS-CELLが‘nil’の場合、この関数は‘nil’をリターン する。したがってリストはすべて引数として有効である。引数がコンスセ ルでも‘nil’でもければエラーがシグナルされる。 (cdr '(a b c)) ⇒ (b c) (cdr '()) ⇒ nil -- Function: car-safe object この関数により他のデータ型によるエラーを起こさずに、コンスセルの CARを取得できり。この関数はOBJECTがコンスセルならOBJECTのCAR、それ 以外は‘nil’をリターンする。この関数は、OBJECTがリストでなければエラ ーをシグナルする‘car’とは対象的である。 (car-safe OBJECT) ≡ (let ((x OBJECT)) (if (consp x) (car x) nil)) -- Function: cdr-safe object この関数により他のデータ型によるエラーを起こさずに、コンスセルの CDRを取得できる。この関数はOBJECTがコンスセルならOBJECTのCDR、それ 以外は‘nil’をリターンする。この関数は、OBJECTがリストでないときはエ ラーをシグナルする‘cdr’とは対象的である。 (cdr-safe OBJECT) ≡ (let ((x OBJECT)) (if (consp x) (cdr x) nil)) -- Macro: pop listname このマクロはリストのCARを調べて、それをリストから取り去るのを一度に 行なう便利な方法を提供する。この関数はLISTNAMEに格納されたリストに たいして処理を行なう。この関数はリストから1番目の要素を削除して、 CDRをLISTNAMEに保存し、その後で削除した要素をリターンする。 もっとも単純なケースは、リストに名前をつけるためのクォートされてい ないシンボルの場合である。この場合、このマクロは ‘(prog1 (car listname) (setq listname (cdr listname)))’と等価である 。 x ⇒ (a b c) (pop x) ⇒ a x ⇒ (b c) より一般的なのはLISTNAMEが汎変数(generalized variable)の場合である 。この場合、このマクロは‘setf’を使用してLISTNAMEに保存する。*note Generalized Variables::を参照のこと。 リストに要素を追加する‘push’マクロについては*note List Variables::を 参照のこと。 -- Function: nth n list この関数はLISTのN番目の要素をリターンする。要素は0から数えられるの でLISTのCARは要素0になる。LISTの長さがN以下なら値は‘nil’。 (nth 2 '(1 2 3 4)) ⇒ 3 (nth 10 '(1 2 3 4)) ⇒ nil (nth n x) ≡ (car (nthcdr n x)) これは関数‘elt’も類似しているが、任意の種類のシーケンスに適用される 。歴史的な理由によりこの関数は逆の順序で引数を受け取る。*note Sequence Functions::を参照のこと。 -- Function: nthcdr n list この関数はLISTのN番目のCDRをリターンする。言い換えると、この関数は LISTの最初のN個のリンクをスキップしてから、それ以降をリターンする。 Nが0なら‘nthcdr’はLIST全体をリターンする。LISTの長さがN以下なら ‘nthcdr’は‘nil’をリターンする。 (nthcdr 1 '(1 2 3 4)) ⇒ (2 3 4) (nthcdr 10 '(1 2 3 4)) ⇒ nil (nthcdr 0 '(1 2 3 4)) ⇒ (1 2 3 4) -- Function: last list &optional n この関数はLISTの最後のリンクをリターンする。このリンクの‘car’はこの リストの最後の要素。LISTがnullなら‘nil’がリターンされる。Nが非 ‘nil’ならN番目から最後までのリンクがリターンされる。NがLISTの長さよ り大きければLIST全体がリターンされる。 -- Function: safe-length list この関数はエラーや無限ループの危険なしで、LISTの長さをリターンする 。この関数は一般的に、リスト内のコンスセルの個数をリターンする。し かし循環リストでは単に上限値が値となるため、非常に大きくなる場合が あります。 LISTが‘nil’]とコンスセルのいずれでもなければ‘safe-length’は0をリタ ーンする。 循環リストを考慮しなくてもよい場合にリストの長さを計算するもっとも一 般的な方法は、‘length’を使う方法です。*note Sequence Functions::を参照し てください。 -- Function: caar cons-cell これは‘(car (car CONS-CELL))’と同じ。 -- Function: cadr cons-cell これは‘(car (cdr CONS-CELL))’か‘(nth 1 CONS-CELL)’と同じ。 -- Function: cdar cons-cell これは‘(cdr (car CONS-CELL))’と同じ。 -- Function: cddr cons-cell これは‘(cdr (cdr CONS-CELL))’か‘(nthcdr 2 CONS-CELL)’と同じ。 -- Function: butlast x &optional n この関数はリストXから、最後の要素か最後のN個の要素を削除してリター ンする。Nが0より大きければこの関数はリストのコピーを作成するので、 元のリストに影響はない。一般的に‘(append (butlast X N) (last X N))’は、Xと等しいリストをリターンする。 -- Function: nbutlast x &optional n この関数はリストのコピーを作成するのではなく、‘cdr’を適切な要素に変 更することにより破壊的に機能するバージョンの‘butlast’である。 5.4 コンスセルおよびリストの構築 ================================ リストはLispの中核にあたる機能なので、リストを構築するために多くの関数が あります。‘cons’はリストを構築する基本的な関数です。しかしEmacsのソース コードでは、‘cons’より‘list’のほうが多く使用されているのは興味深いことで す。 -- Function: cons object1 object2 この関数は新しいリスト構造を構築するための、もっとも基本的な関数で ある。この関数はOBJECT1をCAR、OBJECT2をCDRとする新しいコンスセルを 作成して、それから新しいコンスセルをリターンする。引数OBJECT1と OBJECT2には任意のLispオブジェクトを指定できるが、ほとんどの場合 OBJECT2はリストである。 (cons 1 '(2)) ⇒ (1 2) (cons 1 '()) ⇒ (1) (cons 1 2) ⇒ (1 . 2) リストの先頭に1つの要素を追加するために、‘cons’がよく使用される。こ れを“リストに要素をコンスする”と言います。(1)たとえば: (setq list (cons newelt list)) この例で使用されている‘list’という名前の変数と、以下で説明する ‘list’という名前の関数は競合しないことに注意されたい。すべてのシン ボルが、変数ト関数の両方の役割を果たすことができる。 -- Function: list &rest objects この関数はOBJECTSを要素とするリストを作成する。結果となるリストは常 に‘nil’終端される。OBJECTSを指定しないと空リストがリターンされる。 (list 1 2 3 4 5) ⇒ (1 2 3 4 5) (list 1 2 '(3 4 5) 'foo) ⇒ (1 2 (3 4 5) foo) (list) ⇒ nil -- Function: make-list length object この関数は各要素がOBJECTであるような、LENGTH個の要素からなるリスト を作成する。‘make-list’と‘make-string’(*note Creating Strings::を参 照)を比較してみよ。 (make-list 3 'pigs) ⇒ (pigs pigs pigs) (make-list 0 'pigs) ⇒ nil (setq l (make-list 3 '(a b))) ⇒ ((a b) (a b) (a b)) (eq (car l) (cadr l)) ⇒ t -- Function: append &rest sequences この関数はSEQUENCESのすべての要素を含むリストをreturnします。 SEQUENCESにはリスト、ベクター、ブールベクター、文字列も指定できるが 、通常は最後にリストを指定すること。最後の引数を除くすべての引数は コピーされるので、変更される引数はない(コピーを行なわずにリストを結 合する方法については*note Rearrangement::の‘nconc’を参照のこと)。 より一般的には‘append’にたいする最後の引数は、任意のLispオブジェク トを指定できる。最後の引数はコピーや変換はされない。最後の引数は、 新しいリストの最後のコンスセルのCDRとなる。最後の引数もリストならば 、このリストの要素は実質的には結果リストの要素になる。最後の要素が リストでなければ、最後のCDRが(真リストで要求される)‘nil’ではないの で、結果はドットリストになる。 以下は‘append’を使用した例です: (setq trees '(pine oak)) ⇒ (pine oak) (setq more-trees (append '(maple birch) trees)) ⇒ (maple birch pine oak) trees ⇒ (pine oak) more-trees ⇒ (maple birch pine oak) (eq trees (cdr (cdr more-trees))) ⇒ t ‘append’がどのように機能するか、ボックスダイアグラムで確認できます。 変数‘trees’はリスト‘(pine oak)’にセットされ、それから変数‘more-trees’に リスト‘(maple birch pine oak)’がセットされます。しかし変数‘trees’は継続 して元のリストを参照します: more-trees trees | | | --- --- --- --- -> --- --- --- --- --> | | |--> | | |--> | | |--> | | |--> nil --- --- --- --- --- --- --- --- | | | | | | | | --> maple -->birch --> pine --> oak 空のシーケンスは‘append’によりリターンされる値に寄与しません。この結 果、最後の引数に‘nil’を指定すると、それより前の引数のコピーを強制するこ とになります。 trees ⇒ (pine oak) (setq wood (append trees nil)) ⇒ (pine oak) wood ⇒ (pine oak) (eq wood trees) ⇒ nil 関数‘copy-sequence’が導入される以前は,これがリストをコピーする通常の方法 でした。*note Sequences Arrays Vectors::を参照してください。 以下は‘append’の引数としてベクターと文字列を使用する例です: (append [a b] "cd" nil) ⇒ (a b 99 100) ‘apply’ (*note Calling Functions::を参照)の助けを借りることにより、リ ストのリストの中のすべてのリストをappendできます。 (apply 'append '((a b c) nil (x y z) nil)) ⇒ (a b c x y z) SEQUENCESが与えられなければ‘nil’がリターンされます: (append) ⇒ nil 以下は最後の引数がリストでない場合の例です: (append '(x y) 'z) ⇒ (x y . z) (append '(x y) [z]) ⇒ (x y . [z]) 2番目の例は最後の引数はリストではないシーケンスの場合で、このシーケンス の要素は、結果リストの要素にはなりません。かわりに最後の引数がリストでな いときと同様、シーケンスが最後のCDRになります。 -- Function: copy-tree tree &optional vecp この関数はツリー‘tree’のコピーをリターンする。TREEがコンスセルなら 、同じCARとCDRをもつ新しいコンスセルを作成してから、同じ方法により CARとCDRを再帰的にコピーする。 TREEがコンスセル以外の場合、通常は‘copy-tree’は単にTREEをリターンす る。しかしVECPが非‘nil’なら、この関数はベクターでもコピーします(そ してベクターの要素を再帰的に処理する)。 -- Function: number-sequence from &optional to separation これはFROMからSEPARATIONづつインクリメントして、TOの直前で終わる、 数字のリストをリターンする。SEPARATIONには正か負の数を指定でき、デ フォルトは1。TOが‘nil’、または数値的にFROMと等しければ、値は1要素の リスト‘(FROM)’になる。SEPARATIONが正でTOがFROMより小さい、または SEPARATIONが負でTOがFROMより大きければ、これらの引数は空のシーケン スを指示することになるので、値は‘nil’になります。 SEPARATIONが0で、TOが‘nil’でもなく、数値的にFROMとも等しくまければ 、これらの引数は無限シーケンスを指示することになるので、エラーがシ グナルされる。 引数はすべて数字である。浮動少数点数の計算は正確ではないので、浮動 少数点数の引数には注意する必要がある。たとえばマシンへの依存により 、‘(number-sequence 0.4 0.8 0.2)’が3要素のリストをリターンして、 ‘(number-sequence 0.4 0.6 0.2)’が1要素のリスト‘(0.4)’をリターンnす ることがよく起こる。リストのN番目の要素は、厳密に‘(+ FROM (* N SEPARATION))’という式により計算される。リストに確実にTOが含まれるよ うにするために、この式に適切な型のTOを渡すことができる。別の方法と してTOを少しだけ大きな値(SEPARATIONが負なら少しだけ小さな値)に置き 換えることもできる。 例をいくつか示す: (number-sequence 4 9) ⇒ (4 5 6 7 8 9) (number-sequence 9 4 -1) ⇒ (9 8 7 6 5 4) (number-sequence 9 4 -2) ⇒ (9 7 5) (number-sequence 8) ⇒ (8) (number-sequence 8 5) ⇒ nil (number-sequence 5 8 -1) ⇒ nil (number-sequence 1.5 6 2) ⇒ (1.5 3.5 5.5) ---------- Footnotes ---------- (1) リストの最後に要素を追加するための、これと完全に同等な方法はあり ません。LISTNAMEをコピーすることにより新しいリストを作成してから、 NEWELTをそのリストの最後に追加する‘(append LISTNAME (list NEWELT))’を使 用することができます。すべてのCDRを辿って終端の‘nil’を置き換える、 ‘(nconc LISTNAME (list NEWELT))’を使用することもできます。コピーも変更も 行なわずにリストの先頭に要素を追加する‘cons’と比較してみてください。 5.5 リスト変数の変更 ==================== 以下の関数と1つのマクロは、変数に格納されたリストを変更する便利な方法を 提供します。 -- Macro: push element listname このマクロはCARがELEMENTで、CDRがLISTNAMEのリストであるような新しい リストを作成して、そのリストをLISTNAMEに保存する。LISTNAMEがリスト に名前をつけるクォートされていないシンボルのときは単純で、この場合 マクロは‘(setq LISTNAME (cons ELEMENT LISTNAME))’と等価になる。 (setq l '(a b)) ⇒ (a b) (push 'c l) ⇒ (c a b) l ⇒ (c a b) より一般的なのは‘listname’が汎変数の場合である。この場合、このマク ロは‘(setf LISTNAME (cons ELEMENT LISTNAME))’と等価になる。*note Generalized Variables::を参照のこと。 リストから1番目の要素を取り出す‘pop’マクロについては、*note List Elements::を参照されたい。 以下の2つの関数は、変数の値であるリストを変更します。 -- Function: add-to-list symbol element &optional append compare-fn この関数はELEMENTがSYMBOLの値のメンバーでなければ、SYMBOLに ELEMENTをコンスすることにより、変数SYMBOLをセットする。この関数はリ ストが更新されているか否かに関わらず、結果のリストをリターンする。 SYMBOLの値は呼び出し前にすでにリストであることが望ましい。ELEMENTが リストの既存メンバーか比較するために、‘add-to-list’はCOMPARE-FNを使 用する。COMPARE-FNが‘nil’なら‘equal’を使用する。 ELEMENTが追加される場合は、通常はSYMBOLの前に追加されるが、オプショ ンの引数APPENDが非‘nil’なら最後に追加される。 引数SYMBOLは暗黙にクォートされない。‘setq’とは異なり‘add-to-list’は ‘set’のような通常の関数である。クォートしたい場合には自分で引数をク ォートすること。 以下に‘add-to-list’を使用する方法をシナリオで示します: (setq foo '(a b)) ⇒ (a b) (add-to-list 'foo 'c) ;; ‘c’を追加 ⇒ (c a b) (add-to-list 'foo 'b) ;; 効果なし ⇒ (c a b) foo ;; ‘foo’が変更された ⇒ (c a b) 以下は‘(add-to-list 'VAR VALUE)’と等価な式です: (or (member VALUE VAR) (setq VAR (cons VALUE VAR))) -- Function: add-to-ordered-list symbol element &optional order この関数は古い値のORDER (リストであること)で指定された位置に、 ELEMENTを挿入して変数SYMBOLをセットする。ELEMENTがすでにこのリスト のメンバなら、リスト内の要素の位置はORDERにしたがって調整される。メ ンバーか否かは‘eq’を使用してテストされる。この関数は更新されている かどうかに関わらず、結果のリストをリターンする。 ORDERは通常は数字(整数か浮動小数点数)で、リストの要素はその数字の昇 順で並べられる。 ORDERは省略または‘nil’を指定できる。これによりリストにELEMENTがすで に存在するなら、ELEMENTの数字順序は変更されない。それ以外なら ELEMENTは数字順序をもたない。リストの数字順序をもたない要素はリスト の最後に配置され、特別な順序はつかない。 ORDERに他の値を指定すると、ELEMENTがすでに数字順序をもつときは数字 順序が削除される。それ以外はなら‘nil’と同じ。 引数SYMBOLは暗黙にクォートされない。‘add-to-ordered-list’は‘setq’な どとは異なり、‘set’のような通常の関数である。必要なら引数を自分でク ォートすること。 順序の情報はSYMBOLの‘list-order’プロパティーにハッシュテーブルで保 存される。 以下に‘add-to-ordered-list’を使用する方法をシナリオで示します: (setq foo '()) ⇒ nil (add-to-ordered-list 'foo 'a 1) ;; ‘a’を追加 ⇒ (a) (add-to-ordered-list 'foo 'c 3) ;; ‘c’を追加 ⇒ (a c) (add-to-ordered-list 'foo 'b 2) ;; ‘b’を追加 ⇒ (a b c) (add-to-ordered-list 'foo 'b 4) ;; ‘b’を移動 ⇒ (a c b) (add-to-ordered-list 'foo 'd) ;; ‘d’を後に追加 ⇒ (a c b d) (add-to-ordered-list 'foo 'e) ;; ‘e’を追加 ⇒ (a c b e d) foo ;; ‘foo’が変更された ⇒ (a c b e d) 5.6 既存のリスト構造の変更 ========================== プリミティブ関数‘setcar’と‘setcdr’により、コンスセルのCARおよびCDRの内容 を変更できます。わたしたちは、これらは既存のリスト構造を変更するので、こ れらを破壊的処理です。 Common Lispに関する注意: Common Lispはリスト構造の変更に‘rplaca’と ‘rplacd’を使用する。これらは‘setcar’や‘setcdr’と同じ方法でリスト構 造を変更するが、‘setcar’と‘setcdr’は新しいCARやCDRをリターンするの にたいして、Common Lispの関数はコンスセルをリターンする。 5.6.1 ‘setcar’によるリスト要素の変更 ------------------------------------ コンスセルのCARの変更は‘setcar’で行ないます。リストにたいして使用すると ‘setcar’はリストの1つの要素を別の要素に置き換えます。 -- Function: setcar cons object この関数は以前のCARを置き換えて、CONSの新しいCARにOBJECTを格納する 。言い換えると、この関数はCONSのCARスロットをOBJECTを参照するように 変更する。この関数は値OBJECTをリターンする。たとえば: (setq x '(1 2)) ⇒ (1 2) (setcar x 4) ⇒ 4 x ⇒ (4 2) コンスセルが複数のリストを共有する構造の一部なら、コンスに新しいCARを 格納することにより、これら共有されたリストの各1つの要素を変更します。以 下は例です: ;; 部分的に共有された2つのリストを作成 (setq x1 '(a b c)) ⇒ (a b c) (setq x2 (cons 'z (cdr x1))) ⇒ (z b c) ;; 共有されたリンクのCARを置き換え (setcar (cdr x1) 'foo) ⇒ foo x1 ; 両方のリストが変更された ⇒ (a foo c) x2 ⇒ (z foo c) ;; 共有されていないリンクのCARを置き換え (setcar x1 'baz) ⇒ baz x1 ; 1つのリストだけが変更された ⇒ (baz foo c) x2 ⇒ (z foo c) なぜ‘b’を置き換えると両方が変更されるのかを説明するために、変数‘x1’と ‘x2’の2つのリストによる共有構造を視覚化してみましょう: --- --- --- --- --- --- x1---> | | |----> | | |--> | | |--> nil --- --- --- --- --- --- | --> | | | | | | --> a | --> b --> c | --- --- | x2--> | | |-- --- --- | | --> z 同じ関係を別のボックス図で示すと、以下のようになります: x1: -------------- -------------- -------------- | car | cdr | | car | cdr | | car | cdr | | a | o------->| b | o------->| c | nil | | | | -->| | | | | | -------------- | -------------- -------------- | x2: | -------------- | | car | cdr | | | z | o---- | | | -------------- 5.6.2 リストのCDRの変更 ----------------------- CDRを変更するもっとも低レベルのプリミティブ関数は‘setcdr’です: -- Function: setcdr cons object この関数は前のCDRを置き換えて、CONSの新しいCDRにOBJECTを格納する。 言い換えると、この関数はCONSのCDRがOBJECTを参照するように変更する。 この関数は値OBJECTをリターンする。 以下はリストのCDRを、他のリストに置き換える例です。1番目の要素以外の すべての要素は、別のシーケンスまたは要素のために取り除かれます。1番目の 要素はリストのCARなので変更されず、CDRを通じて到達することもできないから です。 (setq x '(1 2 3)) ⇒ (1 2 3) (setcdr x '(4)) ⇒ (4) x ⇒ (1 4) リスト内のコンスセルのCDRを変更することにより、リストの途中から要素を 削除できます。たとえば以下では、1番目のコンスセルのCDRを変更することによ り、2番目の要素‘b’をリスト‘(a b c)’から削除します。 (setq x1 '(a b c)) ⇒ (a b c) (setcdr x1 (cdr (cdr x1))) ⇒ (c) x1 ⇒ (a c) 以下に結果をボックス表記で示します: -------------------- | | -------------- | -------------- | -------------- | car | cdr | | | car | cdr | -->| car | cdr | | a | o----- | b | o-------->| c | nil | | | | | | | | | | -------------- -------------- -------------- 以前は要素‘b’を保持していた2番目のコンスセルは依然として存在し、その CARも‘b’のままですが、すでにこのリストの一部を形成していません。 CDRを変更して新しい要素を挿入するのも同じくらい簡単です: (setq x1 '(a b c)) ⇒ (a b c) (setcdr x1 (cons 'd (cdr x1))) ⇒ (d b c) x1 ⇒ (a d b c) 以下に結果をボックス表記で示します: -------------- ------------- ------------- | car | cdr | | car | cdr | | car | cdr | | a | o | -->| b | o------->| c | nil | | | | | | | | | | | | --------- | -- | ------------- ------------- | | ----- -------- | | | --------------- | | | car | cdr | | -->| d | o------ | | | --------------- 5.6.3 リストを再配置する関数 ---------------------------- 以下ではリストの構成要素であるコンスセルのCDRを変更することにより、リス トを破壊的に再配置する関数をいくつか示します。これらの関数が破壊的だとい う理由は、これらの関数が引数として渡された元のリストを処理してリターン値 となる新しいリストを形成するために、リストのコンスセルを再リンクするから です。 コンスセルを変更する他の関数については、*note Sets And Lists::の ‘delq’を参照してください。 -- Function: nconc &rest lists この関数はLISTSの要素すべてを含むリストをリターンする。‘append’ (*note Building Lists::を参照)とは異なり、LISTSは_コピーされない_。 かわりにLISTSの各リストの最後のCDRが次のリストを参照するように変更 される。LISTSの最後のリストは変更されない。たとえば: (setq x '(1 2 3)) ⇒ (1 2 3) (nconc x '(4 5)) ⇒ (1 2 3 4 5) x ⇒ (1 2 3 4 5) ‘nconc’の最後の引数は変更されないので、上記の例のように‘'(4 5)’のよ うな定数リストを使用するのが合理的である。また同じ理由により最後の 引数がリストである必要はない。 (setq x '(1 2 3)) ⇒ (1 2 3) (nconc x 'z) ⇒ (1 2 3 . z) x ⇒ (1 2 3 . z) しかし他の(最後を除くすべての)引数はリストでなければなければならな い。 一般的な落とし穴としては、‘nconc’にたいしてクォートされたリスト定数 を最後以外の引数として使用した場合である。これを行なうと、実行する ごとにプログラムはリスト定数を変更するだろう! 何が起こるのかを以下 に示す: (defun add-foo (x) ; この関数では‘foo’ (nconc '(foo) x)) ; を引数の前に追加したい (symbol-function 'add-foo) ⇒ (lambda (x) (nconc (quote (foo)) x)) (setq xx (add-foo '(1 2))) ; 動いているように見える ⇒ (foo 1 2) (setq xy (add-foo '(3 4))) ; 何が起きているのか? ⇒ (foo 1 2 3 4) (eq xx xy) ⇒ t (symbol-function 'add-foo) ⇒ (lambda (x) (nconc (quote (foo 1 2 3 4) x))) 5.7 集合としてのリストの使用 ============================ リストは順序なしの数学的集合 — リスト内に要素があれば集合の要素の値とし てリスト内の順序は無視される — を表すことができます。2つの集合を結合 (union)するには、(重複する要素を気にしなければ)‘append’を使用します。 ‘equal’である重複を取り除くには‘delete-dups’を使用します。集合にたいする 他の有用な関数には‘memq’や‘delq’や、それらの‘equal’バージョンである ‘member’と‘delete’が含まれます。 Common Lispに関する注意: 集合を処理するためにCommon Lispには(要素の 重複がない)関数‘union’がある。これらの関数は標準のGNU Emacs Lispに は存在しないが、‘cl-lib’がこれらを提供する。*note (cl)Lists as Sets::を参照されたい。 -- Function: memq object list この関数はOBJECTがLISTのメンバーかどうかをテストする。メンバーなら ‘memq’は、OBJECTで最初に見つかった要素から開始されるリストをリター ンする。メンバーでなければ‘nil’をリターンする。‘memq’の文字‘q’は、 この関数がOBJECTとリスト内の要素の比較に‘eq’を使用することを示す。 たとえば: (memq 'b '(a b c b a)) ⇒ (b c b a) (memq '(2) '((1) (2))) ; ‘(2)’と‘(2)’は‘eq’ではない。 ⇒ nil -- Function: delq object list この関数はLISTからOBJECTと‘eq’であるような、すべての要素を破壊的に 取り除いて結果のリストをリターンする。‘delq’の文字‘q’は、この関数が OBJECTとリスト内の要素の比較に‘eq’を使用することを示す(‘memq’や ‘remq’と同様)。 ‘delq’を呼び出すときは、通常は元のリストを保持していた変数にリター ン値を割り当てて使用する必要がある(理由は以下参照)。 ‘delq’関数がリストの先頭にある要素を削除する場合は、単にリストを読み 進めてこの要素の後から開始される部分リストをリターンします。つまり: (delq 'a '(a b c)) ≡ (cdr '(a b c)) リストの途中にある要素を削除するときは、必要なCDR (*note Setcdr::を参照 )を変更することで削除を行います。 (setq sample-list '(a b c (4))) ⇒ (a b c (4)) (delq 'a sample-list) ⇒ (b c (4)) sample-list ⇒ (a b c (4)) (delq 'c sample-list) ⇒ (a b (4)) sample-list ⇒ (a b (4)) ‘(delq 'a sample-list)’は何も取り除きませんが(単に短いリストをリター ンする)、‘(delq 'c sample-list)’は3番目の要素を取り除いて‘sample-list’を 変更することに注意してください。引数LISTを保持するように形成された変数が 、実行後にもっと少ない要素になるとか、元のリストを保持すると仮定しないで ください! かわりに‘delq’の結果を保存して、それを使用してください。元のリ ストを保持していた変数に結果を書き戻すことはよく行なわれます。 (setq flowers (delq 'rose flowers)) 以下の例では、‘delq’が比較しようとしている‘(4)’と、‘sample-list’内の ‘(4)’は‘eq’ではありません: (delq '(4) sample-list) ⇒ (a c (4)) 与えられた値と‘equal’な要素を削除したい場合には、‘delete’ (以下参照 )を使用してください。 -- Function: remq object list この関数はOBJECTと‘eq’なすべての要素が除かれた、LISTのコピーをリタ ーンする。‘remq’の文字‘q’は、この関数がOBJECTとリスト内の要素の比較 に‘eq’を使用することを示す。 (setq sample-list '(a b c a b c)) ⇒ (a b c a b c) (remq 'a sample-list) ⇒ (b c b c) sample-list ⇒ (a b c a b c) -- Function: memql object list 関数‘memql’は‘eql’(浮動少数点数の要素は値で比較される)を使用してメ ンバーと‘eql’を比較することにより、OBJECTがLISTのメンバーかどうかを テストする。OBJECTがメンバーなら、‘memql’はLIST内で最初に見つかった 要素から始まるリスト、それ以外なら‘nil’をリターンする。 ‘memq’と比較してみよう: (memql 1.2 '(1.1 1.2 1.3)) ; ‘1.2’と‘1.2’は‘eql’。 ⇒ (1.2 1.3) (memq 1.2 '(1.1 1.2 1.3)) ; ‘1.2’と‘1.2’は‘eq’ではない。 ⇒ nil 以下の3つの関数は‘memq’、‘delq’、‘remq’と似ていますが、要素の比較に ‘eq’ではなく‘equal’を使用します。*note Equality Predicates::を参照してく ださい。 -- Function: member object list 関数‘member’は、メンバーとOBJECTを‘equal’を使用して比較して、 OBJECTがLISTのメンバーかどうかをテストする。OBJECTがメンバーなら、 ‘member’はLISTで最初に見つかったところから開始されるリスト、それ以 外なら‘nil’をリターンする。 ‘memq’と比較してみよう: (member '(2) '((1) (2))) ; ‘(2)’ and ‘(2)’ are ‘equal’. ⇒ ((2)) (memq '(2) '((1) (2))) ; ‘(2)’と‘(2)’は‘eq’ではない。 ⇒ nil ;; 同じ内容の2つの文字列は‘equal’ (member "foo" '("foo" "bar")) ⇒ ("foo" "bar") -- Function: delete object sequence この関数はSEQUENCEからOBJECTと‘equal’な要素を取り除いて、結果のシー ケンスをリターンする。 SEQUENCEがリストなら、‘delete’が‘delq’に対応するように、‘member’は ‘memq’に対応する。つまりこの関数は‘member’と同様、要素とOBJECTの比 較に‘equal’を使用する。マッチする要素が見つかったら、‘delq’が行なう ようにその要素を取り除く。‘delq’と同様、通常は元のリストを保持して いた変数にリターン値を割り当てて使用する。 ‘sequence’がベクターか文字列なら、‘delete’は‘object’と‘equal’なすべ ての要素を取り除いた‘sequence’のコピーをリターンする。 たとえば: (setq l '((2) (1) (2))) (delete '(2) l) ⇒ ((1)) l ⇒ ((2) (1)) ;; ‘l’の変更に信頼性を要するときは ;; ‘(setq l (delete '(2) l))’と記述する。 (setq l '((2) (1) (2))) (delete '(1) l) ⇒ ((2) (2)) l ⇒ ((2) (2)) ;; このケースでは‘l’のセットの有無に違い ;; はないが他のケースに倣ってセットするべき (delete '(2) [(2) (1) (2)]) ⇒ [(1)] -- Function: remove object sequence この関数は‘delete’に対応する非破壊的な関数である。この関数は ‘object’と‘equal’な要素を取り除いた、‘sequence’(リスト、ベクター、 文字列)のコピーをリターンする。たとえば: (remove '(2) '((2) (1) (2))) ⇒ ((1)) (remove '(2) [(2) (1) (2)]) ⇒ [(1)] Common Lispに関する注意: GNU Emacs Lispの関数‘member’、‘delete’、 ‘remove’はCommon Lispではなく、Maclispを継承する。Common Lispでは比 較に‘equal’を使用しない。 -- Function: member-ignore-case object list この関数は‘member’と同様だが、OBJECTが文字列でcaseとテキスト表現の 違いを無視する。文字の大文字と小文字は等しいものとして扱われ、比較 に先立ちユニバイト文字列はマルチバイト文字列に変換される。 -- Function: delete-dups list この関数はLISTからすべての‘equal’な重複を破壊的に取り除いて、結果を LISTに保管してそれをリターンする。LIST内の要素に‘equal’な要素がいく つかあるなら、‘delete-dups’は最初の要素を残す。 変数に格納されたリストへの要素の追加や、それを集合として使用する方法 については、*note List Variables::の関数‘add-to-list’も参照してください 。 5.8 連想リスト ============== “連想配列(association list、短くはalist)”は、キーと値のマッピングを記録 します。これは“連想(associations)”と呼ばれるコンスセルのリストです。各コ ンスセルにおいてCARは“キー(key)”で、CDRは“連想値(associated value)”とな ります。(1) 以下はalistの例です。キー‘pine’は値‘cones’、キー‘oak’は‘acorns’、キー ‘maple’は‘seeds’に関連付けられます。 ((pine . cones) (oak . acorns) (maple . seeds)) alist内の値とキーには、任意のLispオブジェクトを指定できます。たとえば 以下のalist0では、シンボル‘a’は数字‘1’、文字列‘"b"’は_リスト_‘(2 3)’(alist要素のCDR)に関連付けられます。 ((a . 1) ("b" 2 3)) 要素のCDRのCARに連想値を格納するようにalistデザインするほうがよい場合 があります。以下はそのようなalistです。 ((rose red) (lily white) (buttercup yellow)) この例では、‘red’が‘rose’に関連付けられる値だと考えます。この種のalistの 利点は、CDRのCDRの中に他の関連する情報 — 他のアイテムのリストでさえも — を格納することができることです。不利な点は、与えられた値を含む要素を見つ けるために‘rassq’(以下参照)を使用できないことです。これらを検討すること が重要でない場合には、すべての与えられたalistにたいして一貫している限り 、選択は好みの問題といえます。 上記で示したのと同じalistは、要素のCDRに連想値をもつと考えることがで きます。この場合、‘rose’に関連付けられる値はリスト‘(red)’になるでしょう 。 連想リストは新しい連想値を簡単にリストの先頭に追加できるので、スタッ クに保持したいような情報を記録するのによく使用されます。連想リストから与 えられたキーにたいして連想値を検索する場合、それが複数ある場合は、最初に 見つかったものがreturnされます。 Emacs Lispでは、連想リストがコンスセルでなくても、それは_エラーではあ りません_。alist検索関数は、単にそのような要素を無視します。多くの他のバ ージョンのLispでは、このような場合はエラーをシグナルします。 いくつかの観点において、プロパティーリストは連想リストと似ていること に注意してください。それぞれのキーが一度だけ出現するような場合、プロパテ ィーリストは連想リストと同様に振る舞います。プロパティーリストと連想リス トの比較については、*note Property Lists::を参照してください。 -- Function: assoc key alist この関数は、alist要素にたいしてKEYを比較するのに‘equal’を使用して、 ALIST内からKEYをもつ最初の連想をリターンする。CARがKEYと‘equal’であ るような連想値がALISTになければ、この関数は‘nil’をリターンする。た とえば: (setq trees '((pine . cones) (oak . acorns) (maple . seeds))) ⇒ ((pine . cones) (oak . acorns) (maple . seeds)) (assoc 'oak trees) ⇒ (oak . acorns) (cdr (assoc 'oak trees)) ⇒ acorns (assoc 'birch trees) ⇒ nil 以下はキーと値がシンボルでない場合の例である: (setq needles-per-cluster '((2 "Austrian Pine" "Red Pine") (3 "Pitch Pine") (5 "White Pine"))) (cdr (assoc 3 needles-per-cluster)) ⇒ ("Pitch Pine") (cdr (assoc 2 needles-per-cluster)) ⇒ ("Austrian Pine" "Red Pine") 関数‘assoc-string’は‘assoc’と似ていますが、文字列間の特定の違いを無視 する点が異なります。*note Text Comparison::を参照してください。 -- Function: rassoc value alist この関数はALISTの中から値VALUEをもつ最初の連想をリターンする。CDRが VALUEと‘equal’であるような連想値がALISTになければ、この関数は ‘nil’をリターンする。 ‘rassoc’は‘assoc’と似てイルが、CARではなくALISTの連想値のCDRを比較 する。この関数は与えられた値に対応するキーを探す、‘assoc’の逆バージ ョンと考えることができよう。 -- Function: assq key alist この関数は、ALISTからKEYをもつ最初の連想値をリターンする点は ‘assoc’と同様だが、比較に‘equal’ではなく‘eq’を使用する点が異なる。 CARがKEYと‘eq’であるような連想値がALIST内に存在しなければ、‘assq’は ‘nil’をリターンする。‘eq’は‘equal’より早く、ほとんどのalistはキーに シンボルを使用するので、この関数は‘assoc’より多用される。*note Equality Predicates::を参照のこと。 (setq trees '((pine . cones) (oak . acorns) (maple . seeds))) ⇒ ((pine . cones) (oak . acorns) (maple . seeds)) (assq 'pine trees) ⇒ (pine . cones) 逆にキーがシンボルではないalistでは、通常は‘assq’は有用ではない: (setq leaves '(("simple leaves" . oak) ("compound leaves" . horsechestnut))) (assq "simple leaves" leaves) ⇒ nil (assoc "simple leaves" leaves) ⇒ ("simple leaves" . oak) -- Function: alist-get key value &optional default remove この関数は‘assq’と似ているが、KEYにたいする連想値全体‘(KEY . VALUE)’ではなく、VALUEだけをリターンする点が異なる。KEYがALIST内で 見つからなければDEFAULTをリターンする。 これは‘setf’での値の変更に使用できる汎変数である(*note Generalized Variables::を参照)。値の値へのセットにこれを使用する際、オプション 引数REMOVEが非nilの場合は、新たな値がDEFAULTと‘eql’なら、ALISTから KEYを削除することを意味する。 -- Function: rassq value alist この関数は、ALIST内から値VALUEをもつ最初の連想値をリターンする。 ALIST内にCDRがVALUEと‘eq’であるような連想値が存在しないなら‘nil’を リターンする。 ‘rassq’は‘assq’と似ていますが、CARではなくALISTの各連想のCDRを比較 します。この関数を、与えられた値に対応するキーを探す‘assq’の逆バー ジョンと考えることができます。 たとえば: (setq trees '((pine . cones) (oak . acorns) (maple . seeds))) (rassq 'acorns trees) ⇒ (oak . acorns) (rassq 'spores trees) ⇒ nil ‘rassq’は要素のCDRのCARに保管された値の検索はできません: (setq colors '((rose red) (lily white) (buttercup yellow))) (rassq 'white colors) ⇒ nil この場合、連想‘(lily white)’のCDRは‘white’ではなくリスト‘(white)’で す。これは連想をドットペア表記で記述すると明確になります: (lily white) ≡ (lily . (white)) -- Function: assoc-default key alist &optional test default この関数は、KEYにたいするマッチをALISTから検索する。ALISTの各要素に たいして、この関数はKEYと要素(アトムの場合)、または要素のCAR(コンス の場合)を比較する。比較はTESTに2つの引数 — 要素(か要素のCAR)とKEY — を与えて呼び出すことにより行なわれる。引数はこの順番で渡されるので 、正規表現(*note Regexp Search::を参照)を含むalistでは、 ‘string-match’を使用することにより有益な結果を得ることができる。 TESTが省略または‘nil’なら比較に‘equal’が使用される。 alistの要素がこの条件によりKEYとマッチすると、‘assoc-default’はその 要素の値をリターンする。要素がコンスなら値は要素のCDR、それ以外なら リターン値はDEFAULTとなる。 KEYにマッチする要素がalistに存在しないければ、‘assoc-default’は ‘nil’をリターンする。 -- Function: copy-alist alist この関数は深さのレベルが2のALISTのコピーをリターンする。この関数は 各連想の新しいコピーを作成するので、元のalistを変更せずに新しい alistを変更できる。 (setq needles-per-cluster '((2 . ("Austrian Pine" "Red Pine")) (3 . ("Pitch Pine")) (5 . ("White Pine")))) ⇒ ((2 "Austrian Pine" "Red Pine") (3 "Pitch Pine") (5 "White Pine")) (setq copy (copy-alist needles-per-cluster)) ⇒ ((2 "Austrian Pine" "Red Pine") (3 "Pitch Pine") (5 "White Pine")) (eq needles-per-cluster copy) ⇒ nil (equal needles-per-cluster copy) ⇒ t (eq (car needles-per-cluster) (car copy)) ⇒ nil (cdr (car (cdr needles-per-cluster))) ⇒ ("Pitch Pine") (eq (cdr (car (cdr needles-per-cluster))) (cdr (car (cdr copy)))) ⇒ t 以下の例は、どのようにして‘copy-alist’が他に影響を与えずにコピーの 連想を変更可能なのかを示す: (setcdr (assq 3 copy) '("Martian Vacuum Pine")) (cdr (assq 3 needles-per-cluster)) ⇒ ("Pitch Pine") -- Function: assq-delete-all key alist この関数は、‘delq’を使用してマッチする要素を1つずつ削除するときのよ うに、CARがKEYと‘eq’であるようなすべての要素をALISTから削除する。こ の関数は短くなったalistをリターンし、ALISTの元のリスト構造を変更す ることもよくある。正しい結果を得るために、ALISTに保存された値ではな く‘assq-delete-all’のリターン値を使用すること。 (setq alist '((foo 1) (bar 2) (foo 3) (lose 4))) ⇒ ((foo 1) (bar 2) (foo 3) (lose 4)) (assq-delete-all 'foo alist) ⇒ ((bar 2) (lose 4)) alist ⇒ ((foo 1) (bar 2) (lose 4)) -- Function: rassq-delete-all value alist この関数は、ALISTからCDRがVALUEと‘eq’であるようなすべての要素を削除 する。この関数は短くなったリストをリターンし、ALISTの元のリスト構造 を変更することもよくある。‘rassq-delete-all’は‘assq-delete-all’と似 ているが、CARではなくALISTの各連想のCDRを比較する。 ---------- Footnotes ---------- (1) ここでの“キー(key)”の使い方は、用語“キーシーケンス(key sequence)”とは関係ありません。キーはテーブルにあるアイテムを探すために使 用される値という意味です。この場合、テーブルはalistでありalistはアイテム に関連付けられます。 5.9 プロパティリスト ==================== “プロパティーリスト(property list、短くはplist)”は、ペアになった要素のリ ストです。各ペアはプロパティー名(通常はシンボル)とプロパティー値を対応づ けます。以下はプロパティーリストの例です: (pine cones numbers (1 2 3) color "blue") このプロパティーリストは‘pine’を‘cones’、‘numbers’を‘(1 2 3)’、‘color’を ‘"blue"’に関連づけます。プロパティー名とプロパティー値には任意のLispオブ ジェクトを指定できますが、通常プロパティー名は(この例のように)シンボルで す。 いくつかのコンテキストでプロパティーリストが使用されます。たとえば関 数‘put-text-property’はプロパティーリストを引数にとり、文字列やバッファ ー内のテキストにたいして、テキストプロパティーとテキストに適用するプロパ ティー値を指定します。*note Text Properties::を参照してください。 プロパティーリストが頻繁に使用される他の例は、シンボルプロパティーの 保管です。すべてのシンボルはシンボルに関する様々な情報を記録するために、 プロパティーのリストを処理します。これらのプロパティーはプロパティーリス トの形式で保管されます。*note Symbol Properties::を参照してください。 5.9.1 プロパティリストと連想リスト ---------------------------------- 連想リスト(*note Association Lists::を参照)は、プロパティーリストとよく 似ています。連想リストとは対照的にプロパティー名は一意でなければならない ので、プロパティーリスト内でペアの順序に意味はありません。 様々なLisp関数や変数に情報を付加するためには、連想リストよりプロパテ ィーリストの方が適しています。プログラムでこのような情報すべてを1つの連 想リストに保持する場合は、特定のLisp関数や変数にたいする連想をチェックす る度にリスト全体を検索する必要が生じ、それにより遅くなる可能性があります 。対照的に関数名や変数自体のプロパティーリストに同じ情報を保持すれば、検 索ごとにそのプロパティーリストの長さだけを検索するようになり、通常はこち らの方が短時間で済みます。変数のドキュメントが‘variable-documentation’と いう名前のプロパティーに記録されているのはこれが理由です。同様にバイトコ ンパイラーも、特別に扱う必要がある関数を記録するためにプロパティーを使用 します。 とはいえ連想リストにも独自の利点があります。アプリケーションに依存し ますが、プロパティーを更新するより連想リストの先頭に連想を追加する方が高 速でしょう。シンボルにたいするすべてのプロパティーは同じプロパティーリス トに保管されるので、プロパティー名を異なる用途のために使用すると衝突の可 能性があります(この理由により、そのプログラムで通常の変数や関数の名前に つけるプレフィクスをプロパティー名の先頭につけて、一意と思われるプロパテ ィー名を選ぶのはよいアイデアだと言える)。連想リストは、連想をリストの先 頭にpushして、その後にある連想は無視されるので、スタックと同様に使用でき ます。これはプロパティーリストでは不可能です。 5.9.2 プロパティリストと外部シンボル ------------------------------------ 以下の関数はプロパティーリストを操作するために使用されます。これらの関数 はすべて、プロパティー名の比較に‘eq’を使用します。 -- Function: plist-get plist property この関数はプロパティーリストPLISTに保管された、プロパティー PROPERTYの値をリターンする。この関数には不正な形式(malformed)の PLIST引数を指定できる。PLISTでPROPERTYが見つからないと、この関数は ‘nil’をリターンする。たとえば、 (plist-get '(foo 4) 'foo) ⇒ 4 (plist-get '(foo 4 bad) 'foo) ⇒ 4 (plist-get '(foo 4 bad) 'bad) ⇒ nil (plist-get '(foo 4 bad) 'bar) ⇒ nil -- Function: plist-put plist property value この関数はプロパティーリストPLISTに、プロパティーPROPERTYの値として VALUEを保管する。この関数はPLISTを破壊的に変更するかもしれず、元の リスト構造を変更せずに新しいリストを構築することもある。この関数は 変更されたプロパティーリストをリターンするので、PLISTを取得した場所 に書き戻すことができる。たとえば、 (setq my-plist '(bar t foo 4)) ⇒ (bar t foo 4) (setq my-plist (plist-put my-plist 'foo 69)) ⇒ (bar t foo 69) (setq my-plist (plist-put my-plist 'quux '(a))) ⇒ (bar t foo 69 quux (a)) -- Function: lax-plist-get plist property ‘plist-get’と同様だがプロパティーの比較に‘eq’ではなく‘equal’を使用 する。 -- Function: lax-plist-put plist property value ‘plist-put’と同様だがプロパティーの比較に‘eq’ではなく‘equal’を使用 する。 -- Function: plist-member plist property この関数は与えられたPROPERTYがPLISTに含まれるなら非‘nil’をリターン する。‘plist-get’とは異なりこの関数は存在しないプロパティーと、値が ‘nil’のプロパティーを区別できる。実際にリターンされる値は、‘car’が PROPERTYで始まるPLISTの末尾部分である。 6 シーケンス、配列、ベクター **************************** “シーケンス(sequence)”型は2つの異なるLisp型 — リストと配列 — を結合した 型です。言い換えると任意のリストはシーケンスであり任意の配列はシーケンス です。すべてのシーケンスがもつ共通な属性は、それぞれが順序づけされた要素 のコレクションであることです。 “配列(array)”はスロットがその要素であるような、固定長のオブジェクトで す。すべての要素に一定時間でアクセスできます。配列の4つの型として文字列 、ベクター、文字テーブル、ブールベクターがあります。 リストは要素のシーケンスですが、要素は単一の基本オブジェクトではあり ません。リストはコンスセルにより作られ、要素ごとに1つのセルをもちます。 N番目の要素を探すにはN個のコンスセルを走査する必要があるので、先頭から離 れた要素ほどアクセスに時間を要します。しかしリストは要素の追加や削除が可 能です。 以下の図はこれらの型の関連を表しています: _____________________________________________ | | | Sequence | | ______ ________________________________ | | | | | | | | | List | | Array | | | | | | ________ ________ | | | |______| | | | | | | | | | | Vector | | String | | | | | |________| |________| | | | | ____________ _____________ | | | | | | | | | | | | | Char-table | | Bool-vector | | | | | |____________| |_____________| | | | |________________________________| | |_____________________________________________| 6.1 シーケンス ============== このセクションでは任意の種類のシーケンスを許す関数を説明します。 -- Function: sequencep object この関数はOBJECTがリスト、ベクター、文字列、ブールベクター、文字テ ーブルなら‘t’、それ以外は‘nil’をリターンする。 -- Function: length sequence この関数はSEQUENCE内の要素の数をリターンする。SEQUENCEがドットリス トなら‘wrong-type-argument’エラーがシグナルされる。循環リストは無限 ループを引き起こす。文字テーブルではEmacsの最大文字コードより1大き い値が常にリターンされる。 関連する関数‘safe-length’については*note Definition of safe-length::を参照のこと。 (length '(1 2 3)) ⇒ 3 (length ()) ⇒ 0 (length "foobar") ⇒ 6 (length [1 2 3]) ⇒ 3 (length (make-bool-vector 5 nil)) ⇒ 5 *note Text Representations::の‘string-bytes’も参照されたい。 ディスプレー上での文字列の幅を計算する必要があるなら、文字数だけを数 えて各文字のディスプレー幅を計算しない‘length’ではなく、‘string-width’ (*note Size of Displayed Text::を参照)を使用すること。 -- Function: elt sequence index この関数はINDEXによりインデックスづけされた、SEQUENCEの要素をリター ンする。INDEXの値として妥当なのは、0からSEQUENCEの長さより1小さい数 までの範囲の整数。SEQUENCEがリストなら範囲外の値は‘nth’と同じように 振る舞う。*note Definition of nth::を参照のこと。それ以外なら範囲外 の値は‘args-out-of-range’エラーを引き起こす。 (elt [1 2 3 4] 2) ⇒ 3 (elt '(1 2 3 4) 2) ⇒ 3 ;; ‘elt’がどの文字をreturnするか明確にするために‘string’を使用 (string (elt "1234" 2)) ⇒ "3" (elt [1 2 3 4] 4) error→ Args out of range: [1 2 3 4], 4 (elt [1 2 3 4] -1) error→ Args out of range: [1 2 3 4], -1 この関数は‘aref’ (*note Array Functions::を参照)と‘nth’ (*note Definition of nth::を参照)を一般化したものである。 -- Function: copy-sequence sequence この関数はSEQUENCEのコピーをリターンする。コピーは元のシーケンスと 同じ型、同じ要素、同じ順番となる。 コピーに新しい要素を格納するのは、元のSEQUENCEに影響を与えずその逆 も真である。しかし新しいシーケンス内の要素がコピーされたものでなけ れば、元のシーケンスの要素と同一(‘eq’)になる。したがって、コピーさ れたシーケンスを介して見つかった要素を変更すると、この変更は元のシ ーケンスでも見ることができる。 シーケンスがテキストプロパティーをもつ文字列なら、コピー内のプロパ ティーリスト自身もコピーとなり、元のシーケンスのプロパティーリスト と共有はされない。しかしプロパティーリストの実際の値は共有される。 *note Text Properties::を参照のこと。 この関数はドットリストでは機能しない。循環リストのコピーは無限ルー プを起こすだろう。 シーケンスをコピーする別の方法については*note Building Lists::の ‘append’、*note Creating Strings::の‘concat’、*note Vector Functions::の‘vconcat’も参照されたい。 (setq bar '(1 2)) ⇒ (1 2) (setq x (vector 'foo bar)) ⇒ [foo (1 2)] (setq y (copy-sequence x)) ⇒ [foo (1 2)] (eq x y) ⇒ nil (equal x y) ⇒ t (eq (elt x 1) (elt y 1)) ⇒ t ;; 一方のシーケンスの要素を置き換え (aset x 0 'quux) x ⇒ [quux (1 2)] y ⇒ [foo (1 2)] ;; 共有された要素の内部を変更 (setcar (aref x 1) 69) x ⇒ [quux (69 2)] y ⇒ [foo (69 2)] -- Function: reverse sequence この関数はSEQUENCEの要素を反転した要素をもつ新たなシーケンスを作成 する。元となる引数SEQUENCEは_変更されない_。文字テーブルは反転でき ないことに注意。 (setq x '(1 2 3 4)) ⇒ (1 2 3 4) (reverse x) ⇒ (4 3 2 1) x ⇒ (1 2 3 4) (setq x [1 2 3 4]) ⇒ [1 2 3 4] (reverse x) ⇒ [4 3 2 1] x ⇒ [1 2 3 4] (setq x "xyzzy") ⇒ "xyzzy" (reverse x) ⇒ "yzzyx" x ⇒ "xyzzy" -- Function: nreverse sequence この関数はSEQUENCEの要素を反転する。‘reverse’とは異なり、元となる SEQUENCEは変更されるかもしれない。 たとえば: (setq x '(a b c)) ⇒ (a b c) x ⇒ (a b c) (nreverse x) ⇒ (c b a) ;; 先頭にあったコンスセルが末尾となった x ⇒ (a) 混乱しないように、通常は元となるリストを保持する同じ変数に、 ‘nreverse’の結果を書き戻す: (setq x (nreverse x)) お馴染の例‘(a b c)’の‘nreverse’を以下に図示する: Original list head: Reversed list: ------------- ------------- ------------ | car | cdr | | car | cdr | | car | cdr | | a | nil |<-- | b | o |<-- | c | o | | | | | | | | | | | | | | ------------- | --------- | - | -------- | - | | | | ------------- ------------ setqが不要なのでベクターはより単純になる: (setq x [1 2 3 4]) ⇒ [1 2 3 4] (nreverse x) ⇒ [4 3 2 1] x ⇒ [4 3 2 1] ‘reverse’とは異なり、この関数は文字列では機能しない。‘aset’を使用し て文字列データを変更できても、文字列は不変として扱うことを強く推奨 する。 -- Function: sort sequence predicate この関数はSEQUENCEを安定ソートする。この関数はすべてのシーケンスに たいしては機能せず、リストとベクターにたいしてのみ使用できることに 注意されたい。SEQUENCEがリストなら破壊的に変更される。この関数はソ ートされたSEQUENCEをリターンして、要素の比較にはPREDICATEを使用する 。安定ソートでは、ソートキーが等しい要素の相対順序がソートの前後で 保たれる。この安定性は異なる条件により要素を並べ替えるために、連続 してソートを行う場合に重要となる。 引数PREDICATEは2つの引数を受け取る関数でなければならない。これは SEQUENCEの2つの要素で呼び出される。昇順でソートするなら、1つ目の要 素が2つ目の要素より“小”なら非‘nil’、それ以外なら‘nil’をリターンする こと。 比較関数PREDICATEは、少なくとも‘sort’の単一の呼び出しにおいて、与え られた任意の引数ペアにたいして信頼できる結果をリターンしなければな らない。これは“非対照的(antisymmetric)”、すなわちAがBより小なら、 BがAより小であってはならず、“推移律(transitive)”、すなわちAがBより 小、かつBがCより小なら、AはCより小でなければならない。これらの要件 に合致しない比較関数を使用すると、‘sort’の結果は予想できない。 ‘sort’のリストにたいする破壊的側面は、CDRを変更することにより SEQUENCEを形成するコンスセルを再配置することにある。非破壊ソート関 数は、それらのソート順に要素を格納するために、新たなコンスセルを作 成するだろう。オリジナルを破壊せずにソートしたコピーを望むなら、ま ず‘copy-sequence’でコピーしてからソートすること。 ソートによりSEQUENCEのコンスセルのCARは変化しない。元々SEQUENCE内で 要素‘a’を含むコンスセルは、ソート後もそのCARに‘a’を保持する。しかし CDRの変更により、ソート後には異なる位置に出現する。たとえば: (setq nums '(1 3 2 6 5 4 0)) ⇒ (1 3 2 6 5 4 0) (sort nums '<) ⇒ (0 1 2 3 4 5 6) nums ⇒ (1 2 3 4 5 6) *警告* ‘nums’内のリストが0を含まないことに注意。これはソート前と同 じコンスセルだがもはやリストの先頭ではない。ソート前に引数を保持し ていた変数がソート済みリスト全体を保持すると仮定してはならない! か わりに‘sort’の結果を保存して、それを使うこと。ほとんどの場合、わた したちは元のリストを保持していた変数に結果を書き戻すようにしている 。 (setq nums (sort nums '<)) 安定ソートの何たるかをより理解するには、以下のベクターのサンプルを 考えてみよ。ソート後、‘car’が8であるようなすべてのアイテムは ‘vector’の先頭にグループ化されるが、それらの相対的な順序は保たれる 。‘car’が9であるようなすべてのアイテムは‘vector’の末尾にグループ化 されるが、それらの相対的な順序も保たれる。 (setq vector (vector '(8 . "xxx") '(9 . "aaa") '(8 . "bbb") '(9 . "zzz") '(9 . "ppp") '(8 . "ttt") '(8 . "eee") '(9 . "fff"))) ⇒ [(8 . "xxx") (9 . "aaa") (8 . "bbb") (9 . "zzz") (9 . "ppp") (8 . "ttt") (8 . "eee") (9 . "fff")] (sort vector (lambda (x y) (< (car x) (car y)))) ⇒ [(8 . "xxx") (8 . "bbb") (8 . "ttt") (8 . "eee") (9 . "aaa") (9 . "zzz") (9 . "ppp") (9 . "fff")] ソートを行う他の関数については*note Sorting::を参照のこと。‘sort’の 有用な例は、*note Accessing Documentation::の‘documentation’を参照 されたい。 ‘seq.el’ライブラリーは、以下のようなプレフィクス‘seq-’がついたシーケ ンス操作用の追加のマクロと関数を提供します。それらを使用するには、最初に ‘seq’ライブラリーをロードしなければなりません。 このライブラリー内で定義されたすべての関数は、副作用をもちません。こ れらは引数として渡されたすべてのシーケンス(リスト、ベクター、文字列)を変 更しません。特に明記しなければ、結果は入力と同じ型のシーケンスです。述語 を受け取る関数では、それらは単一の関数である必要があります。 ‘seq.el’ライブラリーは、シーケンシャルなデータ構造の追加型で機能する ように拡張可能です。そのためにすべての関数は‘cl-defgeneric’を使用して定 義されています。‘cl-defgeneric’を使用した拡張の追加に関する詳細は、*note Generic Functions::を参照してください。 -- Function: seq-elt sequence index この関数はINDEX(有効な範囲は0からSEQUENCEの長さより1少ない整数)で指 定されたSEQUENCEの要素をリターンする。ビルトインのシーケンス型にた いする範囲外(out-of-range)の値にたいして、‘seq-elt’は‘elt’と同様に 振る舞う。詳細は*note Definition of elt::を参照のこと。 (seq-elt [1 2 3 4] 2) ⇒ 3 ‘seq-elt’は‘setf’を使用してセット可能なplaceをリターンする(*note Setting Generalized Variables::を参照)。 (setq vec [1 2 3 4]) (setf (seq-elt vec 2) 5) vec ⇒ [1 2 5 4] -- Function: seq-length sequence この関数はSEQUENCE内の要素の個数をリターンする。ビルトインのシーケ ンス型にたいして‘seq-length’は‘length’と同様に振る舞う。*note Definition of length::を参照のこと。 -- Function: seqp sequence この関数はSEQUENCEがシーケンス(リストか配列)、または‘seq.el’のジェ ネリック関数を通じて定義されたすべての追加シーケンス型なら非‘nil’を リターンする。 (seqp [1 2]) ⇒ t (seqp 2) ⇒ nil -- Function: seq-drop sequence n この関数はSEQUENCEの最初のN個(整数)を除く、すべての要素をリターンす る.Nが0以下なら結果はSEQUENCE。 (seq-drop [1 2 3 4 5 6] 3) ⇒ [4 5 6] (seq-drop "hello world" -4) ⇒ "hello world" -- Function: seq-take sequence n この関数はSEQUENCEの最初のN個(整数)の要素をリターンする。Nが0以下な ら結果は‘nil’。 (seq-take '(1 2 3 4) 3) ⇒ (1 2 3) (seq-take [1 2 3 4] 0) ⇒ [] -- Function: seq-take-while predicate sequence この関数はSEQUENCEのメンバーを順にリターンし、PREDICATEが最初に ‘nil’をリターンした要素の前で停止する。 (seq-take-while (lambda (elt) (> elt 0)) '(1 2 3 -1 -2)) ⇒ (1 2 3) (seq-take-while (lambda (elt) (> elt 0)) [-1 4 6]) ⇒ [] -- Function: seq-drop-while predicate sequence この関数はPREDICATEが最初に‘nil’をリターンした要素から、SEQUENCEの メンバーを順にリターンする。 (seq-drop-while (lambda (elt) (> elt 0)) '(1 2 3 -1 -2)) ⇒ (-1 -2) (seq-drop-while (lambda (elt) (< elt 0)) [1 4 6]) ⇒ [1 4 6] -- Function: seq-do function sequence この関数はSEQUENCEの各要素にたいして、(恐らくは副作用を得るために )順番にFUNCTIONを適用して、SEQUENCEをリターンする。 -- Function: seq-map function sequence この関数はSEQUENCEの各要素にFUNCTIONを適用した結果をリターンする。 リターン値はリスト。 (seq-map #'1+ '(2 4 6)) ⇒ (3 5 7) (seq-map #'symbol-name [foo bar]) ⇒ ("foo" "bar") -- Function: seq-mapn function &rest sequences この関数はSEQUENCESの各要素にFUNCTIONを適用した結果をリターンする。 FUNCTIONのarity (関数が受け取れる引数の個数。*note sub-arity: What Is a Function.を参照)はシーケンスの個数にマッチしなければならない。 マッピングは最短のシーケンス終端で停止する。リターン値はリスト。 (seq-mapn #'+ '(2 4 6) '(20 40 60)) ⇒ (22 44 66) (seq-mapn #'concat '("moskito" "bite") ["bee" "sting"]) ⇒ ("moskitobee" "bitesting") -- Function: seq-filter predicate sequence この関数はPREDICATEが非‘nil’をリターンしたSEQUENCE内のすべての要素 のリストをリターンする。 (seq-filter (lambda (elt) (> elt 0)) [1 -1 3 -3 5]) ⇒ (1 3 5) (seq-filter (lambda (elt) (> elt 0)) '(-1 -3 -5)) ⇒ nil -- Function: seq-remove predicate sequence この関数はPREDICATEが‘nil’をリターンしたSEQUENCE内のすべての要素の リストをリターンする。 (seq-remove (lambda (elt) (> elt 0)) [1 -1 3 -3 5]) ⇒ (-1 -3) (seq-remove (lambda (elt) (< elt 0)) '(-1 -3 -5)) ⇒ nil -- Function: seq-reduce function sequence initial-value この関数はINITIAL-VALUEとSEQUENCEの1つ目の要素でFUNCTIONを呼び出し 、次にその結果とSEQUENCEの2つ目の要素でFUNCTIONを呼び出し、その次に その結果とSEQUENCEの3つ目の要素で、...と呼び出した結果をリターンす る。FUNCTIONは引数が2つの関数であること。SEQUENCEが空なら、これは FUNCTIONを呼び出さずにINITIAL-VALUEをリターンする。 (seq-reduce #'+ [1 2 3 4] 0) ⇒ 10 (seq-reduce #'+ '(1 2 3 4) 5) ⇒ 15 (seq-reduce #'+ '() 3) ⇒ 3 -- Function: seq-some predicate sequence この関数はSEQUENCEの各要素に順にPREDICATEを適用してリターンされた、 最初の非‘nil’値をリターンする。 (seq-some #'numberp ["abc" 1 nil]) ⇒ t (seq-some #'numberp ["abc" "def"]) ⇒ nil (seq-some #'null ["abc" 1 nil]) ⇒ t (seq-some #'1+ [2 4 6]) ⇒ 3 -- Function: seq-find predicate sequence &optional default この関数はPREDICATEが非‘nil’をリターンした、SEQUENCE内の最初の要素 をリターンする。PREDICATEにマッチする要素がなければ、この関数は DEFAULTをリターンする。 この関数は見つかった要素がDEFAULTと等しい場合、要素が見つかったかど うかを知る術がないので曖昧さをもつことに注意。 (seq-find #'numberp ["abc" 1 nil]) ⇒ 1 (seq-find #'numberp ["abc" "def"]) ⇒ nil -- Function: seq-every-p predicate sequence この関数はSEQUENCEの各要素にPREDICATEを適用して、すべてが非‘nil’を リターンしたら非‘nil’をリターンする。 (seq-every-p #'numberp [2 4 6]) ⇒ t (seq-some #'numberp [2 4 "6"]) ⇒ nil -- Function: seq-empty-p sequence この関数はSEQUENCEが空なら‘nil’をリターンする。 (seq-empty-p "not empty") ⇒ nil (seq-empty-p "") ⇒ t -- Function: seq-count predicate sequence この関数はSEQUENCE内でPREDICATEが非‘nil’をリターンした要素の個数を リターンする。 (seq-count (lambda (elt) (> elt 0)) [-1 2 0 3 -2]) ⇒ 2 -- Function: seq-sort function sequence この関数はFUNCTIONに応じてソートされたSEQUENCEのコピーをリターンす る。FUNCTIONは2つの引数を受け取り、1つ目の引数が2つ目より前にソート されるべきなら非‘nil’をリターンする。 -- Function: seq-contains sequence elt &optional function この関数はSEQUENCE内のELTと‘equal’であるような最初の要素をリターン する。オプション引数FUNCTIONが非‘nil’なら、それはデフォルトの ‘equal’のかわりに使用する2つの引数を受け取る関数であること。 (seq-contains '(symbol1 symbol2) 'symbol1) ⇒ symbol1 (seq-contains '(symbol1 symbol2) 'symbol3) ⇒ nil -- Function: seq-position sequence elt &optional function この関数はELTと‘equal’であるようなSEQUENCE内の最初の要素のインデッ クスをリターンする。オプション引数FUNCTIONが非‘nil’なら、それはデフ ォルトの‘equal’のかわりに使用する2つの引数を受け取る関数であること 。 (seq-position '(a b c) 'b) ⇒ 1 (seq-position '(a b c) 'd) ⇒ nil -- Function: seq-uniq sequence &optional function この関数は重複を削除したSEQUENCEの要素のリストをリターンする。オプ ション引数FUNCTIONが非‘nil’なら、それはデフォルトの‘equal’のかわり に使用する2つの引数を受け取る関数であること。 (seq-uniq '(1 2 2 1 3)) ⇒ (1 2 3) (seq-uniq '(1 2 2.0 1.0) #'=) ⇒ [3 4] -- Function: seq-subseq sequence start &optional end この関数はSEQUENCEのSTARTからEND(いずれも整数)までのサブセットをリ ターンする(ENDのデフォルトは最後の要素)。STARTかENDが負なら SEQUENCEの最後から数える。 (seq-subseq '(1 2 3 4 5) 1) ⇒ (2 3 4 5) (seq-subseq '[1 2 3 4 5] 1 3) ⇒ [2 3] (seq-subseq '[1 2 3 4 5] -3 -1) ⇒ [3 4] -- Function: seq-concatenate type &rest sequences この関数はSEQUENCESを結合して作成されたTYPE型のシーケンスをリターン する。TYPEは‘vector’、‘list’、‘string’のいずれか。 (seq-concatenate 'list '(1 2) '(3 4) [5 6]) ⇒ (1 2 3 5 6) (seq-concatenate 'string "Hello " "world") ⇒ "Hello world" -- Function: seq-mapcat function sequence &optional type この関数はSEQUENCEの各要素にFUNCTIONを適用した結果に、 ‘seq-concatenate’を適用した結果をリターンする。結果はTYPE型のシーケ ンス、またはTYPEが‘nil’ならリストである。 (seq-mapcat #'seq-reverse '((3 2 1) (6 5 4))) ⇒ (1 2 3 4 5 6) -- Function: seq-partition sequence n この関数は長さNのサブシーケンスへグループ化したSEQUENCEの要素のリス トをリターンする。最後のシーケンスに含まれる要素はNより少ないかもし れない。Nは整数であること。Nが0以下の整数ならリターン値は‘nil’。 (seq-partition '(0 1 2 3 4 5 6 7) 3) ⇒ ((0 1 2) (3 4 5) (6 7)) -- Function: seq-intersection sequence1 sequence2 &optional function この関数はSEQUENCE1とSEQUENCE2の両方に出現する要素のリストをリター ンする。オプション引数FUNCTIONが非‘nil’なら、それはデフォルトの ‘equal’のかわりに比較に使用する2つの引数を受け取る関数であること。 (seq-intersection [2 3 4 5] [1 3 5 6 7]) ⇒ (3 5) -- Function: seq-difference sequence1 sequence2 &optional function この関数はSEQUENCE1に出現するがSEQUENCE2に出現しない要素のリストを リターンする。オプション引数FUNCTIONが非‘nil’なら、それはデフォルト の‘equal’のかわりに比較に使用する2つの引数を受け取る関数であること 。 (seq-difference '(2 3 4 5) [1 3 5 6 7]) ⇒ (2 4) -- Function: seq-group-by function sequence この関数はSEQUENCEの各要素にFUNCTIONを適用して、その結果をキーとし てSEQUENCEをalistに分割する。キーの比較には‘equal’を使用する。 (seq-group-by #'integerp '(1 2.1 3 2 3.2)) ⇒ ((t 1 3 2) (nil 2.1 3.2)) (seq-group-by #'car '((a 1) (b 2) (a 3) (c 4))) ⇒ ((b (b 2)) (a (a 1) (a 3)) (c (c 4))) -- Function: seq-into sequence type この関数はシーケンスSEQUENCEをTYPE型のシーケンスに変換する。TYPEは ‘vector’、‘string’、‘list’のいずれかであること。 (seq-into [1 2 3] 'list) ⇒ (1 2 3) (seq-into nil 'vector) ⇒ [] (seq-into "hello" 'vector) ⇒ [104 101 108 108 111] -- Function: seq-min sequence この関数はSEQUENCEの最小の要素をリターンする。SEQUENCEの要素は数字 かマーカー(*note Markers::を参照)でなければならない。 (seq-min [3 1 2]) ⇒ 1 (seq-min "Hello") ⇒ 72 -- Function: seq-max sequence この関数はSEQUENCEの最大の要素をリターンする。SEQUENCEの要素は数字 かマーカーでなければならない。 (seq-max [1 3 2]) ⇒ 3 (seq-max "Hello") ⇒ 111 -- Macro: seq-doseq (var sequence) body... このマクロは‘dolist’ (*note dolist: Iteration.を参照)と同様だが、 SEQUENCEにリスト、ベクター、文字列のいずれかを指定できる点が異なる 。これ主な利点は副作用である。 -- Macro: seq-let arguments sequence body... このマクロはARGUMENTS内で定義される変数にSEQUENCEの要素をバインドす る。ARGUMENTSはネストされた非構造化を許容することにより、自身にシー ケンスを含むことができる。 ARGUMENTSシーケンスには、‘sequence’の残りにバインドされる変数名が後 続するような、‘&rest’マーカーを含めることもできる。 (seq-let [first second] [1 2 3 4] (list first second)) ⇒ (1 2) (seq-let (_ a _ b) '(1 2 3 4) (list a b)) ⇒ (2 4) (seq-let [a [b [c]]] [1 [2 [3]]] (list a b c)) ⇒ (1 2 3) (seq-let [a b &rest others] [1 2 3 4] others) ⇒ [3 4] 6.2 配列 ======== “配列(array)”オブジェクトは、いくつかのLispオブジェクトを保持するスロッ トをもち、これらのオブジェクトは配列の要素と呼ばれます。配列内の任意の要 素は一定時間でアクセスされます。対照的にリスト内の要素のアクセスに要する 時間は、その要素がリスト内のどの位置にあるかに比例します。 Emacsは4つの配列型 —“文字列(strings、*note String Type::を参照)”、“ベ クター(vectors、*note Vector Type::を参照)”、“ブールベクター (bool-vectors、*note Bool-Vector Type::を参照)”、“文字テーブル (char-tables、*note Char-Table Type::を参照)” — を定義しており、これらは すべて1次元です。ベクターと文字テーブルは任意の型の要素を保持できますが 、文字列は文字だけ、ブールベクターは‘t’か‘nil’しか保持できません。 4種のすべての配列はこれらの特性を共有します: • 配列の1番目の要素はインデックス0、2番目はインデックス1、...となる。 これは“0基準(zero-origin)”のインデックスづけと呼ばれる。たとえば4要 素の配列のインデックスは0、1、2、3。 • 配列の長さは一度配列が作成されたら固定されるので、既存の配列の長さ は変更できない。 • 評価において配列は定数 — つまりそれ自身へと評価される。 • 配列の要素は関数‘aref’で参照したり、関数‘aset’で変更できる(*note Array Functions::を参照)。 配列を作成したとき、文字テーブル以外では長さを指定しなければなりませ ん。文字テーブルの長さは文字コードの範囲により決定されるので長さを指定で きません。 原則として、テキスト文字の配列が欲しい場合は、文字列とベクターのどち らかを使用できます。実際のところ4つの理由により,そのような用途にたいして は、わたしたちは常に文字列を選択します: • 文字列は同じ要素をもつベクターと比較して占めるスペースが1/4である。 • 文字列の内容はテキストとして、より明解な方法によりプリントされる。 • 文字列はテキストプロパティーを保持できる。*note Text Properties::を 参照のこと。 • Emacsの特化した編集機能とI/O機能の多くが文字列だけに適用される。た とえば文字列をバッファーに挿入する方法では、文字のベクターをバッフ ァーに挿入できない。*note Strings and Characters::を参照のこと 対照的に、(キーシーケンスのような)キーボード入力文字の配列では、多く のキーボード入力文字は文字列に収まる範囲の外にあるので、ベクターが必要に なるでしょう。*note Key Sequence Input::を参照してください。 6.3 配列を操作する関数 ====================== このセクションではすべての型の配列に適用される関数を説明します。 -- Function: arrayp object この関数はOBJECTが配列(ベクター、文字列、ブールベクター、文字テーブ ル)なら‘t’をリターンする。 (arrayp [a]) ⇒ t (arrayp "asdf") ⇒ t (arrayp (syntax-table)) ;; 文字テーブル ⇒ t -- Function: aref array index この関数は ARRAYのINDEX番目の要素をリターンする。1番目の要素のイン デクスは0。 (setq primes [2 3 5 7 11 13]) ⇒ [2 3 5 7 11 13] (aref primes 4) ⇒ 11 (aref "abcdefg" 1) ⇒ 98 ; ‘b’のASCIIコードは98 *note Sequence Functions::の関数‘elt’も参照されたい。 -- Function: aset array index object この関数はARRAYのINDEX番目の要素をOBJECTにセットする。この関数は OBJECTをリターンする。 (setq w [foo bar baz]) ⇒ [foo bar baz] (aset w 0 'fu) ⇒ fu w ⇒ [fu bar baz] (setq x "asdfasfd") ⇒ "asdfasfd" (aset x 3 ?Z) ⇒ 90 x ⇒ "asdZasfd" ARRAYが文字列でOBJECTが文字でなければ、結果は ‘wrong-type-argument’エラーとなる。この関数は文字列の挿入で必要なら 、ユニバイト文字列をマルチバイト文字列に変換する。 -- Function: fillarray array object この関数は配列ARRAYをOBJECTで充填するので、ARRAYのすべての要素は OBJECTになる。この関数はARRAYをリターンする。 (setq a [a b c d e f g]) ⇒ [a b c d e f g] (fillarray a 0) ⇒ [0 0 0 0 0 0 0] a ⇒ [0 0 0 0 0 0 0] (setq s "When in the course") ⇒ "When in the course" (fillarray s ?-) ⇒ "------------------" ARRAYが文字列でOBJECTが文字でなければ、結果は ‘wrong-type-argument’エラーとなる。 配列と判っているオブジェクトにたいしては、一般的なシーケンス関数 ‘copy-sequence’と‘length’が有用なときがよくあります。*note Sequence Functions::を参照してください。 6.4 ベクター ============ “ベクター(vector)”とは任意のLispオブジェクトを要素にもつことができる、一 般用途のための配列です(対照的に文字列の要素は文字のみ。*note Strings and Characters::を参照)。Emacsではベクターはキーシーケンス(*note Key Sequences::を参照)、シンボル検索用のテーブル(*note Creating Symbols::を 参照)、バイトコンパイルされた関数表現の一部(*note Byte Compilation::を参 照)などの多くの目的で使用されます。 他の配列と同様、ベクターは0基準のインデックスづけを使用し、1番目の要 素はインデックス0になります。 ベクターは角カッコ(square brackets)で囲まれた要素としてプリントされま す。したがってシンボル‘a’、‘b’、‘a’を要素にもつベクターは、‘[a b a]’とプ リントされます。Lisp入力として同じ方法でベクターを記述できます。 文字列や数値と同様にベクターは定数として評価され、評価された結果は同 じベクターになります。ベクターの要素は評価も確認もされません。*note Self-Evaluating Forms::を参照してください。 以下はこれらの原理を表す例です: (setq avector [1 two '(three) "four" [five]]) ⇒ [1 two (quote (three)) "four" [five]] (eval avector) ⇒ [1 two (quote (three)) "four" [five]] (eq avector (eval avector)) ⇒ t 6.5 ベクターのための関数 ======================== ベクターに関連した関数をいくつか示します: -- Function: vectorp object この関数はOBJECTがベクターなら‘t’をリターンする。 (vectorp [a]) ⇒ t (vectorp "asdf") ⇒ nil -- Function: vector &rest objects この関数は引数OBJECTSを要素にもつベクターを作成してリターンする。 (vector 'foo 23 [bar baz] "rats") ⇒ [foo 23 [bar baz] "rats"] (vector) ⇒ [] -- Function: make-vector length object この関数は各要素がOBJECTに初期化された、LENGTH個の要素からなる新し いベクターをリターンする。 (setq sleepy (make-vector 9 'Z)) ⇒ [Z Z Z Z Z Z Z Z Z] -- Function: vconcat &rest sequences この関数はSEQUENCESのすべての要素を含む新しいベクターをリターンする 。引数SEQUENCESは真リスト、ベクター、文字列、ブールベクター。 SEQUENCESが与えられければ空のベクターがリターンされる。 値は空のベクター、またはすべての既存ベクターと‘eq’ではないような空 ではない新しいベクターのいずれか。 (setq a (vconcat '(A B C) '(D E F))) ⇒ [A B C D E F] (eq a (vconcat a)) ⇒ nil (vconcat) ⇒ [] (vconcat [A B C] "aa" '(foo (6 7))) ⇒ [A B C 97 97 foo (6 7)] ‘vconcat’関数は、引数としてバイトコード関数オブジェクトも受け取るこ とができる。これはバイトコード関数オブジェクトの内容全体にアクセス するのを容易にするための特別な機能である。*note Byte-Code Objects::を参照のこと。 結合を行なう他の関数については*note Mapping Functions::の ‘mapconcat’、*note Creating Strings::の‘concat’、*note Building Lists::の‘append’を参照されたい。 ‘append’関数はベクターを同じ要素をもつリストに変換する方法も提供しま す: (setq avector [1 two (quote (three)) "four" [five]]) ⇒ [1 two (quote (three)) "four" [five]] (append avector nil) ⇒ (1 two (quote (three)) "four" [five]) 6.6 文字テーブル ================ 文字テーブル(char-table)はベクターとよく似ていますが、文字テーブルは文字 コードによりインデックスづけされます。文字テーブルのインデックスには、修 飾キーをともなわない任意の有効な文字コードを使用できます。他の配列と同様 に、‘aref’と‘aset’で文字テーブルの要素にアクセスできます。加えて、文字テ ーブルは追加のデータを保持するために、特定の文字コードに関連づけられてい ない“エキストラスロット(extra slots)”をもつことができます。ベクターと同 様、文字テーブルは定数として評価され、任意の型の要素を保持できます。 文字テーブルはそれぞれ“サブタイプ(subtype)”をもち、これは2つの目的を もつシンボルです: • サブタイプはそれがなんのための文字テーブルなのかを簡単に表す方法を 提供する。たとえばディスプレーテーブル(display tables)はサブタイプ が‘display-table’の文字テーブルであり、構文テーブル(syntax tables)は サブタイプが‘syntax-table’の文字テーブル。以下で説明するように関数 ‘char-table-subtype’を使用してサブタイプの問い合わせが可能。 • サブタイプは文字テーブル内のいくつかの“エキストラスロット(extra slots)”を制御する。エキストラスロットの数は、そのサブタイプの ‘char-table-extra-slots’シンボルプロパティー(*note Symbol Properties::を参照)により指定され、値は0から10の整数。サブタイプに そのようなシンボルプロパティーがなければ、その文字テーブルはエキス トラスロットをもたない。 文字テーブルは“親(parent)”をもつことができ、これは他の文字テーブルで す。文字テーブルが親をもつ場合、その文字テーブルで特定の文字Cにたいして ‘nil’が指定されていたら、親と指定された文字テーブルで指定された値を継承 します。言い方を変えると、文字テーブルCHAR-TABLEでCに‘nil’が指定されてい たら、‘(aref CHAR-TABLE C)’はCHAR-TABLEの親の値をリターンします。 文字テーブルは“デフォルト値(default value)”をもつこともできます。デフ ォルト値をもつとき、文字テーブルCHAR-TABLEがCにたいして‘nil’値を指定する と、‘(aref CHAR-TABLE C)’はデフォルト値をリターンします。 -- Function: make-char-table subtype &optional init サブタイプSUBTYPE(シンボル)をもつ、新たに作成された文字テーブルをリ ターンする。各要素はINITに初期化され、デフォルトは‘nil’。文字テーブ ルが作成された後で、文字テーブルのサブタイプを変更することはできな い。 すべての文字テーブルは、インデックスとなる任意の有効な文字テーブル のための空間をもつので、文字テーブルの長さを指定する引数はない。 SUBTYPEがシンボルプロパティー‘char-table-extra-slots’をもつなら、そ れはその文字列テーブル内のエキストラスロットの数を指定する。値には 0から10の整数を指定し、これ以外なら‘make-char-table’はエラーとなる 。SUBTYPEがシンボルプロパティー‘char-table-extra-slots’(*note Property Lists::を参照)をもたなければ、その文字テーブルはエキストラ スロットをもたない。 -- Function: char-table-p object この関数はOBJECTが文字テーブルなら‘t’、それ以外は‘nil’をリターンす る。 -- Function: char-table-subtype char-table この関数はCHAR-TABLEのサブタイプのシンボルをリターンする。 文字テーブルのデフォルト値にアクセスするための特別な関数は存在しませ ん。これを行なうには‘char-table-range’を使用します(以下参照)。 -- Function: char-table-parent char-table この関数はCHAR-TABLEの親をリターンする。親は常に‘nil’か他の文字テー ブルである。 -- Function: set-char-table-parent char-table new-parent この関数はCHAR-TABLEの親をNEW-PARENTにセットする。 -- Function: char-table-extra-slot char-table n この関数はCHAR-TABLEのエキストラスロットN (0基準)の内容をリターンす る。文字テーブルのエキストラスロットの数は文字テーブルのサブタイプ により決定される。 -- Function: set-char-table-extra-slot char-table n value この関数はCHAR-TABLEのエキストラスロットN (0基準)にVALUEを格納する 。 文字テーブルは1つの文字コードにたいして1つの要素値(element value)を指 定できます。文字テーブルは文字セット全体にたいして値を指定することもでき ます。 -- Function: char-table-range char-table range この関数は文字範囲RANGEにたいしてCHAR-TABLEで指定された値をリターン する。可能なRANGEは以下のとおり: ‘nil’ デフォルト値への参照。 CHAR 文字CHARにたいする要素への参照(CHARは有効な文字コードであると 仮定)。 ‘(FROM . TO)’ 包括的な範囲‘[FROM..TO]’内のすべての文字を参照するコンスセル。 -- Function: set-char-table-range char-table range value この関数はCHAR-TABLE内の文字範囲RANGEにたいして値をセットする。可能 なRANGEは以下のとおり: ‘nil’ デフォルト値への参照。 ‘t’ 文字コード範囲の全体を参照。 CHAR 文字CHARにたいする要素への参照(CHARは有効な文字コードであると 仮定)。 ‘(FROM . TO)’ 包括的な範囲‘[FROM..TO]’内のすべての文字を参照するコンスセル。 -- Function: map-char-table function char-table この関数はCHAR-TABLEの非‘nil’値ではない各要素にたいして引数 FUNCTIONを呼び出す。FUNCTIONの呼び出しでは2つの引数(keyとvalue)が指 定される。keyは‘char-table-range’にたいする可能なRANGE (有効な文字 か、同じ値を共有する文字範囲を指定するコンスセル‘(FROM . TO)’)。 valueは‘(char-table-range CHAR-TABLE KEY)’がリターンする値。 全体として、FUNCTIONに渡されるkey-valueのペアはCHAR-TABLEに格納され たすべての値を表す。 リターン値は常に‘nil’である。‘map-char-table’呼び出しを有用にするた めにFUNCTIONは副作用をもつこと。たとえば以下は構文テーブルを調べる 方法: (let (accumulator) (map-char-table #'(lambda (key value) (setq accumulator (cons (list (if (consp key) (list (car key) (cdr key)) key) value) accumulator))) (syntax-table)) accumulator) ⇒ (((2597602 4194303) (2)) ((2597523 2597601) (3)) ... (65379 (5 . 65378)) (65378 (4 . 65379)) (65377 (1)) ... (12 (0)) (11 (3)) (10 (12)) (9 (0)) ((0 8) (3))) 6.7 ブールベクター ================== ブールベクター(bool-vector)はベクターとよく似ていますが、値に‘t’と ‘nil’しか格納できません。ブールベクターの要素に非‘nil’値の格納を試みたる と、そこには‘t’が格納されます。すべての配列と同様、ブールベクターのイン デックスは0から開始され、一度ブールベクターが作成されたら長さを変更する ことはできません。ブールベクターは定数として評価されます。 ブールベクターを処理する特別な関数がいくつかあります。その関数以外に も、他の種類の配列に使用されるのと同じ関数でブールベクターを操作できます 。 -- Function: make-bool-vector length initial INITIALに初期化されたLENGTH要素の新しいブールベクターをリターンする 。 -- Function: bool-vector &rest objects この関数は引数OBJECTSを要素にもつブールベクターを作成してリターンす る。 -- Function: bool-vector-p object この関数はOBJECTがブールベクターであれば‘t’、それ以外は‘nil’をリタ ーンする。 以下で説明するように、ブールベクターのセット処理を行なう関数がいくつ かあります: -- Function: bool-vector-exclusive-or a b &optional c ブールベクターAとBの“ビットごとの排他的論理和(bitwise exclusive or)”をリターンする。オプション引数Cが与えられたら、この処理の結果は Cに格納される。引数にはすべて同じ長さのブールベクターを指定すること 。 -- Function: bool-vector-union a b &optional c ブールベクターAとBの“ビットごとの論理和(bitwise or)”をリターンする 。オプション引数Cが与えられたら、この処理の結果はCに格納される。引 数にはすべて同じ長さのブールベクターを指定すること。 -- Function: bool-vector-intersection a b &optional c ブールベクターAとBの“ビットごとの論理積(bitwise and)”をリターンする 。オプション引数Cが与えられたら、この処理の結果はCに格納される。引 数にはすべて同じ長さのブールベクターを指定すること。 -- Function: bool-vector-set-difference a b &optional c ブールベクターAとBの“差集合(set difference)”をリターンする。オプシ ョン引数Cが与えられたら、この処理の結果はCに格納される。引数にはす べて同じ長さのブールベクターを指定すること。 -- Function: bool-vector-not a &optional b ブールベクターAの“補集合(set complement)”をリターンする。オプション 引数Bが与えられたら、この処理の結果はBに格納される。引数にはすべて 同じ長さのブールベクターを指定すること。 -- Function: bool-vector-subsetp a b A内のすべての‘t’値が、Bでも‘t’値なら‘t’、それ以外は‘nil’をリターン する。引数にはすべて同じ長さのブールベクターを指定すること。 -- Function: bool-vector-count-consecutive a b i Iから始まるAの、Bと等しい連続する要素の数をリターンする。‘a’はブー ルベクターで、Bは‘t’か‘nil’、Iは‘a’のインデックス。 -- Function: bool-vector-count-population a ブールベクターAから‘t’であるような要素の数をリターンする。 長さ8以下のブール値のプリント表記は1文字で表されます。 (bool-vector t nil t nil) ⇒ #&4"^E" (bool-vector) ⇒ #&0"" 他のベクター同様、‘vconcat’を使用してブールベクターをプリントできます : (vconcat (bool-vector nil t nil t)) ⇒ [nil t nil t] 以下はブールベクターを作成、確認、更新する別の例です: (setq bv (make-bool-vector 5 t)) ⇒ #&5"^_" (aref bv 1) ⇒ t (aset bv 3 nil) ⇒ nil bv ⇒ #&5"^W" control-_の2進コードは11111、control-Wは10111なので、この結果は理にかな っています。 6.8 オブジェクト用固定長リングの管理 ==================================== “リング(ring)”は挿入、削除、ローテーション、剰余(modulo)でインデックスづ けされた、参照と走査(traversal)をサポートする固定長のデータ構造です。 ‘ring’パッケージにより効率的なリングデータ構造が実装されています。このパ ッケージは、このセクションにリストした関数を提供します。 Emacsにあるkillリングやマークリングのようないくつかのリングは、実際に は単なるリストとして実装されていることに注意してください。したがってこれ らのリングにたいしては、以下の関数は機能しないでしょう。 -- Function: make-ring size この関数はSIZEオブジェクトを保持できる、新しいリングをリターンする 。SIZEは整数。 -- Function: ring-p object この関数はOBJECTがリングなら‘t’、それ以外は‘nil’をリターンする。 -- Function: ring-size ring この関数はRINGの最大の要素数をリターンする。 -- Function: ring-length ring この関数はRINGに現在含まれるオブジェクトの数をリターンする。値が ‘ring-size’のリターンする値を超えることはない。 -- Function: ring-elements ring この関数はRING内のオブジェクトのリストをリターンする。リストの順序 は新しいオブジェクトが先頭になる。 -- Function: ring-copy ring この関数は新しいリングとしてRINGのコピーをリターンする。新しいリン グはRINGと同じ(‘eq’な)オブジェクトを含む。 -- Function: ring-empty-p ring この関数はRINGが空なら‘t’、それ以外は‘nil’をリターンする。 リング内の1番新しい要素は常にインデックス0をもちます。より大きいイン デックスは、より古い要素に対応します。インデックスはリング長のmoduloによ り計算されます。インデックス−1は1番古い要素、−2は次に古い要素、...となり ます。 -- Function: ring-ref ring index この関数はインデックスINDEXにあるRING内のオブジェクトをリターンする 。INDEXには負やリング長より大きい数を指定できる。RINGが空なら ‘ring-ref’はエラーをシグナルする。 -- Function: ring-insert ring object この関数は1番新しい要素としてOBJECTをRINGに挿入してOBJECTをリターン する。 リングが満杯なら新しい要素用の空きを作るために、挿入により1番古い要 素が削除される。 -- Function: ring-remove ring &optional index RINGからオブジェクトを削除してそのオブジェクトをリターンする。引数 INDEXはどのアイテムを削除するかを指定する。これが‘nil’なら、それは 1番古いアイテムを削除することを意味する。RINGが空なら ‘ring-remove’はエラーをシグナルする。 -- Function: ring-insert-at-beginning ring object この関数は1番古い要素としてOBJECTをRINGに挿入する。リターン値に意味 はない。 リングが満杯なら、この関数は挿入される要素のための空きを作るために 1番新しい要素を削除する。 リングサイズを超過しないよう注意すれば、そのリングを FIFO(first-in-first-out: 先入れ先出し)のキューとして使用することができま す。たとえば: (let ((fifo (make-ring 5))) (mapc (lambda (obj) (ring-insert fifo obj)) '(0 one "two")) (list (ring-remove fifo) t (ring-remove fifo) t (ring-remove fifo))) ⇒ (0 t one t "two") 7 ハッシュテーブル ****************** ハッシュテーブル(hash table)は非常に高速なルックアップテーブルの一種で、 キーに対応する値をマップするという点ではalist(*note Association Lists::を 参照)に似ています。ハッシュテーブルは以下の点でalistと異なります: • ハッシュテーブルでのルックアップ(lookup: 照合)は、巨大なテーブルに たいして非常に高速である — 実際のところルックアップに必要な時間は、 そのテーブルに格納されている要素数とは基本的に_無関係_である。ハッ シュテーブルには一定のオーバーヘッドが多少あるので、小さいテーブル (数十の要素)ではalistのほうが高速だろう。 • ハッシュテーブル内の対応関係に特定の順序はない。 • 2つのalistで共通の末尾(tail)を共有させるような、2つのハッシュテーブ ル間で構造を共有する方法はない。 Emacs Lispは一般的な用途のハッシュテーブルデータ型とともに、それらを 処理する一連の関数を提供します。ハッシュテーブルは‘#s’、その後にハッシュ テーブルのプロパティーと内容を指定するリストが続く、特別なプリント表現を もちます。*note Creating Hash::を参照してください(ハッシュ表記の最初に使 用される‘#’文字は、読み取り表現をもたないオブジェクトのプリント表現であ り、これはハッシュテーブルに何も行わない。*note Printed Representation::を参照のこと)。 obarray(オブジェクト配列)もハッシュテーブルの一種ですが、これらは異な る型のオブジェクトであり、intern(インターン)されたシンボルを記録するため だけに使用されます(*note Creating Symbols::を参照)。 7.1 ハッシュテーブルの作成 ========================== ハッシュテーブルを作成する基本的な関数は‘make-hash-table’です。 -- Function: make-hash-table &rest keyword-args この関数は指定された引数に対応する新しいハッシュテーブルを作成する 。引数はキーワード(特別に認識される独自のシンボル)と、それに対応す る値を交互に指定することで構成される。 ‘make-hash-table’ではいくつかのキーワードが意味をもつが、実際に知る 必要があるのは‘:test’と‘:weakness’の2つだけである。 ‘:test TEST’ これはそのハッシュテーブルにたいしてキーを照合する方法を指定す る。デフォルトは‘eql’であり他の代替としては‘eq’や‘equal’がある : ‘eql’ キーが数字ならそれらが‘equal’、つまりそれらの値が等しくど ちらも整数か浮動少数点数なら同一。それ以外なら別の2つのオ ブジェクトは決して同一とならない。 ‘eq’ 別の2つのLispオブジェクトはすべて別のキーになる。 ‘equal’ 別の2つのLispオブジェクトで、それらが‘equal’なら同一のキ ーである。 TESTにたいして追加の選択肢を定義するために、 ‘define-hash-table-test’ (*note Defining Hash::を参照)を使用す ることができる。 ‘:weakness WEAK’ ハッシュテーブルのweakness(強度)は、ハッシュテーブル内に存在す るキーと値をガーベージコレクションから保護するかどうかを指定す る。 値WEAKには‘nil’、‘key’、‘value’、‘key-or-value’、 ‘key-and-value’、または‘t’(‘key-and-value’のエイリアス)のいず れかを指定しなければならない。WEAKが‘key’ならそのハッシュテー ブルは、(キーが他の場所で参照されていなければ)ハッシュテーブル のキーがガーベージコレクトされるのを妨げられない。ある特定のキ ーがガーベージコレクトされると、それに対応する連想はハッシュテ ーブルから削除される。 WEAKが‘value’ならそのハッシュテーブルは、(値が他の場所で参照さ れていなければ)ハッシュテーブルの値がガベージコレクトされるの を妨げませんられない。ある特定の値がガーベージコレクトされると 、それに対応する連想はハッシュテーブルから削除される。 WEAKが‘key-and-value’(か‘t’)なら、その連想を保護するためにはキ ーと値の両方が生きていなければならない。したがってそのハッシュ テーブルは、キーと値の一方だけをガーベージコレクトから守ること はしない。キーか値のどちらか一方がガーベージコレクトされたら、 その連想は削除される。 WEAKが‘key-or-value’nara、キーか値のどちらか一方で、その連想を 保護することができる。したがってキーと値の両方がガベージコレク トされたときだけ(それがハッシュテーブル自体にたいする参照でな ければ)、ハッシュテーブルからその連想が削除される。 WEAKのデフォルトは‘nil’なので、ハッシュテーブルから参照されて いるキーと値はすべてガーベージコレクションから保護される。 ‘:size SIZE’ これはそのハッシュテーブルに保管しようとしている、連想の数にた いするヒントを指定する。数が概算で判っていれば、この方法でそれ を指定して処理を若干効率的にすることができる。小さすぎるサイズ を指定すると、そのハッシュテーブルは必要に応じて自動的に拡張さ れるが、これを行なうために時間が余計にかかる。 デフォルトのサイズは65。 ‘:rehash-size REHASH-SIZE’ ハッシュテーブルに連想を追加するとき、そのテーブルが満杯ならテ ーブルを自動的に拡張する。この値はその際にどれだけハッシュテー ブルを拡張するかを指定する。 REHASH-SIZEが整数(正であること)なら、通常のサイズに REHASH-SIZEを加えてハッシュテーブルを拡張する。REHASH-SIZEが浮 動小数点数(1より大きい方がよい)なら、古いサイズにその数を乗じ てハッシュテーブルを拡張する。 デフォルト値は1.5。 ‘:rehash-threshold THRESHOLD’ これはハッシュテーブルが満杯(なのでもっと大きく拡張する必要が ある)と判断される基準を指定する。THRESHOLDの値は、1以下の正の 浮動小数点数であること。実際のエントリー数が、通常のサイズにた いする指定割合を超えると、そのハッシュテーブルは満杯となる。 THRESHOLDのデフォルトは0.8。 ハッシュテーブルのプリント表現を使用して、新しいハッシュテーブルを作 成することもできます。指定されたハッシュテーブル内の各要素が、有効な入力 構文(*note Printed Representation::を参照)をもっていれば、Lispリーダーは このプリント表現を読み取ることができます。たとえば以下は値‘val1’(シンボ ル)と‘300’(数字)に関連づけられた、キー‘key1’と‘key2’(両方ともシンボル)を 、新しいハッシュテーブルに指定します。 #s(hash-table size 30 data (key1 val1 key2 300)) ハッシュテーブルのプリント表現は‘#s’と、その後の‘hash-table’で始まるリス トにより構成されます。このリストの残りの部分はそのハッシュテーブルのプロ パティーと初期内容を指定する、0個以上のプロパティーと値からなるペアで構 成されるべきです。プロパティーと値はそのまま読み取られます。有効なプロパ ティー名は‘size’、‘test’、‘weakness’、‘rehash-size’、 ‘rehash-threshold’、‘data’です。‘data’プロパティーは、初期内容にたいする キーと値のペアのリストであるべきです。他のプロパティーは、上記で説明した ‘make-hash-table’のキーワード(‘:size’、‘:test’など)と同じ意味をもちます 。 バッファーやフレームのような、入力構文をもたないオブジェクトを含んだ 初期内容をもつハッシュテーブルを指定できないことに注意してください。その ようなオブジェクトは、ハッシュテーブルを作成した後に追加します。 7.2 ハッシュテーブルへのアクセス ================================ このセクションではハッシュテーブルにアクセスしたり、連想を保管する関数を 説明します。比較方法による制限がない限り、一般的には任意のLispオブジェク トをハッシュキーとして使用できます。 -- Function: gethash key table &optional default この関数はTABLEのKEYを照合してそれに関連づけられたVALUE、TABLE内に KEYをもつ連想が存在しなければDEFAULTをリターンする。 -- Function: puthash key value table この関数はTABLE内に値VALUEをもつKEYの連想を挿入します。TABLEがすで にKEYの連想をもつなら、VALUEで古い連想値を置き換える。 -- Function: remhash key table この関数はTABLEにKEYの連想があればそれを削除する。KEYが連想をもたな ければ‘remhash’は何も行なわない。 Common Lispに関する注意: Common Lispでは‘remhash’が実際に連想を削除 したときは非‘nil’、それ以外は‘nil’をリターンする。Emacs Lispでは ‘remhash’は常に‘nil’をリターンする。 -- Function: clrhash table この関数はハッシュテーブルTABLEからすべての連想を削除するので、その ハッシュテーブルは空になる。これはハッシュテーブルの“クリーニング (clearing)”とも呼ばれる。 Common Lispに関する注意: Common Lispでは‘clrhash’は空のTABLEをリタ ーンする。Emacs Lispでは‘nil’をリターンする。 -- Function: maphash function table この関数はTABLE内の各連想にたいして一度ずつFUNCTIONを呼び出す。関数 FUNCTIONは2つの引数 — TABLEにリストされたKEYと、それに関連づけられ たVALUE — を受け取ること。‘maphash’は‘nil’をリターンする。 7.3 ハッシュの比較の定義 ======================== ‘define-hash-table-test’でキーを照合する新しい方法を定義できます。この機 能を使用するにはハッシュテーブルの動作方法と、“ハッシュコード(hash code)”の意味を理解する必要があります。 概念的にはハッシュテーブルを1つの連想を保持できるスロットがたくさんあ る巨大な配列として考えることができます。キーを照合するにはまず、 ‘gethash’がキーから整数のハッシュコードを計算します。配列内のインデック スを生成するために、‘gethash’は配列の長さからこの整数のmoduloを得ます。 それからキーが見つかったかどうか確認するためにそのスロット、もし必要なら 近くのスロットを探します。 したがってキー照合の新しい方法を定義するためには、キーからハッシュコ ードを計算する関数と、2つのキーを直接比較する関数の両方が必要です。 -- Function: define-hash-table-test name test-fn hash-fn この関数はNAMEという名前の新たなハッシュテーブルテストを定義します 。 この方法でNAMEを定義した後は、‘make-hash-table’の引数TESTにこれを使 用することができる。これを行なう際は、そのハッシュテーブルのキー値 の比較にTEST-FN、キー値からハッシュコードを計算するためにHASH-FNを 使用することになる。 関数TEST-FNは2つの引数(2つのキー)をとり、それらが同一と判断されたと きは非‘nil’をリターンする。 関数HASH-FNは1つの引数(キー)を受け取り、そのキーのハッシュコード(整 数)をリターンすること。よい結果を得るために、その関数は負の整数を含 む整数の全範囲をハッシュコードに使用するべきある。 指定された関数は、プロパティー‘hash-table-test’の配下の、NAMEという プロパティーリストに格納される。そのプロパティーの値形式は‘(TEST-FN HASH-FN)’。 -- Function: sxhash obj この関数はLispオブジェクトOBJのハッシュコードをリターンする。リター ン値はOBJと、それが指す別のLispオブジェクトの内容を表す整数。 2つのオブジェクトOBJ1とOBJ2がequalなら、‘(sxhash OBJ1)’と‘(sxhash OBJ2)’は同じ整数になる。 2つのオブジェクトがequalでなければ、通常は‘sxhash’がリターンする値 は異なるが、常に異なるとは限らない。稀(運次第)にだが‘sxhash’が同じ 結果を与えるような、2つの異なった外見のオブジェクトに遭遇するかもし れない。 以下はlcaseを区別しない文字列のキーをもつハッシュテーブルを作成する例 です。 (defun case-fold-string= (a b) (eq t (compare-strings a nil nil b nil nil t))) (defun case-fold-string-hash (a) (sxhash (upcase a))) (define-hash-table-test 'case-fold 'case-fold-string= 'case-fold-string-hash) (make-hash-table :test 'case-fold) 以下は事前に定義されたテスト値‘equal’と等価なテストを行なうハッシュテ ーブルを定義できるという例です。キーは任意のLispオブジェクトで、equalに 見えるオブジェクトは同じキーと判断されます。 (define-hash-table-test 'contents-hash 'equal 'sxhash) (make-hash-table :test 'contents-hash) 7.4 ハッシュテーブルのためのその他関数 ====================================== 以下はハッシュテーブルに作用する他の関数です。 -- Function: hash-table-p table この関数はTABLEがハッシュテーブルオブジェクトなら非‘nil’をリターン する。 -- Function: copy-hash-table table この関数はTABLEのコピーを作成してリターンする。そのテーブル自体がコ ピーされたものである場合のみ、キーと値が共有される。 -- Function: hash-table-count table この関数はTABLE内の実際のエントリー数をリターンする。 -- Function: hash-table-test table この関数はハッシュを行なう方法と、キーを比較する方法を指定するため に、TABLE作成時に与えられたTESTの値をリターンする。*note Creating Hash::の‘make-hash-table’を参照されたい。 -- Function: hash-table-weakness table この関数はハッシュテーブルTABLEに指定されたWEAKの値をリターンする。 -- Function: hash-table-rehash-size table この関数はTABLEのrehash-sizeをリターンする。 -- Function: hash-table-rehash-threshold table この関数はTABLEのrehash-thresholdをリターンする。 -- Function: hash-table-size table この関数はTABLEの現在の定義されたサイズをリターンする。 8 シンボル ********** “シンボル(symbol)”は一意な名前をもつオブジェクトです。このチャプターでは シンボル、シンボルの構成要素とプロパティーリスト、およびシンボルの作成と インターンする方法を説明します。別のチャプターではシンボルを変数として使 用したり、関数名として使用する方法が説明されています。*note Variables::と *note Functions::を参照してください。シンボルの正確な入力構文については 、*note Symbol Type::を参照してください。 ‘symbolp’を使用して、任意のLispオブジェクトがシンボルかどうかをテスト できます: -- Function: symbolp object この関数はOBJECTがシンボルなら‘t’、それ以外は‘nil’をリターンする。 8.1 シンボルの構成要素 ====================== 各シンボルは4つの構成要素(もしくは“セル”)をもち、構成要素はそれぞれ別の オブジェクトを参照します: プリント名(print name) そのシンボルの名前。 値(value) 変数としてのそのシンボルの現在値。 関数(function) そのシンボルの関数定義。シンボル、キーマップ、キーボードマクロも保 持できる。 プロパティーリスト(property list) そのシンボルのプロパティーリスト。 プリント名のセルは常に文字列を保持し、それを変更することはできません。他 の3つのセルには、任意のLispオブジェクトをセットすることができます。 プリント名のセルはシンボルの名前となる文字列を保持します。シンボルは シンボル名によりテキストとして表されるので、2つのシンボルが同じ名前をも たないことが重要です。Lispリーダーはシンボルを読み取るごとに、それを新規 作成する前に、指定されたシンボルがすでに存在するかを調べます。シンボルの 名前を得るには関数‘symbol-name’(*note Creating Symbols::を参照)を使用し ます。 値セルは変数としてのシンボルの値(そのシンボル自身がLisp式として評価さ れたときに得る値)を保持します。“ローカルバインディング(local binding)”や “スコーピングルール(scoping rules)”等のような複雑なものを含めて、変数の セットや取得方法については*note Variables::を参照してください。ほとんど のシンボルは値として任意のLispオブジェクトをもつことができますが、一部の 特別なシンボルは変更できない値をもちます。これらには‘nil’、‘t’、および名 前が‘:’で始まるすべてのシンボル(“キーワード(keyword)”と呼ばれる)が含まれ ます。*note Constant Variables::を参照してください。 関数セルはシンボルの関数定義を保持します。実際はには‘foo’の関数セルの 中に保管されている関数を意味するときに、“関数‘foo’”といってそれを参照す ることがよくあります。わたしたちは必要なときだけ、これを明確に区別するこ とにします。関数セルは通常は関数(*note Functions::を参照)か、マクロ (*note Macros::を参照)を保持するために使用されます。しかし関数セルはシン ボル(*note Function Indirection::を参照)、キーボードマクロ(*note Keyboard Macros::を参照)、キーマップ(*note Keymaps::を参照)、またはオー トロードオブジェクト(*note Autoloading::を参照)を保持するためにも使用で きます。シンボルの関数セルの内容を得るには、関数‘symbol-function’ (*note Function Cells::を参照)を使用します。 プロパティーリストのセルは、通常は正しくフォーマットされたプロパティ ーリストを保持するべきです。シンボルのプロパティーリストを得るには関数 ‘symbol-plist’を使用します。*note Symbol Properties::を参照してください 。 マクロセルと値セルが“void(空)”のときもあります。voidとはそのセルがど のオブジェクトも参照していないことを意味します(これはシンボル‘void’を保 持するのともシンボル‘nil’を保持するのとも異なる)。voidの関数セルまたは値 セルを調べようとすると結果は‘Symbol's value as variable is void’のような エラーとなります。 各シンボルは値セルと関数セルを別個にもつので、変数名と関数名が衝突す ることはありません。たとえばシンボル‘buffer-file-name’が値(カレントバッ ファーでvisitされているファイルの名前)をもつと同様に、関数定義(ファイル の名前をリターンするプリミティブ関数)をもつことができます: buffer-file-name ⇒ "/gnu/elisp/symbols.texi" (symbol-function 'buffer-file-name) ⇒ # 8.2 シンボルの定義 ================== “定義(definition)”とは、特別な方法での使用の意図を宣言する特別な種類の Lisp式です。定義とは通常はシンボルにたいする値を指定するか、シンボルにた いする1つの種類の使用についての意味とその方法で使用する際のシンボルの意 味のドキュメントを指定します。したがってシンボルを変数として定義すると、 その変数の初期値に加えてその変数のドキュメントを提供できます。 ‘defvar’と‘defconst’は“グローバル変数(global variable)” — Lispプログ ラムの任意の箇所からアクセスできる変数 — として定義するためのスペシャル フォームです。変数についての詳細は*note Variables::を参照してください。 カスタマイズ可能な変数を定義するには‘defcustom’ (サブルーチンとして ‘defvar’も呼び出す)を使用します(*note Customization::を参照)。 最初にシンボルが変数として定義されていなくても、原則として‘setq’で任 意のシンボルに値を割り当てることができます。しかし使用したいグローバル変 数それぞれにたいして変数定義を記述するべきです。さもないとレキシカルスコ ープ(*note Variable Scoping::を参照)が有効なときに変数が評価されたると、 あなたのLispプログラムが正しく動作しないかもしれません。 ‘defun’はラムダ式(lambda expression)を生成して、そのシンボルの関数セ ルに格納することにより、そのシンボルを関数として定義します。したがってこ のシンボルの関数定義は、そのラムダ式になります(関数セルの内容を意味する 用語“関数定義(function definition)”は、‘defun’がシンボルに関数としての定 義を与えるというアイデアに由来する)。*note Functions::を参照してください 。 ‘defmacro’はシンボルをマクロとして定義します。これはマクロオブジェク トを作成してシンボルの関数セルにそれを格納します。シンボルにはマクロと関 数を与えることができますが、マクロと関数定義はどちらも関数セルに保持され るのにたいし、関数セルに保持できるのは常にただ1つのLispオブジェクトなの で、一度に両方を行なうことはできないことに注意してください。*note Macros::を参照してください。 前に注記したようにEmacs Lispではシンボルを(たとえば‘defvar’で)変数と して定義して、同じシンボルを(たとえば‘defun’で)関数やマクロとして両方定 義することができます。このような定義は衝突しません。 これらの定義はプログラミングツールのガイドを果たすこともできます。た とえば‘C-h f’と‘C-h v’コマンドは関連する変数、関数、マクロ定義へのリンク を含むヘルプバッファーを作成します。*note (emacs)Name Help::を参照してく ださい。 8.3 シンボルの作成とintern ========================== GNU Emacs Lispでシンボルが作成される方法を理解するには、Lispがシンボルを 読み取る方法を理解しなければなりません。Lispは、同じ文字綴りを読み取った ら、毎回同じシンボルを見つけることを保証しなければなりません。これに失敗 すると、完全な混乱を招くでしょう。 Lispリーダーがシンボルに出会うと、Lispリーダーは名前のすべての文字を 読み取ります。その後Lispリーダーは“obarray(オブジェクト配列)”と呼ばれる テーブル内のインデックスを決めるために、これらの文字をハッシュ(hash)しま す。ハッシュ化(hashing)は何かを照合するのに効果的な方法です。たとえばJan Jonesを見つけるときは、電話帳を表紙から1頁ずつ探すのではなくJの頁から探 し始めます。これはハッシュ化の簡単なバージョンです。obarrayの各要素は与 えられたハッシュコードとともに、すべてのシンボルを保持する“バケット (bucket)”です。与えられた名前を探すためには、バケットの中からハッシュコ ードがその名前であるような、すべてのシンボルを探すのが効果的です(同じア イデアは一般的なEmacsのハッシュテーブルでも使用されていがこれらはデータ 型が異なる。*note Hash Tables::を参照されたい)。 探している名前のシンボルが見つかったら、リーダーはそのシンボルを使用 します。obarrayにその名前のシンボルが含まれなければ、リーダーは新しいシ ンボルを作成してそれをobarrayに追加します。特定の名前のシンボルを探して 追加することを“インターン(intern)”と言い、これが行なわれた後はそのシンボ ルは“インターンされたシンボル(interned symbol)”と呼ばれます。 インターンすることによりある特定の名前のシンボルは、各obarrayに1つだ けであることが保証されます。同じ名前のシンボルが他に存在するかもしれませ んが、同じobarrayには存在しません。したがってリーダーは、(同じobarrayを 読みつづける限り)同じ名前にたいして同じシンボルを取得します。 インターンは通常はリーダー内で自動的に発生しますが、他のプログラムが これを行なう必要がある場合もあります。たとえば‘M-x’コマンドはその後にミ ニバッファーを使用してコマンド名を文字列として取得して、その文字列をイン ターンしてからインターンされたその名前のシンボルを得ます。 すべてのシンボルを含むobarrayはありません。実際にどのobarrayにも含ま れないシンボルがいくつかあります。これらは“インターンされていないシンボ ル(uninterned symbols)”と呼ばれます。インターンされていないシンボルも、 他のシンボルと同じく4つのセルをもちます。しかしインターンされていないシ ンボルへのアクセスを得る唯一の方法は、他の何らかのオブジェクトとして探す か、変数の値として探す方法だけです。 インターンされていないシンボルの作成は、Lispコードを生成するとき有用 です。なぜなら作成されたコード内で変数として使用されているインターンされ ていないシンボルは、他のLispプログラムで使用されている任意の変数と競合す ることはありえないからです。 Emacs Lispではobarrayはベクターです。ベクター内の各要素がバケットにな ります。要素の値は、名前がそのバケットにハッシュされるようなインターンさ れたシンボル、またはバケットが空のときは0です。インターンされたシンボル は、そのバケット内の次のシンボルへの内部リンク(ユーザーからは見えない)を もちます。これらのリンクは不可視なので、‘mapatoms’ (以下参照)を使用する 方法をのぞき、obarray内のすべてのシンボルを探す方法はありません。バケッ ト内のシンボルの順番に意味はありません。 空のobarrayではすべての要素が0なので、‘(make-vector LENGTH 0)’で obarrayを作成することができます。*obarrayを作成する有効な方法はこれだけ です。*長さに素数を指定するとよいハッシュ化がされる傾向があります。2の累 乗から1減じた長さもよい結果を生む傾向があります。 *自分でobarrayにシンボルを置かないでください。*これはうまくいきません — obarrayに正しくシンボルを入力できるのは‘intern’だけです。 Common Lispに関する注意: Common Lispとは異なりEmacs Lispは1つのシン ボルを複数のobarrayにインターンする方法を提供しない。 以下の関数のほとんどは、引数に名前とobarrayをとります。名前が文字列以 外、またはobarrayがベクター以外なら‘wrong-type-argument’エラーがシグナル されます。 -- Function: symbol-name symbol この関数はSYMBOLの名前を文字列としてリターンする。たとえば: (symbol-name 'foo) ⇒ "foo" *警告: *文字の置き換えにより文字列を変更すると、それはシンボルの名 前を変更しますが、obarrayの更新には失敗するので行なわないこと! -- Function: make-symbol name この関数は新たに割り当てられた、名前がNAME(文字列でなかればならない )であるような、インターンされていないシンボルをリターンする。このシ ンボルの値と関数はvoidで、プロパティーリストは‘nil’。以下の例では ‘sym’の値は‘foo’と‘eq’ではない。なぜならこれは名前が‘foo’という、イ ンターンされていないシンボルだからである。 (setq sym (make-symbol "foo")) ⇒ foo (eq sym 'foo) ⇒ nil -- Function: intern name &optional obarray この関数は名前がNAMEであるような、インターンされたシンボルをリター ンする。オブジェクト配列OBARRAYの中にそのようなシンボルが存在しなけ れば、‘intern’は新たにシンボルを作成してobarrayに追加してそれをリタ ーンする。OBARRAYが省略されると、グローバル変数‘obarray’の値が使用 される。 (setq sym (intern "foo")) ⇒ foo (eq sym 'foo) ⇒ t (setq sym1 (intern "foo" other-obarray)) ⇒ foo (eq sym1 'foo) ⇒ nil Common Lispに関する注意: Common Lispでは既存のシンボルをobarrayにイ ンターンできる。Emacs Lispでは‘intern’の引数はシンボルではなく文字 列なのでこれを行なうことはできない。 -- Function: intern-soft name &optional obarray この関数はOBARRAY内の名前がNAMEのシンボル、OBARRAYにその名前のシン ボルが存在しなければ‘nil’をリターンする。したがって与えられた名前の シンボルがすでにインターンされているかテストするために、 ‘intern-soft’を使用することができる。OBARRAYが省略されるとグローバ ル変数‘obarray’の値が使用される。 引数NAMEにはシンボルも使用できる。この場合、指定されたobarrayに NAMEがインターンされていればNAME、それ以外なら‘nil’をリターンする。 (intern-soft "frazzle") ; そのようなシンボルは存在しない ⇒ nil (make-symbol "frazzle") ; インターンされていないシンボルを作成する ⇒ frazzle (intern-soft "frazzle") ; そのようなシンボルは見つからない ⇒ nil (setq sym (intern "frazzle")) ; インターンされたシンボルを作成する ⇒ frazzle (intern-soft "frazzle") ; シンボルが見つかった! ⇒ frazzle (eq sym 'frazzle) ; そしてそれは同じシンボル ⇒ t -- Variable: obarray この変数は‘intern’と‘read’が使用する標準のobarrayである。 -- Function: mapatoms function &optional obarray この関数はオブジェクト配列OBARRAYの中の各シンボルにたいして、 FUNCTIONを一度呼び出しその後‘nil’をリターンする。OBARRAYが省略され ると、通常のシンボルにたいする標準のオブジェクト配列‘obarray’の値が デフォルトになる。 (setq count 0) ⇒ 0 (defun count-syms (s) (setq count (1+ count))) ⇒ count-syms (mapatoms 'count-syms) ⇒ nil count ⇒ 1871 ‘mapatoms’を使用する他の例については、*note Accessing Documentation::の‘documentation’を参照のこと。 -- Function: unintern symbol obarray この関数はオブジェクト配列OBARRAYからSYMBOLを削除する。obarrayの中 に‘symbol’が存在ければ、‘unintern’は何も行なわない。OBARRAYが ‘nil’なら現在のobarrayが使用される。 SYMBOLにシンボルではなく文字列を与えると、それはシンボルの名前を意 味する。この場合、‘unintern’は(もしあれば)obarrayからその名前のシン ボルを削除する。そのようなシンボルが存在するなら‘unintern’は何も行 なわない。 ‘unintern’がシンボルを削除したら‘t’、それ以外は‘nil’をリターンする 。 8.4 シンボルのプロパティ ======================== シンボルはそのシンボルについての様々な情報を記録するために使用される、任 意の数の“シンボルプロパティー(symbol properties)”をもつことができます。 たとえばシンボルの‘risky-local-variable’プロパティーが‘nil’なら、その変 数の名前が危険なファイルローカル変数(*note File Local Variables::を参照 )であることを意味します。 シンボルのプロパティーとプロパティー値はそれぞれ、シンボルのプロパテ ィーリストセル(*note Symbol Components::を参照)に、プロパティーリスト形 式(*note Property Lists::を参照)で格納されます。 8.4.1 シンボルのプロパティへのアクセス -------------------------------------- 以下の関数を使用してシンボルプロパティーにアクセスできます。 -- Function: get symbol property この関数はSYMBOLのプロパティーリスト内の、名前がPROPERTYというプロ パティーの値をリターンする。そのようなプロパティーが存在しなければ ‘nil’をリターンする。したがって値が‘nil’のときとプロパティーが存在 しないときの違いはない。 名前PROPERTYは‘eq’を使用して既存のプロパティーと比較されるので、す べてのオブジェクトがプロパティーとして適正である。 ‘put’の例を参照のこと。 -- Function: put symbol property value この関数はSYMBOLのプロパティーリストの、プロパティー名PROPERTYに VALUEをputして、前のプロパティー値を置き換える。‘put’関数はVALUEを リターンする。 (put 'fly 'verb 'transitive) ⇒'transitive (put 'fly 'noun '(a buzzing little bug)) ⇒ (a buzzing little bug) (get 'fly 'verb) ⇒ transitive (symbol-plist 'fly) ⇒ (verb transitive noun (a buzzing little bug)) -- Function: symbol-plist symbol この関数はSYMBOLのプロパティーリストをリターンする。 -- Function: setplist symbol plist この関数はSYMBOLのプロパティーリストをPLISTにセットする。PLISTは通 常は適正なプロパティーリストであるべきだが、これは強制ではない。リ ターン値はPLISTです。 (setplist 'foo '(a 1 b (2 3) c nil)) ⇒ (a 1 b (2 3) c nil) (symbol-plist 'foo) ⇒ (a 1 b (2 3) c nil) 通常の用途には使用されない特別なobarray内のシンボルでは、非標準的な 方法でプロパティーリストセルを使用することに意味があるかもしれない 。実際にabbrev(*note Abbrevs::を参照)のメカニズムでこれを行なってい る。 以下のように‘setplist’と‘plist-put’で‘put’を定義できる: (defun put (symbol prop value) (setplist symbol (plist-put (symbol-plist symbol) prop value))) -- Function: function-get symbol property &optional autoload この関数は‘get’と等価だがSYMBOLが関数のエイリアス名なら。実際の関数 を命名するシンボルのプロパティリストを照合する点が異なる。*note Defining Functions::を参照のこと。オプション引数AUTOLOADが非‘nil’で 、SYMBOLが自動ロードされていれば、その自動ロードによりSYMBOLの PROPERTYがセットされるかもしれないので、この関数はそれの自動ロード を試みるだろう。AUTOLOADがシンボル‘macro’なら、SYMBOLが自動ロードさ れたマクロのときだけ自動ロードを試みる。 -- Function: function-put function property value この関数はFUNCTIONのPROPERTYにVALUEをセットする。FUNCTIONはシンボル であること。関数のプロパティのセットには、‘put’よりこの関数を呼び出 すほうがよい。この関数を使用すれば、いつか古いプロパティから新しい プロパティへのリマップを実装することができるからである。 8.4.2 シンボルの標準的なプロパティ ---------------------------------- Emacsで特別な目的のために使用されるシンボルプロパティーを以下に一覧しま す。以下のテーブルで、“命名される関数(the named function)”と言うときは、 関数名がそのシンボルであるような関数を意味します。“命名される変数(the named variable)”等の場合も同様です。 ‘:advertised-binding’ このプロパティーリストは、命名される関数のドキュメントを表示する際 に優先されるキーバインディングを指定する。*note Keys in Documentation::を参照のこと。 ‘char-table-extra-slots’ 値が非‘nil’なら、それは命名される文字テーブル型の追加スロットの数を 指定する。*note Char-Tables::を参照のこと。 ‘customized-face’ ‘face-defface-spec’ ‘saved-face’ ‘theme-face’ これらのプロパティーはフェイスの標準のフェイス仕様(face specs)と、 フォント仕様のsaved-fase、customized-face、themed-faceを記録するた めに使用される。これらのプロパティーを直接セットしないこと。これら のプロパティーは‘defface’と関連する関数により管理される。*note Defining Faces::を参照のこと。 ‘customized-value’ ‘saved-value’ ‘standard-value’ ‘theme-value’ これらのプロパティーは、カスタマイズ可能な変数のstandard-value、 saved-value、customized-value(しかし保存はされない)、themed-valueを 記録するために使用される。これらのプロパティーを直接セットしないこ と。これらは‘defcustom’と関連する関数により管理される。*note Variable Definitions::を参照のこと。 ‘disabled’ 値が非‘nil’なら命名される関数はコマンドとして無効になる。*note Disabling Commands::を参照のこと。 ‘face-documentation’ 値には命名されるフェイスのドキュメント文字列が格納される。これは ‘defface’により自動的にセットされる。*note Defining Faces::を参照の こと。 ‘history-length’ 値が非‘nil’なら、命名されるヒストリーリスト変数のミニバッファーヒス トリーの最大長を指定する。*note Minibuffer History::を参照のこと。 ‘interactive-form’ この値は命名される関数のインタラクティブ形式である。通常はこれを直 接セットするべきではない。かわりにスペシャルフォーム‘interactive’を 使用すること。*note Interactive Call::を参照されたい。 ‘menu-enable’ この値は命名されるメニューアイテムが、メニュー内で有効であるべきか 否かを決定するための式である。*note Simple Menu Items::を参照のこと 。 ‘mode-class’ 値が‘special’なら命名されるメジャーモードはspecial(特別)である。 *note Major Mode Conventions::を参照のこと。 ‘permanent-local’ 値が非‘nil’なら命名される変数はバッファーローカル変数となり、メジャ ーモードの変更によって変数の値はリセットされない。*note Creating Buffer-Local::を参照のこと。 ‘permanent-local-hook’ 値が非‘nil’なら、命名される関数はメジャーモード変更時にフック変数の ローカル値から削除されない。*note Setting Hooks::を参照のこと。 ‘pure’ 値が非‘nil’なら、命名される関数は副作用の影響を受けないとみなされる 。定数であるような引数で呼び出される場合には、コンパイル時に評価が 可能。これは実行時のエラーをコンパイル時へとシフトする。 ‘risky-local-variable’ 値が非‘nil’なら、命名される変数はファイルローカル変数としては危険だ とみなされる。*note File Local Variables::を参照のこと。 ‘safe-function’ 値が非‘nil’なら、命名される関数は評価において一般的に安全だとみなさ れます。*note Function Safety::を参照のこと。 ‘safe-local-eval-function’ 値が非‘nil’なら、命名される関数はファイルローカルの評価フォーム内で 安全に呼び出すことができる。*note File Local Variables::を参照のこ と。 ‘safe-local-variable’ 値は命名される変数の、安全なファイルローカル値を決定する関数を指定 する。*note File Local Variables::を参照のこと。 ‘side-effect-free’ 非‘nil’値は関数の安全性(*note Function Safety::を参照)、およびバイ トコンパイラーの最適化を決定するために、命名される関数に副作用がな いことを示す。これをセットしないこと。 ‘variable-documentation’ 非‘nil’なら、それは命名される変数のドキュメント文字列を指定する。ド キュメント文字列は‘defvar’と関連する関数により自動的にセットされる 。*note Defining Faces::を参照のこと。 9 評価 ****** Emacs Lispでの式の“評価(evaluation)”は、“Lispインタープリター” — 入力と してLispオブジェクトを受け取り、それの“式としての値(value as an expression)”を計算する — により処理されます。評価を行なう方法はそのオブ ジェクトのデータ型に依存していて、それはこのチャプターで説明するルールに より行なわれます。インタープリターはプログラムの一部を評価するために自動 的に実行されますが、Lispプリミティブ関数の‘eval’を通じて明示的に呼び出す こともできます。 9.1 評価の概要 ============== Lispインタープリター(またはLispエバリュエーター)はEmacsの一部であり、与 えられた式の値を計算します。Lispで記述された関数が呼び出されると、エバリ ュエーターはその関数のbody(本文)の中の式を評価してその関数の値を計算しま す。したがってLispプログラムを実行するとは、実際にはLispインタープリター を実行することを意味します。 評価を意図したLispオブジェクトは“フォーム(form)”、または“式 (expression)”と呼ばれます(1)。フォームはデータオブジェクトであって単なる テキストではないという事実は、Lisp風の言語と通常のプログラミング言語との 間にある基本的な相違点の1つです。任意のオブジェクトを評価できますが、実 際に評価される事が非常に多いのは数字、シンボル、リスト、文字列です。 以降のセクションでは、各種フォームにたいしてそれを評価することが何を 意味するかの詳細を説明します。 Lispフォームを読み取ってそのフォームを評価するのは、非常に一般的なア クティビティーですが、読み取りと評価は別のアクティビティーであって、どち らか一方を単独で処理することができます。読み取っただけでは何も評価されま せん。読み取りはLispオブジェクトのプリント表現をそのオブジェクト自体に変 換します。そのオブジェクトが評価されるべきフォームなのか、そのれともまっ たく違う目的をもつかを指定するのは、‘read’の呼び出し元の役目です。*note Input Functions::を参照してください。 評価とは再帰的な処理であり、あるフォームを評価するとそのフォームの一 部が評価されるといったことがよくあります。たとえば‘(car x)’のような“関数 呼び出し(function call)”のフォームを評価する場合、Emacsは最初にその引数 (サブフォーム‘x’)を評価します。引数を評価した後、Emacsはその関数 (‘car’)を“実行(executes)”します。その関数がLispで記述されていれば、関数 の“body(本文)”を評価することによって実行が行なわれます(しかしこの例で使 用している‘car’はLisp関数ではなくCで実装されたプリミティブ関数である)。 関数と関数呼び出しについての情報は*note Functions::を参照してください。 評価は“環境(environment)”と呼ばれるコンテキストの内部で行なわれます。 環境はすべてのLisp変数(*note Variables::を参照)のカレント値とバインディ ングにより構成されます。(2)フォームが新たなバインディングを作成せずに変 数を参照する際、その変数はカレントの環境から与えられる値へと評価されます 。フォームの評価は、変数のバインディングによって一時的にその環境を変更す ることもあります(*note Local Variables::を参照)。 フォームの評価が永続する変更を行なうこともあります。これらの変更は“副 作用(side effects)”と呼ばれます。副作用を生成するフォームの例は‘(setq foo 1)’です。 コマンドキー解釈での評価と混同しないでください。エディターのコマンド ループはアクティブなキーマップを使用して、キーボード入力をコマンド(イン タラクティブに呼び出すことができる関数)に変換してからそのコマンドを実行 するために、‘call-interactively’を使用します。そのコマンドがLispで記述さ れていれば、そのコマンドの実行には通常は評価を伴います。しかしこのステッ プはコマンドキー解釈の一部とは考えません。*note Command Loop::を参照して ください。 ---------- Footnotes ---------- (1) “S式(S-expression)”、短くは“sexp”という言葉でも呼ばれることがあり ますが、わたしたちはこのマニュアル内では通常はこの用語は使用しません。 (2) “環境”にたいするこの定義は、プログラムの結果に影響し得るすべての データを特に意図したものではありません。 9.2 フォームの種類 ================== 評価される事を意図したLispオブジェクトは“フォーム(form)”、または“式 (expression)”)と呼ばれます。Emacsがフォームを評価する方法はフォームのデ ータ型に依存します。Emacsは3種の異なるフォーム — シンボル、リスト、およ びその他すべての型 — をもち、それらが評価される方法は異なります。このセ クションではまず最初に自己評価フォームのその他の型から開始して、3つの種 類をすべて1つずつ説明します。 9.2.1 自己評価を行うフォーム ---------------------------- “自己評価フォーム(self-evaluating form)”はリストやシンボルではないすべて のフォームです。自己評価フォームはそのフォーム自身を評価します。評価の結 果は評価されたオブジェクトと同じです。したがって数字の25は25、文字列 ‘"foo"’は文字列‘"foo"’に評価されます。同様にベクターの評価では、ベクター の要素の評価は発生しません — 内容が変更されずに同じベクターがリターンさ れます。 '123 ; 評価されずに表示される数字 ⇒ 123 123 ; 通常どおり評価され、同じものがreturnされる ⇒ 123 (eval '123) ; 手動での評価 — 同じものがreturnされる ⇒ 123 (eval (eval '123)) ; 2度評価しても何も変わらない。 ⇒ 123 自己評価されるという事実による利点から数字、文字、文字列、そしてベク ターでさえLispコード内で記述されるのが一般的です。しかし入力構文がない型 にたいしてこれを行なうのは極めて異例です。なぜなら、これらをテキスト的に 記述する方法がないからです。Lispプログラムを使用してこれらの型を含む Lisp式を構築することは可能です。以下は例です: ;; バッファーオブジェクトを含む式を構築する。 (setq print-exp (list 'print (current-buffer))) ⇒ (print #) ;; それを評価する。 (eval print-exp) ⊣ # ⇒ # 9.2.2 シンボルのフォーム ------------------------ シンボルが評価されるときは変数として扱われます。それが値をもつなら結果は その変数の値になります。そのシンボルが変数としての値をもたなければ、 Lispインタープリターはエラーをシグナルします。変数の使用法についての情報 は*note Variables::を参照してください。 以降の例では‘setq’でシンボルに値をセットしています。その後シンボルを 評価してからを‘setq’に戻します。 (setq a 123) ⇒ 123 (eval 'a) ⇒ 123 a ⇒ 123 シンボル‘nil’と‘t’は特別に扱われるので、‘nil’の値は常に‘nil’、‘t’の値 は常に‘t’になります。これらに他の値をセットしたり、他の値にバインドする ことはできません。したがってこの2つのシンボルは、(たとえ‘eval’がそれらを 他の任意のシンボルと同様に扱うとはいえ)自己評価フォームと同じように振る 舞います。名前が‘:’で始まるシンボルも同じ方法で自己評価されます。そして 、(通常は)値を変更できない点も同じです。*note Constant Variables::を参照 してください。 9.2.3 リストフォームの分類 -------------------------- 空ではないリストフォームは関数呼び出し、マクロ呼び出し、スペシャルフォー ムのいずれかで、それは1番目の引数にしたがいます。これら3種のフォームは、 以下で説明するように異なる方法で評価されます。残りの要素は関数、マクロ、 またはスペシャルフォームにたいする“引数(arguments)”を構成します。 空ではないリストを評価する最初のステップは、1番目の要素の確認です。こ の要素は単独でそのリストがどの種類のフォームなのかと、残りの引数をどのよ うに処理するがを決定します。SchemeのようなLisp方言とは異なり、1番目の要 素は評価されません。 9.2.4 シンボル関数インダイレクション ------------------------------------ リストの最初の要素がシンボルなら、評価はそのシンボルの関数セルを調べて、 元のシンボルの代わりに関数セルの内容を使用します。その内容が他のシンボル なら、シンボルではないものが得られるまでこのプロセスが繰り返されます。こ のプロセスのことを“シンボル関数インダイレクション(symbol function indirection: indirectionは間接の意)”と呼びます。シンボル関数インダイレク ションについての情報は*note Function Names::を参照してください。 このプロセスの結果、シンボルの関数セルが同じシンボルを参照する場合に は、無限ループを起こす可能性があります。それ以外なら最終的には非シンボル にたどりつき、それは関数か他の適切なオブジェクトである必要があります。 適切なオブジェクトとは、より正確にはLisp関数(ラムダ式)、バイトコード 関数、プリミティブ関数、Lispマクロ、スペシャルフォーム、またはオートロー ドオブジェクトです。これらそれぞれの型については以降のセクションで説明し ます。これらの型以外のオブジェクトならEmacsは‘invalid-function’エラーを シグナルします。 以下の例はシンボルインダイレクションのプロセスを説明するものです。わ たしたちはシンボルの関数セルへの関数のセットに‘fset’、関数セルの内容 (*note Function Cells::を参照)の取得に‘symbol-function’を使用します。具 体的には‘first’の関数セルにシンボル‘car’を格納して、シンボル‘first’を ‘erste’の関数セルに格納します。 ;; この関数セルのリンクを構築する: ;; ------------- ----- ------- ------- ;; | # | <-- | car | <-- | first | <-- | erste | ;; ------------- ----- ------- ------- (symbol-function 'car) ⇒ # (fset 'first 'car) ⇒ car (fset 'erste 'first) ⇒ first (erste '(1 2 3)) ; ‘erste’により参照される関数を呼び出す ⇒ 1 対照的に、以下の例ではシンボル関数インダイレクションを使用せずに関数 を呼び出しています。なぜなら1番目の要素はシンボルではなく、無名Lisp関数 (anonymous Lisp function)だからです。 ((lambda (arg) (erste arg)) '(1 2 3)) ⇒ 1 関数自身を実行するとその関数のbodyを評価します。ここでは‘erste’を呼び出 すとき、シンボル関数インダイレクションが行なわれています。 このフォームが使用されるのは稀であり、現在では推奨されていません。か わりに以下のように記述するべきです: (funcall (lambda (arg) (erste arg)) '(1 2 3)) または単に (let ((arg '(1 2 3))) (erste arg)) ビルトイン関数の‘indirect-function’は、明示的にシンボル関数インダイレ クションを処理するための簡単な方法を提供します。 -- Function: indirect-function function &optional noerror この関数はFUNCTIONが意味するものを関数としてリターンする。 FUNCTIONがシンボルならFUNCTIONの関数定義を探して、その値で最初から やり直す。FUNCTIONがシンボルでなければFUNCTION自身をリターンする。 この関数は最終的なシンボルがバインドされていなければ‘nil’をリターン する。特定のシンボル内にループがれば、この関数は ‘cyclic-function-indirection’エラーをシグナルする。 オペション引数NOERRORは廃れており、後方互換のためだけのもので効果は ない。 以下はLispで‘indirect-function’を定義する例である: (defun indirect-function (function) (if (symbolp function) (indirect-function (symbol-function function)) function)) 9.2.5 関数フォームの評価 ------------------------ リストの1番目の要素がLispの関数オブジェクト、バイトコードオブジェクト、 プリミティブ関数オブジェクトのいずれかと評価されると、そのリストは“関数 呼び出し(function call)”になります。たとえば、以下は関数‘+’を呼び出しま す: (+ 1 x) 関数呼び出しを評価する最初のステップでは、そのリストの残りの要素を左 から右に評価します。結果は引数の実際の値で、リストの各要素にたいして1つ の値となります。次のステップでは関数‘apply’(*note Calling Functions::を 参照)を使用して、引数のリストでその関数を呼び出します。関数がLispで記述 されていたら引数はその関数の引数変数にバインドするために使用されます。そ の後に関数body内のフォームが順番に評価されて、リストのbodyフォームの値が 関数呼び出しの値になります。 9.2.6 Lispマクロの評価 ---------------------- リストの最初の要素がマクロオブジェクトと評価されると、そのリストは“マク ロ呼び出し(macro call)”になります。マクロ呼び出しが評価されるとき、リス トの残りの要素は最初は_評価されません_。そのかわりこれらの要素自体がマク ロの引数に使用されます。そのマクロ定義は、元のフォームが評価される場所で 置換フォームを計算します。これをマクロの“展開(expansion)”と言います。展 開した結果は、任意の種類のフォーム — 自己評価定数、シンボル、リストにな ります。展開した結果自体がマクロ呼び出しなら、結果が他の種類のフォームに なるまで、繰り返し展開処理が行なわれます。 通常のマクロ展開は、その展開結果を評価することにより終了します。しか し他のプログラムもマクロ呼び出しを展開し、それらが展開結果を評価するか、 あるいは評価しないかもしれないので、そのマクロ展開が即時または最終的に評 価される必要がない場合があります。 引数式は通常はマクロ展開の計算の一部としては評価されませんが、展開の 部分として出現するので、展開結果が評価されるときに計算されます。 たとえば以下のようなマクロ定義が与えられたとします: (defmacro cadr (x) (list 'car (list 'cdr x))) ‘(cadr (assq 'handler list))’のような式はマクロ呼び出しであり、展開結果 は以下のようになります: (car (cdr (assq 'handler list))) 引数‘(assq 'handler list)’が展開結果に含まれることに注意してください。 Emacs Lispマクロの完全な説明は*note Macros::を参照してください。 9.2.7 スペシャルフォーム ------------------------ “スペシャルフォーム(special form)”とは、特別だとマークされたプリミティブ 関数であり、その引数のすべては評価されません。もっとも特別なフォームは制 御構文の定義や変数バインディングの処理等、関数ではできないことを行ないま す。 スペシャルフォームはそれぞれ、どの引数を評価して、どの引数を評価しな いかについて独自のルールをもちます。特定の引数が評価されるかどうかは、他 の引数を評価した結果に依存します。 式の最初のシンボルがスペシャルフォームなら、式はそのスペシャルフォー ムのルールにしたがう必要があります。それ以外ならEmacsの挙動は(たとえクラ ッシュしなくいとしても)未定義です。たとえば‘((lambda (x) x . 3) 4)’は ‘lambda’で始まるサブ式を含みますが、これは適正な‘lambda’式ではないので、 Emacsはエラーをシグナルするかもしれないし、3や4や‘nil’をリターンしたり、 もしかしたら他の挙動を示すかもしれません。 -- Function: special-form-p object この述語は引数がスペシャルフォームかをテストして、スペシャルフォー ムなら‘t’、それ以外なら‘nil’をリターンする。 以下にEmacs Lispのスペシャルフォームすべてと、それらがどこで説明され ているかのリファレンスをアルファベット順でリストします。 ‘and’ *note Combining Conditions::を参照のこと。 ‘catch’ *note Catch and Throw::を参照のこと。 ‘cond’ *note Conditionals::を参照のこと。 ‘condition-case’ *note Handling Errors::を参照のこと。 ‘defconst’ *note Defining Variables::を参照のこと。 ‘defvar’ *note Defining Variables::を参照のこと。 ‘function’ *note Anonymous Functions::を参照のこと。 ‘if’ *note Conditionals::を参照のこと。 ‘interactive’ *note Interactive Call::を参照のこと。 ‘lambda’ *note Lambda Expressions::を参照のこと。 ‘let’ ‘let*’ *note Local Variables::を参照のこと。 ‘or’ *note Combining Conditions::を参照のこと。 ‘prog1’ ‘prog2’ ‘progn’ *note Sequencing::を参照のこと。 ‘quote’ *note Quoting::を参照のこと。 ‘save-current-buffer’ *note Current Buffer::を参照のこと。 ‘save-excursion’ *note Excursions::を参照のこと。 ‘save-restriction’ *note Narrowing::を参照のこと。 ‘setq’ *note Setting Variables::を参照のこと。 ‘setq-default’ *note Creating Buffer-Local::を参照のこと。 ‘track-mouse’ *note Mouse Tracking::を参照のこと。 ‘unwind-protect’ *note Nonlocal Exits::を参照のこと。 ‘while’ *note Iteration::を参照のこと。 Common Lispに関する注意: GNU EmacsとCommon Lispのスペシャルフォーム を比較する。‘setq’、‘if’、‘catch’はEmacs LispとCommon Lispの両方で スペシャルフォームである。‘save-excursion’はEmacs Lispではスペシャ ルフォームだが、Common Lispには存在しない。‘throw’はCommon Lispでは スペシャルフォーム(なぜなら複数の値をthrowできなければならない)だが 、Emacs Lispでは(複数の値をもたない)関数である。 9.2.8 自動ロード ---------------- “オートロード(autoload)”機能により、まだ関数定義がEmacsにロードされてい ない関数(またはマクロ)を呼び出すことができます。オートロードは定義がどの ファイルに含まれるかを指定します。オートロードオブジェクトがシンボルの関 数定義にある場合は、関数としてそのシンボルを呼び出すことにより、自動的に 指定されたファイルがロードされます。その後にファイルからロードされた実際 の定義を呼び出します。シンボル内の関数定義としてオートロードオブジェクト をアレンジする方法は*note Autoload::で説明します。 9.3 クォート ============ スペシャルフォーム‘quote’は、単一の引数を記述されたままに評価せずにリタ ーンします。これはプログラムに自己評価オブジェクトではない、定数シンボル や定数リストを含める方法を提供します(数字、文字列、ベクターのような自己 評価オブジェクトをクォートする必要はない)。 -- Special Form: quote object このスペシャルフォームは評価せずにOBJECTをリターンする。 ‘quote’はプログラム中で頻繁に使用されるので、Lispはそれにたいする便利 な入力構文を提供します。アポストロフィー文字(‘'’)に続けてLispオブジェク ト(の入力構文)を記述すると、それは1番目の要素が‘quote’、2番目の要素がそ のオブジェクトであるようなリストに展開されます。つまり入力構文‘'x’は ‘(quote x)’の略記になります。 以下に‘quote’を使用した式の例をいくつか示します: (quote (+ 1 2)) ⇒ (+ 1 2) (quote foo) ⇒ foo 'foo ⇒ foo ''foo ⇒ (quote foo) '(quote foo) ⇒ (quote foo) ['foo] ⇒ [(quote foo)] 他のクォート構文としては、コンパイル用にLispで記述された無名のラムダ 式の元となる‘function’ (*note Anonymous Functions::を参照)、リストを計算 して置き換える際にリストの一部だけをクォートするために使用される ‘`’(*note Backquote::を参照)があります。 9.4 バッククォート ================== “バッククォート構文(backquote constructs)”を使用することにより、リストを クォートしてそのリストのある要素を選択的に評価することができます。もっと も簡単な使い方ではスペシャルフォーム‘quote’と同じです (前のセクションで 説明済み。*note Quoting::を参照)。 たとえば以下の2つのフォームは同じ結果 を生みます: `(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements) '(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements) バッククォートする引数の内側でスペシャルマーカー‘,’を使用すると、それ は値が定数でないことを示します。Emacs Lispエバリュエーターは‘,’がついた 引数を放置して、リスト構文内にその値を配置します: `(a list of ,(+ 2 3) elements) ⇒ (a list of 5 elements) ‘,’による置き換えを、リスト構文のより深いレベルでも使用できます。たとえ ば: `(1 2 (3 ,(+ 4 5))) ⇒ (1 2 (3 9)) スペシャルマーカー‘,@’を使用すれば、評価された値を結果リストに“継ぎ足 す(splice)”こともできます。継ぎ足されたリストの要素は、結果リスト内の他 の要素と同じレベルになります。‘`’を使用しない等価なコードは読むのが困難 なことがよくあります。以下にいくつかの例を示します: (setq some-list '(2 3)) ⇒ (2 3) (cons 1 (append some-list '(4) some-list)) ⇒ (1 2 3 4 2 3) `(1 ,@some-list 4 ,@some-list) ⇒ (1 2 3 4 2 3) (setq list '(hack foo bar)) ⇒ (hack foo bar) (cons 'use (cons 'the (cons 'words (append (cdr list) '(as elements))))) ⇒ (use the words foo bar as elements) `(use the words ,@(cdr list) as elements) ⇒ (use the words foo bar as elements) 9.5 eval ======== フォームはほとんどの場合、実行されるプログラム内に出現することにより自動 的に評価されます。ごく稀に実行時 — たとえば編集されているテキストやプロ パティーリストから取得したフォームを読み取った後 — に計算されるようにフ ォームを評価するコードを記述する必要があるかもしれません。このようなとき は‘eval’関数を使用します。‘eval’が不必要だったり、かわりに他の何かを使用 すべきときがよくあります。たとえば変数から値を取得するには‘eval’も機能し ますが、‘symbol-value’のほうが適しています。‘eval’で評価するためにプロパ ティーリストに式を格納するかわりに、‘funcall’に渡すように関数を格納した 方がよいでしょう。 このセクションで説明する関数と変数はフォームの評価、評価処理の制限の 指定、最後にリターンされた値の記録を行なうものです。ファイルのロードでも 評価が行なわれます(*note Loading::を参照)。 データ構造に式を格納して評価するより、データ構造に関数を格納して ‘funcall’や‘apply’で呼び出すほうが、より明解で柔軟です。関数を使用するこ とにより、引数に情報を渡す能力が提供されます。 -- Function: eval form &optional lexical これは式を評価する基本的な関数である。この関数はカレント環境内で FORMを評価して、その結果をリターンする。FORMオブジェクトの型はそれ が評価される方法を決定します。*note Forms::を参照のこと。 引数LEXICALは、ローカル変数にたいするスコープ規則(*note Variable Scoping::を参照)を指定する。これが省略または‘nil’ならデフォルトのダ イナミックスコープ規則を使用してFORMを評価することを意味する。‘t’な らレキシカルスコープ規則が使用されることを意味する。LEXICALの値には レキシカルバインディングでの特定の“レキシカル環境(lexical environment)”を指定する空ではないalistも指定できる。しかしこの機能 はEmacs Lispデバッガーのような、特別な用途にたいしてのみ有用。*note Lexical Binding::を参照のこと。 ‘eval’は関数なので‘eval’呼び出しに現れる引数式は2回 — ‘eval’が呼び 出される前の準備で一度、‘eval’関数自身によりもう一度 — 評価される。 以下に例を示す: (setq foo 'bar) ⇒ bar (setq bar 'baz) ⇒ baz ;; ‘eval’が引数‘foo’を受け取る (eval 'foo) ⇒ bar ;; ‘eval’が、‘foo’の値である、引数‘bar’を受け取る (eval foo) ⇒ baz ‘eval’で現在アクティブな呼び出しの数は‘max-lisp-eval-depth’に制限さ れる(以下参照)。 -- Command: eval-region start end &optional stream read-function この関数はカレントバッファー内の、位置STARTとENDで定義されるリージ ョン内のフォームを評価する。この関数はリージョンからフォームを読み 取って‘eval’を呼び出す。これはリージョンの最後に達するか、処理され ないエラーがシグナルされるまで行なわれる。 デフォルトでは‘eval-region’は出力を何も生成しない。しかしSTREAMが非 ‘nil’なら出力関数(*note Output Functions::を参照)で生成された任意の 出力、同様にリージョン内の式を評価した結果の値が、STREAMを使用して プリントされる。*note Output Streams::を参照のこと。 READ-FUNCTIONが非‘nil’なら、‘read’のかわりに1つずつ式を読み取るため に使用する関数を指定すること。これは入力を読み取るストリームを指定 する、1つの引数で呼び出される関数である。この関数を指定するために変 数‘load-read-function’(*note How Programs Do Loading: Definition of load-read-function.を参照)も使用できるが、引数READ-FUNCTIONを使用す るほうが堅実である。 ‘eval-region’はポイントを移動しない。常に‘nil’をリターンする。 -- Command: eval-buffer &optional buffer-or-name stream filename unibyte print この関数は‘eval-region’と似ているが、引数は異なるオプション機能を提 供する。‘eval-buffer’はバッファーBUFFER-OR-NAMEのアクセス可能な部分 (*note (emacs)Narrowing::を参照)の全体を処理する。BUFFER-OR-NAMEに はバッファー名(文字列)を指定でき、‘nil’(または省略)のときはカレント バッファーを意味する。STREAMが非‘nil’、またはPRINTが‘nil’なら、 ‘eval-region’のようにSTREAMが使用される。この場合には式の評価結果の 値は依然として破棄されるが、出力関数による出力はエコーエリアにプリ ントされる。FILENAMEは‘load-history’ (*note Unloading::を参照)に使 用されるファイル名であり、デフォルトは‘buffer-file-name’ (*note Buffer File Name::を参照)。UNIBYTEが非‘nil’なら‘read’可能な限りは文 字列をユニコードに変換する。 ‘eval-current-buffer’はこのコマンドのエイリアスである。 -- User Option: max-lisp-eval-depth この変数はエラー(エラーメッセージは‘"Lisp nesting exceeds max-lisp-eval-depth"’)がシグナルされる前に‘eval’、‘apply’、 ‘funcall’の呼び出しで許容される最大の深さを定義する。 制限を超過時のエラーを付随するこの制限は、誤って定義された関数によ る無限再帰をEmacs Lispが回避する方法の1つである。 ‘max-lisp-eval-depth’の値を過大に増加させると、そのようなコードはか わりにスタックオーバーフローを起こすだろう。オーバーフローを処理で きるシステムがいくつかある。この場合には通常のLisp評価は割り込まれ て、制御はトップレベルのコマンドループ(‘top-level’)に戻される。この 状況ではEmacs Lispデバッガにエンターする手段は存在しないことに注意 されたい。*note Error Debugging::を参照のこと。 Lisp式に記述された関数の呼び出し、関数呼び出しの引数と関数bodyフォ ームにたいする再帰評価、Lispコード内での明示的な呼び出し等では内部 的に‘eval’、‘apply’、‘funcall’を使用して深さ制限を計数する。 この変数のデフォルト値は400。この値を100未満にセットして値が与えら れた値に達すると、Lispはそれを100にリセットする。デバッガ自身を実行 するために空きが必要になるので、Lispデバッガに入ったとき空きが少な ければこの値が増加されます。 ‘max-specpdl-size’はネストの他の制限を提供する。*note Local Variables: Definition of max-specpdl-size.を参照のこと。 -- Variable: values この変数の値は読み取り、評価、プリントを行なった標準的なEmacsコマン ドにより、バッファー(ミニバッファーを含む)からリターンされる値のリ ストである(これには‘*ielm*’バッファーでの評価や、 ‘lisp-interaction-mode’での‘C-j’や‘C-x C-e’、類似の評価コマンドを使 用した評価は_含まれない_ことに注意)。要素の順番はもっとも最近のもの が最初になる。 (setq x 1) ⇒ 1 (list 'A (1+ 2) auto-save-default) ⇒ (A 3 t) values ⇒ ((A 3 t) 1 ...) この変数は最近評価されたフォームの値を後で参照するのに有用。 ‘values’自体の値のプリントは、値がおそらく非常に長くなるので通常は 悪いアイデアである。かわりに以下のように特定の要素を調べること: ;; もっとも最近評価された結果を参照する (nth 0 values) ⇒ (A 3 t) ;; これは新たな要素をputするので ;; すべての要素が1つ後に移動する (nth 1 values) ⇒ (A 3 t) ;; これは次に新しい、この例の前の次に新しい要素を取得する (nth 3 values) ⇒ 1 10 制御構造 *********** Lispプログラムは一連の“式”、あるいは“フォーム” (*note Forms::を参照)によ り形成されます。これらのフォームの実行順は“制御構造(control structures)”で囲むことによって制御します。制御構造とはその制御構造が含む フォームをいつ、どのような条件で、何回実行するかを制御するスペシャルフォ ームです。 もっとも単純な実行順は1番目はA、2番目はB、...というシーケンシャル実行 (sequential execution: 順番に実行)です。これは関数のbody内の連続する複数 のフォームや、Lispコードのファイル内のトップレベルを記述したときに発生し ます — つまりフォームは記述した順に実行されます。わたしたちはこれを“テキ スト順(textual order)”と呼びます。たとえば関数のbodyが2つのフォームAと Bから構成される場合、関数の評価は最初にA、次にBを評価します。Bを評価した 結果がその関数の値となります。 明示的に制御構造を使用することにより、非シーケンシャルな順番での実行 が可能になります。 Emacs Lispは他の様々な順序づけ、条件、繰り返し、(制御された)ジャンプ を含む複数の種類の制御構造を提供しており、以下ではそれらのすべてを記述し ます。ビルトインの制御構造は制御構造のサブフォームが評価される必要がなか ったり、順番に評価される必要がないのでスペシャルフォームです。独自の制御 構造を構築するためにマクロを使用することができます(*note Macros::を参照 )。 10.1 順序 ========= フォームを出現順に評価するのは、あるフォームから別のフォームに制御を渡す もっとも一般的な制御です。関数のbodyのようなコンテキストにおいては自動的 にこれが行なわれます。他の場所ではこれを行なうために制御構造を使用しなけ ればなりません。Lispで一単純な制御構造は‘progn’です。 スペシャルフォーム‘progn’は以下のようなものです: (progn A B C ...) これは順番にA、B、C、...を実行するよう指定します。これらは‘progn’フォー ムの“body”と呼ばれます。body内の最後のフォームの値が‘progn’全体の値にな ります。‘(progn)’は‘nil’をリターンします。 初期のLispでは‘progn’は、連続で複数のフォームを実行して最後のフォーム の値を使用する唯一の方法でした。しかしプログラマーは関数のbodyの、(その 時点では)1つのフォームだけが許される場所で‘progn’を使用する必要が多いこ とに気づきました。そのため関数のbodyを暗黙の‘progn’にして、‘progn’の bodyのように複数のフォームを記述できるようにしました。他の多くの制御構造 も暗黙の‘progn’を同様に含みます。結果として昔ほど‘progn’は多用されなくな りました。現在では‘progn’が必要になるのは‘unwind-protect’、‘and’、‘or’、 または‘if’のTHENパートの中であることがほとんどです。 -- Special Form: progn forms... このスペシャルフォームはFORMSのすべてをテキスト順に評価してフォーム の結果をリターンする。 (progn (print "The first form") (print "The second form") (print "The third form")) ⊣ "The first form" ⊣ "The second form" ⊣ "The third form" ⇒ "The third form" 他の2つの構文は一連のフォームを同様に評価しますが、異なる値をリターン します: -- Special Form: prog1 form1 forms... このスペシャルフォームはFORM1とFORMSのすべてをテキスト順に評価して FORM1の結果をリターンする。 (prog1 (print "The first form") (print "The second form") (print "The third form")) ⊣ "The first form" ⊣ "The second form" ⊣ "The third form" ⇒ "The first form" 以下の例は変数‘x’のリストから1番目の要素を削除して、削除した1番目の 要素の値をリターンする: (prog1 (car x) (setq x (cdr x))) -- Special Form: prog2 form1 form2 forms... このスペシャルフォームはFORM1、FORM2、その後のFORMSのすべてをテキス ト順で評価してFORM2の結果をリターンする。 (prog2 (print "The first form") (print "The second form") (print "The third form")) ⊣ "The first form" ⊣ "The second form" ⊣ "The third form" ⇒ "The second form" 10.2 条件 ========= 条件による制御構造は選択肢の中から選択を行ないます。Emacs Lispには4つの 条件フォームをもちます。‘if’は他の言語のものとほとんど同じです。‘when’と ‘unless’は‘if’の変種です。‘cond’は一般化されたcase命令です。 -- Special Form: if condition then-form else-forms... ‘if’はCONDITIONの値にもとづきTHEN-FORMとELSE-FORMSを選択する。評価 されたCONDITIONが非‘nil’ならTHEN-FORMが評価されて結果がリターンされ る。それ以外ならELSE-FORMSがテキスト順に評価されて最後のフォームの 値がリターンされる(‘if’のELSEパートは暗黙の‘progn’の例である。*note Sequencing::を参照)。 CONDITIONの値が‘nil’でELSE-FORMSが与えられなければ、‘if’は‘nil’をリ ターンする。 選択されなかったブランチは決して評価されない — 無視される — ので、 ‘if’はスペシャルフォームである。したがって以下の例では‘print’が呼び 出されることはないので‘true’はプリントされない。 (if nil (print 'true) 'very-false) ⇒ very-false -- Macro: when condition then-forms... これはELSE-FORMSがなく、複数のTHEN-FORMSが可能な‘if’の変種である。 特に、 (when CONDITION A B C) は以下と完全に等価である (if CONDITION (progn A B C) nil) -- Macro: unless condition forms... これはTHEN-FORMがない‘if’の変種です: (unless CONDITION A B C) は以下と完全に等価である (if CONDITION nil A B C) -- Special Form: cond clause... ‘cond’は任意個数の選択肢から選択を行なう。‘cond’内の各CLAUSEはリス トでなければならない。このリストのCARはCONDITIONで、(もしあれば)残 りの要素はBODY-FORMSとなる。したがってclauseは以下のようになる: (CONDITION BODY-FORMS...) ‘cond’は各clauseのCONDITIONを評価することにより、テキスト順で clauseを試みる。CONDITIONの値が非‘nil’ならそのclauseは成り立つ。そ の後に‘cond’はそのclauseのBODY-FORMSを評価して、BODY-FORMSの最後の 値をリターンする。残りのclauseは無視される。 CONDITIONの値が‘nil’ならそのclauseは失敗して、‘cond’は次のclauseに 移動してそれのCONDITIONを試みる。 clauseは以下のようにも見えるかもしれない: (CONDITION) CONDITIONがテストされたときに非‘nil’なら、‘cond’フォームは CONDITIONの値をリターンする。 すべてのCONDITIONが‘nil’に評価された場合 — つまりすべてのclauseが不 成立なら、‘cond’は‘nil’をリターンする。 以下の例は‘x’の値が数字、文字列、バッファー、シンボルなのかをテスト する4つのclauseをもつ: (cond ((numberp x) x) ((stringp x) x) ((bufferp x) (setq temporary-hack x) ; 1つのclauseに (buffer-name x)) ; 複数bodyフォーム ((symbolp x) (symbol-value x))) 前のclauseが不成立のとき最後の条項を実行したいときがよくある。これ を行なうには‘(t BODY-FORMS)’のように、CONDITIONの最後のclauseに ‘t’を使用する。フォーム‘t’は‘t’に評価され決して‘nil’にならないので 、このclauseが不成立になることはなく最終的に‘cond’はこのclauseに到 達する。たとえば: (setq a 5) (cond ((eq a 'hack) 'foo) (t "default")) ⇒ "default" この‘cond’式は‘a’の値が‘hack’なら‘foo’、それ以外は文字列 ‘"default"’をリターンする。 すべての条件構文は‘cond’か‘if’のいずれかで表すことができます。したが ってどちらを選択するかはスタイルの問題になります。たとえば: (if A B C) ≡ (cond (A B) (t C)) 10.2.1 パターンマッチングによるcase文 ------------------------------------- ‘cond’フォームにより、あらかじめ記述された既知の特定の値と式の値を比較す る述語条件を使用して選択肢を選択できます。しかし広範な値クラス間を区別す る、より一般的な条件にもとづいて選択肢を選択するのが有用なこともあります 。‘pcase’マクロにより、一連のパターンにたいする式の値のマッチングにもと づいて選択肢を選択できます。パターンにはリテラル値(‘cond’で使用した比較 用のリテラル値)や、予想される式の値のより一般的な構造記述を使用できます 。 -- Macro: pcase expression &rest clauses EXPRESSIONを評価して、任意個数の選択肢の中からEXPRESSIONの値にもと づいた選択を行う。可能な選択肢はCLAUSESで指定され、それぞれが ‘(PATTERN BODY-FORMS...)’という形式のリストでなければならない。 ‘pcase’はEXPRESSIONの値と各clauseのPATTERNのマッチを試みる。値がマ ッチしたらそのclauseが成功となり、‘pcase’はそれのBODY-FORMSを評価し て、BODY-FORMSの最後の値をリターンする。残りのCLAUSESはすべて無視さ れる。 PATTERNパートはバッククォートでクォートされた“QPattern”と、クォート されていない“UPattern”のいずれかで指定できる。UPatternsのほうが単純 なのでそれから説明する。 注意: 以下のパターンの記述では‘pcase’の1つ目の引数となる EXPRESSIONの値を参照するために、“マッチされる値”という言葉を使用し ている。 UPatternには以下の形式を指定できる: ‘'VAL’ マッチされる値がVALと‘equal’ならマッチ。 ‘ATOM’ 任意のATOM (キーワード、数字、文字列)にマッチする(これらは自己 クォートされるのでこの種のUPatternは実際には‘'ATOM’の略記であ る)。文字列(浮動小数点数)は同じ内容(値)の任意の文字列(浮動小数 点数)とマッチすることに注意。 ‘_’ 任意の値にマッチする。これは“don’t care”や“wildcard”として知ら れる。 ‘SYMBOL’ 任意の値にマッチする。さらにマッチした値をSYMBOLにletバインド するので、BODY-FORMSや後続のパターンからそれを参照することがで きる。 ‘(pred PREDFUN)’ マッチされる値を引数として述語関数PREDFUNを呼び出して、非 ‘nil’をリターンしたらマッチ。PREDFUNは後述するフォームのいずれ かを指定できる。 ‘(guard BOOLEAN-EXPRESSION)’ BOOLEAN-EXPRESSIONが非‘nil’に評価されたらマッチ。これにより以 前のUPatternで、値(マッチされる値を含む)にバインドされたシンボ ルを参照するブール条件をUPatternに含めることができる。典型的に はUPattern ‘and’内で使用される(以下参照)。たとえば ‘(and x (guard (< x 10)))’は10より小さい任意の数にマッチして、 その数を変数‘x’にletバインドする。 ‘(let UPATTERN EXPRESSION)’ 指定されたEXPRESSIONが指定されたUPATTERNにマッチしたらマッチ。 これにより‘pcase’の1つ目の引数だけでなく、_任意_の式の値にパタ ーンをマッチできる(UPATTERNはUPattern SYMBOLを使用してシンボル を値にバインドできるので‘let’と呼ばれる。たとえば ‘((or `(key . ,val) (let val 5)) val)’)。 ‘(app FUNCTION UPATTERN)’ マッチされる値にFUNCTIONを適用してUPATTERNにマッチする値がリタ ーンされたらマッチ。これはUPattern ‘pred’と似ているが、これは ブールの真値ではなくUPATTERNにたいして結果をテストする点が異な る。FUNCTION呼び出しは後述のフォームのいずれかを使用できる。 ‘(or UPATTERN1 UPATTERN2...)’ 引数のUPatternのいずれかがマッチしたらマッチ。マッチする最初の UPatternが見つかったら残りはテストされない。この理由により、マ ッチされる値にシンボルをletバインドするすべてのUPatternは同じ シンボルをバインドすること。 ‘(and UPATTERN1 UPATTERN2...)’ 引数のUPatternすべてがマッチしたらマッチ。 ‘pred’と‘app’のUPatternで使用される関数呼び出しは、以下のいずれかの フォームをもつことができる: ‘integerp’のような関数シンボル この場合には、その名前つき関数がマッチされる値に適用される。 ラムダ関数 ‘(lambda (ARG) BODY)’ この場合には、そのラムダ関数がマッチされる値を単一の引数として 呼び出される。 ‘(FUNC ARGS...)’ これは指定されたN個の引数で呼び出される関数である。関数はこれ らN個の引数と、マッチされる値であるN+1番目の引数を追加して呼び 出される。 以下はUPatternを使用した説明用の例です: (pcase (get-return-code x) ('success (message "Done!")) ('would-block (message "Sorry, can't do it now")) ('read-only (message "The shmliblick is read-only")) ('access-denied (message "You do not have the needed rights")) (code (message "Unknown return code %S" code))) 加えてより強力なバッククォートされたパターンを使用できます。これら を使用すれば‘pcase’の1つ目の引数の式の値を、その_構造(structure)_の 仕様とマッチさせることができます。たとえば1つ目の要素が特定の文字列 で、2つ目の要素が‘`("first" ,second-elem)’のようなバッククォートさ れた任意の値であるような2要素のリストを、値として強制指定することが できます。 バッククォートされたパターンは‘`QPATTERN’という形式をもち、 QPATTERNは以下の形式をもつことができます: ‘(QPATTERN1 . QPATTERN2)’ マッチされる値が、‘car’がQPATTERN1、‘cdr’がQPATTERN2にマッチす るようなコンスセルならマッチ。これは ‘(QPATTERN1 QPATTERN2 ...)’のように、容易にバッククォートされ たリストに一般化できる。 ‘[QPATTERN1 QPATTERN2 ... QPATTERNM]’ マッチされる値が、長さMで‘0’から‘(M-1)’番目の要素がそれぞれ QPATTERN1、QPATTERN2、...、QPATTERNMにマッチするようなベクター ならマッチ。 ‘ATOM’ マッチされる値の対応する要素が指定されたATOMと‘equal’ならマッ チ。 ‘,UPATTERN’ マッチされる値の対応する要素が指定されたUPATTERNとマッチすれば マッチ。 QPatternは後述の‘pcase-defmacro’を使用してUPatternのトップレベルで 実装されているので、QPatternの使用はUPatternを使用することでのみ表 現可能なことに注意。とはいえQPatternの使用により、多くの場合コード の可読性は向上するだろう。 以下は‘pcase’を使用して、小さな式言語用のシンプルなインタープリターを 実装する例です(この例にはレキシカルバインディングが必要なことに注意。 )*note Lexical Binding::を参照のこと): (defun evaluate (exp env) (pcase exp (`(add ,x ,y) (+ (evaluate x env) (evaluate y env))) (`(call ,fun ,arg) (funcall (evaluate fun env) (evaluate arg env))) (`(fn ,arg ,body) (lambda (val) (evaluate body (cons (cons arg val) env)))) ((pred numberp) exp) ((pred symbolp) (cdr (assq exp env))) (_ (error "Unknown expression %S" exp)))) ここで‘`(add ,x ,y)’は、‘exp’がリテラルシンボル‘add’で始まる3要素のリ ストであることをチェックしてから、2つ目と3つ目の要素を抽出して変数‘x’と ‘y’にバインドするパターンです。それから‘x’と‘y’を評価して結果を加算しま す。同様に‘call’と‘fn’のパターンは、関数呼び出しに相当するものを2つ実装 します。‘(pred numberp)’は‘exp’が数であるかをチェックして、もしそうなら それを評価します。‘(pred symbolp)’はシンボルにマッチして、その連想をリタ ーンします。最後に‘_’はすべてにマッチするcatch-allパターンなので、構文エ ラーの報告に適しています。 以下は評価した結果を含む、この小さな言語のサンプルプログラムの例です: (evaluate '(add 1 2) nil) ;=> 3 (evaluate '(add x y) '((x . 1) (y . 2))) ;=> 3 (evaluate '(call (fn x (add 1 x)) 2) nil) ;=> 3 (evaluate '(sub 1 2) nil) ;=> error ‘pcase-defmacro’を使用することにより追加のUPatternを定義できます。 -- Macro: pcase-defmacro name args &rest body ‘pcase’にたいして新たな種類のUPatternを定義する。新たなUPatternは ‘(NAME ACTUAL-ARGS)’のように呼び出されるだろう。BODYには、UPattern NAMEを他の何らかのUPatternに書き換える方法を記述すること。ARGSが ACTUAL-ARGSにバインドされる環境でBODYを評価した結果がこの書き換えと なる。 10.3 条件の組み合わせ ===================== このセクションでは複雑な条件を表現するために‘if’や‘cond’とともによく使用 される3つの構文を説明します。‘and’と‘or’の構文は、ある種の複数条件の構文 として個別に使用することもできます。 -- Function: not condition この関数はCONDITIONが偽であることをテストする。この関数は CONDITIONが‘nil’なら‘t’、それ以外は‘nil’をリターンする。関数‘not’は ‘null’と等価であり、空のリストをテストする場合は‘null’の使用を推奨 する。 -- Special Form: and conditions... スペシャルフォーム‘and’は、すべてのCONDITIONSが真かどうかをテストす る。この関数はCONDITIONSを記述順に1つずつ評価することにより機能する 。 あるCONDITIONSが‘nil’に評価されると、残りのCONDITIONSに関係なく、 ‘and’は‘nil’をリターンしなければならない。この場合‘and’は即座に ‘nil’をリターンして、残りのCONDITIONSは無視される。 すべてのCONDITIONSが非‘nil’なら、それらの最後の値が‘and’フォームの 値になる。CONDITIONSがない単独の‘(and)’は‘t’をリターンする。なぜな らすべてのCONDITIONSが非‘nil’となるので、これは適切である(考えてみ てみよ、非‘nil’でないCONDITIONSはどれか?)。 以下に例を示す。1番目の条件は整数1をリターンし、これは‘nil’ではまい 。同様に2番目の条件は整数2をリターンし、これも‘nil’ではない。3番目 の条件は‘nil’なので、のこりの条件が評価されることは決してない。 (and (print 1) (print 2) nil (print 3)) ⊣ 1 ⊣ 2 ⇒ nil 以下は‘and’を使用した、より現実的な例である: (if (and (consp foo) (eq (car foo) 'x)) (message "foo is a list starting with x")) ‘(consp foo)’が‘nil’をリターンすると、‘(car foo)’は実行されないので エラーにならないことに注意。 ‘if’か‘cond’のいずれかを使用して、‘and’式を記述することもできる。以 下にその方法を示す: (and ARG1 ARG2 ARG3) ≡ (if ARG1 (if ARG2 ARG3)) ≡ (cond (ARG1 (cond (ARG2 ARG3)))) -- Special Form: or conditions... スペシャルフォーム‘or’は、少なくとも1つのCONDITIONSが真かどうかをテ ストする。この関数はすべてのCONDITIONSを1つずつ、記述された順に評価 することにより機能する。 あるCONDITIONSが非‘nil’値に評価されたら、‘or’の結果は非‘nil’でなけ ればならない。この場合‘or’は即座にリターンし、残りのCONDITIONSは無 視される。この関数がリターンする値は、非‘nil’値に評価された条件の値 そのものである。 すべてのCONDITIONSが‘nil’なら、‘or’式は‘nil’をリターンします。 CONDITIONSのない単独の‘(or)’は‘nil’をリターンする。なぜならすべての CONDITIONSが‘nil’になるのでこれは適切である(考えてみよ、‘nil’でない CONDITIONSはどれか?)。 たとえば以下の式は、‘x’が‘nil’か整数0かどうかをテストする: (or (eq x nil) (eq x 0)) ‘and’構文と同様に、‘or’を‘cond’に置き換えて記述することができる。た とえば: (or ARG1 ARG2 ARG3) ≡ (cond (ARG1) (ARG2) (ARG3)) ほとんどの場合は、‘or’を‘if’に置き換えて記述できるが完全ではない: (if ARG1 ARG1 (if ARG2 ARG2 ARG3)) これは完全に同一ではない。なぜならARG1かARG2を2回評価するかもしれな いからである。対照的に‘(or ARG1 ARG2 ARG3)’が2回以上引数を評価する ことは決してない。 10.4 繰り返し ============= 繰り返し(iteration)とは、プログラムの一部を繰り返し実行することを意味し ます。たとえばリストの各要素、または0からNの整数にたいして、繰り返し一度 ずつ何らかの計算を行いたいとしましょう。Emacs Lispではスペシャルフォーム ‘while’でこれを行なうことができます: -- Special Form: while condition forms... ‘while’は最初にCONDITIONを評価する。結果が非‘nil’ならFORMSをテキス ト順に評価する。その後にCONDITIONを再評価して結果が非‘nil’なら、再 度FORMSを評価する。この処理はCONDITIONが‘nil’に評価されるまで繰り返 される。 繰り返し回数に制限はない。このループはCONDITIONが‘nil’に評価される か、エラーになるか、または‘throw’で抜け出す(*note Nonlocal Exits::を 参照)まで継続される。 ‘while’フォームの値は常に‘nil’である。 (setq num 0) ⇒ 0 (while (< num 4) (princ (format "Iteration %d." num)) (setq num (1+ num))) ⊣ Iteration 0. ⊣ Iteration 1. ⊣ Iteration 2. ⊣ Iteration 3. ⇒ nil 各繰り返しごとに何かを実行して、その後も終了テストを行なう repeat-untilループを記述するには、以下のように‘while’の1番目の引数 としてbodyの後に終了テストを記述して、それを‘progn’の中に配置する: (while (progn (forward-line 1) (not (looking-at "^$")))) これは1行前方に移動して、空行に達するまで行単位の移動を継続する。独 特な点は‘while’がbodyをもたず、終了テスト(これはポイント移動という 実処理も行なう)だけを行うことである。 マクロ‘dolist’および‘dotimes’は、2つの一般的な種類のループを記述する 、便利な方法を提供します。 -- Macro: dolist (var list [result]) body... この構文はLISTの各要素に一度BODYを実行して、カレント要素をローカル に保持するように、変数VARにバインドする。その後にRESULTを評価した値 、RESULTが省略された場合は‘nil’をリターンする。たとえば以下は ‘reverse’関数を定義するために‘dolist’を使用する方法の例である: (defun reverse (list) (let (value) (dolist (elt list value) (setq value (cons elt value))))) -- Macro: dotimes (var count [result]) body... この構文は0以上COUNT未満の各整数にたいして、一度BODYを実行してから 、繰り返しのカレント回数となる整数を変数VARにバインドする。その後に RESULTの値、RESULTが省略された場合は‘nil’をリターンする。以下は ‘dotimes’を使用して、何らかの処理を100回行なう例である: (dotimes (i 100) (insert "I will not obey absurd orders\n")) 10.5 Generators =============== “ジェネレーター(generator)”とは、潜在的に無限な値ストリームを生成する関 数です。毎回その関数が値を生成するごとに、呼び出し側が次の値を要求するま で、自身をサスペンドします。 -- Macro: iter-defun name args [doc] [declare] [interactive] body... ‘iter-defun’はジェネレーター関数を定義する。ジェネレーター関数は通 常の関数と同様のsignatureをもつが、異なるように機能する。ジェネレー ター関数は呼び出し時にBODYを実行するのではなく、かわりにiteratorオ ブジェクトをリターンする。このiteratorは値を生成するためにBODYを実 行、値を発行すると‘iter-yield’か‘iter-yield-from’が出現するまで一時 停止する。BODYが正常にリターンした際に、‘iter-next’がコンディション データとなるBODYの結果とともに、‘iter-end-of-sequence’をシグナルす る。 BODY内部では任意の種類のLispコードが有効だが、‘iter-yield’と ‘iter-yield-from’は‘unwind-protect’フォームの内部にあってはならない 。 -- Macro: iter-lambda args [doc] [interactive] body... ‘iter-lambda’は‘iter-defun’で生成されたジェネレーター関数と同様な、 無名のジェネレーター関数を生成する。 -- Macro: iter-yield value ‘iter-yield’がジェネレーター関数内部で出現した際には、カレント iteratorが一時停止して‘iter-next’からVALUEをリターンすることを示す 。‘iter-yield’は、次回‘iter-next’呼び出しの‘value’パラメーターへと 評価される。 -- Macro: iter-yield-from iterator ‘iter-yield-from’はITERATORが生成するすべての値を生成して、その ITERATORのジェネレーター関数が通常リターンする値へと評価される。こ れが制御を得ている間、ITERATORは‘iter-next’を使用して送信された値を 受け取る。 ジェネレーター関数を使用するには、まずそれを普通に呼び出して “iterator”オブジェクトを生成します。iteratorはジェネレーターの固有のイン スタンスです。その後でこのiteratorから値を取得するために‘iter-next’を使 用します。iteratorから取得する値がなくなると、‘iter-next’はその iteratorの最終値とともに‘iter-end-of-sequence’のコンディションをraisesし ます。 ジェネレーター関数のbodyは、‘iter-next’の呼び出しの内側でのみ実行され ることに注意することが重要です。‘iter-defun’で定義された関数の呼び出しは iteratorを生成します。何か興味があることが発生したら、‘iter-next’でこの iteratorを制御しなければなりません。ジェネレーター関数の個々の呼び出しは 、それぞれが独自に状態をもつ_別個_のiteratorを生成します。 -- Function: iter-next iterator value ITERATORから次の値を取得する。(ITERATORのジェネレーター関数がリター ンしていて)生成される値が存在しない場合、‘iter-next’はコンディショ ン‘iter-end-of-sequence’をシグナルする。このコンディションに関連付 けられるデータ値は、ITERATORのジェネレーター関数がリターンした値で ある。 VALUEはiteratorに送信されて、‘iter-yield’を評価した値になる。 ITERATORのジェネレーター関数の開始時には、ジェネレーター関数は ‘iter-yield’フォームを何も評価していないので、与えられたiteratorに たいする最初の‘iter-next’呼び出しではVALUEは無視される。 -- Function: iter-close iterator ITERATORが‘unwind-protect’の‘bodyform’フォーム内でサスペンドされて いたら、ガーベージコレクション処理後にEmacsが最終的にunwindハンドラ ーを実行する(‘unwind-protect’の‘unwindforms’内部では‘iter-yield’は 不当であることに注意)。その前に確実にこれらのハンドラーを実行するに は、‘iter-close’を使用すること。 iteratorを簡単に連携できるように、便利な関数がいくつか提供されていま す: -- Macro: iter-do (var iterator) body ... ITERATORが生成する各値をVARにバインドしつつBODYを実行する。 Common Lispのループ機能にもiteratorと連携する機能が含まれます。*note (cl)Loop Facility::を参照してください。 以下のコード片はiteratorとの連携における重要な原則を示すものです。 (iter-defun my-iter (x) (iter-yield (1+ (iter-yield (1+ x)))) ;; 普通にリターンする -1) (let* ((iter (my-iter 5)) (iter2 (my-iter 0))) ;; 6をプリント (print (iter-next iter)) ;; 9をプリント (print (iter-next iter 8)) ;; 1をプリント ;; iterとiterは異なる状態をもつ (print (iter-next iter2 nil)) ;; ここでiterシーケンスの終了を待機 (condition-case x (iter-next iter) (iter-end-of-sequence ;; my-iterが通常の方法でリターンした-1をプリント (print (cdr x))))) 10.6 非ローカル脱出 =================== “非ローカル脱出(nonlocal exit)”とは、プログラム内のある位置から別の離れ た位置へ制御を移します。Emacs Lispではエラーの結果として非ローカル脱出が 発生することがあります。明示的な制御の下で非ローカル脱出を使用することも できます。非ローカル脱出は脱出しようとしている構文により作成された、すべ ての変数バインディングのバインドを解消します。 10.6.1 明示的な非ローカル脱出: ‘catch’と‘throw’ ----------------------------------------------- ほとんどの制御構造は、その構文自身の内部の制御フローだけに影響します。関 数‘throw’は、この通常のプログラム実行でのルールの例外です。これはリクエ ストにより非ローカル脱出を行ないます(他にも例外はあるがそれらはエラー処 理用のものだけ)。‘throw’は‘catch’の内部で使用され、‘catch’に制御を戻しま す。たとえば: (defun foo-outer () (catch 'foo (foo-inner))) (defun foo-inner () ... (if x (throw 'foo t)) ...) ‘throw’フォームが実行されると、対応する‘catch’に制御を移して、‘catch’は 即座にリターンします。‘throw’の後のコードは実行されません。‘throw’の2番 目の引数は‘catch’のリターン値として使用されます。 関数‘throw’は1番目の引数にもとづいて、それにマッチする‘catch’を探しま す。‘throw’は1番目の引数が、‘throw’で指定されたものと‘eq’であるような ‘catch’を検索します。複数の該当する‘catch’がある場合には、最内のものが優 先されます。したがって上記の例では‘throw’が‘foo’を指定していて、 ‘foo-outer’内の‘catch’が同じシンボルを指定しているので、(この間に他のマ ッチする‘catch’は存在しないと仮定するなら)その‘catch’が該当します。 ‘throw’の実行により、マッチする‘catch’までのすべてのLisp構文(関数呼び 出しを含む)を脱出します。この方法により‘let’や関数呼び出しのようなバイン ディング構文を脱出する場合には、これらの構文を正常にexitしたときのように 、そのバインディングは解消されます(*note Local Variables::を参照)。同様 に‘throw’は‘save-excursion’(*note Excursions::を参照)によって保存された バッファーと位置を復元します。‘throw’がスペシャルフォーム ‘unwind-protect’を脱出した場合には、‘unwind-protect’により設定されたいく つかのクリーンアップも実行されます。 ジャンプ先となる‘catch’内にレキシカル(局所的)である必要はありません。 ‘throw’は‘catch’内で呼び出された別の関数から、同じようにに呼び出すことが できます。‘throw’が行なわれたのが、時系列的に‘catch’に入った後で、かつ exitする前である限り、その‘throw’は‘catch’にアクセスできます。エディター のコマンドループから戻る‘exit-recursive-edit’のようなコマンドで、 ‘throw’が使用されるのはこれが理由です。 Common Lispに関する注意: Common Lispを含む、他のほとんどのバージョ ンのLispは非シーケンシャルに制御を移すいくつかの方法 — たとえば ‘return’、‘return-from’、‘go’ — をもつ。Emacs Lispは‘throw’のみ。 ‘cl-lib’ライブラリーはこれらのうちいくつかを提供する。*note (cl)Blocks and Exits::を参照のこと。 -- Special Form: catch tag body... ‘catch’は‘throw’関数にたいするリターン位置を確立する。リターン位置 はTAGにより、この種の他のリターン位置と区別される。TAGは‘nil’以外の 任意のLispオブジェクト。リターン位置が確立される前に、引数TAGは通常 どおり評価される。 リターン位置が効果をもつことにより、‘catch’はBODYのフォームをテキス ト順に評価する。フォームが(エラーや非ローカル脱出なしで)通常に実行 されたなら、bodyの最後のフォームの値が‘catch’からリターンされる。 BODYの実行の間に‘throw’が実行された場合、TAGと同じ値を指定すると ‘catch’フォームは即座にexitする。リターンされる値は、それが何であれ ‘throw’の2番目の引数に指定された値である。 -- Function: throw tag value ‘throw’の目的は、以前に‘catch’により確立されたリターン位置に戻るこ とである。引数TAGは、既存のさまざまなリターン位置からリターン位置を 選択するために使用される。複数のリターン位置がTAGにマッチしたら、最 内のものが使用される。 引数VALUEは‘catch’からリターンされる値として使用される。 タグTAGのリターン位置が存在しなければ、データ‘(TAG VALUE)’とともに ‘no-catch’エラーがシグナルされます。 10.6.2 ‘catch’と‘thrown’の例 ---------------------------- 2重にネストされたループから脱出する1つの方法は、‘catch’と‘throw’を使うこ とです(これはほとんどの言語では‘goto’により行なわれるだろう)。ここでは IとJを0から9に変化させて、‘(foo I J)’を計算します: (defun search-foo () (catch 'loop (let ((i 0)) (while (< i 10) (let ((j 0)) (while (< j 10) (if (foo i j) (throw 'loop (list i j))) (setq j (1+ j)))) (setq i (1+ i)))))) ‘foo’が非‘nil’をリターンしたら即座に処理を中止して、IとJのリストをリター ンしています。‘foo’が常に‘nil’をリターンする場合には、‘catch’は通常どお りリターンして、その値は‘while’の結果である‘nil’となります。 以下では2つのリターン位置を一度に表す、微妙に異なるトリッキーな例を 2つ示します。まず同じタグ‘hack’にたいして2つのリターン位置があります: (defun catch2 (tag) (catch tag (throw 'hack 'yes))) ⇒ catch2 (catch 'hack (print (catch2 'hack)) 'no) ⊣ yes ⇒ no どちらのリターン位置も‘throw’にマッチするタグをもつので内側のもの、つま り‘catch2’で確立されたcatchへgotoします。したがって‘catch2’は通常どおり 値‘yes’をリターンして、その値がプリントされます。最後に外側の‘catch’の 2番目のbody、つまり‘'no’が評価されて外側の‘catch’からそれがリターンされ ます。 ここで‘catch2’に与える引数を変更してみましょう: (catch 'hack (print (catch2 'quux)) 'no) ⇒ yes この場合も2つのリターン位置がありますが、今回は外側だけがタグ‘hack’で、 内側はかわりにタグ‘quux’をもちます。したがって‘throw’により、外側の ‘catch’が値‘yes’をリターンします。関数‘print’が呼び出されることはなく bodyのフォーム‘'no’も決して評価されません。 10.6.3 エラー ------------- Emacs Lispが何らかの理由で評価できないようなフォームの評価を試みると、 “エラー(error)”が“シグナル(signal)”されます。 エラーがシグナルされるとエラーメッセージを表示して、カレントコマンド の実行を終了するのがEmacsデフォルトの反応です。たとえばバッファーの最後 で‘C-f’とタイプしたときのように、ほとんどの場合にはこれは正しい反応にな ります。 複雑なプログラムでは単なる終了が望ましくない場合もあるでしょう。たと えばそのプログラムがータ構造に一時的に変更を行なっていたり、プログラム終 了前に削除する必要がある一時バッファーを作成しているかもしれません。この ような場合には、エラー時に評価される“クリーンアップ式(cleanup expressions)”を設定するために、‘unwind-protect’を使用するでしょう(*note Cleanups::を参照)。サブルーチン内のエラーにもかかわらずに、プログラムの 実行を継続したいときがあるかもしれません。このような場合には、エラー時の リカバリーを制御する“エラーハンドラー(error handlers)”を設定するために ‘condition-case’を使用するでしょう。 エラーハンドラーを使用せずにプログラムの一部から別の部分へ制御を移す ためには、‘catch’と‘throw’を使用します。*note Catch and Throw::を参照し てください。 10.6.3.1 エラーをシグナルする方法 ................................. エラーの“シグナリング(signaling)”とは、エラーの処理を開始することを意味 します。エラー処理は通常は実行中のプログラムのすべて、または一部をアボー ト(abort)してエラーをハンドルするためにセットアップされた位置にリターン します。ここではエラーをシグナルする方法を記述します。 ほとんどのエラーは、たとえば整数にたいしてCARの取得を試みたり、バッフ ァーの最後で1文字前方に移動したときなどのように、他の目的のために呼び出 したLispプリミティブ関数の中で自動的にシグナルされます。関数‘error’と ‘signal’で明示的にエラーをシグナルすることもできます。 ユーザーが‘C-g’をタイプしたときに発生するquitはエラーとは判断されませ んが、ほとんどはエラーと同様に扱われます。*note Quitting::を参照してくだ さい。 すべてのエラーメッセージはそれぞれ、何らかのエラーメッセージを指定し ます。そのメッセージは何が悪いのか(“File does not exist”)、物事がどうし てそうあるべきではない(“File must exist”)かを示すべきです。Emacs Lispの 慣習ではエラーメッセージは大文字で開始され、区切り文字で終わるべきではあ りません。 -- Function: error format-string &rest args この関数はFORMAT-STRINGとARGSにたいして、‘format-message’ (*note Formatting Strings::を参照)を適用して構築されたエラーメッセージとと もに、エラーをシグナルする。 以下は‘error’を使用する典型的な例である: (error "That is an error -- try something else") error→ That is an error -- try something else (error "Invalid name `%s'" "A%%B") error→ Invalid name ‘A%%B’ 2つの引数 — エラーシンボル‘error’と‘format-message’がリターンするる 文字列を含むリスト — で‘signal’を呼び出すことにより‘error’は機能す る。 ‘text-quoting-style’変数は何のクォートを生成するかを制御する。*note Keys in Documentation::を参照のこと。グレイブアクセントやアポストロ フィーを含む"Missing `%s'"のようなフォーマットを使用した呼び出しで は、通常は\"Missing ‘foo’\"のようにマッチするcurved quotesをもつメ ッセージが生成される。対照的に"Missing '%s'"のようにアポストロフィ ーだけのフォーマットを使用した場合には、通常は"Missing ’foo’"のよう に、closing curved quotesだけをもつ英語では普通使用されないスタイル のメッセージが生成される。 *警告: *エラーメッセージとして固定の文字列を使用したい場合、単に ‘(error STRING)’とは記述しないこと。もしSTRINGが‘%’、‘`’、‘'’を含ん でいると、再フォーマットされて望む結果は得られないだろう。かわりに 、‘(error "%s" STRING)’を使用すること。 -- Function: signal error-symbol data この関数はERROR-SYMBOLで命名されるエラーをシグナルする。引数DATAは エラー状況に関連する追加のLispオブジェクトのリスト。 引数ERROR-SYMBOLは“エラーシンボル(error symbol)” — ‘define-error’で 定義されたシンボル — でなければならない。これはEmacs Lispが異なる種 類のエラーをクラス分けする方法である。エラーシンボル(error symbol)、 エラーコンディション(error condition)、コンディション名(condition name)の説明については*note Error Symbols::を参照のこと。 エラーが処理されない場合には、エラーメッセージをプリントするために 2つの引数が使用される。このエラーメッセージは通常、ERROR-SYMBOLの ‘error-message’プロパティーにより提供される。DATAが非‘nil’なら、そ の後にコロンとDATAの未評価の要素をカンマで区切ったリストが続く。 ‘error’にたいするエラーメッセージはDATAのCARである(文字列であること )。サブカテゴリー‘file-error’は特別に処理される。 DATA内のオブジェクトの数と意味はERROR-SYMBOLに依存する。たとえば ‘wrong-type-argument’エラーではリスト内に2つのオブジェクト — 期待す る型を記述する述語とその型への適合に失敗したオブジェクト — であるこ と。 エラーを処理する任意のエラーハンドラーにたいしてERROR-SYMBOLと DATAの両方を利用できる。‘condition-case’はローカル変数を ‘(ERROR-SYMBOL . DATA)’というフォームでバインドする(*note Handling Errors::を参照)。 関数‘signal’は決してリターンしない。 (signal 'wrong-number-of-arguments '(x y)) error→ Wrong number of arguments: x, y (signal 'no-such-error '("My unknown error condition")) error→ peculiar error: "My unknown error condition" -- Function: user-error format-string &rest args この関数は、‘error’とまったく同じように振る舞うが、‘error’ではなく エラーシンボル‘user-error’を使用する。名前が示唆するように、このエ ラーはコード自身のエラーではなく、ユーザー側のエラーの報告を意図し ている。たとえばInfoの閲覧履歴の開始を超えて履歴を遡るためにコマン ド‘Info-history-back’ (‘l’)を使用した場合、Emacsは‘user-error’をシ グナルする。このようなエラーでは、たとえ‘debug-on-error’が非‘nil’で あっても、デバッガーへのエントリーは発生しない。*note Error Debugging::を参照のこと。 Common Lispに関する注意: Emacs LispにはCommon Lispのような継続可能 なエラーのような概念は存在しない。 10.6.3.2 Emacsがエラーを処理する方法 .................................... エラーがシグナルされたとき、‘signal’はそのエラーにたいするアクティブな “ハンドラー(handler)”を検索します。ハンドラーとは、Lispプログラムの一部 でエラーが発生したときに実行するよう意図されたLisp式のシーケンスです。そ のエラーが適切なハンドラーをもっていればそのハンドラーが実行され、そのハ ンドラーの後から実行が再開されます。ハンドラーはそのハンドラーが設定され た‘condition-case’の環境内で実行されます。‘condition-case’内のすべての関 数呼び出しはすでに終了しているので、ハンドラーがそれらにリターンすること はありません。 そのエラーにたいする適切なハンドラーが存在しなければ、カレントコマン ドを終了してエディターのコマンドループに制御をリターンします(コマンドル ープにはすべての種類のエラーにたいする暗黙のハンドラーがある)。コマンド ループのハンドラーは、エラーメッセージをプリントするためにエラーシンボル と、それに関連付けられたデータを使用します。変数 ‘command-error-function’を使用して、これが行なわれる方法を制御できます: -- Variable: command-error-function この変数が非‘nil’なら、それはEmacsのコマンドループに制御をリターン したエラーの処理に使用する関数を指定する。この関数は3つの引数を受け 取る。1つ目のDATAは、‘condition-case’が自身の変数にバインドするのと 同じフォーム。2つ目のCONTEXTはエラーが発生した状況を記述する文字列 か、(大抵は)‘nil’。3つ目のCALLERはエラーをシグナルしたプリミティブ 関数を呼び出したLisp関数。 明示的なハンドラーがないエラーは、Lispデバッガーを呼び出すかもしれま せん。変数‘debug-on-error’ (*note Error Debugging::を参照)が非‘nil’なら デバッガーが有効です。エラーハンドラーと異なり、デバッガーはそのエラーの 環境内で実行されるので、エラー時の変数の値を正確に調べることができます。 10.6.3.3 エラーを処理するコードの記述 ..................................... エラーをシグナルすることによる通常の効果は、実行されていたコマンドを終了 してEmacsエディターのコマンドループに即座にリターンすることです。スペシ ャルフォーム‘condition-case’を使用してエラーハンドラーを設定することによ り、プログラム内の一部で発生するエラーのをトラップを調整することができま す。以下は単純な例です: (condition-case nil (delete-file filename) (error nil)) これはFILENAMEという名前のファイルを削除して、任意のエラーをcatch、エラ ーが発生した場合は‘nil’をリターンします(このような単純なケースではマクロ ‘ignore-errors’を使用することもできる。以下を参照のこと)。 ‘condition-case’構文は、‘insert-file-contents’呼び出しによるファイル オープンの失敗のような、予想できるエラーをトラップするために多用されます 。‘condition-case’構文はユーザーから読み取った式を評価するプログラムのよ うな、完全には予測できないエラーのトラップにも使用されます。 ‘condition-case’の2番目の引数は“保護されたフォーム(protected form)”と 呼ばれます(上記の例では保護されたフォームは‘delete-file’の呼び出し)。こ のフォームの実行が開始されるとエラーハンドラーが効果をもち、このフォーム がリターンすると不活性になります。その間のすべてにおいてエラーハンドラー は効果をもちます。特にこのフォームで呼び出された関数とそのサブルーチン等 を実行する間、エラーハンドラーは効果をもちます。厳密にいうと保護されたフ ォーム自身ではなく、保護されたフォームから呼び出されたLispプリミティブ関 数(‘signal’と‘error’を含む)だけがシグナルされるというのは、よいことと言 えます。 保護されたフォームの後の引数はハンドラーです。各ハンドラーはそれぞれ 、どのエラーを処理するかを指定する1つ以上の“コンディション名”(シンボル )をリストします。エラーがシグナルされたとき、エラーシンボルはコンディシ ョン名のリストも定義します。エラーが共通のコンディション名をもつ場合、そ のハンドラーがそのエラーに適用されます。上記の例では1つのハンドラーがあ り、それはすべてのエラーをカバーするコンディション名‘error’を指定してい ます。 適切なハンドラーの検索は、もっとも最近に設定されたハンドラーから始ま り、設定されたすべてのハンドラーをチェックします。したがってネストされた ‘condition-case’フォームに同じエラー処理がある場合には、内側のハンドラー がそれを処理します。 何らかの‘condition-case’によりエラーが処理されると、 ‘debug-on-error’でエラーによりデバッガーが呼び出されるようにしていても、 通常はデバッガーの実行が抑制されます。 ‘condition-case’で補足されるようなエラーをデバッグできるようにしたい なら、変数‘debug-on-signal’に非‘nil’値をセットします。以下のようにコンデ ィション内に‘debug’を記述することにより、最初にデバッガーを実行するよう な特定のハンドラーを指定することもできます: (condition-case nil (delete-file filename) ((debug error) nil)) ここでの‘debug’の効果は、デバッガー呼び出しを抑制する‘condition-case’を 防ぐことだけです。‘debug-on-error’とその他のフィルタリングメカニズムがデ バッガーを呼び出すように指定されているときだけ、エラーによりデバッガーが 呼び出されます。*note Error Debugging::を参照してください。 -- Macro: condition-case-unless-debug var protected-form handlers... マクロ‘condition-case-unless-debug’は、そのようなフォームのデバッギ ングを処理する、別の方法を提供する。このマクロは変数 ‘debug-on-error’が‘nil’、つまり任意のエラーを処理しないようなケース 以外は、‘condition-case’とまったく同様に振る舞う。 特定のハンドラーがそのエラーを処理するとEmacsが判断すると、Emacsは制 御をそのハンドラーにreturnします。これを行うために、Emacsはそのとき脱出 しつつあるバインディング構成により作成されたすべての変数のバインドを解き 、そのとき脱出しつつあるすべての‘unwind-protect’フォームを実行します。制 御がそのハンドラーに達すると、そのハンドラーのbodyが通常どおり実行されま す。 そのハンドラーのbodyを実行した後、‘condition-case’フォームから実行が returnされます。保護されたフォームは、そのハンドラーの実行の前に完全に exitしているので、そのハンドラーはそのエラーの位置から実行を再開すること はできず、その保護されたフォーム内で作られた変数のバインディングを調べる こともできません。ハンドラーが行なえることは、クリーンアップと、処理を進 行させることだけです。 エラーのシグナルとハンドルには‘throw’と‘catch’ (*note Catch and Throw::を参照)に類似する点がいくつかありますが、これらは完全に別の機能で す。エラーは‘catch’でキャッチできず、‘throw’をエラーハンドラーで処理する ことはできません(しかし対応する‘catch’が存在しないときに‘throw’を使用す ることによりシグナルされるエラーは処理できる)。 -- Special Form: condition-case var protected-form handlers... このスペシャルフォームはPROTECTED-FORMの実行を囲い込むエラーハンド ラーHANDLERSを確立する。エラーなしでPROTECTED-FORMが実行されると、 リターンされる値は‘condition-case’フォームの値になる。この場合、 ‘condition-case’は効果をもたない。PROTECTED-FORMの間にエラーが発生 すると、‘condition-case’フォームは違いを生じる。 HANDLERSはそれぞれ、‘(CONDITIONS BODY...)’というフォームのリストで ある。ここでCONDITIONSはハンドルされるエラーコンディション名、また はそのハンドラーの前にデバッガーを実行するためのコンディション名 (‘debug’を含む)。BODYはこのハンドラーがエラーを処理するときに実行さ れる1つ以上のLisp式。 (error nil) (arith-error (message "Division by zero")) ((arith-error file-error) (message "Either division by zero or failure to open a file")) 発生するエラーはそれぞれ、それが何の種類のエラーかを記述する“エラー シンボル(error symbol)”をもち、これはコンディション名のリストも記述 する(*note Error Symbols::を参照)。Emacsは1つ以上のコンディション名 を指定するハンドラーにたいして、すべてのアクティブな ‘condition-case’フォームを検索する。‘condition-case’の最内のマッチ がそのエラーを処理する。‘condition-case’内では、最初に適合したハン ドラーがそのエラーを処理する。 ハンドラーのbodyを実行した後、‘condition-case’は通常どおりリターン して、ハンドラーのbodyの最後の値をハンドラー全体の値として使用する 。 引数VARは変数である。PROTECTED-FORMを実行するとき、 ‘condition-case’はこの変数をバインドせず、エラーを処理するときだけ バインドする。その場合には、VARを“エラー記述(error description)”に バインドする。これはエラーの詳細を与えるリストである。このエラー記 述は‘(ERROR-SYMBOL . DATA)’というフォームをもつ。ハンドラーは何を行 なうか決定するために、このリストを参照することができる。たとえばフ ァイルオープンの失敗にたいするエラーなら、ファイル名がDATA(エラー記 述の3番目の要素)の2番目の要素になる。 VARが‘nil’なら、それはバインドされた変数がないことを意味する。この 場合、エラーシンボルおよび関連するデータは、そのハンドラーでは利用 できない。 より外側のレベルのハンドラーにcatchさせるために、‘condition-case’に よりcatchされたシグナルを再度throwする必要がある場合もある。以下は これを行なう方法である: (signal (car err) (cdr err)) ここで‘err’はエラー記述変数(error description variable)で、 ‘condition-case’の1番目の引数は、再throwしたいエラーコンディション 。*note Definition of signal::を参照のこと。 -- Function: error-message-string error-descriptor この関数は与えられたエラー記述子(error descriptor)にたいするエラー メッセージ文字列をリターンする。これはそのエラーにたいする通常のエ ラーメッセージをプリントすることにより、エラーを処理したい場合に有 用。*note Definition of signal::を参照のこと。 以下は0除算の結果によるエラーを処理するために、‘condition-case’を使用 する例です。このハンドラーは、(beepなしで)エラーメッセージを表示して、非 常に大きい数をリターンします。 (defun safe-divide (dividend divisor) (condition-case err ;; 保護されたフォーム (/ dividend divisor) ;; ハンドラー (arith-error ; コンディション ;; このエラーにたいする、通常のメッセージを表示する (message "%s" (error-message-string err)) 1000000))) ⇒ safe-divide (safe-divide 5 0) ⊣ Arithmetic error: (arith-error) ⇒ 1000000 このハンドラーはコンディション名‘arith-error’を指定するので、 division-by-zero(0除算)エラーだけを処理します。他の種類のエラーは(この ‘condition-case’によっては)、処理されません。したがって: (safe-divide nil 3) error→ Wrong type argument: number-or-marker-p, nil 以下は‘error’によるエラーを含む、すべての種類のエラーをcatchする ‘condition-case’です: (setq baz 34) ⇒ 34 (condition-case err (if (eq baz 35) t ;; 関数‘error’の呼び出し (error "Rats! The variable %s was %s, not 35" 'baz baz)) ;; フォームではないハンドラー (error (princ (format "The error was: %s" err)) 2)) ⊣ The error was: (error "Rats! The variable baz was 34, not 35") ⇒ 2 -- Macro: ignore-errors body... この構文は、それの実行中に発生する任意のエラーを無視してBODYを実行 する。その実行中にエラーがなければ、‘ignore-errors’はBODY内の最後の フォームの値を、それ以外は‘nil’をリターンする。 以下はこのセクションの最初の例を‘ignore-errors’を使用して記述する例 である: (ignore-errors (delete-file filename)) -- Macro: with-demoted-errors format body... このマクロはいわば‘ignore-errors’の穏やかなバージョンである。これは エラーを完全に抑止するのではなく、エラーをメッセージに変換する。こ れはメッセージのフォーマットに、文字列FORMATを使用する。FORMATは ‘"Error: %S"’のように、単一の‘%’シーケンスを含むこと。エラーをシグ ナルするとは予測されないが、もし発生した場合は堅牢であるべきような コードの周囲に‘with-demoted-errors’を使用する。このマクロは ‘condition-case’ではなく、‘condition-case-unless-debug’を使用するこ とに注意。 10.6.3.4 エラーシンボルとエラー条件 ................................... エラーをシグナルするとき、想定するエラーの種類を指定するために“エラーシ ンボル(error symbol)”を指定します。エラーはそれぞれ、それをカテゴリー分 けするために単一のエラーシンボルをもちます。これはEmacs Lisp言語で定義さ れるエラーを分類する、もっともよい方法です。 これらの狭義の分類は“エラー条件(error conditions)”と呼ばれる、より広 義のクラス階層にグループ化され、それらは“コンディション名(condition names)”により識別されます。そのようなもっとも狭義なクラスは、エラーシン ボル自体に属します。つまり各エラーシンボルは、コンディション名でもあるの です。すべての種類のエラー(‘quit’を除く)を引き受けるコンディション名 ‘error’に至る、より広義のクラスにたいするコンディション名も存在します。 したがって各エラーは1つ以上のコンディション名をもちます。つまり‘error’、 ‘error’とは区別されるエラーシンボル、もしかしたらその中間に分類されるも のかもしれません。 -- Function: define-error name message &optional parent シンボルをエラーシンボルとするために、シンボルは親コンディションを 受け取る‘define-error’で定義されなければならない。この親はその種の エラーが属するコンディションを定義する。親の推移的な集合は、常にそ のエラーシンボルとシンボル‘error’を含む。quitはエラーと判断されない ので、‘quit’の親の集合は単なる‘(quit)’である。 親のコンディションに加えてエラーシンボルはメッセージ(MESSAGE)をもち、 これは処理されないエラーがシグナルされたときプリントされる文字列です。そ のメッセージが有効でなければ、エラーメッセージ‘peculiar error’が使用され ます。*note Definition of signal::を参照してください。 内部的には親の集合はエラーシンボルの‘error-conditions’プロパティーに 格納され、メッセージはエラーシンボルの‘error-message’プロパティーに格納 されます。 以下は新しいエラーシンボル‘new-error’を定義する例です: (define-error 'new-error "A new error" 'my-own-errors) このエラーは複数のコンディション名 — もっとも狭義の分類‘new-error’、より 広義の分類を想定する‘my-own-errors’、および‘my-own-errors’のコンディショ ンすべてを含む‘error’であり、これはすべての中でもっとも広義なものです。 エラー文字列は大文字で開始されるべきですが、ピリオドで終了すべきでは ありません。これはEmacsの他の部分との整合性のためです。 もちろんEmacs自身が‘new-error’をシグナルすることはありません。あなた のコード内で明示的に‘signal’ (*note Definition of signal::を参照)を呼び 出すことにより、これを行なうことができます。 (signal 'new-error '(x y)) error→ A new error: x, y このエラーは、エラーの任意のコンディション名により処理することができ ます。以下の例は‘new-error’とクラス‘my-own-errors’内の他の任意のエラーを 処理します: (condition-case foo (bar nil t) (my-own-errors nil)) エラーが分類される有効な方法はコンディション名による方法で、その名前 はハンドラーのエラーのマッチに使用されます。エラーシンボルは意図されたエ ラーメッセージと、コンディション名のリストを指定する便利な方法であるとい う役割をもつだけです。1つのエラーシンボルではなく、コンディション名のリ ストを‘signal’に与えるのは面倒でしょう。 対照的にコンディション名を伴わずにエラーシンボルだけを使用すると、そ れは‘condition-case’の効果を著しく減少させるでしょう。コンディション名は エラーハンドラーを記述するとき、一般性のさまざまなレベルにおいて、エラー をカテゴリー分けすることを可能にします。エラーシンボルを単独で使用するこ とは、もっとも狭義なレベルの分類を除くすべてを捨ててしまうことです。 主要なエラーシンボルとそれらのコンディションについては、*note Standard Errors::を参照してください。 10.6.4 非ローカル脱出のクリーンアップ ------------------------------------- ‘unwind-protect’構文は、データ構造を一時的に不整合な状態に置くときに重要 です。これはエラーやthrouのイベントにより、再びデータを整合された状態に することができます(バッファー内容の変更だけに使用される他のクリーンアッ プ構成はアトミックな変更グループである。*note Atomic Changes::を参照)。 -- Special Form: unwind-protect body-form cleanup-forms... ‘unwind-protect’は制御がBODY-FORMを離れる場合に、CLEANUP-FORMSが評 価されるという保証の下において、何が起こったかに関わらずBODY-FORMを 実行する。BODY-FORMは通常どおり完了するかもしれず、 ‘unwind-protect’の外側で‘throw’の実行やエラーが発生するかもしれない が、CLEANUP-FORMSは評価される。 BODY-FORMが正常に終了したら、‘unwind-protect’はCLEANUP-FORMSを評価 した後に、BODY-FORMの値をリターンする。BODY-FORMが終了しなかったら 、‘unwind-protect’は通常の意味におけるような値はリターンしない。 ‘unwind-protect’で保護されるのはBODY-FORMだけである。 CLEANUP-FORMS自体の任意のフォームが、(‘throw’またはエラーにより)非 ローカルにexitすると、‘unwind-protect’は残りのフォームが評価される ことを_保証しない_。CLEANUP-FORMSの中の1つが失敗することが問題とな るようなら、そのフォームの周囲に他の‘unwind-protect’を配置して保護 すること。 現在アクティブな‘unwind-protect’フォーム数とローカルの変数バインデ ィング数の和は、‘max-specpdl-size’ (*note Local Variables: Definition of max-specpdl-size.を参照)により制限される。 たとえば以下は一時的な使用のために不可視のバッファーを作成して、終了 する前に確実にそのバッファーをkillする例です: (let ((buffer (get-buffer-create " *temp*"))) (with-current-buffer buffer (unwind-protect BODY-FORM (kill-buffer buffer)))) ‘(kill-buffer (current-buffer))’のように記述して、変数‘buffer’を使用せず に同様のことを行えると思うかもしれません。しかし上の例は、別のバッファー にスイッチしたときにBODY-FORMでエラーが発生した場合、より安全なのです(一 時的なバッファーをkillするとき、そのバッファーがカレントとなることを確実 にするために、かわりにBODY-FORMの周囲に‘save-current-buffer’を記述するこ ともできる)。 Emacsには上のコードとおおよそ等しいコードに展開される、 ‘with-temp-buffer’という標準マクロが含まれます(*note Current Buffer: Definition of with-temp-buffer.を参照)。このマニュアル中で定義されるいく つかのマクロは、この方法で‘unwind-protect’を使用します。 以下はFTPパッケージ由来の実例です。これはリモートマシンへの接続の確立 を試みるために、プロセス(*note Processes::を参照)を作成しています。関数 ‘ftp-login’は関数のライター(writer)が予想できないことによる多くの問題か ら非常に影響を受けるので、失敗イベントでプロセスの削除を保証するフォーム で保護されています。そうしないとEmacsは無用なサブプロセスで一杯になって しまうでしょう。 (let ((win nil)) (unwind-protect (progn (setq process (ftp-setup-buffer host file)) (if (setq win (ftp-login process host user password)) (message "Logged in") (error "Ftp login failed"))) (or win (and process (delete-process process))))) この例には小さなバグがあります。ユーザーがquitするために‘C-g’とタイプ すると、関数‘ftp-setup-buffer’のリターン後に即座にquitが発生しますが、そ れは変数‘process’がセットされる前なので、そのプロセスはkillされないでし ょう。このバグを簡単に訂正する方法はありませんが、少なくともこれは非常に 稀なことだと言えます。 11 変数 ******* “変数(variable)”とはプログラム内で値を表すために使用される名前です。 Lispでは変数はそれぞれLispシンボルとして表されます(*note Symbols::を参照 )。変数名は単にそのシンボルの名前であり、変数の値はそのシンボルの値セル (value cell)に格納されます(1)。*note Symbol Components::を参照してくださ い。Emacs Lispではシンボルを変数として使用することは、同じシンボルを関数 名として使用することと関係ありません。 このマニュアルで前述したとおり、Lispプログラムはまず第1にLispオブジェ クトとして表され、副次的にテキストとして表現されます。Lispプログラムのテ キスト的な形式は、そのプログラムを構成するLispオブジェクトの入力構文によ り与えられます。したがってLispプログラム内の変数のテキスト的な形式は、そ の変数を表すシンボルの入力構文を使用して記述されます。 ---------- Footnotes ---------- (1) 正確に言うとデフォルトの“ダイナミックスコープ(dynamic scoping)”の ルールでは、値セルは常にその変数のカレント値を保持しますが、“レキシカル スコープ(lexical scoping)”では異なります。詳細は*note Variable Scoping::を参照してください。 11.1 グローバル変数 =================== 変数を使用するための一番シンプルな方法は、“グローバル(globally)”を使用す る方法です。これはある時点でその変数はただ1つの値をもち、その値が(少なく ともその時点では)Lispシステム全体で効果をもつことを意味します。あらたな 値を指定するまでその値が効果をもちます。新しい値で古い値を置き換えるとき 、古い値を追跡する情報は変数内に残りません。 シンボルの値は‘setq’で指定します。たとえば、 (setq x '(a b)) これは変数‘x’に値‘(a b)’を与えます。‘setq’はスペシャルフォームであること に注意してください。これは1番目の引数(変数の名前)は評価しませんが、2番目 の引数(新しい値)は評価します。 変数が一度値をもつと、そのシンボル自身を式として使用することによって 参照することができます。したがって、 x ⇒ (a b) これは上記の‘setq’フォームが実行された場合です。 同じ変数を再びセットすると、古い値は新しい値で置き換えられます: x ⇒ (a b) (setq x 4) ⇒ 4 x ⇒ 4 11.2 Variables that Never Change ================================ Emacs Lispでは特定のシンボルは、通常は自分自身に評価されます。これらのシ ンボルには‘nil’と‘t’、同様に名前が‘:’で始まる任意のシンボル(これらは“キ ーワード”と呼ばれる)が含まれます。これらのシンボルはリバインドや、値の変 更はできません。‘nil’や‘t’へのセットやリバインドは、‘setting-constant’エ ラーをシグナルします。これはキーワード(名前が‘:’で始まるシンボル)につい ても当てはまります。ただしキーワードが標準のobarrayにinternされていれば 、そのようなシンボルを自分自身にセットしてもエラーになりません。 nil ≡ 'nil ⇒ nil (setq nil 500) error→ Attempt to set constant symbol: nil -- Function: keywordp object この関数はOBJECTが‘:’で始まる名前のシンボルであり、標準のobarrayに internされていれば‘t’、それ以外は‘nil’をリターンする。 これらの定数はスペシャルフォーム‘defconst’(*note Defining Variables::を参照)を使用して定義された定数(constant)とは根本的に異なりま す。‘defconst’フォームは、人間の読み手に値の変更を意図しない変数であるこ とを知らせる役目は果たしますが、実際にそれを変更してもEmacsはエラーを起 こしません。 11.3 ローカル変数 ================= グローバル変数は新しい値で明示的に置き換えるまで値が持続します。変数に “ローカル値(local value)” — Lispプログラム内の特定の部分で効果をもつ — を 与えると便利なときがあります。変数がローカル値をもつとき、わたしたちは変 数がその値に“ローカルにバインド(locally bound)”されていると言い、その変 数を“ローカル変数(local variable)”と呼びます。 たとえば関数が呼び出されるとき、関数の引数となる変数はローカル値(その 関数の呼び出しにおいて実際の引数に与えられた値)を受け取ります。これらの ローカルバインディングは、その関数のbody内で効果をもちます。他にもたとえ ばスペシャルフォーム‘let’は特定の変数にたいして明示的にローカルなバイン ディングを確立し、これは‘let’フォームのbody内で効果を持ちます。 これにたいして“グローバルなバインディング(global binding)”とは、(概念 的には)グローバルな値が保持される場所です。 ローカルバインディングを確立すると、その変数の以前の値は他の場所に保 存されます(または失われる)。わたしたちはこれを、以前の値が“シャドー (shadowed)”されたと言います。シャドーはグローバル変数とローカル変数の両 方で発生し得ます。ローカルバインディングが効果を持つときには、ローカル変 数に‘setq’を使用することにより、指定した値をローカルバインディングに格納 します。ローカルバインディングが効果を持たなくなったとき、以前にシャドー された値が復元されます(または失われる)。 変数は同時に複数のローカルバインディングを持つことができます(たとえば その変数をバインドするネストされた‘let’)。“カレントバインディング (current binding)”とは、実際に効果を持つローカルバインディングのことです 。カレントバインディングは、その変数の評価によりリターンされる値を決定し 、‘setq’により影響を受けるバインディングです。 ほとんどの用途において、最内(innermost)のローカルバインディングとロー カルバインディングをもたないグローバルバインディングを、カレントバインデ ィングと考えることができます。より正確に言うと、“スコープルール(scoping rule)”と呼ばれるルールは、プログラム内でローカルバインディングが効果を持 つ任意の与えられた場所を決定します。Emacs Lispのスコープルールは“ダイナ ミックスコープ(dynamic scoping)”と呼ばれ、これは単に実行中のプログラム内 の与えられた位置でのカレントバインディングを示しており、その変数がまだ存 在すれば、その変数にたいしてもっとも最近作成されたバインディングです。ダ イナミックスコープについての詳細、およびその代替である“レキシカルスコー プ(lexical scoping)”と呼ばれるスコープルールについては、*note Variable Scoping::を参照してください。 スペシャルフォーム‘let’と‘let*’は、ローカルバインディングを作成するた めに存在します: -- Special Form: let (bindings...) forms... このスペシャルフォームは、BINDINGSにより指定される特定の変数セット にたいするローカルバインディングをセットアップしてから、FORMSのすべ てをテキスト順に評価する。これはFORMS内の最後のフォームの値をリター ンする。 BINDINGSの各バインディングは2つの形式のいずれかである。(i) シンボル なら、そのシンボルは‘nil’にローカルにバインドされる。(ii) フォーム ‘(SYMBOL VALUE-FORM)’のリストなら、SYMBOLはVALUE-FORMを評価した結果 へローカルにバインドされる。VALUE-FORMが省略されたら‘nil’が使用され る。 BINDINGS内のすべてのVALUE-FORMは、シンボルがそれらにバインドされる _前_に、記述された順番に評価される。以下の例では‘z’は‘y’の新しい値 (つまり1)にではなく、古い値(つまり2)にバインドされる。 (setq y 2) ⇒ 2 (let ((y 1) (z y)) (list y z)) ⇒ (1 2) -- Special Form: let* (bindings...) forms... このスペシャルフォームは‘let’と似ているが、次の変数値にたいするロー カル値を計算する前に、ローカル値を計算してそれを変数にバインドする 。したがてBINDINGS内の式は、この‘let*’フォーム内の前のシンボルのバ インドを参照できる。以下の例を上記‘let’の例と比較されたい。 (setq y 2) ⇒ 2 (let* ((y 1) (z y)) ; ‘y’の値に今計算されたばかりの値を使用する (list y z)) ⇒ (1 1) 以下はローカルバインディングを作成する他の機能のリストです: • 関数呼び出し(*note Functions::を参照)。 • マクロ呼び出し(*note Macros::を参照)。 • ‘condition-case’ (*note Errors::を参照)。 変数はバッファーローカルなバインディングを持つこともできます(*note Buffer-Local Variables::を参照)。数は多くありませんが、端末ローカル (terminal-local)なバインディングをもつ変数もあります(*note Multiple Terminals::を参照)。この種のバインディングは、通常のローカルバインディン グのように機能することもありますが、これらはEmacs内のどこにいるかに依存 してローカルになります。 -- User Option: max-specpdl-size この変数はローカルな変数バインディングと、‘unwind-protect’にゆるク リーンアップ(*note Cleaning Up from Nonlocal Exits: Cleanups.を参照 )の総数にたいする制限を定義し、この変数を越えるとEmacsは(データ ‘"Variable binding depth exceeds max-specpdl-size"’とともに)エラー をシグナルする。 このリミットは、もし超過したときにエラーが関連付けられていれば、誤 って定義された関数による無限再起を避けるための1つの手段になる。ネス トの深さにたいする他の制限としては、‘max-lisp-eval-depth’がある。 *note Eval: Definition of max-lisp-eval-depth.を参照のこと。 デフォルト値は1300。Lispデバッガーのエントリーしたとき、もし残りが 少なければ、デバッガーを実行するための空きを作るために値が増加され る。 11.4 変数がvoidのとき ===================== シンボルの値セル(*note Symbol Components::を参照)に値が割り当てられてい ない場合、その変数はvoid(空)であると言います。 Emacs Lispのデフォルトであるダイナミックスコープルール(*note Variable Scoping::を参照)の下では、値セルはその変数のカレント値(ローカルまたはグ ローバル)を保持します。値が割り当てられていない値セルは、値セルに‘nil’を もつのとは_異なる_ことに注意してください。シンボル‘nil’はLispオブジェク トであり、他のオブジェクトと同様に変数の値となることができます。‘nil’は 値なのです。変数がvoidの場合にその変数の評価を試みると、値をリターンする かわりに、‘void-variable’エラーがシグナルされます。 オプションであるレキシカルスコープルール(lexical scoping rule)の下で は、値セル保持できるのはその変数のグローバル値 — 任意のレキシカルバイン ディング構造の外側の値だけです。変数がレキシカルにバインドされている場合 、ローカル値はそのレキシカル環境により決定されます。したがってこれらのシ ンボルの値セルに値が割り当てられていなくても、変数はローカル値を持つこと ができます。 -- Function: makunbound symbol この関数はSYMBOLの値セルを空にして、その変数をvoidにする。この関数 はSYMBOLをリターンする。 SYMBOLがダイナミックなローカルバインディングをもつなら、 ‘makunbound’はカレントのバインディングをvoidにして、そのローカルバ インディングが効果を持つ限りvoidにする。その後で以前にシャドーされ たローカル値(またはグローバル値)が再び有効になって、再び有効になっ た値がvoidでなければ、その変数はvoidではなくなる。 いくつか例を示す(ダイナミックバインディングが有効だとする): (setq x 1) ; グローバルバインディングに値をセットする ⇒ 1 (let ((x 2)) ; それをローカルにバインドする (makunbound 'x) ; ローカルバインディングをvoidにする x) error→ Symbol's value as variable is void: x x ; グローバルバインディングは変更されない ⇒ 1 (let ((x 2)) ; ローカルにバインドする (let ((x 3)) ; もう一度 (makunbound 'x) ; 最内のローカルバインディングをvoidにする x)) ; それを参照すると、void error→ Symbol's value as variable is void: x (let ((x 2)) (let ((x 3)) (makunbound 'x)) ; 内側のバインディングをvoidにしてから取り除く x) ; 外側の‘let’バインディングが有効になる ⇒ 2 -- Function: boundp variable この関数はVARIABLE(シンボル)がvoidでなければ‘t’、voidなら‘nil’をリ ターンする。 いくつか例を示す(ダイナミックバインディングが有効だとする): (boundp 'abracadabra) ; 最初はvoid ⇒ nil (let ((abracadabra 5)) ; ローカルにバインドする (boundp 'abracadabra)) ⇒ t (boundp 'abracadabra) ; グローバルではまだvoid ⇒ nil (setq abracadabra 5) ; グローバルで非voidにする ⇒ 5 (boundp 'abracadabra) ⇒ t 11.5 グローバル変数の定義 ========================= “変数定義(variable definition)”とは、そのシンボルをグローバル変数として 使用する意図を表明する構文です。これには以下で説明するスペシャルフォーム ‘defvar’や‘defconst’が使用されます。 変数宣言は3つの目的をもちます。1番目はコードを読む人にたいして、その シンボルが特定の方法(変数として)使用されることを_意図_したものだと知らせ ることです。2番目はLispシステムにたいしてオプションで初期値とドキュメン ト文字列を与えて、これを知らせることです。3番目は‘etags’のようなプログラ ミングツールにたいして、その変数が定義されている場所を見つけられるように 情報を提供することです。 ‘defconst’と‘defvar’の主な違いは、人間の読み手に値が変更されるかどう かを知らせることにあります。Emacs Lispは実際に、‘defconst’で定義された変 数の値の変更を妨げません。この2つのフォームの特筆すべき違いは、 ‘defconst’は無条件で変数を初期化して、‘defvar’は変数が元々voidのときだけ 初期化することです。 カスタマイズ可能な変数を定義する場合は、‘defcustom’を使用するべきです (これはサブルーチンとして‘defvar’を呼び出す)。*note Variable Definitions::を参照してください。 -- Special Form: defvar symbol [value [doc-string]] このスペシャルフォームは変数としてSYMBOLを定義する。SYMBOLが評価さ れないことに注意。シンボルは‘defvar’フォーム内に明示的に表記して定 義される必要がある。この変数は“特別”だとマークされて、これは常に変 数がダイナミックにバインドされることを意味する(*note Variable Scoping::を参照)。 VALUEが指定されていてSYMBOLがvoid(たとえばこのシンボルがダイナミッ クにバインドされた値を持たないとき。*note Void Variables::を参照)な らVALUEが評価されて、その結果がSYMBOLにセットされる。しかしSYMBOLが voidでなければ、VALUEは評価されずSYMBOLの値は変更されない。VALUEが 省略された場合は、いかなる場合もSYMBOLの値は変更されない。 SYMBOLがカレントバッファー内でバッファーローカルなバインディングを もつ場合、‘defvar’はデフォルト値に作用する。デフォルト値はバッファ ーローカルなバインディングではなく、バッファーにたいして独立である 。デフォルト値がvoidのときはデフォルト値をセットする。*note Buffer-Local Variables::を参照のこと。 すでにSYMBOLがレキシカルにバインドされている場合(たとえばレキシカル バインドが有効な状態で‘let’フォーム内に‘defvar’があるような場合)、 ‘defvar’はダイナミックな値をセットする。バインディング構文を抜ける まで、レキシカルバインディングは効果をもつ。*note Variable Scoping::を参照のこと。 Emacs Lispモードで‘C-M-x’ (‘eval-defun’)でトップレベルの‘defvar’を 評価するとき、‘eval-defun’の特別な機能はその値がvoidであるかテスト することなく、その変数を無条件にセットする。 引数DOC-STRINGが与えられたら、それは変数にたいするドキュメント文字 列を指定する(そのシンボルの‘variable-documentation’プロパティーに格 納される)。*note Documentation::を参照のこと。 以下にいくつか例を示す。これは‘foo’を定義するが初期化は行わない: (defvar foo) ⇒ foo 以下の例は‘bar’の値を‘23’に初期化してドキュメント文字列を与える: (defvar bar 23 "The normal weight of a bar.") ⇒ bar ‘defvar’フォームはSYMBOLをリターンするが、これは通常は値が問題にな らないファイル内のトップレベルで使用される。 -- Special Form: defconst symbol value [doc-string] このスペシャルフォームはある値でSYMBOLを定義して、それを初期化する 。これはコードを読む人に、SYMBOLがここで設定される標準的なグローバ ル値をもち、ユーザーや他のプログラムがそれを変更すべきではないこと を知らせる。SYMBOLが評価されないことに注意。定義されるシンボルは ‘defconst’内に明示的に記されなければならない。 ‘defvar’と同様、‘defconst’は変数を“特別” — この変数が常にダイナミッ クにバインドされているという意味 — であるとマークする(*note Variable Scoping::を参照)。加えてこれはその変数を危険であるとマーク する(*note File Local Variables::を参照)。 ‘defconst’は常にVALUEを評価して、その結果をSYMBOLの値にセットする。 カレントバッファー内でSYMBOLがバッファーローカルなバインディングを もつなら、‘defconst’はデフォルト値ではなくバッファーローカルな値を セットする(しかし‘defconst’で定義されたシンボルにたいしてバッファー ローカルなバインディングを作らないこと)。 ‘defconst’の使い方の例は、Emacsの‘float-pi’ — (たとえインディアナ州 議会が何を試みようと)何者かにより変更されるべきではない数学定数piに たいする定義である。しかし2番目の‘defconst’の例のように、これは単に アドバイス的なものである。 (defconst float-pi 3.141592653589793 "The value of Pi.") ⇒ float-pi (setq float-pi 3) ⇒ float-pi float-pi ⇒ 3 *警告:* 変数がローカルバインディングをもつとき(‘let’により作成された 、または関数の引数の場合)に、スペシャルフォーム‘defconst’または ‘defvar’を使用すると、これらのフォームはグローバルバインディングではなく 、ローカルバインディングをセットします。これは通常は、あなたが望むことで はないはずです。これを防ぐには、これらのスペシャルフォームをファイル内の トップレベルで使用します。この場所は通常、何のローカルバインディングも効 果をもたないので、その変数にたいするローカルバインディングが作成される前 にファイルがロードされることが確実だからです。 11.6 堅牢な変数定義のためのヒント ================================= 値が関数(または関数のリスト)であるような変数を定義するときには、変数の名 前の最後に‘-function’(または‘-functions’)を使用します。 他にも変数名に関する慣習があります。以下はその完全なリストです: ‘...-hook’ 変数はノーマルフック(*note Hooks::を参照)。 ‘...-function’ 値は関数。 ‘...-functions’ 値は関数のリスト。 ‘...-form’ 値はフォーム(式)。 ‘...-forms’ 値はフォーム(式)のリスト。 ‘...-predicate’ 値は述語(predicate) — 1つの引数をとる関数 — であり成功なら非‘nil’、 失敗なら‘nil’をリターンする。 ‘...-flag’ ‘nil’か否かだけが意味をもつような値。結局そのような変数は、やがては 多くの値をもつことが多いので、この慣習を強く推奨はしない。 ‘...-program’ 値はプログラム名。 ‘...-command’ 値は完全なシェルコマンド。 ‘...-switches’ 値はコマンドにたいして指定するオプション。 変数を定義するときは、その変数を安全(safe)とマークすべきか、それとも 危険(risky)とマークすべきかを常に考慮してください。*note File Local Variables::を参照してください。 複雑な値を保持する変数(バインディングをもつkeymapなど)の定義や初期化 を行う場合は、以下のように値の計算をすべて‘defvar’の中に配置するのが最良 です: (defvar my-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-a" 'my-command) ... map) DOCSTRING) この方法にはいくつかの利点があります。1つ目はファールをロード中にユーザ ーが中断した場合、変数はまだ初期化されていないか、初期化されているかのど ちらかであり、その中間ということはありません。まだ初期化されていなければ 、ファイルをリロードすれば正しく初期化されます。2つ目は一度初期化された 変数は、ファイルをリロードしても変更されないことです。コンテンツの一部を 変更(たとえばキーのリバインド)するフックをユーザーが実行した場合などに、 これは重要です。3つ目は‘C-M-x’で‘defvar’を評価すると、そのマップは完全に 再初期化されることです。 ‘defvar’フォーム内に多すぎるコードを配置することが不利な点が1つありま す。ドキュメント文字列が変数の名前から離れた場所に配置されることです。こ れを避ける安全な方法は以下の方法です: (defvar my-mode-map nil DOCSTRING) (unless my-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-a" 'my-command) ... (setq my-mode-map map))) これは初期化を‘defvar’の内側に配置した場合とまったく同じ利点をもちますが 、変数を再度初期化したい場合は、各フォームにたいして1回ずつ、‘C-M-x’を 2回タイプしなければならない点が異なります。 11.7 変数の値へのアクセス ========================= 変数を参照する通常の方法は、それに名前をつけるシンボルを記述する方法です 。*note Symbol Forms::を参照してください。 実行時にのみ決定される変数を参照したいときがあるかもしれません。その ような場合、プログラム中のテキストで変数名を指定することはできません。そ のような値を抽出するために‘symbol-value’を使うことができます。 -- Function: symbol-value symbol この関数はSYMBOLの値セルに格納された値をリターンする。これはその変 数の(ダイナミックな)カレント値が格納された場所である。その変数がロ ーカルバインディングをもたなければ単にその変数のグローバル値になる 。変数がvoidなら‘void-variable’はエラーをシグナルする。 その変数がレキシカルにバインドされていれば、‘symbol-value’が報告す る値は、その変数のレキシカル値と同じである必要はない。レキシカル値 はそのシンボルの値セルではなく、レキシカル環境により決定される。 *note Variable Scoping::を参照のこと。 (setq abracadabra 5) ⇒ 5 (setq foo 9) ⇒ 9 ;; ここでシンボル‘abracadabra’ ;; は値がテストされるシンボル (let ((abracadabra 'foo)) (symbol-value 'abracadabra)) ⇒ foo ;; ここでは‘abracadabra’の値、 ;; つまり‘foo’が値を ;; テストされるシンボル (let ((abracadabra 'foo)) (symbol-value abracadabra)) ⇒ 9 (symbol-value 'abracadabra) ⇒ 5 11.8 変数の値のセット ===================== ある変数の値を変更する通常の方法は、スペシャルフォーム‘setq’を使用する方 法です。実行時に変数選択を計算する必要がある場合には関数‘set’を使用しま す。 -- Special Form: setq [symbol form]... このスペシャルフォームは、変数の値を変更するためのもっとも一般的な 方法である。SYMBOLにはそれぞれ、新しい値(対応するFORMが評価された結 果)が与えられる。そのシンボルのカレントバインディングは変更される。 ‘setq’はSYMBOLを評価せずに、記述されたシンボルをセットする。この引 数のことを“自動的にクォートされた(automatically quoted)”と呼ぶ。 ‘setq’の‘q’は“quoted(クォートされた)”が由来。 ‘setq’フォームの値は最後のFORMの値となる。 (setq x (1+ 2)) ⇒ 3 x ; ここで‘x’はグローバル値をもつ ⇒ 3 (let ((x 5)) (setq x 6) ; ‘x’のローカルバインディングをセット x) ⇒ 6 x ; グローバル値は変更されない ⇒ 3 1番目のFORMが評価されてから1番目のSYMBOLがセット、次に2番目のFORMが 評価されてからSYMBOLが評価されて、...となることに注意: (setq x 10 ; ここで、‘x’がセットされるのは y (1+ x)) ; ‘y’の計算前であることに注目 ⇒ 11 -- Function: set symbol value この関数はSYMBOLの値セルにVALUEを配置する。これはスペシャルフォーム ではなく関数なので、シンボルにセットするためにSYMBOLに記述された式 は評価される。リターン値はVALUE。 ダイナミックな変数バインドが有効(デフォルト)なら、‘set’は自身の引数 SYMBOLを評価するが、‘setq’は評価しないという点を除き、‘set’は ‘setq’と同じ効果をもつ。しかし変数がレキシカルバインドなら、‘set’は 変数の_ダイナミック_な値に、‘setq’は変数のカレント値(レキシカル値 )に影響する。*note Variable Scoping::を参照のこと。 (set one 1) error→ Symbol's value as variable is void: one (set 'one 1) ⇒ 1 (set 'two 'one) ⇒ one (set two 2) ; ‘two’はシンボル‘one’に評価される ⇒ 2 one ; したがって‘one’がセットされる ⇒ 2 (let ((one 1)) ; ‘one’のこのバインディングがセットされる (set 'one 3) ; のであってグローバル値はセットされない one) ⇒ 3 one ⇒ 2 SYMBOLが実際のシンボルでなければ‘wrong-type-argument’エラーがシグナ ルされる。 (set '(x y) 'z) error→ Wrong type argument: symbolp, (x y) 11.9 変数のバインディングのスコーピングルール ============================================= ある変数にたいするローカルバインディングを作成するとき、そのバインディン グはプログラムの限られた一部だけに効果をもちます(*note Local Variables::を参照)。このセクションでは、これが正確には何を意味するかにつ いて説明します。 ローカルバインディングはそれぞれ、個別に“スコープ(scope: 範囲という意 味)”と“エクステント(extent: これも範囲を意味する)”をもちます。“スコープ ”はそのバインディングにアクセスできるのが、テキストのソースコードの_どこ (where)_であるかを示します。“エクステント”はプログラムの実行中に、そのバ インディングが存在するのが_いつ(when)_であるかを示します。 デフォルトではEmacsが作成したローカルバインディングは、“ダイナミック バインディング(dynamic binding)”です。このようなバインディングは“ダイナ ミックスコープ(dynamic scope)”をもち、それはプログラムの任意の範囲が、そ の変数バインディングにアクセスするかもしれないことを意味します。これは “ダイナミックエクステント(dynamic extent)”ももっています。これはそのバイ ンディング構造(‘let’フォームのbodyなど)が実行される間だけ、そのバインデ ィングが存続することを意味します。 Emacsはオプションで“レキシカルバインディング(lexical binding)”を作成 することができます。レキシカルバインディングは“レキシカルスコープ (lexical scope)”をもち、これはその変数にたいするすべての参照が、バインデ ィング構文内にテキスト的に配置されなければならないことを意味します(1)。 レキシカルバインディングは“不定エクステント(indefinite extent)”ももって います。これはある状況下において、“クロージャー(closures)”と呼ばれるスペ シャルオブジェクトにより、バインディング構造が実行を終えた後でさえも、存 続し続けることを意味します。 以降のサブセクションでは、ダイナミックバインディングとレキシカルバイ ンディング、およびEmacs Lispプログラムでレキシカルバインディングを有効に する方法についてより詳細に説明します。 ---------- Footnotes ---------- (1) これにはいくつか例外があります。たとえばレキシカルバインディング は、Lispデバッガーからもアクセスできます。 11.9.1 ダイナミックバインディング --------------------------------- デフォルトでは、Emacsにより作成されるローカル変数のバインディングはダイ ナミックバインディングです。ある変数がダイナミックにバインドされていると 、Lispプログラムの実行における任意のポイントでのカレントバインディングは 、単にそのシンボルにたいしてもっとも最近作成されたダイナミックなローカル バインディング、またはそのようなローカルバインディングが存在しなければグ ローバルバインディングになります。 以下の例のように、ダイナミックバインディングはダイナミックスコープと ダイナミックエクステントをもちます: (defvar x -99) ; ‘x’は初期値として−99を受け取る (defun getx () x) ; この関数内では‘x’は自由に使用される (let ((x 1)) ; ‘x’はダイナミックにバインドされている (getx)) ⇒ 1 ;; ‘let’フォームが終了した後に ;; ‘x’は前の値−99にリバートされる (getx) ⇒ -99 関数‘getx’は‘x’を参照します。‘defun’構文自体の中に‘x’にたいするバインデ ィングが存在しないという意味において、これは“フリー”な参照です。‘x’が(ダ イナミックに)バインドされている‘let’フォーム内から‘getx’を呼び出すと、ロ ーカル値(つまり1)が取得されます。しかしその後‘let’フォームの外側から ‘getx’を呼び出すと、グローバル値(つまり−99)が取得されます。 以下は‘setq’を使用してダイナミックに変数をバインドする例です: (defvar x -99) ; ‘x’は初期値として−99を受け取る (defun addx () (setq x (1+ x))) ; ‘x’に1加算して新しい値をリターンする (let ((x 1)) (addx) (addx)) ⇒ 3 ; ‘addx’を2回呼び出すと‘x’に2回加算される ;; ‘let’フォームが終了した後に ;; ‘x’は前の値−99にリバートされる (addx) ⇒ -98 Emacs Lispでのダイナミックバインディングは、シンプルな方法で実装され ています。シンボルはそれぞれ、シンボルのカレントのダイナミック値(または 値の不在)を指定する値セルをもちます。*note Symbol Components::を参照して ください。あるシンボルがダイナミックなローカル値を与えられたとき、 Emacsは値セルの内容(または値の不在)をスタックに記録して、新しいローカル 値を値セルに格納します。バインディング構文が実行を終えたとき、Emacsはス タックから古い値をpopして値セルにそれを配置します。 11.9.2 ダイナミックバインディングの正しい使用 --------------------------------------------- ダイナミックバインディングは、プログラムにたいしてテキスト的なローカルス コープ内で定義されていない変数を参照することを許容する、強力な機能です。 しかし無制限に使用した場合には、プログラムの理解を困難にしてしまうことも あります。このテクニックを使用するために2つの明解な方法があります: • ある変数がグローバルな定義をもたなければ、ローカル変数としてバイン ディング構文内(その変数がバインドされる‘let’フォームのbodyなどの場 所)だけでそれを使用する。プログラムでこの慣習に一貫してしたがえば、 プログラム内の他の場所で同じ変数シンボルを任意に使用しても、その変 数の値に影響を与えたり、影響を受けることがなくなる。 • それ以外なら‘defvar’、‘defconst’、‘defcustom’で変数を定義する。 *note Defining Variables::を参照のこと。この定義は通常はEmacs Lispフ ァイル内のトップレベルにあること。この定義には変数の意味と目的を説 明するドキュメント文字列を可能な限り含めるべきである。また名前の衝 突を避けるように変数を命名すること(*note Coding Conventions::を参照 )。 そうすればプログラム内のどこか別の場所で、それが何に影響するか確信 をもって変数をバインドすることができます。その変数にどこで出会って も、(たとえば変数の定義がEmacsにロードされていれば‘C-h v’コマンドを 通じて)定義を参照するのが簡単になります。*note (emacs)Name Help::を 参照してください。 たとえば‘case-fold-search’のようなカスタマイズ可能な変数にたいして ローカルバインディングを使用するのは一般的です: (defun search-for-abc () "Search for the string \"abc\", ignoring case differences." (let ((case-fold-search nil)) (re-search-forward "abc"))) 11.9.3 レキシカルバインディング ------------------------------- Emacsのバージョン24.1からオプションの機能としてレキシカルバインディング が導入されました。わたしたちはこの機能の重要性が、将来増加することを期待 しています。レキシカルバインディングは最適化の機会をより広げるので、この 機能を使用するプログラムはおそらくEmacsの将来のバージョンで高速に実行さ れるようになるでしょう。レキシカルバインディングは、わたしたちがEmacsに 将来追加したいと考える並列性(concurrency)とも互換性があります。 レキシカルにバインドされた変数は“レキシカルスコープ(lexical scope)”を もちます。これはその変数にたいする参照が、そのバインディング構文内にテキ スト的に配置されなければならないことを意味しています。以下は例です (実際 にレキシカルバインディングを有功にする方法は、*note Using Lexical Binding::を参照のこと): (let ((x 1)) ; ‘x’はレキシカルにバインドされる (+ x 3)) ⇒ 4 (defun getx () x) ; この関数内では‘x’は自由に使用される (let ((x 1)) ; ‘x’はレキシカルにバインドされる (getx)) error→ Symbol's value as variable is void: x ここでは‘x’はグローバル値をもちません。‘let’フォーム内でレキシカルにバイ ンドされたとき、この変数は‘let’のテキスト境界内で使用できます。しかしこ の‘let’内から呼び出される‘getx’関数からは、‘getx’の関数定義が‘let’フォー ムの外側なので使用することが_できません_。 レキシカルバインディングが機能する方法を説明します。バインディング構 文はぞれぞれ、その構文内でローカル値にバインドする変数を指定する、“レキ シカル環境(lexical environment)”を定義します。Lispの評価機能(Lisp evaluator)が、ある変数のカレント値を得たいときは、最初にレキシカル環境内 を探します。そこで変数が指定されていなければ、ダイナミック値が格納される シンボルの値セルを探します。 (内部的にはレキシカル環境はシンボルと値がペアになったalistでり、 alistの最後の要素はコンスセルではなくシンボル‘t’である。そのような alistはフォームを評価するためのレキシカル環境を指定するために、‘eval’関 数の2番目の引数として渡すことができる。*note Eval::を参照のこと。しかし ほとんどのEmacs Lispプログラムは、この方法で直接レキシカル環境を使用する べきではない。デバッガーのような特化されたプログラムだけが使用すること。 ) レキシカルバインディングは不定エクステント(indefinite extent)をもちま す。バインディング構造が終了した後でも、そのレキシカル環境は“クロージャ ー(closures)”と呼ばれるLispオブジェクト内に“保持”されるかもしれ、あせん 。クロージャーはレキシカルバインディングが有効な、名前つきまたは無名 (anonymous)の関数が作成されたときに作成されます。詳細は*note Closures::を 参照してください。 クロージャーが関数として呼び出されたとき、その関数の定義内のレキシカ ル変数にたいする任意の参照は、維持されたレキシカル環境を使用します。以下 は例です: (defvar my-ticker nil) ; クロージャーを格納するために ; この変数を使用する (let ((x 0)) ; ‘x’はレキシカルにバインドされる (setq my-ticker (lambda () (setq x (1+ x))))) ⇒ (closure ((x . 0) t) () (setq x (1+ x))) (funcall my-ticker) ⇒ 1 (funcall my-ticker) ⇒ 2 (funcall my-ticker) ⇒ 3 x ; ‘x’はグローバル値をもたないことに注意 error→ Symbol's value as variable is void: x ‘let’バインディングは、内部に変数‘x’をもつレキシカル環境を定義して、変数 は0にローカルにバインドされます。このバインディング構文内で‘x’を1増加し て、増加された値をリターンするクロージャーを定義しています。このラムダ式 は自動的にクロージャーとなり、たとえ‘let’構文を抜けた後でも、その内部で はレキシカル環境が存続します。クロージャーを評価するときは、毎回レキシカ ル環境内の‘x’のバインディングが使用されて、‘x’が加算されます。 シンボルオブジェクト自体に束縛されるダイナミック変数と異なり、レキシ カル変数とシンボルの関係はインタープリター(かコンパイラー)内にのみ存在し ます。したがって(‘symbol-value’、‘boundp’、‘set’のような)シンボル引数を 受け取る関数ができるのは、変数のダイナミックなバインディング(そのシンボ ルの値セルの内容)の取得と変更だけです。 11.9.4 レキシカルバインディングの使用 ------------------------------------- Emacs LispファイルのロードやLispバッファーを評価するとき、バッファーロー カルな変数‘lexical-binding’が非‘nil’なら、レキシカルバインディングが有効 になります: -- Variable: lexical-binding このバッファーローカルな変数が非‘nil’なら、Emacs Lispファイルとバッ ファーはダイナミックバインディングではなくレキシカルバインディング を使用して評価される(しかし特別な変数はダイナミックにバインドされた まま。以下を照)。‘nil’ならすべてのローカル変数にたいしてダイナミッ クバインディングが使用される。この変数は、通常はファイルローカル変 数として、Emacs Lispファイル全体にたいしてセットされる(*note File Local Variables::を参照)。他のファイルローカル変数などとは異なり、 ファイルの最初の行でセットされなければならないことに注意。 ‘eval’呼び出しを使用してEmacs Lispコードを直接評価するとき、‘eval’の LEXICAL引数が非‘nil’なら、レキシカルバインディングが有効になります。 *note Eval::を参照してください。 レキシカルバインディングが有効な場合でも、特定の変数はダイナミックに バインドされたままです。これらは“スペシャル変数(special variable)”と呼ば れます。‘defvar’、‘defcustom’、‘defconst’で定義されたすべての変数はスペ シャル変数です(*note Defining Variables::を参照)。その他のすべての変数は レキシカルバインディングの対象になります。 -- Function: special-variable-p symbol この関数はSYMBOLがスペシャル変数(つまり変数が‘defvar’、 ‘defcustom’、‘defconst’による定義をもつ)なら非‘nil’をリターンする。 、それ以外ならリターン値は‘nil’。 関数内での通常の引数としてのスペシャル変数の使用は、推奨されません。 レキシカルバインディングモードが有効なときにこれを行うと、(あるときはレ キシカルバインディング、またあるときはダイナミックバインディングのような )不定な動作が起こります。 Emacs Lispプログラムをレキシカルバインディングに変換するのは簡単です 。最初にEmacs Lispソースファイルのヘッダー行で‘lexical-binding’を‘t’にし て、ファイルローカル変数を追加します(*note File Local Variables::を参照 )。次に意図せずレキシカルにバインドしてしまわないように、ダイナミックな バインドをもつ必要がある変数が変数定義をもつことを各変数ごとにチェックし ます。 どの変数が変数定義をもつ必要があるか見つけるシンプルな方法は、ソース ファイルをバイトコンパイルすることです。*note Byte Compilation::を参照し てください。‘let’フォームの外側で非スペシャル変数が使用されていれば、バ イトコンパイラーはフリーな変数にたいする参照や割り当てについて警告するで しょう。非スペシャル変数がバインドされているが‘let’フォーム内で使用され ていなければ、バイトコンパイラーは使用されないレキシカル変数に関して警告 するでしょう。バイトコンパイラーは、スペシャル変数を関数の引数として使用 している場合も問題を警告します。 (使用されていない変数についての警告を抑制するためには、単に変数名をア ンダースコアーで開始すればよい。そうすればバイトコンパイラーはその変数が 使用されないことを示すと解釈する。) 11.10 バッファーローカル変数 ============================ グローバルおよびローカルな変数バインディングは、いずれかの形式をほとんど のプログラミング言語で見つけることができます。しかしEmacsは1つのバッファ ーだけに適用される“バッファーローカル(buffer-local)”なバインディング用に 、普通には存在しない類の変数バインディングもサポートしています。ある変数 にたいして異なるバッファーごとに別の値をもつのは、カスタマイズでの重要な 手法です(変数は端末ごとにローカルなバインディングをもつこともできる。 *note Multiple Terminals::を参照)。 11.10.1 バッファーローカル変数の概要 ------------------------------------ バッファーローカル変数は特定のバッファーに関連づけられた、バッファーロー カルなバインディングをもちます。このバインディングはそのバッファーがカレ ントのときに効果をもち、カレントでないときには効果がありません。バッファ ーローカルなバインディングが効力をもつときにその変数をセットすると、その バインディングは新しい値をもちますが他のバインディングは変更されません。 これはバッファーローカルなバインディングを作成したバッファーだけで変更が 見えることを意味します。 その変数にたいする特定のバッファーに関連しない通常のバインディングは 、“デフォルトバインディング(default binding)”と呼ばれます。これはほとん どの場合はグローバルバインディングです。 変数はあるバッファーではバッファーローカルなバインディングをもつこと ができ、他のバッファーではもたないことができます。デフォルトバインディン グは、その変数にたいして自身のバインディングをもたないすべてのバッファー で共有されます(これには新たに作成されたバッファーが含まれる)。ある変数に たいして、その変数のバッファーローカルなバインディングをもたないバッファ ーでその変数をセットすると、それによりデフォルトバインディングがセットさ れるので、デフォルトバインディングを参照するすべてのバッファーで新しい値 を見ることになります。 バッファーローカルなバインディングのもっとも一般的な使用は、メジャー モードがコマンドの動作を制御するために変数を変更する場合です。たとえば CモードやLispモードは、空行だけがパラグラフの区切りになるように変数 ‘paragraph-start’をセットします。これらのモードは、CモードやLispモードに なるようなバッファー内でこの変数をバッファーローカルにすることでこれを行 って、その後そのモードにたいする新しい値をセットします。*note Major Modes::を参照してください。 バッファーローカルなバインディングを作成する通常の方法は、 ‘make-local-variable’による方法で、これは通常はメジャーモードが使用しま す。これはカレントバッファーだけに効果があります。その他すべてのバッファ ー(まだ作成されていないバッファーを含む)は、それらのバッファー自身が明示 的にバッファーローカルなバインディングを与えるまでデフォルト値を共有し続 けます。 変数を“自動的にバッファーローカルになる”ようにマークする、より強力な 操作は‘make-variable-buffer-local’を呼び出すことにより行われます。これは たとえその変数がまだ作成されていなくても、変数をすべてのバッファーにたい してローカルにすると考えることができます。より正確には変数を自動的にセッ トすることにより、その変数がカレントバッファーにたいしてローカルでなくて も、変数をローカルにする効果があります。すべてのバッファーは最初は通常の ようにデフォルト値を共有しますが、変数をセットすることでカレントバッファ ーにたいしてバッファーローカルなバインディングを作成します。新たな値はバ ッファーローカルなバインディングに格納され、デフォルトバインディングは変 更されずに残ります。これは任意のバッファーで‘setq’によりデフォルト値を変 更できないことを意味します。変更する唯一の方法は‘setq-default’だけです。 *警告:* ある変数が1つ以上のバッファーでバッファーローカルなバインディ ングをもつ際に、‘let’はそのとき効力がある変数のバインディングをリバイン ドします。たとえばカレントバッファーがバッファーローカルな値をもつなら、 ‘let’は一時的にそれをリバインドします。効力があるバッファーローカルなバ インディングが存在しなければ‘let’はデフォルト値をリバインドします。 ‘let’の内部で、別のバインディングが効力をもつ別のバッファーをカレントバ ッファーにすると、それ以上‘let’バインディングを参照できなくなります。他 のバッファーにいる間に‘let’を抜けると、(たとえそれが正しくても)バインデ ィングの解消を見ることはできません。以下にこれを示します: (setq foo 'g) (set-buffer "a") (make-local-variable 'foo) (setq foo 'a) (let ((foo 'temp)) ;; foo ⇒ 'temp ; バッファー‘a’内でのletバインディング (set-buffer "b") ;; foo ⇒ 'g ; fooは‘b’にたいしてローカルではないためグローバル値 BODY...) foo ⇒ 'g ; exitによりバッファー‘a’のローカル値が復元されるが ; バッファー‘b’では見ることができない (set-buffer "a") ; ローカル値が復元されたことを確認 foo ⇒ 'a BODY内の‘foo’にたいする参照は、バッファー‘b’のバッファーローカルなバイン ディングにアクセスすることに注意してください。 あるファイルがローカル変数の値をセットする場合、これらの変数はファイ ルをvisitするときバッファーローカルな値になります。*note (emacs)File Variables::を参照してください。 バッファーローカル変数を端末ローカル(terminal-local)にすることはでき ません(*note Multiple Terminals::を参照)。 11.10.2 バッファーローカルなバインディングの作成と削除 ------------------------------------------------------ -- Command: make-local-variable variable この関数はカレントバッファー内で、VARIABLE(シンボル)にたいするバッ ファーローカルなバインディングを作成する。他のバッファーは影響を受 けない。リターンされる値はVARIABLE。 VARIABLEのバッファーローカルな値は、最初は以前にVARIABLEがもってい た値と同じ値をもつ。VARIABLEがvoidのときはvoidのまま。 ;; バッファー‘b1’で行う: (setq foo 5) ; すべてのバッファーに影響する。 ⇒ 5 (make-local-variable 'foo) ; ‘b1’内でローカルになった ⇒ foo foo ; 値は変更されない ⇒ 5 (setq foo 6) ; ‘b1’内で値を変更 ⇒ 6 foo ⇒ 6 ;; バッファー‘b2’では、値は変更されていない (with-current-buffer "b2" foo) ⇒ 5 変数を‘let’バインディングでバッファーローカルにしても、‘let’への出 入り時の両方でこれを行うバッファーがカレントでなければ信頼性はない 。これは‘let’がバインディングの種類を区別しないからである。‘let’に 解るのはバインディングが作成される変数だけである。 変数が端末ローカル(*note Multiple Terminals::を参照)なら、この関数 はエラーをシグナルする。そのような変数はバッファーローカルなバイン ディングをもつことができない。 *警告:* フック変数にたいして‘make-local-variable’を使用しないこと。 フック変数は‘add-hook’か‘remove-hook’のLOCAL引数を使用すると、必要 に応じて自動でバッファーローカルになる。 -- Macro: setq-local variable value このマクロはカレントバッファー内でVARIABLEにたいするバッファーロー カルなバインディングを作成して、それにバッファーローカルな値VALUEを 与える。このマクロは‘make-local-variable’に続けて‘setq’を呼び出すの と同じ。VARIABLEはクォートされていないシンボル。 -- Command: make-variable-buffer-local variable このコマンドはVARIABLE(シンボル)が自動的にバッファーローカルになる ようにマークするので、それ以降にその変数へのセットを試みると、その 時点でカレントのバッファーにローカルになる。しばしば混乱を招く ‘make-local-variable’とは異なり、これが取り消されることはなく、すべ てのバッファー内での変数の挙動に影響する。 この機能特有の欠点は、(‘let’やその他のバインディング構文による)変数 のバインディングが、その変数にたいするバッファーローカルなバインデ ィングを作成しないことである。(‘set’や‘setq’による)変数のセットだけ は、その変数がカレントバッファーで作成された‘let’スタイルのバインデ ィングをもたないので、ローカルなバインディングを作成する。 VARIABLEがデフォルト値をもたない場合、このコマンドの呼び出しは ‘nil’のデフォルト値を与える。VARIABLEがすでにデフォルト値をもつなら 、その値は変更されずに残る。それ以降にVARIABLEにたいして ‘makunbound’を呼び出すと、バッファーローカル値をvoidにして、デフォ ルト値は影響を受けずに残る。 ▼リターン値はVARIABLE。 *警告:* ユーザーオプション変数では、ユーザーは異なるバッファーにた いして異なるカスタマイズを望む_かもしれない_ので、 ‘make-variable-buffer-local’を使うべきだと決め込むべきではない。ユ ーザーは望むなら任意の変数をローカルにできる。その選択の余地を残す ほうがよい。 ‘make-variable-buffer-local’を使用すべきなのは、複数のバッファーが 同じバインディングを共有しないことが自明な場合である。たとえばバッ ファーごとに個別な値をもつことに依存するLispプログラム内の内部プロ セスにたいして変数が使用されるときは、 ‘make-variable-buffer-local’の使用が最善の解決策になるかもしれない 。 -- Macro: defvar-local variable value &optional docstring このマクロはVARIABLEを初期値VALUEとDOCSTRINGの変数として定義して、 それを自動的にバッファーローカルとマークする。これは‘defvar’の後に つづけて‘make-variable-buffer-local’を呼び出すのと同じ。VARIABLEは クォートされていないシンボル。 -- Function: local-variable-p variable &optional buffer これはVARIABLEがバッファーBUFFER(デフォルトはカレントバッファー)内 でバッファーローカルなら‘t’、それ以外は‘nil’をリターンする。 -- Function: local-variable-if-set-p variable &optional buffer これはVARIABLEがバッファーBUFFER内でバッファーローカル値をもつ、ま たは自動的にバッファーローカルになるなら‘t’、それ以外は‘nil’をリタ ーンする。BUFFERが省略または‘nil’の場合のデフォルトはカレントバッフ ァー。 -- Function: buffer-local-value variable buffer この関数はバッファーBUFFER内の、VARIABLE(シンボル)のバッファーロー カルなバインディングをリターンする。VARIABLEがバッファーBUFFER内で バッファーローカルなバインディングをもたなければ、かわりに VARIABLEのデフォルト値(*note Default Value::を参照)をリターンする。 -- Function: buffer-local-variables &optional buffer この関数はバッファーBUFFER内のバッファーローカル変数を表すリストを リターンする(BUFFERが省略された場合はカレントバッファーが使用される )。リストの各要素は通常は‘(SYM . VAL)’という形式をもつ。ここでSYMは バッファーローカル変数(シンボル)、VALはバッファーローカル値。しかし BUFFER内のある変数のバッファーローカルなバインディングがvoidなら、 その変数に対応するリスト要素は単にSYMとなる。 (make-local-variable 'foobar) (makunbound 'foobar) (make-local-variable 'bind-me) (setq bind-me 69) (setq lcl (buffer-local-variables)) ;; 最初はすべてのバッファー内でローカルなビルトイン変数: ⇒ ((mark-active . nil) (buffer-undo-list . nil) (mode-name . "Fundamental") ... ;; 次にビルトインでないバッファーローカル変数 ;; This one is buffer-local and void: foobar ;; これはバッファーローカルでvoidではない: (bind-me . 69)) このリスト内のコンスセルのCDRに新たな値を格納しても、その変数のバッ ファーローカル値は_変化しない_ことに注意。 -- Command: kill-local-variable variable この関数はカレントバッファー内のVARIABLE(シンボル)にたいするバッフ ァーローカルなバインディング(もしあれば)を削除する。その結果として 、このバッファー内でVARIABLEのデフォルトバインディングが可視になる 。これは通常はVARIABLEの値を変更する。デフォルト値は削除されたバッ ファーローカル値とは異なるのが普通だからである。 セットしたとき自動的にバッファーローカルになる変数のバッファーロー カルなバインディングをkillすると、これによりカレントバッファー内で デフォルト値が可視になる。しかし変数を再度セットすると、その変数に たいするバッファーローカルなバインディングが再作成される。 ‘kill-local-variable’はVARIABLEをreturnします。 この関数はコマンドである。なぜならバッファーローカル変数のインタラ クティブな作成が有用な場合があるように、あるバッファーローカル変数 のインタラクティブなkillが有用な場合があるからである。 -- Function: kill-all-local-variables この関数はpermanent(永続的)とマークされた変数と ‘permanent-local-hook’プロパティーに非‘nil’をもつローカルフック関数 (*note Setting Hooks::を参照)を除き、カレントバッファーのすべてのバ ッファーローカルなバインディングを解消する。結果として、そのバッフ ァーのほとんどの変数がデフォルト値を参照するようになる。 この関数はそのバッファーに関連する他の特定の情報もリセットする。こ れはローカルキーマップを‘nil’、構文テーブルを ‘(standard-syntax-table)’の値、caseテーブルを ‘(standard-case-table)’、abbrevテーブルを ‘fundamental-mode-abbrev-table’の値にセットする。 この関数が1番最初に行うのはノーマルフック ‘change-major-mode-hook’(以下参照)の実行である。 すべてのメジャーモードコマンドは、Fundamentalモードにスイッチする効 果をもち、以前のメジャーモードのほとんどの効果を消去する、この関数 を呼び出すことによって開始されます。この関数が処理を行うのを確実に するために、メジャーモードがセットする変数はpermanentとマークすべき ではない。 ‘kill-all-local-variables’ returns ‘nil’. -- Variable: change-major-mode-hook 関数‘kill-all-local-variables’は、何か他のことを行う前にまずこのノ ーマルフックを実行する。この関数はメジャーモードにたいして、ユーザ ーが他のメジャーモードにスイッチした場合に行われる何か特別なことを 準備する方法を与える。この関数はユーザーがメジャーモードを変更した 場合に忘れられるべき、バッファー固有のマイナーモードにたいしても有 用。 最善の結果を得るために、この変数をバッファーローカルにすれば、処理 が終了したときに消えるので、以降のメジャーモードに干渉しなくなる。 *note Hooks::を参照のこと。 変数名(シンボル)が非‘nil’の‘permanent-local’プロパティーをもつなら、 そのバッファーローカル変数は“permanent(永続的)”です。そのような変数は ‘kill-all-local-variables’の影響を受けず、したがってメジャーモードの変更 によりそれらのローカルバインディングは作成されません。permanentなローカ ル変数はファイルの内容を編集する方法ではなく、どこから読み込んだファイル か、あるいはどのように保存するかといったことに関連するデータに適していま す。 11.10.3 バッファーローカル変数のデフォルト値 -------------------------------------------- バッファーローカルなバインディングをもつ変数のグローバル値も“デフォルト 値(default)”値と呼ばれます。なぜならその変数にたいしてカレントバッファー や選択されたフレームもバインディングをもたなければ、その値が常に効果をも つからです。 関数‘default-value’と‘setq-default’は、カレントバッファーがバッファー ローカルなバインディングをもつかどうかに関わらず、その変数のデフォルト値 にアクセスまたは変更します。たとえばほとんどのバッファーにたいして、 ‘paragraph-start’のデフォルトのセッティングを変更するために、 ‘setq-default’を使用できます。そしてこの変数にたいするバッファーローカル な値をもつCモードやLispモードにいるときでさえ、これは機能します。 スペシャルフォーム‘defvar’と‘defconst’もバッファーローカルな値ではな く、(もし変数にセットする場合は)デフォルト値をセットします。 -- Function: default-value symbol この関数はSYMBOLのデフォルト値をリターンする。これはこの変数にたい して独自の値をもたないバッファーやフレームから参照される値である。 SYMBOLがバッファーローカルでなければ、これは‘symbol-value’(*note Accessing Variables::を参照)と同じ。 -- Function: default-boundp symbol 関数‘default-boundp’はSYMBOLのデフォルト値がvoidでないか報告する。 ‘(default-boundp 'foo)’が‘nil’をリターンした場合には‘(default-value 'foo)’はエラーになる。 ‘default-boundp’は‘default-value’、‘boundp’は‘symbol-value’にたいす る述語である。 -- Special Form: setq-default [symbol form]... このスペシャルフォームは各SYMBOLに新たなデフォルト値として、対応す るFORMを評価した結果を与える。これはSYMBOLを評価しないがFORMは評価 する。‘setq-default’フォームの値は最後のFORMの値。 カレントバッファーにたいしてSYMBOLがバッファーローカルでなく、自動 的にバッファーローカルにマークされていなければ、‘setq-default’は ‘setq’と同じ効果をもつ。カレントバッファーにたいしてSYMBOLがバッフ ァーローカルなら、(バッファーローカルな値をもたない)他のバッファー から参照できる値を変更するが、それはカレントバッファーが参照する値 ではない。 ;; バッファー‘foo’で行う: (make-local-variable 'buffer-local) ⇒ buffer-local (setq buffer-local 'value-in-foo) ⇒ value-in-foo (setq-default buffer-local 'new-default) ⇒ new-default buffer-local ⇒ value-in-foo (default-value 'buffer-local) ⇒ new-default ;; (新しい)バッファー‘bar’で行う: buffer-local ⇒ new-default (default-value 'buffer-local) ⇒ new-default (setq buffer-local 'another-default) ⇒ another-default (default-value 'buffer-local) ⇒ another-default ;; バッファー‘foo’に戻って行う: buffer-local ⇒ value-in-foo (default-value 'buffer-local) ⇒ another-default -- Function: set-default symbol value この関数は‘setq-default’と似ているが、SYMBOLは通常の引数として評価 される。 (set-default (car '(a b c)) 23) ⇒ 23 (default-value 'a) ⇒ 23 11.11 ファイルローカル変数 ========================== ファイルにローカル変数の値を指定できます。そのファイルをvisitしているバ ッファー内で、これらの変数にたいしてバッファーローカルなバインディングを 作成するために、Emacsはこれらを使用します。ファイルローカル変数の基本的 な情報については、*note Local Variables in Files: (emacs)File Variables.を参照してください。このセクションではファイルローカル変数が処 理される方法に影響する関数と変数を説明します。 ファイルローカル変数が勝手に関数や、後で呼び出されるLisp式を指定でき たら、ファイルのvisitによってEmacsが乗っ取られてしまうかもしれません。 Emacsは既知のファイルローカル変数だけにたいして、指定された値が安全だと 自動的にセットすることにより、この危険から保護します。これ以外のファイル ローカル変数は、ユーザーが同意した場合のみセットされます。 追加の安全策としてEmacsがファイルローカル変数を読み込むとき、一時的に ‘read-circle’を‘nil’にバインドします(*note Input Functions::を参照)。こ れは循環認識と共有されたLisp構造からLispリーダーを保護します(*note Circular Objects::を参照)。 -- User Option: enable-local-variables この変数はファイルローカル変数を処理するかどうかを制御する。以下の 値が利用できる: ‘t’(デフォルト) 安全な変数をセット、安全でない変数は問い合わせる(1回)。 ‘:safe’ 安全な変数だけをセット、問い合わせはしない。 ‘:all’ 問い合わせをせずに、すべての変数をセット。 ‘nil’ 変数をセットしない。 その他 すべての変数にたいして問い合わせる(1回)。 -- Variable: inhibit-local-variables-regexps これは正規表現のリストである。ファイルがこのリストの要素にマッチす る名前をもつなら、すべてのファイルローカル変数のフォームはスキャン されない。どんなときにこれを使いたいかの例は、*note Auto Major Mode::を参照のこと。 -- Function: hack-local-variables &optional mode-only この関数はカレントバッファーの内容により指定された任意のローカル変 数にたいしてパースを行い、適切にバインドと評価を行う。変数 ‘enable-local-variables’はここでも効果をもつ。しかしこの関数は ‘-*-’行の、‘mode:’ローカル変数を探さない。‘set-auto-mode’はこれを行 って‘enable-local-variables’も考慮する(*note Auto Major Mode::を参 照)。 この関数は‘file-local-variables-alist’内に格納されたalistを調べて、 各ローカル変数を順に適用することにより機能する。この関数は変数に適 用する前(か後)に、‘before-hack-local-variables-hook’(か ‘hack-local-variables-hook’)を呼び出す。alistが非‘nil’の場合のみ、 事前のフック(before-hook)を呼び出し、その他のフックは常に呼び出す。 この関数はそのバッファーがすでにもつメジャーモードと同じメジャーモ ードが指定された場合は‘mode’要素を無視する。 オプションの引数MODE-ONLYが非‘nil’なら、メジャーモードを指定するシ ンボルをリターンするのがこの関数が行うのすべてであり、‘-*-’行かロー カル変数リストがメジャーモードを指定していればそのモード、それ以外 は‘nil’をリターンする。この関数はモードや他のファイルローカル変数を セットしない。 -- Variable: file-local-variables-alist このバッファーローカルな変数は、ファイルローカル変数のセッティング のalistを保持する。alistの各要素は‘(VAR . VALUE)’という形式で、 VARはローカル変数のシンボル、VALUEはその値である。Emacsがファイルを visitするとき、最初にすべてのファイルローカル変数をこのalistに収集 して、その後で変数に1つずつ関数‘hack-local-variables’を適用する。 -- Variable: before-hack-local-variables-hook Emacsは‘file-local-variables-alist’に格納されたファイルローカル変数 を適用する直前にこのフックを呼び出す。 -- Variable: hack-local-variables-hook Emacsは‘file-local-variables-alist’に格納されたファイルローカル変数 を適用し終えた直後にこのフックを呼び出す。 ある変数にたいして‘safe-local-variable’プロパティーによって安全な値を 指定できます。このプロパティーは引数を1つとる関数です。与えられた値にた いして、その関数が非‘nil’をリターンしたらその値は安全です。一般的に目に するファイル変数の多くは、‘safe-local-variable’プロパティーをもちます。 これらのファイル変数には‘fill-column’、‘fill-prefix’、 ‘indent-tabs-mode’が含まれます。ブーリーン値の変数にたいしては、プロパテ ィーの値に‘booleanp’を使用します。 ‘defcustom’を使用してユーザーオプションを定義する際、‘defcustom’に引 数‘:safe FUNCTION’を追加することにより、‘safe-local-variable’プロパティ ーをセットできます(*note Variable Definitions::を参照)。 -- User Option: safe-local-variable-values この変数はある変数の値が安全であることをマークする、別の方法を提供 する。これはコンスセル‘(VAR . VAL)’のリストでありVARは変数名、VALは その変数にたいして安全な値である。 Emacsが一連のファイルローカル変数にしたがうかどうかユーザーに尋ねる とき、ユーザーはそれらの変数が安全だとマークすることができる。安全 とマークすると‘safe-local-variable-values’にこれらの variable/valueペアーが追加されて、ユーザーのカスタムファイルに保存 する。 -- Function: safe-local-variable-p sym val この関数は上記の条件に基づき、SYMに値VALを与えても安全ななら非 ‘nil’をリターンする。 いくつかの変数は“危険(risky)”だと判断されます。ある変数が危険なら、そ の変数が‘safe-local-variable-values’に自動的に追加されることはありません 。ユーザーが‘safe-local-variable-values’を直接カスタマイズすることで明示 的に値を許さない限り、危険な変数をセットする前にEmacsは常に確認を求めま す。 名前が非‘nil’の‘risky-local-variable’プロパティーをもつすべての変数は 危険だと判断されます。‘defcustom’を使用してユーザーオプションを定義する とき、‘defcustom’に引数‘:risky VALUE’を追加することにより、ユーザーオプ ションに‘risky-local-variable’プロパティーをセットできます。それに加えて 名前が‘-command’、‘-frame-alist’、‘-function’、‘-functions’、‘-hook’、 ‘-hooks’、‘-form’、‘-forms’、‘-map’、‘-map-alist’、‘-mode-alist’、 ‘-program’、‘-predicate’で終わるすべての変数は自動的に危険だと判断されま す。後に数字をともなう変数‘font-lock-keywords’と‘font-lock-keywords’、さ らには‘font-lock-syntactic-keywords’も危険だと判断されます。 -- Function: risky-local-variable-p sym この関数はSYMが上記の条件にもとづき危険な変数なら非‘nil’をリターン する。 -- Variable: ignored-local-variables この変数はファイルによりローカル値を与えらるべきではない変数のリス トを保持する。これらの変数に指定された任意の値は、完全に無視される 。 “変数”‘Eval:’も抜け道になる可能性があるので、Emacsは通常はそれを処理 する前に確認を求めます。 -- User Option: enable-local-eval この変数は‘-*-’の行中、またはvisitされるファイル内のローカル変数リ ストにたいする、‘Eval:’の処理を制御する。値‘t’は無条件に実行し、 ‘nil’はそれらを無視することを意味します。それ以外なら各ファイルにた いして何を行うか、ユーザーに確認を求めることを意味する。デフォルト 値は‘maybe’。 -- User Option: safe-local-eval-forms この変数はファイルローカル変数リスト内で‘Eval:’“変数”が見つかった際 に評価しても安全な式のリストを保持する。 式が関数呼び出しであり、その関数が‘safe-local-eval-function’プロパテ ィーをもつなら、その式の評価が安全かどうかはそのプロパティー値が決定しま す。プロパティー値はその式をテストするための述語(predicate)、そのような 述語のリスト(成功した述語があれば安全)、または‘t’(引数が定数である限り常 に安全)を指定できます。 テキストプロパティーには、それらの値に関数呼び出しを含めることができ るので抜け道になる可能性があります。したがってEmacsはファイルローカル変 数にたいして指定された文字列値から、テキストプロパティーを取り除きます。 11.12 ディレクトリーローカル変数 ================================ ディレクトリーは、そのディレクトリー内のすべてのファイルに共通なローカル 変数値を指定することができます。Emacsはそのディレクトリー内の任意のファ イルをvisitしているバッファー内で、それらの変数にたいするバッファーロー カルなバインディングを作成するためにこれを使用します。これはそのディレク トリー内のファイルが何らかの“プロジェクト”に属していて、同じローカル変数 を共有するときなどに有用です。 ディレクトリーローカル変数を指定するために2つの異なる方法があります: 1つは特別なファイルにそれを記述する方法、もう1つはそのディレクトリーに “プロジェクトクラス(project class)”を定義する方法です。 -- Constant: dir-locals-file この定数はEmacsがディレクトリーローカル変数が見つけることができると 期待するファイルの名前である。ファイル名は‘.dir-locals.el’(1)。ディ レクトリー内でその名前をもつファイルにより、Emacsはディレクトリー内 の任意のファイル、または任意のサブディレクトリー(オプションでサブデ ィレクトリーを除外できる。以下参照)にセッティングを適用する。独自に ‘.dir-locals.el’をもつサブディレクトリーがあるなら、Emacsはサブディ レクトリーで見つかった1番深いファイルのディレクトリーからディレクト リーツリーを上方に移動しつつ、1番深いファイルのセッティングを使用す る。このファイルはローカル変数をフォーマットされたリストとして指定 する。詳細は*note Per-directory Local Variables: (emacs)Directory Variables.を参照のこと。 -- Function: hack-dir-local-variables この関数は‘.dir-locals.el’ファイルを読み込み、そのディレクトリー内 の任意のファイルをvisitしているバッファーにローカルな ‘file-local-variables-alist’内に、それらを適用することなくディレク トリーローカル変数を格納する。この関数はディレクトリーローカルなセ ッティングも‘dir-locals-class-alist’(‘.dir-locals.el’ファイルが見つ かったディレクトリーにたいする特別なクラスを定義する)内に格納する。 この関数は以下で説明するように、‘dir-locals-set-class-variables’と ‘dir-locals-set-directory-class’を呼び出すことにより機能する。 -- Function: hack-dir-local-variables-non-file-buffer この関数はディレクトリーローカル変数を探して、即座にそれらをカレン トバッファーに適用する。これはDiredバッファーのような、非ファイルバ ッファーをディレクトリーローカル変数のセッティングにしたがわせるた めに、モードコマンド呼び出しの中から呼び出されることを意図したもの である。非ファイルバッファーにたいしては、Emacsは ‘default-directory’とその親ディレクトリーの中から、ディレクトリーロ ーカル変数を探す。 -- Function: dir-locals-set-class-variables class variables この関数はCLASSという名前がつけられたシンボルにたいして、一連の変数 セッティングを定義する。ユーザーは後からこのクラスを1つ以上のディレ クトリーに割り当てることができる。そしてEmacsはこれらの変数セッティ ングを、それらのディレクトリー内のすべてのファイルに適用するだろう 。VARIABLES内のリストは2つの形式 — ‘(MAJOR-MODE . ALIST)’か ‘(DIRECTORY . LIST)’ — のいずれかをもつことができる。1番目の形式で はそのファイルのバッファーがMAJOR-MODEを継承するモードに切り替わる ときに、連想リストALIST内のすべての変数が適用される。ALISTは‘(NAME . VALUE)’という形式。MAJOR-MODEにたいする特別な値‘nil’は、そのセッ ティングが任意のモードに適用できることを意味する。ALIST内では特別な NAMEとして、‘subdirs’を使用することができる。連想値が‘nil’なら、 alistは関連するディレクトリー内のファイルだけに適用され、それらのサ ブディレクトリーには適用されない。 VARIABLESの2番目の形式では、DIRECTORYがそのファイルのディレクトリー の最初のサブディレクトリーなら、上記のルールにしたがいLISTが再帰的 に適用される。LISTはこの関数のVARIABLESで指定できる2つの形式のうち 1つを指定する。 -- Function: dir-locals-set-directory-class directory class &optional mtime この関数は‘directory’とサブディレクトリー内のすべてのファイルに CLASSを割り当てる。その後、CLASSにたいして指定されたすべての変数セ ッティングは、DIRECTORYとその子ディレクトリー内でvisitされたすべて のファイルに適用される。CLASSは事前に ‘dir-locals-set-class-variables’で定義されていなければならない。 Emacsが‘.dir-locals.el’ファイルからディレクトリー変数をロードする際 、内部的にこの関数を使用する。その場合、オプションの引数MTIMEはファ イルの修正日時(modification time。‘file-attributes’によりリターンさ れる)を保持する。Emacsは記憶されたローカル変数がまだ有効化チェック するために、この日時を使用する。ファイルを介さず直接クラスを割り当 てる場合、この引数は‘nil’になる。 -- Variable: dir-locals-class-alist このalistはクラスシンボル(class symbol)とそれに関連づけられる変数の セッティングを保持する。これは‘dir-locals-set-class-variables’によ り更新される。 -- Variable: dir-locals-directory-cache このalistはディレクトリー名、それらに割り当てられたクラス名、および このエントリーに関連するディレクトリーローカル変数ファイルの修正日 時を保持する。関数‘dir-locals-set-directory-class’はこのlistを更新 する。 -- Variable: enable-dir-local-variables ‘nil’ならディレクトリーローカル変数は無視される。この変数はファイル ローカル変数(*note File Local Variables::を参照)にはしたがうが、デ ィレクトリーローカル変数は無視したいモードにたいして有用かもしれな い。 ---------- Footnotes ---------- (1) MS-DOS版のEmacsはDOSファイルシステムの制限により、かわりに ‘_dir-locals.el’という名前を使用します。 11.13 変数のエイリアス ====================== シノニムとして2つの変数を作成できれば便利なときがあります。2つの変数は常 に同じ値をもち、どちらか一方を変更すると、もう一方も変更されます。変数の 名前を変更 — 古い名前はよく考慮して選択されたものではなかったとか、変数 の意味が部分的に変更された等の理由で — するとき、互換性のために新しい名 前の_エイリアス(alias)_として古い名前を維持できれば便利なときがあるかも しれません。‘defvaralias’によってこれを行うことができます。 -- Function: defvaralias new-alias base-variable &optional docstring この関数はシンボルBASE-VARIABLEのエイリアスとして、シンボル NEW-ALIASを定義する。これはNEW-ALIASから値を取得すると BASE-VARIABLEの値がリターンされ、NEW-ALIASの値を変更すると BASE-VARIABLEの値が変更されることを意味する。エイリアスされた2つの 変数名は、常に同じ値と同じバインディングを共有する。 DOCSTRING引数が非‘nil’なら、それはNEW-ALIASのドキュメント文字列を指 定する。それ以外なら、エイリアスは(もしあれば)BASE-VARIABLEと同じド キュメント文字列となる。ただしそれはBASE-VARIABLE自体がエイリアスで はない場合で、エイリアスならNEW-ALIASはエイリアスチェーンの最後の変 数のドキュメント文字列になる。 この関数はBASE-VARIABLEをリターンする。 変数のエイリアスは、変数にたいする古い名前を新しい名前に置き換える便 利な方法です。‘make-obsolete-variable’は古い名前を陳腐化(obsolete)してい ると宣言して。それが将来のある時点で削除されるかもしれないことを宣言しま す。 -- Function: make-obsolete-variable obsolete-name current-name when &optional access-type この関数はバイトコンパイラーに変数OBSOLETE-NAMEが陳腐化していると警 告させる。CURRENT-NAMEがシンボルなら、それはこの変数の新たな名前で ある。警告メッセージはその後、OBSOLETE-NAMEのかわりにCURRENT-NAMEを 使用するよう告げるようになる。CURRENT-NAMEが文字列なら、それはメッ セージであり、置き換えられる変数はない。WHENはその変数が最初に陳腐 化するのがいつかを示す文字列(通常はバージョン番号文字列)。 オプションの引数ACCESS-TYPEが非‘nil’なら、それは陳腐化の警告を引き 起こすアクセスの種類を指定すること。‘get’か‘set’を指定できる。 2つの変数シノニムを作成してマクロ‘define-obsolete-variable-alias’を使 用することにより、1つが陳腐化していると同時に宣言できます。 -- Macro: define-obsolete-variable-alias obsolete-name current-name &optional when docstring このマクロは変数OBSOLETE-NAMEが陳腐化しているとマークして、それを変 数CURRENT-NAMEにたいするエイリアスにする。これは以下と等価である: (defvaralias OBSOLETE-NAME CURRENT-NAME DOCSTRING) (make-obsolete-variable OBSOLETE-NAME CURRENT-NAME WHEN) -- Function: indirect-variable variable この関数はVARIABLEのエイリアスチェーンの最後の変数をリターンする。 VARIABLEがシンボルでない、またはVARIABLEがエイリアスとして定義され ていなければ、この関数はVARIABLEをリターンする。 この関数はシンボルのチェーンがループしていたら、 ‘cyclic-variable-indirection’エラーをシグナルする。 (defvaralias 'foo 'bar) (indirect-variable 'foo) ⇒ bar (indirect-variable 'bar) ⇒ bar (setq bar 2) bar ⇒ 2 foo ⇒ 2 (setq foo 0) bar ⇒ 0 foo ⇒ 0 11.14 値を制限された変数 ======================== 通常のLisp変数には、有効なLispオブジェクトである任意の値を割り当てること ができます。しかしLispではなくCで定義されたLisp変数もあります。これらの 変数のほとんどは、‘DEFVAR_LISP’を使用してCコードで定義されています。 Lispで定義された変数と同様、これらは任意の値をとることができます。しかし いくつかの変数は‘DEFVAR_INT’や‘DEFVAR_BOOL’を使用して定義されています。 C実装の概要的な議論は、*note Writing Emacs Primitives: Defining Lisp variables in C.、特に‘syms_of_FILENAME’型の関数の説明を参照してください 。 ‘DEFVAR_BOOL’型の変数は、値に‘nil’か‘t’しかとることができません。他の 値の割り当てを試みると‘t’がセットされます: (let ((display-hourglass 5)) display-hourglass) ⇒ t -- Variable: byte-boolean-vars この変数は‘DEFVAR_BOOL’型のすべての変数のリストを保持する。 ‘DEFVAR_INT’型の変数は、整数値だけをとることができます。他の値の割り 当てを試みると結果はエラーになります: (setq undo-limit 1000.0) error→ Wrong type argument: integerp, 1000.0 11.15 ジェネリック変数 ====================== “ジェネリック変数(generalized variable: 汎変数)”または“placeフォーム (place form)”は、値が格納されるLispメモリー内の多くの場所のうちの1つです 。1番シンプルなplaceフォームは通常のLisp変数です。しかしリストのCARと CDR、配列の要素、シンボルのプロパティー、その他多くのロケーション (location)もLisp値が格納される場所です。 ジェネリック変数は、C言語のlvalues(左辺値)と類似しています。C言語の lvalueでは‘x = a[i]’で配列から要素を取得し、同じ表記を使用して‘a[i] = x’で要素を格納します。Cでは‘a[i]’のような特定のフォームがlvalueになれる ように、Lispでジェネリック変数になることができる一連のフォームが存在しま す。 11.15.1 ‘setf’マクロ -------------------- ‘setf’マクロはジェネリック変数を操作する、もっとも基本的な方法です。 ‘setf’フォームは‘setq’と似ていますが、シンボルだけでなく左辺の任意の placeフォームを受け入れます。たとえば‘(setf (car a) b)’は‘a’のcarを‘b’に セットして、‘(setcar a b)’と同じ操作を行いますが、すべてのplace型へのセ ットとアクセスを行うために2つの別個の関数を覚える必要はありません。 -- Macro: setf [place form]... このマクロはFORMを評価して、それをPLACEに格納する。PLACEは有効なジ ェネリック変数フォームでなければならない。複数のPLACE/FORMペアーが ある場合、割り当ては‘setq’の場合と同様。‘setf’は最後のFORMの値をリ ターンする。 以下のLispフォームはジェネリック変数として機能するので、‘setf’の PLACE引数にすることができます: • 変数を命名するシンボル。言い換えると‘(setf x y)’は完全に‘(setq x y)’と等しく、厳密に言うと‘setq’自体は‘setf’が存在するので冗長である 。これは純粋にスタイルと歴史的な理由によるが、多くのプログラマーは 依然として単純な変数へのセットに‘setq’を好む。実際にはマクロ‘(setf x y)’は‘(setq x y)’に展開されるので、コンパイルされたコードでこれを 使用することにパフォーマンス的な不利はない。 • 以下の標準的なLisp関数の呼び出し: aref cddr symbol-function car elt symbol-plist caar get symbol-value cadr gethash cdr nth cdar nthcdr • 以下のEmacs特有な関数の呼び出し: alist-get process-get frame-parameter process-sentinel terminal-parameter window-buffer keymap-parent window-display-table match-data window-dedicated-p overlay-get window-hscroll overlay-start window-parameter overlay-end window-point process-buffer window-start process-filter default-value どのように処理すれば良いか未知なPLACEフォームを渡すと、‘setf’はエラーを シグナルします。 ‘nthcdr’の場合、関数のリスト引数はそれ自体が有効なPLACEフォームでなけ ればならないことに注意してください。たとえば‘(setf (nthcdr 0 foo) 7)’は 、‘foo’自体に7をセットするでしょう。 マクロ‘push’(*note List Variables::を参照)と‘pop’(*note List Elements::を参照)は、リストだけでなくジェネリック変数を操作できます。 ‘(pop PLACE)’はPLACE内に格納されたリストの最初の要素を削除してリターンし ます。これは‘(prog1 (car PLACE) (setf PLACE (cdr PLACE)))’と似ていますが 、すべてのサブフォームを一度だけ評価します。‘(push X PLACE)’はPLACE内に 格納されたリストの1番前にXを挿入します。これは‘(setf PLACE (cons X PLACE))’と似ていますが、サブフォームの評価が異なります。‘nthcdr’ placeへ の‘push’と‘pop’は、リスト内の任意の位置での挿入ち削除に使用できることに 注意してください。 ‘cl-lib’ライブラリーでは追加の‘setf’ placeを含む、ジェネリック変数に たいするさまざまな拡張が定義されています。*note (cl)Generalized Variables::を参照してください。 11.15.2 新たな‘setf’フォーム ---------------------------- このセクションでは、‘setf’が操作できる新たなフォームの定義方法を説明しま す。 -- Macro: gv-define-simple-setter name setter &optional fix-return このマクロは単純なケースで‘setf’メソッドを簡単に定義することを可能 にする。NAMEは関数、マクロ、スペシャルフォームの名前。NAMEがそれを 更新するための対応するSETTER関数をもつなら、このマクロを使用できる (たとえば‘(gv-define-simple-setter car setcar)’)。 このマクロは以下のフォームの呼び出しを (setf (NAME ARGS...) VALUE) 以下のように変換する。 (SETTER ARGS... VALUE) このような‘setf’の呼び出しはVALUEをリターンするとドキュメントされて いる。これは‘car’と‘setcar’では問題はない。‘setcar’はそれがセットす る値をリターンするからである。SETTER関数がVALUEをリターンしない場合 には、‘gv-define-simple-setter’のFIX-RETURN引数に、非‘nil’値を使用 すること。これは以下のようなものに展開される (let ((temp VALUE)) (SETTER ARGS... temp) temp) これで正しい結果がリターンされることが保証される。 -- Macro: gv-define-setter name arglist &rest body このマクロは上述のフォームより複雑な‘setf’展開を可能にする。たとえ ば呼び出すべきシンプルなsetter関数が存在しないときや、もしそれが存 在してもplaceフォームとは異なる引数を要求するなら、このフォームを使 う必要があるかもしれない。 このマクロは最初に‘setf’引数フォーム‘(VALUE ARGS...)’をARGLISTにバ インドして、その後BODYを実行することによって、フォーム‘(setf (NAME ARGS...) VALUE)’を展開する。BODYは割り当てを行うLispフォームをリタ ーンして、最終的にはセットされた値をリターンすること。以下はこのマ クロの使用例である: (gv-define-setter caar (val x) `(setcar (car ,x) ,val)) 展開をさらに制御するならマクロ‘gv-define-expander’を参照してください 。マクロ‘gv-letplace’は‘setf’のような処理を行うマクロを定義するのに有用 です。詳細は‘gv.el’のソースファイルを参照してください。 Common Lispに関する注意: Common Lispは関数としての‘setf’、すなわち 関数名がシンボルではなくリスト‘(setf NAME)’であるような‘setf’関数の 挙動を指定するために別の方法を定義する。たとえば‘(defun (setf foo) ...)’は、‘setf’が‘foo’に適用されるときに使用される関数を定義する。 Emacsはこれをサポートしない。適切な展開が定義されていないフォームに ‘setf’を使用するとコンパイル時エラーとなる。Common Lispでは後で関数 ‘(setf FUNC)’が定義されるのでエラーにならない。 12 関数 ******* Lispプログラムは主にLisp関数で構成されます。このチャプターはで関数とは何 か、引数を受け取る方法、そして関数を定義する方法を説明します。 12.1 関数とは? ============== 一般的な意味において関数とは、“引数(arguments)”と呼ばれる与えられた入力 値の計算を担うルールです。計算の結果はその関数の“値(value)”、または“リタ ーン値(return value)”と呼ばれます。計算では変数の値やデータ構造の内容を 変更する等の副作用をもつこともできます。 ほとんどのコンピューター言語では、関数はそれぞれ名前をもちます。しか しLispでは厳密な意味において関数は名前をもちません。関数はオブジェクトで あり、関数の名前の役割を果たすシンボルに関連づけることができますが(たと えば‘car’)、それは_オプション_です。*note Function Names::を参照してくだ さい。関数が名前を与えられたとき、通常はそのシンボルを“関数”として参照し ます(たとえば関数‘car’のように参照する)。このマニュアルでは、関数名と関 数オブジェクト自身との間の区別は通常は重要ではありませんが、それが意味を もつような場合には注記します。 “スペシャルフォーム(special form)”、“マクロ(macro)”と呼ばれる関数 likeなオブジェクトがいくつかあり、それらも引数を受け取って計算を行います 。しかし以下で説明するようにEmacs Lispではこれらは関数とはみなされません 。 以下は関数と関数likeなオブジェクトにたいする重要な条件です: “lambda expression” Lispで記述された関数(厳密には関数オブジェクト)。これらについては以 降のセクションで説明します。 *note Lambda Expressions::を参照のこと 。 “primitive” Lispから呼び出すことができるが実際にはCで記述されている。プリミティ ブは“ビルトイン関数(built-in functions)”とか“サブルーチン(subr)”の ようにも呼ばれる。それらの例には関数likeな‘car’や‘append’が含まれる 。加えてすべてのスペシャルフォーム(以下参照)もプリミティブとみなさ れる。 関数はLispの基礎となる部分(たとえば‘car’)であり、オペレーティングシ ステムのサービスにたいして低レベルのインターフェースを与え、高速に 実行される必要があるために、通常はプリミティブとして実装されている 。Lispで定義された関数と異なり、プリミティブの修正や追加には、Cソー スの変更とEmacsのリコンパイルが必要となる。*note Writing Emacs Primitives::を参照のこと。 “special form” プリミティブは関数と似ているが、すべての引数が通常の方法で評価され ない。いくつかの引数だけが評価されるかもしれず、通常ではない順序で 評価されるか、複数回評価されるかもしれない。プリミティブの例には ‘if’、‘and’、‘while’が含まれる。*note Special Forms::を参照のこと。 “macro” あるLisp式をオリジナルの式のかわりに評価される別の式に変換する、関 数とは別のLispで定義された構文。マクロはスペシャルフォームが行う一 連のことを、Lispプログラマーが行うのを可能にする。*note Macros::を 参照のこと。 “command” プリミティブ‘command-execute’を通じて呼び出すことができるオブジェク トで、通常はそのコマンドに“バインド”されたキーシーケンスをユーザー がタイプすることにより呼び出される。*note Interactive Call::を参照 のこと。コマンドは通常は関数である。その関数がLispで記述されていれ ば、関数の定義内の‘interactive’フォームによってコマンドとなる(*note Defining Commands::を参照)。関数であるコマンドは他の関数と同様、 Lisp式から呼び出すこともできる。 キーボードマクロ(文字列かベクター)は関数ではないが、これらもコマン ドである。*note Keyboard Macros::を参照のこと。シンボルの関数セルに コマンドが含まれてれば、わたしたちはそのシンボルをコマンドと言う (*note Symbol Components::を参照)。そのような“名前つきコマンド (named command)”は‘M-x’で呼び出すことができる。 “closure” ラムダ式とよく似た関数オブジェクトだが、クロージャーはレキシカル変 数バインディングの環境にも囲われている。*note Closures::を参照のこ と。 “byte-code function” バイトコンパイラーによりコンパイル済みの関数。*note Byte-Code Type::を参照のこと。 “autoload object” 実際の関数のプレースホルダー。autoloadオブジェクトが呼び出されると 、Emacsは実際の関数の定義を含むファイルをロードした後に実際の関数を 呼び出す。*note Autoload::を参照のこと。 関数‘functionp’を使用して、あるオブジェクトが関数かどうかテストできま す: -- Function: functionp object この関数はOBJECTが任意の種類の関数(‘funcall’に渡すことができる)なら ‘t’をリターンする。‘functionp’は関数を名づけるシンボルにたいしては ‘t’、スペシャルフォームにたいしては‘nil’をリターンすることに注意。 ‘functionp’と異なり、以下の3つの関数はシンボルをそれの関数定義としては _扱いません_。 -- Function: subrp object この関数はOBJECTがビルトイン関数(たとえばLispプリミティブ)なら‘t’を リターンする。 (subrp 'message) ; ‘message’はシンボルであり、 ⇒ nil ; subrオブジェクトではない (subrp (symbol-function 'message)) ⇒ t -- Function: byte-code-function-p object この関数はOBJECTがバイトコード関数なら‘t’をリターンする。たとえば: (byte-code-function-p (symbol-function 'next-line)) ⇒ t -- Function: subr-arity subr この関数はプリミティブSUBRの引数リストに関する情報を提供する。リタ ーン値は‘(MIN . MAX)’というペアである。MINは引数の最小数、MAXは最大 数、または引数‘&rest’を伴う関数ではシンボル‘many’、SUBRがスペシャル フォームならシンボル‘unevalled’となる。 12.2 ラムダ式 ============= ラムダ式(lambda expression)はLispで記述された関数オブジェクトです。以下 は例です: (lambda (x) "Xの双曲線コサインをreturnする" (* 0.5 (+ (exp x) (exp (- x))))) Emacs Lispではこのようなリストは、関数オブジェクトに評価される有効な式で す。 ラムダ式自身は名前をもたない“無名関数(anonymous function)”です。ラム ダ式をこの方法で使用できますが(*note Anonymous Functions::を参照)、“名前 付き関数(named functions)”を作成するためにシンボルに関連付けられる方が一 般的です(*note Function Names::を参照)。これらの詳細に触れる前に以下のサ ブセクションではラムダ式の構成要素と、それらが行うことについて説明します 。 12.2.1 ラムダ式の構成要素 ------------------------- ラムダ式は以下のようなリストです: (lambda (ARG-VARIABLES...) [DOCUMENTATION-STRING] [INTERACTIVE-DECLARATION] BODY-FORMS...) ラムダ式の1番目の要素は常にシンボル‘lambda’です。これはそのリストが関 数を表すことを示します。‘lambda’で関数定義を開始する理由は、別の目的での 使用が意図された他のリストが、意図せずに関数として評価されないようにする ためです。 2番目の要素はシンボル — 引数変数名のリストです。これは“ラムダリスト (lambda list)”と呼ばれます。Lisp関数が呼び出されたとき、引数値はラムダリ スト内の変数と対応付けされます。ラムダリストには、与えられた値にたいする ローカルバインディングが付与されます。*note Local Variables::を参照して ください。 ドキュメント文字列(documentation string)はEmacs Lispのヘルプ機能にた いして、その関数を説明する関数定義に配されたLispの文字列オブジェクトです 。*note Function Documentation::を参照してください。 インタラクティブ宣言(interactive declaration)は、‘(interactive CODE-STRING)’という形式のリストです。これはこの関数が対話的に使用された 場合に引数を提供する方法を宣言します。この宣言をもつ関数は、“コマンド (command)”と呼ばれます。コマンドは‘M-x’を使用したり、キーにバインドして 呼び出すことができます。この方法で呼び出されることを意図しない関数は、イ ンタラクティブ宣言を持つべきではありません。インタラクティブ定義を記述す る方法は、*note Defining Commands::を参照してください。 残りの要素はその関数の“body(本体)” — その関数が処理を行うためのLispコ ード(Lispプログラマーは“評価されるLispフォームのリスト”と言うだろう)です 。この関数からリターンされる値は、bodyの最後の要素によりリターンされる値 です。 12.2.2 単純なラムダ式の例 ------------------------- 以下の例を考えてみてください: (lambda (a b c) (+ a b c)) 以下のように‘funcall’に渡すことにより、この関数を呼び出すことができます: (funcall (lambda (a b c) (+ a b c)) 1 2 3) この呼び出しは変数‘a’に1、‘b’に2、‘c’に3をバインドして、ラムダ式のbodyを 評価します。bodyの評価によってこれら3つの数が加算されて、6が結果として生 成されます。したがってこの関数呼び出しにより6がリターンされます。 以下のように引数は他の関数の結果であってもよいことに注意してください: (funcall (lambda (a b c) (+ a b c)) 1 (* 2 3) (- 5 4)) これは引数‘1’、‘(* 2 3)’、‘(- 5 4)’を左から右に評価します。その後ラムダ 式に引数1、6、1を適用して値8が生成されます。 これらの例が示すように、ローカル変数を作成してそれらに値を与えるフォ ームとして、CARがラムダ式であるようなフォームを使用することができます。 古い時代のLispでは、この方法がローカル変数をバインドして初期化する唯一の 方法でした。しかし現在ではこの目的にはフォーム‘let’を使用するほうが明解 です(*note Local Variables::を参照)。ラムダ式は主に他の関数の引数として 渡される無名関数(*note Anonymous Functions::を参照)として、あるいは名前 つき関数(*note Function Names::を参照)を生成するためにシンボルの関数定義 に格納するために使用されます。 12.2.3 引数リストのその他機能 ----------------------------- シンプルなサンプル関数‘(lambda (a b c) (+ a b c))’は3つの引数変数を指定 しているので、3つの引数で呼び出されなければなりません。引数を2つしか指定 しなかったり4つ指定した場合は、‘wrong-number-of-arguments’エラーとなりま す。 特定の引数を省略できる関数を記述できると便利なこともあります。たとえ ば関数‘substring’は3つの引数 — 文字列、開始インデックス、終了インデック ス — を受け取りますが、3つ目の引数を省略すると、デフォルトでその文字列の LENGTHとなります。関数‘list’や‘+’が行うように、特定の関数にたいして不定 個の引数を指定できると便利なときもあります。 関数が呼び出されるとき省略されるかもしれないオプションの引数を指定す るには、オプションの引数の前にキーワード‘&optional’を含めるだけです。0個 以上の追加引数のリストを指定するには、最後の引数の前にキーワード ‘&rest’を含めます。 したがって引数リストの完全な構文は以下のようになります: (REQUIRED-VARS... [&optional OPTIONAL-VARS...] [&rest REST-VAR]) 角カッコ(square bracket)は‘&optional’と‘&rest’、およびそれらに続く変数が 省略できることを示します。 この関数の呼び出しではREQUIRED-VARSのそれぞれにたいして、実際の引数が 要求されます。0個以上のOPTIONAL-VARSにたいして実際の引数があるかもしれま せんが、ラムダ式が‘&rest’を使用していなければ、その個数を超えて実際の引 数を記述することはできません。‘&rest’が記述されていれば、追加で任意の個 数の実際の引数があるかもしれません。 optionaやrest変数にたいして実際の引数が省略されると、それらのデフォル トは常に‘nil’になります。関数にたいして引数に明示的に‘nil’が使用されたの か、引数が省略されたのかを区別することはできません。しかし関数のbodyが、 ‘nil’を他の有意な値が省略されたと判断することは自由です。‘substring’はこ れを行います。‘substring’の3つ目の引数が‘nil’なら、それは文字列の長さを 使用することを意味します。 Common Lispに関する注意: Common Lispではオプションの引数が省略され たときに使用するデフォルト値を指定できる。Emacs Lispでは、引数が明 示的に渡されたかを調べる‘supplied-p’変数はサポートされない。 例えば引数リストは以下のようになります: (a b &optional c d &rest e) ‘a’と‘b’は最初の2つの実引数となり、これらは必須です。さらに1つまたは2つ の引数が指定された場合、それらは順番に‘c’と‘d’にバインドされます。1つ目 から4つ目の引数の後の引数は、リストにまとめられて‘e’にそのリストがバイン ドされます。2つしか引数が指定されなかったら、‘c’は‘nil’になります。2つか 3つの引数なら、‘d’は‘nil’です。引数が4つ以下なら、‘e’は‘nil’になります。 オプションの引数の後ろに必須の引数を指定する方法はありません — これは 意味を成さないからです。なぜそうなるかは、この例で‘c’がオプションで‘d’が 必須な場合を考えてみてください。実際に3つの引数が与えられたとします。3番 めの引数は何を指定したのでしょうか? この引数はCなのでしょうか、それとも Dに使用されるのでしょうか? 両方の場合が考えられます。同様に‘&rest’引数の 後に、さらに引数(必須またはオプション)をもつことも意味を成しません。 以下に引数リストと、それを正しく呼び出す例をいくつか示します: (funcall (lambda (n) (1+ n)) ; 1つの必須: 1) ; これは正確に1つの引数を要求する ⇒ 2 (funcall (lambda (n &optional n1) ; 1つは必須で、1つはオプション: (if n1 (+ n n1) (1+ n))) ; 1つまたは2つの引数 1 2) ⇒ 3 (funcall (lambda (n &rest ns) ; 1つは必須で、後は残り: (+ n (apply '+ ns))) ; 1つ以上の引数 1 2 3 4 5) ⇒ 15 12.2.4 関数のドキュメント文字列 ------------------------------- ラムダ式はラムダリストの直後に、オプションで“ドキュメント文字列 (documentation string)”をもつことができます。この文字列は、その関数の実 行に影響を与えません。これはコメントの一種ですがLisp機構に内在するシステ ム化されたコメントであり。Emacsのヘルプ機能で使用できます。ドキュメント 文字列にアクセスする方法は、*note Documentation::を参照してください。 たとえその関数があなたのプログラム内だけで呼び出される関数だとしても 、すべての関数にドキュメント文字列を与えるのはよいアイデアです。ドキュメ ント文字列はコメントと似ていますが、コメントより簡単にアクセスできます。 ドキュメント文字列の1行目は、関数自体にもとづくものであるべきです。な ぜなら‘apropos’は、最初の1行目だけを表示するからです。ドキュメント文字列 の1行目は、その関数の目的を要約する1つか2つの完全なセンテンスで構成され るべきです。 ドキュメント文字列の開始は通常、ソースファイル内ではインデントされて いますが、ドキュメント文字列の開始のダブルクォート文字の前にインデントの スペースがあるので、インデントはドキュメント文字列の一部にはなりません。 ドキュメント文字列の残りの行がプログラムソース内で揃うようにインデントす る人がいます。_これは間違いです_。後続の行のインデントは文字列の内部にあ ります。これはソースコード内での見栄えはよくなりますが、ヘルプコマンドで 表示したとき見栄えが悪くなります。 ドキュメント文字列がなぜオプションになるのか不思議に思うかもしれませ ん。なぜならドキュメント文字列の後には必須となる関数の構成要素である bodyが続くからです。文字列を評価するとその文字列自身がリターンされるので 、それがbody内の最後のフォームでない限りなんの効果もありません。したがっ て実際はbodyの1行目とドキュメント文字列で混乱が生じることはありません。 bodyの唯一のフォームが文字列なら、それはリターン値とドキュメントの両方の 役目を果たします。 ドキュメント文字列の最後の行には、実際の関数引数とは異なる呼び出し規 約を指定できます。これは以下のようなテキストを記述します \(fn ARGLIST) そのテキストの後に空行を配置して、テキスト自身は行頭から記述、ドキュメン ト文字列内でこのテキストの後に改行が続かないように記述します(‘\’は Emacsの移動コマンドが混乱するのを避けるために使用する)。この方法で指定さ れた呼び出し規約は、ヘルプメッセージ内で関数の実引数から生成される呼び出 し例と同じ場所に表示されます。 マクロ定義内に記述された引数は、ユーザーがマクロ呼び出しの一部だと考 える方法とは合致しない場合がしばしばあるので、この機能はマクロ定義で特に 有用です。 12.3 関数の命名 =============== シンボルは関数の名前となることができます。これはそのシンボルの“関数セル (function cell”: *note Symbol Components::を参照)が、関数オブジェクト(た とえばラムダ式)を含むときに起こります。するとそのシンボル自身が呼び出し 可能な有効な関数、つまりそのシンボルの関数セルの関数と等価になります。 関数セルの内容はそのシンボルの“関数定義(function definition)”と呼ぶこ ともできます。そのシンボルのかわりにシンボルの関数定義を使う手続きのこと を“シンボル関数インダイレクション(symbol function indirection)”と呼びま す。*note Function Indirection::を参照。与えられたシンボルに関数定義がな ければシンボルの関数セルは“void”と呼ばれ、それを関数として使用することは できません。 実際のところほとんどすべての関数は名前をもち、その名前により参照され ます。ラムダ式を定義することで名前つきのLisp関数を作成、それを関数セル (*note Function Cells::を参照)に置くことができます。しかしより一般的なの は‘defun’スペシャルフォーム(次のセクションで説明)を使う方法です。 *note Defining Functions::を参照してください。 わたしたちが関数に名前を与えるのは、Lisp式内で関数を名前で参照するの が便利だからです。また名前つきの関数は簡単に自分自身を — 再帰的 (recursive)に参照することができます。さらにプリミティブはテキスト的な名 前だけで参照することができます。なぜならプリミティブ関数は入力構文(read syntax)をもたないオブジェクトだからです(*note Primitive Function Type::を 参照)。 関数が一意な名前をもつ必要はありません。与えられた関数オブジェクトは _通常_は1つのシンボルの関数セルだけに存在しますが、これは単に慣習的なも のです。‘fset’を使用すれば関数を複数のシンボルに格納するのは簡単です。そ れらのシンボルはそれぞれ、同じ関数にたいする有効な名前となります。 関数として使用しているシンボルを、変数としても利用できることに注意し てください。シンボルのこれら2つの利用法は独立しており、競合はしません(こ れはSchemaのような他のいくつかのLisp方言には当てはまらない)。 12.4 関数の定義 =============== わたしたちは通常は関数を最初に作成したときに名前を与えます。これは“関数 の定義(defining a function)”と呼ばれ、‘defun’マクロにより行われます。 -- Macro: defun name args [doc] [declare] [interactive] body... ‘defun’は新たなLisp関数を定義する通常の方法である。これは引数リスト ARGS、およびBODYにより与えられるbodyフォームとともに、シンボル NAMEを関数として定義する。NAMEとARGSをクォートする必要はない。 DOCが与えられたら、それはその関数のドキュメント文字列を指定する文字 列であること(*note Function Documentation::を参照)。DECLAREが与えら れたら、それは関数のメタデータを指定する‘declare’フォームであること (*note Declare Form::を参照)。INTERACTIVEが与えられたら、それは関数 が対話的に呼び出される方法を指定する‘interactive’フォームであるこ (*note Interactive Call::を参照)。 ‘defun’のリターン値は定義されていません。 以下にいくつか例を示す: (defun foo () 5) (foo) ⇒ 5 (defun bar (a &optional b &rest c) (list a b c)) (bar 1 2 3 4 5) ⇒ (1 2 (3 4 5)) (bar 1) ⇒ (1 nil nil) (bar) error→ Wrong number of arguments. (defun capitalize-backwards () "Upcase the last letter of the word at point." (interactive) (backward-word 1) (forward-word 1) (backward-char 1) (capitalize-word 1)) 意図せず既存の関数を再定義しないように注意されたい。‘defun’は ‘car’のようなプリミティブ関数でさえ、問い合わせせずに躊躇なく再定義 する。Emacsがこれを妨げることはない。なぜなら関数の再定義は故意に行 われることがあり、そのような意図した再定義を、意図しない再定義と見 分ける方法はがないからである。 -- Function: defalias name definition &optional doc この関数は定義DEFINITION(任意の有効なLisp関数)とともに、シンボル NAMEを関数として定義する。この関数のリターン値は_未定義_。 DOCが非‘nil’なら、それは関数NAMEのドキュメントとなる。それ以外なら DEFINITIONにより提供されるドキュメントが使用される。 内部的には‘defalias’は、通常は定義のセットに‘fset’を使用する。しか しNAMEが‘defalias-fset-function’プロパティーをもつなら、‘fset’を呼 び出すかわりにそれに割り当てられた値を使用する。 ‘defalias’を使う正しい場所は、特定の関数名が正に定義される場所 — 特 にソースファイルがロードされるとき明示的にその名前が出現する場所で ある。これは‘defalias’が‘defun’と同じように、どれが関数を定義するフ ァイルなのか記録するからである(*note Unloading::を参照)。 それとは対象的に他の目的のために関数を操作するプログラムでは、その ような記録を保持しない‘fset’を使用するほうがよいだろう。*note Function Cells::を参照のこと。 ‘defun’や‘defalias’で新たなプリミティブ関数を作成することはできません が、任意の関数定義を変更するのに使用することができ、通常の定義がプリミテ ィブである‘car’や‘x-popup-menu’のような関数でさえ変更することができます 。しかしこれは危険なことです。たとえばLispの完全性を損なうことなく、 ‘car’を再定義するのはほとんど不可能だからです。それほど有名ではない ‘x-popup-menu’のような関数の再定義では、危険は減少しますが、それでも期待 したとおりに機能しないかもしれません。Cコードにそのプリミティブの呼び出 しがあれば、それは直接そのプリミティブのC定義を呼び出すので、シンボル定 義を変更してもそれらに影響はありません。 ‘defsubst’も参照してください。これは‘defun’のように関数を定義して、そ れのインライン展開を処理するようLispコンパイラーに指示します。*note Inline Functions::を参照してください。 かわりにコンパイラーマクロとしてインライン展開されるコードを記述する ことにより関数を定義できます。以下のマクロがこれを可能にします。 -- Macro: define-inline name args [doc] [declare] body... 自身をインライン化するコードを提供することにより、コンパイラーマク ロとして関数NAMEを定義する。この関数は引数リストARGSを受け取り、指 定されたBODYをもつ。 DOCが与えられたなら、それは関数のドキュメント文字列であること(*note Function Documentation::を参照)。DECLAREが与えられたなら、それは関 数のメタデータを指定する‘declare’フォームであること(*note Declare Form::を参照)。 ‘define-inline’で定義された関数は、‘defsubst’や‘defmacro’で定義された マクロにたいして複数の利点をもちます。 − ‘mapcar’に渡すことができる(*note Mapping Functions::を参照)。 − より効率的である。 − 値を格納するための“placeフォーム(place forms)”として使用できる (*note Generalized Variables::を参照)。 − ‘cl-defsubst’より予測可能な方法で振る舞う(*note (cl)Argument Lists::を参照)。 ‘defmacro’と同様に、‘define-inline’でインライン化された関数は、呼び出 し側からダイナミックかレキシカルいずれかのスコーピングルールを継承します 。*note Variable Scoping::を参照してください。 以下のマクロは‘define-inline’で定義された関数のbody内で使用する必要が あります。 -- Macro: inline-quote expression ‘define-inline’にたいしてEXPRESSIONをクォートする。これはバッククォ ート(*note Backquote::を参照)と似ているが、コードをクォートして ‘,’だけを受け入れ、‘,@’は受け入れない。 -- Macro: inline-letevals (bindings...) body... これは‘let’ (*note Local Variables::を参照)と似ているが、BINDINGSで 指定されたようにローカル変数をセットアップして、そのバインディング の効力の下でBODYを評価する。BINDINGSのそれぞれの要素はシンボルか ‘(VAR EXPR)’という形式のリストのいずれかであること。後者ならEXPRを 評価した結果がVARにバインドされる。BINDINGSの末尾には‘nil’、または 引数のリストを保持するシンボルを指定できる。後者の場合は各引数が評 価されて、そのシンボルに結果リストがバインドされる。 -- Macro: inline-const-p expression EXPRESSIONの値が既知なら非‘nil’をリターンする。 -- Macro: inline-const-val expression EXPRESSIONの値をリターンする。 -- Macro: inline-error format &rest args FORMATに応じてARGSをフォーマットしてエラーをシグナルする。 以下は‘define-inline’を使用した例です: (define-inline myaccessor (obj) (inline-letevals (obj) (inline-quote (if (foo-p ,obj) (aref (cdr ,obj) 3) (aref ,obj 2))))) これは以下と等価です (defsubst myaccessor (obj) (if (foo-p obj) (aref (cdr obj) 3) (aref obj 2))) 12.5 関数の呼び出し =================== 関数を定義しただけでは半分しか終わっていません。関数はそれを“呼び出す (call)” — たとえば実行(run)するまでは何も行いません。関数のcallは “invocation”としても知られています。 関数を呼び出すもっとも一般的な方法は、リストの評価によるものです。た とえばリスト‘(concat "a" "b")’を評価することにより、関数‘concat’が引数 ‘"a"’と‘"b"’で呼び出されます。評価については*note Evaluation::を参照して ください。 プログラム内で式としてリストを記述するときは、プログラム内にテキスト でどの関数を呼び出すか、いくつの引数を与えるかを指定します。通常はこれが 行いたいことです。どの関数を呼び出すかを実行時に計算する必要がある場合も あります。これを行うには関数‘funcall’を使用します。実行時にいくつの引数 を渡すか決定する必要があるときは‘apply’を使用します。 -- Function: funcall function &rest arguments ‘funcall’は関数FUNCTIONを引数ARGUMENTSで呼び出して、FUNCTIONがリタ ーンした値をリターンする。 ‘funcall’は関数なので、FUNCTIONを含むすべての引数は‘funcall’の呼び 出し前に評価される。これは呼び出される関数を得るために任意の式を使 用できることを意味している。また‘funcall’がARGUMENTSに記述した式で はなく、その値だけを見ることを意味している。これらの値はFUNCTION呼 び出し中では、2回目は_評価されない_。‘funcall’の処理は関数の通常の 呼び出し手続きと似ており、すでに評価された引数は評価されない。 引数FUNCTIONはLisp関数かプリミティブ関数でなければならない。つまり スペシャルフォームやマクロは、未評価の引数式を与えられたときだけ意 味があるので、指定することはできない。上述したように最初の場所で ‘funcall’がそれらを知らないので、‘funcall’がそれらを提供することは できない。 コマンドの呼び出しに‘funcall’を使用して、それがインタラクティブに呼 び出されたように振る舞うようにする必要があるなら、 ‘funcall-interactively’を使用すること(*note Interactive Call::を参 照)。 (setq f 'list) ⇒ list (funcall f 'x 'y 'z) ⇒ (x y z) (funcall f 'x 'y '(z)) ⇒ (x y (z)) (funcall 'and t nil) error→ Invalid function: # これらの例を‘apply’の例と比較されたい。 -- Function: apply function &rest arguments ‘apply’は関数FUNCTIONを引数ARGUMENTSで呼び出す。これは‘funcall’と同 様だが1つ違いがある。ARGUMENTSの最後はオブジェクトのリストである。 これは1つのリストではなく、個別の引数としてFUNCTIONに渡される。わた したちはこれを、‘apply’がこのリストを“展開(spread)”(個々の要素が引 数となるので)すると言う。 ‘apply’はFUNCTIONを呼び出した結果をリターンする。‘funcall’と同様、 FUNCTIONはLisp関数かプリミティブ関数でなければならない。つまりスペ シャルフォームやマクロは‘apply’では意味をもたない。 (setq f 'list) ⇒ list (apply f 'x 'y 'z) error→ Wrong type argument: listp, z (apply '+ 1 2 '(3 4)) ⇒ 10 (apply '+ '(1 2 3 4)) ⇒ 10 (apply 'append '((a b c) nil (x y z) nil)) ⇒ (a b c x y z) ‘apply’を使用した興味深い例は*note Definition of mapcar::を参照のこ と。 ある関数にたいして、その関数のある引数を特定の値に固定して、他の引数 は実際に呼びだされたときの値にできれば便利なことがあります。関数のいくつ かの引数を固定することは、その関数の“部分適用(partial application)”と呼 ばれます(1)。これの結果は残りの引数をとる新たな関数で、すべての引数を合 わせて元の関数を呼び出します。 Emacs Lispで部分適用を行う方法を示します: -- Function: apply-partially func &rest args この関数は新たな関数をリターンする。この新しい関数は呼びだされたと きにARGS、および呼び出し時に指定された追加の引数から成る引数リスト でFUNCを呼び出す関数である。FUNCにN個の引数を指定できる場合、 ‘M < N’個の引数で‘apply-partially’を呼び出すと、‘N - M’個の新たな関 数を生成する。 以下はビルトイン関数‘1+’が存在しないものとして、‘apply-partially’と 他のビルトイン関数‘+’を使用して‘1+’を定義する例である: (defalias '1+ (apply-partially '+ 1) "Increment argument by one.") (1+ 10) ⇒ 11 引数として関数を受け取ったり、データ構造(特にフック変数やプロパティー リスト)から関数を探す関数はLispでは一般的で、それらは‘funcall’や ‘apply’を使用してそれらの関数を呼び出します。引数として関数をとる関数は 、“ファンクショナル(functional)”と呼ばれるときもあります。 ファンクショナルを呼び出すとき、引数としてno-op関数(何も行わない関数 )を指定できると便利なときがあります。以下に2つの異なるno-op関数を示しま す: -- Function: identity arg この関数はARGをリターンする。副作用はない。 -- Function: ignore &rest args この関数はすべての引数を無視して‘nil’をリターンする。 関数のいくつかはユーザーに可視な“コマンド”で、これらは(通常はキーシー ケンスを介して)対話的に呼び出すことができます。そのようなコマンドは、 ‘call-interactively’関数を使用することにより、対話的に呼びだされたときと 同様に呼び出すことができます。*note Interactive Call::を参照してください 。 ---------- Footnotes ---------- (1) これは“カリー化(currying)”と関連しますが異なる機能です。カーリン グは複数の引数を受け取る関数を、関数チェーンとして呼び出せるような1つの 引数を取る個々の関数に変換するような方法です。 12.6 関数のマッピング ===================== “マップ関数(mapping function)”は与えられた関数(スペシャルフォームやマク ロでは_ない_)を、リストや他のコレクションの各要素に適用します。Emacs Lispにはそのような関数がいくつかあります。このセクションではリストにたい してマッピングを行う‘mapcar’、‘mapc’、‘mapconcat’を説明します。obarray内 のシンボルにたいしてマッピングを行う関数‘mapatoms’は、*note Definition of mapatoms::を参照してください。ハッシュテーブル内のkey/value関係にたい してマッピングを行う関数‘maphash’は、*note Definition of maphash::を参照 してください。 これらのマップ関数は文字テーブル(char-table)には適用されません。なぜ なら文字テーブルは非常に広い範囲の疎な配列だからです。疎な配列であるとい う性質に適う方法で文字テーブルにマッピングするには、関数 ‘map-char-table’を使用します(*note Char-Tables::を参照)。 -- Function: mapcar function sequence ‘mapcar’は関数FUNCTIONをSEQUENCEの各要素にたいして順番に適用して、 その結果をリストでリターンする。 引数SEQUENCEには、文字テーブルを除く任意の種類のシーケンス — つまり リスト、ベクター、ブールベクター、文字列を指定できる。結果は常にリ ストになる。結果の長さはSEQUENCEの長さと同じ。たとえば: (mapcar 'car '((a b) (c d) (e f))) ⇒ (a c e) (mapcar '1+ [1 2 3]) ⇒ (2 3 4) (mapcar 'string "abc") ⇒ ("a" "b" "c") ;; ‘my-hooks’内の各関数を呼び出す (mapcar 'funcall my-hooks) (defun mapcar* (function &rest args) "Apply FUNCTION to successive cars of all ARGS. Return the list of results." ;; リストが消費されていなければ (if (not (memq nil args)) ;; CARに関数を適用する (cons (apply function (mapcar 'car args)) (apply 'mapcar* function ;; 残りの要素のための再帰 (mapcar 'cdr args))))) (mapcar* 'cons '(a b c) '(1 2 3 4)) ⇒ ((a . 1) (b . 2) (c . 3)) -- Function: mapc function sequence ‘mapc’は‘mapcar’と似ているが、FUNCTIONは副作用のためだけに使用され る — つまりFUNCTIONがリターンする値は無視されてリストに収集されない 。‘mapc’は常にSEQUENCEをリターンする。 -- Function: mapconcat function sequence separator ‘mapconcat’はSEQUENCEのそれぞれの要素にFUNCTIONを適用する。結果は文 字のシーケンス(文字列、ベクター、リスト)でなければならず、単一の文 字列に結合されてリターン値となる。‘mapconcat’は結果シーケーンスの各 ペアの間にSEPARATORの文字を挿入する。これも文字列、または文字のベク ターかリストでなければならない。*note Sequences Arrays Vectors::を 参照のこと。 引数FUNCTIONは1つの引数を受け取り文字のシーケンス、すなわち文字列、 ベクター、リストのいずれかをリターンする。引数SEQUENCEは文字テーブ ル以外の任意の種類のシーケンス、すなわちリスト、ベクター、ブールベ クター、または文字列を指定できる。 (mapconcat 'symbol-name '(The cat in the hat) " ") ⇒ "The cat in the hat" (mapconcat (function (lambda (x) (format "%c" (1+ x)))) "HAL-8000" "") ⇒ "IBM.9111" 12.7 無名関数 ============= 関数は通常は‘defun’により定義されて、同時に名前が与えられますが、明示的 にラムダ式を使う — “無名関数(anonymous function)”のほうが便利なときもあ ります。無名関数は名前つき関数が有効な場所ならどこでも有効です。無名関数 は変数や関数の引数に割り当てられることがよくあります。たとえばある関数を リストの各要素に適用する‘mapcar’のFUNCTION引数に渡すかもしれません(*note Mapping Functions::を参照)。現実的な例は*note describe-symbols example::を参照してください。 無名関数として使用するためのラムダ式を定義するとき、原則的にはリスト を構築する任意の手法を使用できます。しかし通常はマクロ‘lambda’、スペシャ ルフォーム‘function’、または入力構文‘#'’を使用するべきです。 -- Macro: lambda args [doc] [interactive] body... このマクロは引数リストARGS、(もしあれば)ドキュメント文字列DOC、(も しあれば)インタラクティブ指定INTERACTIVE、およびBODYで与えられる bodyフォームをもつ無名関数をリターンする。 実際にはこのマクロは‘lambda’フォームを自己クォート(self-quoting)す る。つまりCARが‘lambda’であるようなフォームはそのフォーム自身を取得 する。 (lambda (x) (* x x)) ⇒ (lambda (x) (* x x)) ‘lambda’フォームは別の1つの効果をもつ。このマクロは‘function’(以下 参照)をサブルーチンとして使用することにより、Emacs評価機能(Emacs evaluator)とバイトコンパイラーに、その引数が関数であることを告げる 。 -- Special Form: function function-object このスペシャルフォームは評価を行わずにFUNCTION-OBJECTをリターンする 。この点では‘quote’(*note Quoting::を参照)と似ている。しかし ‘quote’とは異なり、Emacs評価機能とバイトコンパイラーに、これを関数 として使用する意図を告げる役割をもつ。FUNCTION-OBJECTが有効なラムダ 式と仮定すると、これは2つの効果をもつ: • そのコードがバイトコンパイルされているとき、FUNCTION-OBJECTは バイトコード関数オブジェクトにコンパイルされる(*note Byte Compilation::を参照)。 • レキシカルバインドが有効ならFUNCTION-OBJECTはクロージャーに変 換される。*note Closures::を参照のこと。 入力構文‘#'’は‘function’の使用の略記です。以下のフォームは等価です: (lambda (x) (* x x)) (function (lambda (x) (* x x))) #'(lambda (x) (* x x)) 以下の例では3つ目の引数に関数をとる‘change-property’関数を定義して、 その後の‘change-property’で無名関数を渡してこれを使用しています: (defun change-property (symbol prop function) (let ((value (get symbol prop))) (put symbol prop (funcall function value)))) (defun double-property (symbol prop) (change-property symbol prop (lambda (x) (* 2 x)))) ‘lambda’フォームをクォートしていないことに注意してください。 上記のコードをコンパイルすると無名関数もコンパイルされます。リストを クォートすることにより無名関数を構築した場合にはコンパイルはされません。 (defun double-property (symbol prop) (change-property symbol prop '(lambda (x) (* 2 x)))) この場合、無名関数はコンパイルされたコード内のラムダ式に保持されます。バ イトコンパイラーは‘change-property’が関数としての使用を意図していること を知ることができないので、たとえこの関数が関数のように見えるとしても、こ のリストが関数であると決め込むことができません。 12.8 Generic Functions ====================== ‘defun’を使用して定義された関数は、その引数の型と期待する値に関して、ハ ードコードされた一連の仮定をもちます。たとえば数字か数字のリストを引数値 として処理するようにデザインされた関数は、ベクターや文字列のような他の型 の値で呼び出されると失敗したりエラーをシグナルするでしょう。これはその関 数実装が、デザイン時に想定した以外の型に対応しないために発生します。 対照的に“多相型関数(polymorphic functions)”を使用したオブジェクト指向 プログラムでは、同一の名前をもつ一連の特化した関数のそれぞれが、特定の引 数型セットにたいして記述されます。どの関数が実際に呼び出されるかは、実際 の引数の型にもとづいて実行時に決定されます。 Emacsはポリモーフィズム(polymorphism)にたいするサポートを提供します。 他のLisp環境、特にCommon LispとCommon Lispオブジェクトシステム(CLOS)と同 じように、このサポートは“ジェネリック関数(generic functions)”を基礎とし ています。Emacsのジェネリック関数は同一名の使用を含むCLOSに密接にしたが っているので、CLOSの経験があればこのセクションの残りの部分は非常に身近に 感じるでしょう。 ジェネリック関数は、その名前と引数のリストを指定して、(通常は)実装さ れていない抽象操作(abstract operation)を指定します。引数のいくつかの固有 クラスにたいする実際の実装は“メソッド(methods)”により提供され、これは個 別に定義されるべきです。ジェネリック関数を実装するそれぞれのメソッドはジ ェネリック関数としてとして同じ名前をもちますが、そのジェネリック関数で定 義された引数の“スペシャライジング(specializing)”により、メソッドの定義は どの種類の引数を処理可能かを示します。これらの“引数スペシャライザー (argument specializers)”は多少の差はあれ特化したものにできます。たとえば ‘string’型は‘sequence’のようなより一般的な型より特化した型です。 C++やSimulaのようなメッセージベースのOO言語と異なり、ジェネリック関数 を実装するメソッドはクラスに属さずに、それらが実装するジェネリック関数に 属することに注意してください。 ジェネリック関数が呼び出されると、呼び出し側に渡された実際の引数と各 メソッドの引数スペシャライザーを比較することにより、適用可能なメソッドを 呼び出します。その呼び出しの実際の引数がメソッドのスペシャライザーと互換 性があれば、そのメソッドが適用可能です。複数のメソッドが適用可能ならば、 それらは以下で説明する特定のルールにより合成されて、その組み合わせが呼び 出しを処理します。 -- Macro: cl-defgeneric name arguments [documentation] [options-and-methods...] &rest body このマクロは指定したNAMEとARGUMENTSでジェネリック関数を定義する。 BODYが与えられたなら、それは実装のデフォルトを与える。(常に与えられ るべきであるが)DOCUMENTATIONが与えられたなら、それは ‘(:documentation DOCSTRING)’の形式でそのジェネリック関数のドキュメ ント文字列を指定する。オプションのOPTIONS-AND-METHODSは以下のフォー ムのいずれかを指定できる: ‘(declare DECLARATIONS)’ *note Declare Form::で説明するようなdeclareフォーム。 ‘(:argument-precedence-order &rest ARGS)’ このフォームは適用可能なメソッド合成にたいするソート順に影響を 与える。合成において2つのメソッドを比較する際、メソッドの引数 は通常は左から右に試験されて、引数スペシャライザーがより特化し た最初のメソッドが他のメソッドより前になる。このフォームで定義 された順序はそれをオーバーライドして、左から右ではなくこのフォ ームの順に応じて試験される。 ‘(:method [QUALIFIERS...] args &rest body)’ このメソッドは‘cl-defmethod’が行うようなメソッドを定義する。 -- Macro: cl-defmethod name [qualifier] arguments &rest [docstring] body このマクロはNAMEと呼ばれるジェネリック関数の、特定の実装を定義する 。実装コードはBODYで与えられる。もし与えられたらDOCSTRINGはそのメソ ッドのドキュメント文字列である。リストARGUMENTSはジェネリック関数を 実装するすべてのメソッドで等しく、その関数の引数リストとマッチしな ければならず、‘(ARG SPEC)’という形式の引数スペシャライザーを提供す る。ここでARGは‘cl-defgeneric’呼び出しで指定された引数名、SPECは以 下のスペシャライザーフォームのいずれかであること: ‘TYPE’ このスペシャライザーは、引数がTYPEのいずれかであることを要求す る。TYPEは以下で説明する型ヒエラルキーのいずれかの型である。 ‘(eql OBJECT)’ このスペシャライザーは、引数がOBJECTと‘eql’であることを要求す る。 ‘(head OBJECT)’ 引数は‘car’がOBJECTと‘eql’であるようなコンスセルでなければなら ない。 ‘STRUCT-TAG’ 引数は‘cl-defstruct’で定義されるSTRUCT-TAGという名前のクラス (*note (cl)Structures::を参照)、またはその親クラスのいずれかの のインスタンスでなければならない。 かわりに引数スペシャライザーは‘&context (EXPR SPEC)’という形式でも よく、この場合はEXPRの値はSPECが提供するスペシャライザーと互換性が なければならない。SPECには以下で説明する任意のフォームを指定できる 。言い換えると、このスペシャライザーのフォームはメソッドが適用可能 かどうかの判定にたいして、引数のかわりにEXPRの値を使用する。たとえ ば‘&context (overwrite-mode (eql t))’は‘overwrite-mode’がオンのとき だけ互換性のあるメソッドを作成する。 型スペシャライザー‘(ARG TYPE)’は以下のリストの“システム型(system types)”のいずれかを指定できる。親の型が指定されたときは、型がより特 化した子型、孫型、曾孫型、...のいずれかであるような任意の引数も互換 となるだろう。 ‘integer’ 親型: ‘number’。 ‘number’ ‘null’ 親型: ‘symbol’。 ‘symbol’ ‘string’ 親型: ‘array’。 ‘array’ 親型: ‘sequence’。 ‘cons’ 親型: ‘list’。 ‘list’ 親型: ‘sequence’。 ‘marker’ ‘overlay’ ‘float’ 親型: ‘number’。 ‘window-configuration’ ‘process’ ‘window’ ‘subr’ ‘compiled-function’ ‘buffer’ ‘char-table’ 親型: ‘array’。 ‘bool-vector’ 親型: ‘array’。 ‘vector’ 親型: ‘array’。 ‘frame’ ‘hash-table’ ‘font-spec’ ‘font-entity’ ‘font-object’ オプションのQUALIFIERは複数の適用可能なメソッドの合成を許容する。与 えられなければ定義されるメソッドは“primary(主)”メソッドとなり、スペ シャライズされた引数にたいする主要な実装の提供に責任を有する。 QUALIFIERとして以下の値のいずれかを使用して“auxiliary(副)”メソッド も定義できる: ‘:before’ このauxiliaryメソッドはprimaryメソッドの前に実行される。より正 確にはすべての‘:before’メソッドは、より特化したメソッドが最初 になる順で、primaryメソッドの前に実行される。 ‘:after’ このauxiliaryメソッドはprimaryメソッドの後に実行される。より正 確にはすべてのこの類のメソッドは、より特化したメソッドが最後に なる順で、primaryメソッドの後に実行される。 ‘:around’ このauxiliaryメソッドはprimaryメソッドの_代替え_として実行され る。この類のメソッドでもっとも特化したものが他のメソッドより前 に実行される。このようなメソッドは他のauxiliaryメソッドや primaryメソッドを呼び出すために、通常は以下で説明する ‘cl-call-next-method’を使用する。 ‘:extra STRING’ これにより同一のspecializerとqualifierにたいして、STRINGで区別 されるメソッドを追加できる。 ジェネリック関数が呼び出されると、毎回その関数にたいして定義された適 用可能なメソッドを合成することによってその呼び出しを処理する“effectiveメ ソッド(effective method)”を構築します。適用可能なメソッドを探して effectiveメソッドを生成するプロセスは“dispatch”と呼ばれます。その呼び出 しの実際の引数と互換性があるスペシャライザーをもつすべてのメソッドが、互 換性のあるメソッドです。すべての引数がスペシャライザーと互換でなければな らないので、それらはすべてメソッドが適用可能かどうか判定します。複数の引 数に明示的に特化したメソッドを“multiple-dispatchメソッド (multiple-dispatch methods)”と呼びます。 適用可能なメソッドはそれらが合成される順にソートされます。最左の引数 スペシャライザーがもっとも特化したものであるようなメソッドが、順序の最初 になります(上述したように‘cl-defmethod’の一部として ‘:argument-precedence-order’を指定することによりこれをオーバーライドでき る)。そのメソッドのbodyが‘cl-call-next-method’を呼び出すと、もっとも特化 した次のメソッドが実行されます。適用可能な‘:around’メソッドがあれば、そ れらのうちもっとも特化したメソッドが実行されます。そのメソッドはより特化 していない任意の‘:around’メソッドを実行するために、 ‘cl-call-next-method’を呼び出すべきです。次に‘:before’メソッドがその特化 した順に、その後にspecificityメソッドが実行されます。そして後に ‘:after’メソッドがその特化した順と逆順で実行されます。 -- Function: cl-call-next-method &rest args primaryメソッドか‘:around’ auxiliaryメソッド内のレキシカルbody内で 呼び出されると、同じジェネリック関数にたいして適用可能な次のメソッ ドを呼び出す。通常これは引数なしで呼び出され、これは次の適用可能な メソッドを呼び出すメソッドが、呼び出されたときと同じ引数で次のメソ ッドを呼び出すことを意味する。それ以外ならかわりに指定された引数が 使用される。 -- Function: cl-next-method-p primaryメソッドか‘:around’ auxiliaryメソッドのレキシカルbody内から この関数を呼び出したときは、呼び出す次のメソッドが存在すれば非 ‘nil’をリターンする。 12.9 関数セルの内容へのアクセス =============================== シンボルの“関数定義(function definition)”とは、そのシンボルの関数セルに 格納されたオブジェクトのことです。ここではシンボルの関数セルへのアクセス やテスト、それをセットする関数を説明します。 *note Definition of indirect-function::の関数‘indirect-function’も参 照してください。 -- Function: symbol-function symbol これはSYMBOLの関数セル内のオブジェクトをリターンする。これはリター ンされたオブジェクトが本物のの関数であるかチェックしない。 関数セルがvoidならリターン値は‘nil’。関数セルがvoidのときと‘nil’が セットされているときを区別するには‘fboundp’(以下参照)を使用する。 (defun bar (n) (+ n 2)) (symbol-function 'bar) ⇒ (lambda (n) (+ n 2)) (fset 'baz 'bar) ⇒ bar (symbol-function 'baz) ⇒ bar シンボルに何の関数定義も与えていなければ、そのシンボルの関数セルは “void”だと言います。言い換えると、その関数セルはどんなLispオブジェクトも 保持しません。そのシンボルを関数として呼びだそうとすると、Emacsは ‘void-function’エラーをシグナルします。 voidは‘nil’やシンボル‘void’とは異なることに注意してください。シンボル ‘nil’と‘void’はLispオブジェクトであり、他のオブジェクトと同じように関数 セルに格納することができます(これらのシンボルは‘defun’を使用して有効な関 数になることができる)。voidであるような関数セルは、どのようなオブジェク トも含んでいません。 ‘fboundp’を使用して任意のシンボルの関数定義がvoidかどうかテストするこ とができます。シンボルに関数定義を与えた後は、‘fmakunbound’を使用して再 びvoidにすることができます。 -- Function: fboundp symbol この関数はそのシンボルが関数セルにオブジェクトをもっていれば‘t’、そ れ以外は‘nil’をリターンする。これはそのオブジェクトが本物の関数であ るかチェックしない。 -- Function: fmakunbound symbol この関数はSYMBOLの関数セルをvoidにする。そのためこれ以降に関数セル へのアクセスを試みると、‘void-function’エラーが発生する。これは SYMBOLをリターンします(*note Void Variables::の‘makunbound’も参照 )。 (defun foo (x) x) (foo 1) ⇒1 (fmakunbound 'foo) ⇒ foo (foo 1) error→ Symbol's function definition is void: foo -- Function: fset symbol definition この関数はSYMBOLの関数セルにDEFINITIONを格納する。結果は DEFINITION。DEFINITIONは通常は関数か関数の名前であるべきだが、これ はチェックされない。引数SYMBOLは通常のどおり評価される引数である。 この関数は主に関数を定義したり変更して構築を行う、‘defun’や ‘advice-add’のようなものからサブルーチンとして使用される。たとえば キーボードマクロ(*note Keyboard Macros::を参照)のような、関数ではな い関数定義をシンボルに与えるためにも使用することができる: ;; 名前つきのキーボードマクロを定義する。 (fset 'kill-two-lines "\^u2\^k") ⇒ "\^u2\^k" 関数にたいして別の名前を作成するために‘fset’を使いたいなら、かわり に‘defalias’の使用を考慮すること。*note Definition of defalias::を 参照。 12.10 クロージャー ================== *note Variable Scoping::で説明したように、Emacsはオプションで変数のレキ シカルバインディングを有効にできます。レキシカルバインディングが有効な場 合は、(たとえば‘defun’などで)作成したすべての名前つき関数、同様に ‘lambda’マクロや‘function’スペシャルフォーム、‘#'’構文を使用して作成した すべての無名関数(*note Anonymous Functions::を参照)が、自動的に“クロージ ャー(closure)”に変換されます。 クロージャーとはその関数が定義されたどときに存在したレキシカル環境の 記録をあわせもつ関数です。クロージャーが呼び出されたとき、定義内のレキシ カル変数の参照には、その保持されたレキシカル環境が使用されます。他のすべ ての点では、クロージャーは通常の関数と同様に振る舞います。特にクロージャ ーは通常の関数と同じ方法で呼び出すことができます。 クロージャー使用する例は*note Lexical Binding::を参照してください。 現在のところEmacs Lispのクロージャーオブジェクトは、1つ目の要素にシン ボル‘closure’をもつリストとして表現されます。そのリストは2つ目の要素とし てレキシカル環境、残りの要素で引数リストとbodyフォームを表します: ;; レキシカルバインディングが有効 (lambda (x) (* x x)) ⇒ (closure (t) (x) (* x x)) しかし実際にはクロージャーの内部構造は、内部的な実装の詳細と判断される残 りのLisp界を晒け出すものだと言えます。この理由により、クロージャーオブジ ェクトの構造を直接調べたり変更することは推奨しません。 12.11 Emacs Lisp関数にたいするアドバイス ======================================== 他のライブラリーの関数定義を変更する必要があるとき、および ‘FOO-function’oのようなフックやプロセスフィルター(process filter)や、関 数を値としてもつ任意の変数やオブジェクトを変更する必要があるときには、名 前つきの関数には‘fset’か‘defun’、フック変数には‘setq’、プロセスフィルタ ーには‘set-process-filter’のように、適切なセッター関数(setter function)を 使用することができます。しかしこれらが以前の値を完全に破棄してしまうのが 好ましくない場合もあります。 “アドバイス(advice)”機能によって“関数にアドバイス”することにより、既 存の関数定義に機能を追加できます。これは関数全体を再定義するより明解な手 法です。 Emacsのアドバイスシステムは2つのプリミティブセットを提供します。コア となるセットは変数やオブジェクトのフィールドに保持された関数値にたいする ものです(対応するプリミティブは‘add-function’と‘remove-function’)。もう 1つのセットは名前つき関数の最上位のレイヤーとなるものです(主要なプリミテ ィブは‘advice-add’と‘advice-remove’)。 たとえばプロセスPROCのプロセスフィルターの呼び出しをトレースするため に以下を使用できます: (defun my-tracing-function (proc string) (message "Proc %S received %S" proc string)) (add-function :before (process-filter PROC) #'my-tracing-function) これによりそのプロセスの出力は元のプロセスフィルターに渡される前に、 ‘my-tracing-function’に渡されるようになります。‘my-tracing-function’は元 の関数と同じ引数を受け取ります。これを行えば以下のようにしてトレースを行 う前の振る舞いにリバートすることができます。 (remove-function (process-filter PROC) #'my-tracing-function) 同様に‘display-buffer’という名前つきの関数の実行をトレースしたいなら 以下を使用できます: (defun his-tracing-function (orig-fun &rest args) (message "display-buffer called with args %S" args) (let ((res (apply orig-fun args))) (message "display-buffer returned %S" res) res)) (advice-add 'display-buffer :around #'his-tracing-function) ここで‘his-tracing-function’は元の関数のかわりに呼び出されて、元の関 数(に加えてその関数の引数)を引数として受け取るので、必要な場合はそれを呼 び出すことができます。出力を確認し終えたら、以下のようにしてトレースを行 う前の振る舞いにリバートできます: (advice-remove 'display-buffer #'his-tracing-function) 上記の例で使用されている引数‘:before’と‘:around’は、2つの関数が構成さ れる方法を指定します(これを行う多くの方法があるから)。追加された関数も _アドバイス(advice)_と呼ばれます。 12.11.1 アドバイスを操作するためのプリミティブ ---------------------------------------------- -- Macro: add-function where place function &optional props このマクロはPLACE(*note Generalized Variables::を参照)に格納された 関数に、アドバイスFUNCTIONを追加する手軽な方法である。 WHEREは既存の関数のどこ — たとえば元の関数の前や後 — にFUNCTIONが構 成されるかを決定する。2つの関数を構成するために利用可能な方法のリス トは、*note Advice combinators::を参照のこと。 (通常は名前が‘-function’で終わる)変数を変更するときには、FUNCTIONが グローバルに使用されるか、あるいはカレントバッファーだけに使用され るか選ぶことができる。PLACEが単にシンボルならFUNCTIONはPLACEのグロ ーバル値に追加される。PLACEが‘(local SYMBOL)’というフォームなら、 SYMBOLはその変数の名前をリターンする式なので、FUNCTIONはカレントバ ッファーだけに追加される。最後にレキシカル変数を変更したければ、 ‘(var VARIABLE)’を使用する必要があるだろう。 ‘add-function’で追加されたすべての関数は、自動的にプロパティー PROPSの連想リストに加えることができる。現在のところ特別な意味をもつ のは以下の2つのプロパティーのみ: ‘name’ これはアドバイスの名前を与える。この名前は‘remove-function’が 取り除く関数を識別するのに使用できます。これは通常はFUNCTIONが 無名関数のときに使用されます。 ‘depth’ これは複数のアドバイスが与えられたときに、どのようにアドバイス を順番づけるべきかを指定する。depthのデフォルト0。depthが100な ら、そのアドバイスは可能な限りの深さを保持すべきことを、-100な ら最外のアドバイスに留めることを意味する。同じdepthで2つのアド バイスが指定されたら、もっとも最近に追加されたアドバイスが最外 となる。 ‘:before’アドバイスにたいしては、最外(outermost)になるというこ とは、このアドバイスが他のすべてのアドバイスの前、つまり1番目 に実行されることを意味し、最内(innermost)とは元の関数が実行さ れる直前、すなわちこのアドバイスと元の関数の間に実行されるアド バイスは存在しないことを意味する。同様に‘:after’アドバイスにた いしては、最内とは元の関数の直後、つまりこの元の関数とアドバイ スの間に実行される他のアドバイスは存在せず、最外とは他のすべて のアドバイスが実行された直後にこのアドバイスが実行されることを 意味する。‘:override’の最内アドバイスは、元の関数だけをオーバ ーライドし、他のアドバイスはそれに適用されるが、‘:override’の 最外アドバイスは元の関数だけではなく、その他すべての適用済みの アドバイスをも同様にオーバーライドする。 FUNCTIONがインタラクティブでなければ合成された関数は、(もしあれば )元の関数のインタラクティブ仕様(interactive spec)を継承します。それ 以外なら合成された関数はインタラクティブとなりFUNCTIONのインタラク ティブ仕様を使用します。1つ例外があります。FUNCTIONのインタラクティ ブ仕様が(式や文字列ではない)関数なら、元の関数のインタラクティブ仕 様を唯一の引数としてその関数を呼び出して、それが合成された関数のイ ンタラクティブ指定になります。引数として受け取ったインタラクティブ 仕様を解釈するためには‘advice-eval-interactive-spec’を使用します。 注意: FUNCTIONのインタラクティブ仕様は合成された関数に適用され、 FUNCTIONではなく結合された関数の呼び出し規約に従うこと。多くの場合 これらは等しいので差異は生じないが、FUNCTIONの‘:around’、 ‘:filter-args’、‘filter-return’では重要になる。 -- Macro: remove-function place function このマクロはPLACEに格納された関数からFUNCTIONを取り除く。これは ‘add-function’を使用してFUNCTIONがPLACEに追加されたときだけ機能する 。 FUNCTIONはPLACEに追加された関数にたいして、ラムダ式にたいしても機能 するように‘equal’を使用して比較を試みる。これは追加でPLACEに追加さ れた関数の‘name’プロパティーも比較する。これは‘equal’を使用してラム ダ式を比較するより信頼性がある。 -- Function: advice-function-member-p advice function-def ADVICEがすでにFUNCTION-DEF内にあれば非‘nil’をリターンする。上記の ‘remove-function’と同様、実際の関数ADVICEのかわりにアドバイスの ‘name’も使用できる。 -- Function: advice-function-mapc f function-def FUNCTION-DEFに追加されたすべてのアドバイスにたいして、関数Fを呼び出 す。Fは2つの引数 — アドバイス関数とそれのプロパティーで呼びだされる 。 -- Function: advice-eval-interactive-spec spec そのようなインタラクティブ仕様で関数がインタラクティブに呼び出され たようにSPECを評価して、構築された引数のリストに対応するリストをリ ターンする。たとえば‘(advice-eval-interactive-spec "r\nP")’はリージ ョンの境界、カレントプレフィクス引数を含む、3つの要素からなるリスト をリターンする。 12.11.2 名前つき関数にたいするアドバイス ---------------------------------------- アドバイスは名前つき関数やマクロにたいして使用するのが一般的な使い方です 。これは単に‘add-function’を使用して以下のように行うことができます: (add-function :around (symbol-function 'FUN) #'his-tracing-function) しかしかわりに‘advice-add’と‘advice-remove’を使うべきです。この異なる 関数セットは名前つき関数に適用されるアドバイスを操作するためのもので、 ‘add-function’と比較して以下の追加機能があります。まずこれらはマクロとオ ートロードされた関数を扱う方法を知っています。次に‘describe-function’に たいして追加されたアドバイスと同様に、元のドキュメント文字列を維持します 。さらに関数が定義される前でも、アドバイスの追加と削除ができます。 既存の関数全体を再定義せずに既存の呼び出しを変更するために、 ‘advice-add’が有用になります。しかしその関数の既存の呼び出し元は古い振る 舞いを前提としているかもしれず、アドバイスによりその振る舞いが変更された ときに正しく機能しないかもしれないので、これはソー内スのバグにもなり得ま す。アドバイスはデバッグを難しくする可能性もあります。デバッグを行う人は その関数がアドバイスにより変更されたことに気づかなかったり、失念している かもしれません。 これらの理由により、他の方法で関数の振る舞いを変更できない場合に備え るために、アドバイスの使用は控えるべきです。フックを通じて同じことが行え るならフック(*note Hooks::を参照)の使用が望ましい方法です。特定のキーが 行う何かを変更したいだけなら、新しいコマンドを記述して、古いコマンドのキ ーバインドを新しいコマンドにリマップ(*note Remapping Commands::を参照)す るのが、おそらくより良い方法です。特にEmacs自身のソースファイルは、 Emacs内の関数をアドバイスするべきではありません(現在のところこの慣習には いくつかの例外があるが、わたしたちはこれを改善しようと思っている)。 スペシャルフォーム(*note Special Forms::を参照)はアドバイスできません が、マクロは関数と同じ方法でアドバイスできます。もちろんこれはすでにマク ロ展開されたコードには影響しないため、マクロ展開前にアドバイスが確実にイ ンストールされる必要があります。 プリミティブ(*note What Is a Function::を参照)にアドバイスするのは可 能ですが、2つの理由により通常は_行うべきではありません_。1つ目の理由はい くつかのプリミティブがアドバイスのメカニズム内で使用されているため、それ らにたいしてアドバイスを行うと無限再帰が発生するからです。2つ目の理由は 多くのプリミティブがCから直接呼び出されていて、そのような呼び出しはアド バイスを無視するからです。したがってプリミティブにたいしてアドバイスの使 用を控えることにより、ある呼び出しはアドバイスにしたがい(Lispコードから 呼びだされたため)、他の呼び出しではアドバイスにしたがわない(Cコードから 呼び出されたため)という混乱した状況を解決できます。 -- Macro: define-advice symbol (where lambda-list &optional name depth) &rest body このマクロはアドバイスを定義して、それをSYMBOLという名前の関数に追 加する。NAMEがnilか‘symbol@name’という名前の関数なら、そのアドバイ スは無名関数である。他の引数の説明は‘advice-add’を参照のこと。 -- Function: advice-add symbol where function &optional props 名前つき関数SYMBOLにアドバイスFUNCTIONを追加する。WHEREとPROPSは ‘add-function’(*note Core Advising Primitives::を参照)のときと同じ 意味をもつ。 -- Function: advice-remove symbol function 名前つき関数SYMBOLからアドバイスFUNCTIONを取り除く。FUNCTIONにアド バイスの‘name’を指定することもできる。 -- Function: advice-member-p function symbol 名前つき関数SYMBOL内にすでにアドバイスFUNCTIONがあれば非‘nil’をリタ ーンする。FUNCTIONにアドバイスの‘name’を指定することもできる。 -- Function: advice-mapc function symbol 名前つき関数SYMBOLにすでに追加されたすべての関数にたいして FUNCTIONを呼び出す。FUNCTIONはアドバイス関数とそのプロパティーとい う2つの引数で呼び出される。 12.11.3 アドバイスの構築方法 ---------------------------- 以下は‘add-function’と‘advice-add’のWHERE引数に可能な値であり、そのアド バイスFUNCTIONと元の関数が構成される方法を指定します。 ‘:before’ 古い関数の前にFUNCTIONを呼び出す。関数は両方とも同じ引数を受け取り 、2つの関数の結合のリターン値は古い関数のリターン値である。より正確 に言うと2つの関数の結合は以下のように振る舞う: (lambda (&rest r) (apply FUNCTION r) (apply OLDFUN r)) ‘(add-function :before FUNVAR FUNCTION)’はノーマルフックにたいする ‘(add-hook 'HOOKVAR FUNCTION)’のような1関数のフックと同等。 ‘:after’ 古い関数の後にFUNCTIONを呼び出す。関数は両方とも同じ引数を受け取り 、2つの関数の結合のリターン値は古い関数のリターン値である。より正確 に言うと2つの関数の結合は以下のように振る舞う: (lambda (&rest r) (prog1 (apply OLDFUN r) (apply FUNCTION r))) ‘(add-function :after FUNVAR FUNCTION)’はノーマルフックにたいする ‘(add-hook 'HOOKVAR FUNCTION 'append)’のような1関数のフックと同等。 ‘:override’ これは古い関数を新しい関数に完全に置き換える。もちろん ‘remove-function’を呼び出した後に古い関数が復元される。 ‘:around’ 古い関数のかわりにFUNCTIONを呼び出すが、古い関数はFUNCTIONの追加の 引数になる。これはもっとも柔軟な結合である。たとえば古い関数を異な る引数で呼び出したり、複数回呼び出したり、letバインディングで呼び出 したり、あるときは古い関数に処理を委譲し、またあるときは完全にオー バーライドすることが可能になる。より正確に言うと2つの関数の結合は以 下のように振る舞う: (lambda (&rest r) (apply FUNCTION OLDFUN r)) ‘:before-while’ 古い関数の前にFUNCTIONを呼び出し、FUNCTIONが‘nil’をリターンしたら古 い関数を呼び出さない。関数は両方とも同じ引数を受け取り、2つの関数の 結合のリターン値は古い関数のリターン値である。より正確に言うと2つの 関数の結合は以下のように振る舞う: (lambda (&rest r) (and (apply FUNCTION r) (apply OLDFUN r))) ‘(add-function :before-while FUNVAR FUNCTION)’は ‘run-hook-with-args-until-failure’を通じてHOOKVARが実行されたときの ‘(add-hook 'HOOKVAR FUNCTION)’のような1関数のフックと同等。 ‘:before-until’ 古い関数の前にFUNCTIONを呼び出し、FUNCTIONが‘nil’をリターンした場合 だけ古い関数を呼び出す。より正確に言うと2つの関数の結合は以下のよう に振る舞う: (lambda (&rest r) (or (apply FUNCTION r) (apply OLDFUN r))) ‘(add-function :before-until FUNVAR FUNCTION)’は ‘run-hook-with-args-until-success’を通じてHOOKVARが実行されたときの ‘(add-hook 'HOOKVAR FUNCTION)’のような1関数のフックと同等。 ‘:after-while’ 古い関数が非‘nil’をリターンした場合だけ、古い関数の後にFUNCTIONを呼 び出す。関数は両方とも同じ引数を受け取り、2つの関数の結合のリターン 値はFUNCTIONのリターン値である。より正確に言うと2つの関数の結合は以 下のように振る舞う: (lambda (&rest r) (and (apply OLDFUN r) (apply FUNCTION r))) ‘(add-function :after-while FUNVAR FUNCTION)’は ‘run-hook-with-args-until-failure’を通じてHOOKVARが実行されたときの ‘(add-hook 'HOOKVAR FUNCTION 'append)’のような1関数のフックと同等。 ‘:after-until’ 古い関数が‘nil’をリターンした場合だけ、古い関数の後にFUNCTIONを呼び 出す。より正確に言うと2つの関数の結合は以下のように振る舞う: (lambda (&rest r) (or (apply OLDFUN r) (apply FUNCTION r))) ‘(add-function :after-until FUNVAR FUNCTION)’は ‘run-hook-with-args-until-success’を通じてHOOKVARが実行されたときの ‘(add-hook 'HOOKVAR FUNCTION 'append)’のような1関数のフックと同等。 ‘:filter-args’ 最初にFUNCTIONを呼び出し、その結果(リスト)を新たな引数として古い関 数に渡す。より正確に言うと2つの関数の結合は以下のように振る舞う: (lambda (&rest r) (apply OLDFUN (funcall FUNCTION r))) ‘:filter-return’ 最初に古い関数を呼び出し、その結果をFUNCTIONに渡す。より正確に言う と2つの関数の結合は以下のように振る舞う: (lambda (&rest r) (funcall FUNCTION (apply OLDFUN r))) 12.11.4 古いdefadviceを使用するコードの改良 ------------------------------------------- 多くのコードは古い‘defadvice’メカニズムを使用しており、これらの大半は ‘advice-add’によって陳腐化しました。‘advice-add’の実装とセマンティックは 非常にシンプルです。 古いアドバイスは以下のようなものです: (defadvice previous-line (before next-line-at-end (&optional arg try-vscroll)) "Insert an empty line when moving up from the top line." (if (and next-line-add-newlines (= arg 1) (save-excursion (beginning-of-line) (bobp))) (progn (beginning-of-line) (newline)))) 新しいアドバイスメカニズムを使用すれば、これを通常の関数に変換できま す: (defun previous-line--next-line-at-end (&optional arg try-vscroll) "Insert an empty line when moving up from the top line." (if (and next-line-add-newlines (= arg 1) (save-excursion (beginning-of-line) (bobp))) (progn (beginning-of-line) (newline)))) これが実際の‘previous-line’を変更しないことは明確です。古いアドバイス には以下が必要です: (ad-activate 'previous-line) 一方、新しいアドバイスメカニズムでは以下が必要です: (advice-add 'previous-line :before #'previous-line--next-line-at-end) ‘ad-activate’はグローバルな効果をもつことに注意してください。これは指 定された関数にたいして、アドバイスのすべてを有効にします。特定のアドバイ スだけをアクティブ、または非アクティブにしたいなら、‘ad-enable-advice’か ‘ad-disable-advice’でアドバイスを_有効_か_無効_にする必要があります。新 しいメカニズムではこの区別はなくなりました。 以下のようなaroundのアドバイスがあるとします: (defadvice foo (around foo-around) "Ignore case in `foo'." (let ((case-fold-search t)) ad-do-it)) (ad-activate 'foo) これは以下のように変換できます: (defun foo--foo-around (orig-fun &rest args) "Ignore case in `foo'." (let ((case-fold-search t)) (apply orig-fun args))) (advice-add 'foo :around #'foo--foo-around) アドバイスの_クラス_について、新たな‘:before’は古い‘before’は完全に等 価ではないことに注意してください。なぜなら古いアドバイス内では、(たとえ ば‘ad-set-arg’を使って)その関数の引数を変更でき、それは元の関数が参照す る引数値に影響します。しかし新しい‘:before’は、‘setq’を通じてアドバイス 内の引数を変更して、その変更は元の関数からの参照に影響しません。この振る 舞いにもとづいて‘before’アドバイスを移行するときは、代わりにそれを新たな アドバイス‘:around’か‘:filter-args’に変更する必要があるでしょう。 同様に古い‘after’アドバイスは、‘ad-return-value’を変更することにより リターン値を変更できますが、新しい‘:after’は変更できないので、そのような ‘after’を移行するときは、かわりにそれらを新しいアドバイス‘:around’か ‘:filter-return’に変更する必要があるでしょう。 12.12 関数を陳腐と宣言する ========================== 名前つき関数を“陳腐化している(obsolete)”とマークすることができます。これ はその関数が将来のある時点で削除されるかもしれないことを意味します。陳腐 化しているとマークされた関数を含むコードをバイトコンパイルしたとき、 Emacsは警告を発します。またその関数のヘルプドキュメントは表示されなくな ります。他の点では陳腐化した関数は他の任意の関数と同様に振る舞います。 関数を陳腐化しているとマークするもっとも簡単な方法は、その関数の ‘defun’定義に‘(declare (obsolete ...))’を配置することです。*note Declare Form::を参照してください。かわりに以下で説明している‘make-obsolete’関数 を使うこともできます。 ‘make-obsolete’を使用してマクロ(*note Macros::を参照)を陳腐化している とマークすることもできます。これは関数のときと同じ効果をもちます。関数や マクロにたいするエイリアスも、陳腐化しているとマークできます。これはエイ リアス自身をマークするのであって、名前解決される関数やマクロにたいしてで はありません。 -- Function: make-obsolete obsolete-name current-name &optional when この関数はOBSOLETE-NAMEを陳腐化しているとマークする。 OBSOLETE-NAMEには関数かマクロを命名するシンボル、または関数やマクロ にたいするエイリアスを指定する。 CURRENT-NAMEがシンボルならOBSOLETE-NAMEのかわりにCURRENT-NAMEの使用 を促す警告メッセージになる。CURRENT-NAMEがOBSOLETE-NAMEのエイリアス である必要はない。似たような機能をもつ別の関数かもしれない。 CURRENT-NAMEには警告メッセージとなる文字列も指定できる。メッセージ は小文字で始まりピリオドで終わること。‘nil’も指定でき、この場合には 警告メッセージに追加の詳細は提供されない。 WHENが与えられたら、それは最初にその関数が陳腐化する時期を示す文字 列 — たとえば日付やリリース番号を指定する。 -- Macro: define-obsolete-function-alias obsolete-name current-name &optional when doc この便利なマクロは関数OBSOLETE-NAMEを陳腐化しているとマークして、そ れを関数CURRENT-NAMEのエイリアスにする。これは以下と等価: (defalias OBSOLETE-NAME CURRENT-NAME DOC) (make-obsolete OBSOLETE-NAME CURRENT-NAME WHEN) 加えて陳腐化した関数にたいする特定の呼び出し規約をマークできます。 -- Function: set-advertised-calling-convention function signature when この関数はFUNCTIONを呼び出す正しい方法として、引数リストSIGNATUREを 指定する。これによりEmacs Lispプログラムが他の方法でFUNCTIONを呼び 出していたら、Emacsのバイトコンパイラーが警告を発する(それでもコー ドはバイトコンパイルされる)。WHENにはその変数が最初に陳腐化するとき を示す文字列(通常はバージョン番号)を指定する。 たとえば古いバージョンのEmacsでは、‘sit-for’には以下のように3つの引 数を指定していた (sit-for seconds milliseconds nodisp) しかしこの方法による‘sit-for’の呼び出しは陳腐化していると判断される (*note Waiting::を参照)。以下のように古い呼び出し規約は推奨されない : (set-advertised-calling-convention 'sit-for '(seconds &optional nodisp) "22.1") 12.13 インライン関数Inline Functions ==================================== “インライン関数(inline function)”は関数と同様に機能しますが、1つ例外があ ります。その関数の呼び出しがバイトコンパイルされると(*note Byte Compilation::を参照)、その関数の定義が呼び出し側に展開されます。インライ ン関数を定義するには、‘defun’のかわりに‘defsubst’を使用します。 -- Macro: defsubst name args [doc] [declare] [interactive] body... このマクロはインライン関数を定義する。マクロの構文は‘defun’とまった く同じ(*note Defining Functions::を参照)。 関数をインラインにすることにより、その関数の呼び出しが高速になる場合 があります、が欠点もありその1つは柔軟性の減少です。その関数の定義を変更 すると、すでにインライン化された呼び出しは、リコンパイルを行うまで古い定 義を使用することになります。 もう1つの欠点は、大きな関数をインライン化することにより、コンパイルさ れたコードのファイル上およびメモリー上のサイズが増大することです。スピー ド面でのインライン化の有利性は小さい関数で顕著なので、一般的に大きな関数 をインライン化するべきではありません。 インライン関数はデバッグ、トレース、アドバイス(*note Advising Functions::を参照)に際してうまく機能しません。デバッグの容易さと関数の再 定義の柔軟さはEmacsの重要な機能なので、スピードがとても重要であって ‘defun’の使用が実際に性能の面で問題となるのか検証するためにすでにコード をチューニングしたのでなければ、たとえその関数が小さくてもインライン化す るべきではありません。 インライン関数を定義した後そのインライン展開はマクロ同様、同じファイ ル内の後の部分で処理されます。 ‘defsubst’を使用してインライン関数が実行するのと同じコードに展開され るマクロ(*note Macros::を参照)を定義することは可能です。しかし式内でのマ クロの直接の使用には制限があります — ‘apply’、‘mapcar’などでマクロを呼び 出すことはできません。通常の関数からマクロへの変換には、そのための余分な 作業が必要になります。通常の関数をインライン関数に変換するのは簡単です。 ‘defun’を‘defsubst’に置き換えるだけです。インライン関数の引数はそれぞれ 正確に1回評価されるので、マクロのときのようにbodyで引数を何回使用するか 心配する必要はありません。 ‘defsubst’の代替えとして、完全なコンパイラーマクロを通じて関数を定義 する‘define-inline’を使用できます、*note define-inline: Defining Functions.を参照してください。 12.14 ‘declare’フォーム ======================= ‘declare’(宣言)は特別なマクロで、関数やマクロにメタプロパティーを追加す るために使用できます。たとえば陳腐化しているとマークしたり、Emacs Lispモ ード内の特別なインデント規約を与えることができます。 -- Macro: declare specs... このマクロは引数を無視して‘nil’として評価されるので、実行時の効果は ない。しかし‘defun’や‘defsubst’(*note Defining Functions::を参照)、 または‘defmacro’マクロ(*note Defining Macros::を参照)の定義の DECLARE引数に‘declare’フォームがある場合は、SPECSで指定されたプロパ ティーを関数またはマクロに追加します。これは‘defun’、‘defsubst’、 ‘defmacro’により特別に処理される。 SPECS内の各要素は‘(PROPERTY ARGS...)’というフォームをもつこと。また それあをクォートしないこと。これらは以下の効果をもつ: ‘(advertised-calling-convention SIGNATURE WHEN)’ これは‘set-advertised-calling-convention’(*note Obsolete Functions::を参照)の呼び出しと同じように振る舞う。SIGNATUREに はその関数(またはマクロ)にたいする正しい引数リスト、WHENには古 い引数リストが最初に陳腐化する時期を示す文字列を指定する。 ‘(debug EDEBUG-FORM-SPEC)’ これはマクロだけに有効である。Edebugでそのマクロ入ったときに、 EDEBUG-FORM-SPECを使用する。*note Instrumenting Macro Calls::を 参照のこと。 ‘(doc-string N)’ 自身が関数やマクロ、変数のようなエンティティーを定義するために 使用されるような関数やマクロを定義するときにこれが使用される。 これはN番目の引数というこを示し、もしそれがあれば、それはドキ ュメント文字列とみなされる。 ‘(indent INDENT-SPEC)’ この関数(かマクロ)にたいするインデント呼び出しは、 INDENT-SPECにしたがう。これは関数でも機能するが、通常はマクロ で使用される。*note Indenting Macros::を参照のこと。 ‘(interactive-only VALUE)’ その関数の‘interactive-only’プロパティにVALUEをセットする。 *note The interactive-only property::を参照のこと。 ‘(obsolete CURRENT-NAME WHEN)’ ‘make-obsolete’(*note Obsolete Functions::を参照)と同様に、関 数(かマクロ)が陳腐化しているとマークする。CURRENT-NAMEにはシン ボル(かわりにこのシンボルを使うことを促す警告メッセージになる )、文字列(警告メッセージを指定)、または‘nil’(警告メッセージに は追加の詳細が含まれない)を指定すること。WHENにはその関数(かマ クロ)が最初に陳腐化する時期を示す文字列を指定すること。 ‘(compiler-macro EXPANDER)’ これは関数だけに使用でき、最適化関数(optimization function)と してEXPANDERを使用するようコンパイラーに告げる。‘(FUNCTION ARGS...)’のようなその関数への呼び出しフォームに出会うと、マク ロ展開機能(macro expander)はARGS...と同様のフォームで EXPANDERを呼び出す。EXPANDERはその関数呼び出しのかわりに使用す るための新しい式、または変更されていないフォーム(その関数呼び 出しを変更しないことを示す)のどちらかをリターンすることができ る。EXPANDERにはシンボルかフォーム‘(lambda (ARG) BODY)’を指定 できる。フォームならARGは元の関数呼び出し式を保持して、その関 数の形式に適う引数を使用することにより、その関数にたいする(評 価されていない)引数にアクセスができる。 ‘(gv-expander EXPANDER)’ EXPANDERが‘gv-define-expander’と同様、ジェネリック変数としてマ クロ(か関数)にたいする呼び出しを処理する関数であることを宣言す る。EXPANDERはシンボルかフォーム‘(lambda (ARG) BODY)’を指定で きる。フォームなら、その関数は追加でそのマクロ(か関数)の引数に アクセスできる。 ‘(gv-setter SETTER)’ SETTERがジェネリック変数としてマクロ(か関数)にたいする呼び出し を処理する関数であることを宣言する。SETTERはシンボルかフォーム を指定できる。シンボルなら、そのシンボルは ‘gv-define-simple-setter’に渡される。フォームなら‘(lambda (ARG) BODY)’という形式で、その関数は追加でマクロ(か関数)の引数 にアクセスでき、それは‘gv-define-setter’に渡される。 12.15 コンパイラーへの定義済み関数の指示 ======================================== あるファイルをバイトコンパイルするとき、コンパイラーが知らない関数につい て警告が生成されるときがあります(*note Compiler Errors::を参照)。実際に 問題がある場合もありますが、問題となっている関数がそのコードの実行時にロ ードされる他のファイルで定義されている場合が通常です。たとえば以前は ‘fortran.el’をバイトコンパイルすると、以下のような警告が出ていました: In end of data: fortran.el:2152:1:Warning: the function ‘gud-find-c-expr’ is not known to be defined. 実際のところ‘gud-find-c-expr’はFortranモードが使用する ‘gud-find-expr-function’のローカル値(GUDからのコールバック)の中だけで使 用されていて、呼び出されるとGUD関数がロードされます。そのような警告が実 際には問題を示さないことを知っているときには、警告を抑制したほうがよいで しょう。そうすれば実際に問題があることを示す新しい警告の識別性が良くなり ます。‘declare-function’を使用してこれを行うことができます。 必要なのは問題となっている関数を最初に使用する前に ‘declare-function’命令を追加するだけです: (declare-function gud-find-c-expr "gud.el" nil) これは‘gud-find-c-expr’が‘gud.el’(‘.el’は省略可)の中で定義されている ことを告げます。コンパイラーは関数がそのファイルでそれが実際に定義されて いるとみなして、チェックを行いません。 3つ目の引数はオプションで‘gud-find-c-expr’の引数リストを指定します。 この例では引数はありません(‘nil’と値が未指定なのは異なる)。それ以外なら ‘(file &optional overwrite)’のようになります。引数リストを指定する必要は ありませんが、指定すればコンパイラーはその呼び出しが宣言と合致するかチェ ックできます。 -- Macro: declare-function function file &optional arglist fileonly バイトコンパイラーにたいして引数ARGLISTを受け取るFUNCTIONが定義され ていて、その定義はFILEにあるとみなすように告げる。FILEONLYが非 ‘nil’なら、FILEが存在することだけをチェックして実際のFUNCTIONの定義 はチェックしないことを意味する。 これらの関数が‘declare-function’が告げる場所で実際に宣言されているか どうかを検証するには、‘check-declare-file’を使用して1つのソースファイル 中のすべての‘declare-function’呼び出しをチェックするか、 ‘check-declare-directory’を使用して特定のディレクトリー配下のすべてのフ ァイルをチェックする。 これらのコマンドは、‘locate-library’で使用する関数の定義を含むはずの ファイルを探す。ファイルが見つからなければ、これらのコマンドは ‘declare-function’の呼び出しを含むファイルがあるディレクトリーからの相対 ファイル名に、定義ファイル名を展開する。 ‘.c’や‘.m’で終わるファイル名を指定することにより、プリミティブ関数を 指定することもできる。これが有用なのは特定のシステムだけで定義されるプリ ミティブを呼び出す場合だけである。ほとんどのプリミティブは常に定義されて いるので、それらについて警告を受け取ることはありえないはずである。 あるファイルがオプションとして外部のパッケージの関数を使う場合もある 。‘declare-function’命令内のファイル名のプレフィクスを‘ext:’にすると、そ のファイルが見つかった場合はチェックして、見つからない場合はエラーとせず にスキップする。 ‘check-declare’が理解しない関数定義もいくつか存在する(たとえば ‘defstruct’やその他いくつかのマクロ)。そのような場合は ‘declare-function’のFILEONLY引数に非‘nil’を渡すことができる。これはファ イルの存在だけをチェックして、その関数の実際の定義はチェックしないことを 意味する。これを行うなら引数リストを指定する必要はないが、ARGLIST引数に は‘t’をセットする必要があることに注意(なぜなら‘nil’は引数リストが指定さ れなかったという意味ではなく空の引数リストを意味するため)。 12.16 安全に関数を呼び出せるかどうかの判断 ========================================== SESのようないくつかのメジャーモードは、ユーザーファイル内に格納された関 数を呼び出します(SESの詳細は*note (ses)Top::を参照)。 ユーザーファイルは 素性があやふやな場合があります — 初対面の人から受け取ったスプレッドシー トかもしれず、会ったことのない誰かから受け取ったeメールかもしれません。 そのためユーザーファイルに格納されたソースコードの関数を呼び出すのは、そ れが安全だと決定されるすまでは危険です。 -- Function: unsafep form &optional unsafep-vars FORMが“安全(safe)”なLisp式なら‘nil’、危険ならなぜその式が危険かもし れないのか説明するリストをリターンする。引数UNSAFEP-VARSは、この時 点で一時的なバインドだと判っているシンボルのリスト。これは主に内部 的な再帰呼び出しで使用される。カレントバッファーは暗黙の引数になり 、これはバッファーローカルなバインディングのリストを提供する。 高速かつシンプルにするために、‘unsafep’は非常に軽量な分析を行うので、 実際には安全な多くのLisp式を拒絶します。安全ではない式にたいして ‘unsafep’が‘nil’をリターンするケースは確認されていません。しかし安全な Lisp式は‘display’プロパティーと一緒に文字列をリターンでき、これはその文 字列がバッファーに挿入された後に実行される、割り当てられたLisp式を含むこ とができます。割り当てられた式はウィルスかもしれません。安全であるために はバッファーへ挿入する前に、ユーザーコードで計算されたすべての文字列から プロパティーを削除しなければなりません。 12.17 関数に関するその他トピック ================================ 以下のテーブルは関数呼び出しと関数定義に関連したことを行ういくつかの関数 です。これらは別の場所で説明されているので、ここではクロスリファレンスを 提供します。 ‘apply’ *note Calling Functions::を参照のこと。 ‘autoload’ *note Autoload::を参照のこと。 ‘call-interactively’ *note Interactive Call::を参照のこと。 ‘called-interactively-p’ *note Distinguish Interactive::を参照のこと。 ‘commandp’ *note Interactive Call::を参照のこと。 ‘documentation’ *note Accessing Documentation::を参照のこと。 ‘eval’ *note Eval::を参照のこと。 ‘funcall’ *note Calling Functions::を参照のこと。 ‘function’ *note Anonymous Functions::を参照のこと。 ‘ignore’ *note Calling Functions::を参照のこと。 ‘indirect-function’ *note Function Indirection::を参照のこと。 ‘interactive’ *note Using Interactive::を参照のこと。 ‘interactive-p’ *note Distinguish Interactive::を参照のこと。 ‘mapatoms’ *note Creating Symbols::を参照のこと。 ‘mapcar’ *note Mapping Functions::を参照のこと。 ‘map-char-table’ *note Char-Tables::を参照のこと。 ‘mapconcat’ *note Mapping Functions::を参照のこと。 ‘undefined’ *note Functions for Key Lookup::を参照のこと。 13 マクロ ********* “マクロ(macros)”により新たな制御構造や、他の言語機能の定義を可能にします 。マクロは関数のように定義されますが、値の計算方法を指定するかわりに、値 を計算する別のLisp式を計算する方法を指示します。わたしたちはこの式のこと をマクロの“展開(expansion)”と呼んでいます。 マクロは関数が行うように引数の値を処理するのではなく、引数にたいする 未評価の式を処理することによって、これを行うことができます。したがってマ クロは、これらの引数式かその一部を含む式を構築することができます。 て通常の関数が行えることをマクロを使用して行う場合、単にそれが速度面 の理由ならばかわりにインライン関数の使用を考慮してください。*note Inline Functions::を参照してください。 13.1 単純なマクロの例 ===================== Cの‘++’演算子のように、変数の値をインクリメントするためのLisp構造を定義 したいとしましょう。‘(inc x)’のように記述すれば、‘(setq x (1+ x))’という 効果を得たいとします。以下はこれを行うマクロ定義です: (defmacro inc (var) (list 'setq var (list '1+ var))) これを‘(inc x)’のように呼び出すと、引数VARはシンボル‘x’になります — 関 数のときのように‘x’の_値ではありません_。このマクロのbodyはこれを展開の 構築に使用して、展開形は‘(setq x (1+ x))’になります。マクロが一度この展 開形をリターンするとLispはそれを評価するので、‘x’がインクリメントされま す。 -- Function: macrop object この述語はその引数がマクロかどうかテストして、もしマクロなら‘t’、そ れ以外は‘nil’をリターンする。 13.2 マクロ呼び出しの展開 ========================= マクロ呼び出しは関数の呼び出しと同じ外観をもち、マクロの名前で始まるリス トで表されます。そのリストの残りの要素はマクロの引数になります。 マクロ呼び出しの評価は1つの重大な違いを除いて、関数の評価と同じように 開始されます。重要な違いとはそのマクロの引数はマクロ呼び出し内で実際の式 として現れます。これらの引数はマクロ定義に与えられる前には評価されません 。対象的に関数の引数はその関数の呼び出しリストの要素を評価した結果です。 こうして得た引数を使用して、Lispは関数呼び出しのようにマクロ定義を呼 び出します。マクロの引数変数はマクロ呼び出しの引数値にバインドされるか、 a ‘&rest’引数の場合は引数地のリストになります。そしてそのマクロのbodyが 実行されて、関数bodyが行うようにマクロbodyの値をリターンします。 マクロと関数の2つ目の重要な違いは、マクロのbodyからリターンされる値が 代替となるLisp式であることで、これはマクロの“展開(expansion)”としても知 られています。Lispインタープリターはマクロから展開形が戻されると、すぐに その展開形の評価を行います。 展開形は通常の方法で評価されるので、もしかしたらその展開形は他のマク ロの呼び出しを含むかもしれません。一般的ではありませんが、もしかすると同 じマクロを呼び出すかもしれません。 EmacsはコンパイルされていないLispファイルをロードするときに、マクロの 展開を試みることに注意してください。これは常に利用可能ではありませんが、 もし可能ならそれ以降の実行の速度を改善します。*note How Programs Do Loading::を参照してください。 ‘macroexpand’を呼び出すことにより、与えられたマクロ呼び出しにたいする 展開形を確認することができます。 -- Function: macroexpand form &optional environment この関数はそれがマクロ呼び出しならFORMを展開する。結果が他のマクロ 呼び出しなら、結果がマクロ呼び出しでなくなるまで順番に展開を行う。 これが‘macroexpand’からリターンされる値になる。FORMがマクロ呼び出し で開始されなければ、与えられたFORMをそのままリターンする。 ‘macroexpand’は、(たとえいくつかのマクロ定義がそれを行っているとし ても)FORMの部分式(subexpression)を調べないことに注意。たとえ部分式 自身がマクロ呼び出しでも、‘macroexpand’はそれらを展開しない。 関数‘macroexpand’はインライン関数の呼び出しを展開しない。なぜならイ ンライン関数の呼び出しは、通常の関数呼び出しと比較して理解が難しい 訳ではないので、通常はそれを行う必要がないからである。 ENVIRONMENTが与えられたら、それはそのとき定義されているマクロをシャ ドーするマクロのalistを指定する。バイトコンパイルではこの機能を使用 している。 (defmacro inc (var) (list 'setq var (list '1+ var))) (macroexpand '(inc r)) ⇒ (setq r (1+ r)) (defmacro inc2 (var1 var2) (list 'progn (list 'inc var1) (list 'inc var2))) (macroexpand '(inc2 r s)) ⇒ (progn (inc r) (inc s)) ; ここでは‘inc’は展開されない -- Function: macroexpand-all form &optional environment ‘macroexpand-all’は‘macroexpand’と同様にマクロを展開するが、ドップ レベルだけではなくFORM内のすべてのマクロを探して展開する。展開され たマクロがなければリターン値はFORMと‘eq’になる。 上記‘macroexpand’で使用した例を‘macroexpand-all’に用いると、 ‘macroexpand-all’が‘inc’に埋め込まれた呼び出しの展開を_行う_ことを 確認できる (macroexpand-all '(inc2 r s)) ⇒ (progn (setq r (1+ r)) (setq s (1+ s))) -- Function: macroexpand-1 form &optional environment この関数は‘macroexpand’のようにマクロを展開するが、展開の1ステップ だけを行う。結果が別のマクロ呼び出しなら‘macroexpand-1’はそれを展開 しない。 13.3 マクロとバイトコンパイル ============================= なぜわざわざマクロにたいする展開形を計算して、その後に展開形を評価する手 間をかけるのか、不思議に思うかもしれません。なぜマクロbodyは直接望ましい 結果を生成しないのでしょうか? それはコンパイルする必要があるからです。 コンパイルされるLispプログラム内にマクロ呼び出しがあるとき、Lispコン パイラーはインタープリターが行うようにマクロ定義を呼び出して展開形を受け 取ります。しかし展開形を評価するかわりに、コンパイラーは展開形が直接プロ グラム内にあるかのようにコンパイルを行います。結果としてコンパイルされた コードはそのマクロにたいする値と副作用を生成しますが、実行速度は完全にコ ンパイルされたときと同じになります。もしマクロbody自身が値と副作用を計算 したら、このようには機能しません — コンパイル時に計算されることになり、 それは有用ではありません。 マクロ呼び出しのコンパイルが機能するためには、マクロを呼び出すコード がコンパイルされるとき、そのマクロがLisp内ですでに定義されていなければな りません。コンパイラーにはこれを行うのを助ける特別な機能があります。コン パイルされるファイルが‘defmacro’フォームを含むなら、そのファイルの残りの 部分をコンパイルするためにそのマクロが一時的に定義されます。 ファイルをバイトコンパイルすると、ファイル内のトップレベルにあるすべ ての‘require’呼び出しも実行されるので、それらを定義しているファイルを requireすることにより、コンパイルの間に必要なマクロ定義が利用できること が確実になります(*note Named Features::を参照)。誰かがコンパイルされたプ ログラムを_実行_するときに、マクロ定義ファイルのロードをしないようにする には、‘require’呼び出しの周囲に‘eval-when-compile’を記述します(*note Eval During Compile::を参照)。 13.4 マクロの定義 ================= Lispのマクロオブジェクトは、CARが‘macro’でCDRが関数であるようなリストで す。マクロの展開形はマクロ呼び出しから、_評価されていない_引数のリストに 、(‘apply’を使って)関数を適用することにより機能します。 無名関数のように無名Lispマクロを使用することも可能ですが、無名マクロ を‘mapcar’のような関数に渡すことに意味がないので、これが行われることはあ りません。実際のところすべてのLispマクロは名前をもち、ほとんど常に ‘defmacro’マクロで定義されます。 -- Macro: defmacro name args [doc] [declare] body... ‘defmacro’はシンボルNAME(クォートはしない)を、以下のようなマクロと して定義する: (macro lambda ARGS . BODY) (このリストのCDRはラムダ式であることに注意。) このマクロオブジェク トはNAMEの関数セルに格納される。ARGSの意味は関数の場合と同じで、キ ーワード‘&rest’や‘&optional’が使用されることもある(*note Argument List::を参照)。NAMEとARGSはどちらもクォートされるべきではない。 ‘defmacro’のリターン値は未定義。 DOCが与えられたら、それはマクロのドキュメント文字列を指定する文字列 であること。DECLAREが与えられたら、それはマクロのメタデータを指定す る‘declare’フォームであること(*note Declare Form::を参照)。マクロを 対話的に呼び出すことはできないので、インタラクティブ宣言をもつこと はできないことに注意。 マクロが定数部と非定数部の混合体から構築される巨大なリスト構造を必要 とする場合があります。これを簡単に行うためには‘`’構文(*note Backquote::を 参照)を使用します。たとえば: (defmacro t-becomes-nil (variable) `(if (eq ,variable t) (setq ,variable nil))) (t-becomes-nil foo) ≡ (if (eq foo t) (setq foo nil)) マクロ定義のbodyには、そのマクロに関する追加のプロパティーを指定する ‘declare’フォームを含めることができます。*note Declare Form::を参照して ください。 13.5 マクロ使用に関する一般的な問題 =================================== マクロ展開が直感に反する結果となることがあり得ます。このセクションでは問 題になりやすい重要な結果と、問題を避けるためにしたがうべきルールをいくつ か説明します。 13.5.1 タイミング間違い ----------------------- マクロを記述する際のもっとも一般的な問題として、展開形の中ではなくマクロ 展開中に早まって実際に何らかの作業を行ってしまうことがあります。たとえば 実際のパッケージが以下のマクロ定義をもつとします: (defmacro my-set-buffer-multibyte (arg) (if (fboundp 'set-buffer-multibyte) (set-buffer-multibyte arg))) この誤ったマクロ定義は解釈(interpret)されるときは正常に機能しますがコ ンパイル時に失敗します。このマクロ定義はコンパイル時に ‘set-buffer-multibyte’を呼び出してしまいますが、それは間違っています。そ の後でコンパイルされたパッケージを実行しても何も行いません。プログラマー が実際に望むのは以下の定義です: (defmacro my-set-buffer-multibyte (arg) (if (fboundp 'set-buffer-multibyte) `(set-buffer-multibyte ,arg))) このマクロは、もし適切なら‘set-buffer-multibyte’の呼び出しに展開され、そ れはコンパイルされたプログラム実行時に実行されるでしょう。 13.5.2 マクロ引数の多重評価 --------------------------- マクロを定義する場合、展開形が実行されるときに引数が何回評価されるか注意 を払わなければなりません。以下の(繰り返し処理を用意にする)マクロで、この 問題を示してみましょう。このマクロでfor-loop構文を記述できます。 (defmacro for (var from init to final do &rest body) "Execute a simple \"for\" loop. For example, (for i from 1 to 10 do (print i))." (list 'let (list (list var init)) (cons 'while (cons (list '<= var final) (append body (list (list 'inc var))))))) (for i from 1 to 3 do (setq square (* i i)) (princ (format "\n%d %d" i square))) ↦ (let ((i 1)) (while (<= i 3) (setq square (* i i)) (princ (format "\n%d %d" i square)) (inc i))) ⊣1 1 ⊣2 4 ⊣3 9 ⇒ nil マクロ内の引数‘from’、‘to’、‘do’は構文糖(syntactic sugar)であり完全に無 視されます。このアイデアはマクロ呼び出し中で(‘from’、‘to’、‘do’のような )余計な単語をこれらの位置に記述できるようにするというものです。 以下はバッククォートの使用により、より単純化された等価の定義です: (defmacro for (var from init to final do &rest body) "Execute a simple \"for\" loop. For example, (for i from 1 to 10 do (print i))." `(let ((,var ,init)) (while (<= ,var ,final) ,@body (inc ,var)))) この定義のフォームは両方(バッククォートのあるものとないもの)とも、各 繰り返しにおいて毎回FINALが評価されるという欠点をもちます。FINALが定数の ときは問題がありません。しかしこれがより複雑な、たとえば ‘(long-complex-calculation x)’のようなフォームならば、実行速度は顕著に低 下し得ます。FINALが副作用をもつなら、複数回実行するとおそらく誤りになり ます。 うまく設計されたマクロ定義は、繰り返し評価することがそのマクロの意図 された目的でない限り、引数を正確に1回評価を行う展開形を生成することで、 この問題を避けるためのステップを費やします。以下は‘for’マクロの正しい展 開形です: (let ((i 1) (max 3)) (while (<= i max) (setq square (* i i)) (princ (format "%d %d" i square)) (inc i))) 以下はこの展開形を生成するためのマクロ定義です: (defmacro for (var from init to final do &rest body) "Execute a simple for loop: (for i from 1 to 10 do (print i))." `(let ((,var ,init) (max ,final)) (while (<= ,var max) ,@body (inc ,var)))) 残念なことにこの訂正により以下のセクションで説明する、別の問題が発生 します。 13.5.3 マクロ展開でのローカル変数 --------------------------------- 前のセクションでは‘for’の定義を、展開形がマクロ引数を正しい回数評価する ように訂正しました: (defmacro for (var from init to final do &rest body) "Execute a simple for loop: (for i from 1 to 10 do (print i))." `(let ((,var ,init) (max ,final)) (while (<= ,var max) ,@body (inc ,var)))) ‘for’の新しい定義には新たな問題があります。この定義はユーザーが意識し ていない、‘max’という名前のローカル変数を導入しています。これは以下の例 で示すようなトラブルを招きます: (let ((max 0)) (for x from 0 to 10 do (let ((this (frob x))) (if (< max this) (setq max this))))) ‘for’のbody内部の‘max’への参照は、‘max’のユーサーバインディングの参照を 意図したものですが、実際には‘for’により作られたバインディングにアクセス します。 これを修正する方法は、‘max’のかわりにinternされていない(uninterned)シ ンボルを使用することです(*note Creating Symbols::を参照)。internされてい ないシンボルは他のシンボルと同じようにバインドして参照することができます が、‘for’により作成されるので、わたしたちはすでにユーザーのプログラムに 存在するはずがないことを知ることができます。これはinternされていないので 、プログラムの後続の部分でそれを配置する方法はありません。これは‘for’に より配置された場所をのぞき、他の場所で配置されることがないのです。以下は この方法で機能する‘for’の定義です: (defmacro for (var from init to final do &rest body) "Execute a simple for loop: (for i from 1 to 10 do (print i))." (let ((tempvar (make-symbol "max"))) `(let ((,var ,init) (,tempvar ,final)) (while (<= ,var ,tempvar) ,@body (inc ,var))))) 作成されたinternされていないシンボルの名前は‘max’で、これを通常の internされたシンボル‘max’のかわりに、式内のその位置に記述します。 13.5.4 展開におけるマクロ引数の評価 ----------------------------------- マクロ定義自体が‘eval’(*note Eval::を参照)の呼び出しなどによりマクロ引数 式を評価した場合には別の問題が発生します。その引数がユーザーの変数を参照 する場合、ユーザーがマクロ引数と同じ名前で変数を使用しようとした場合に問 題となるでしょう。マクロのbody内では、マクロ引数のバインディングはその変 数のもっともローカルなバインディングなので、そのフォーム内部の任意の参照 はそれを参照するように評価されます。以下は例です: (defmacro foo (a) (list 'setq (eval a) t)) (setq x 'b) (foo x) ↦ (setq b t) ⇒ t ; ‘b’がセットされる ;; but (setq a 'c) (foo a) ↦ (setq a t) ⇒ t ; しかし‘c’ではなく‘a’がセットされる ユーザーの変数の名前が‘a’か‘x’かということで違いが生じています。これ は‘a’がマクロの引数変数‘a’と競合しているからです。 マクロ定義内での‘eval’の呼び出しにまつわる別の問題は、それがおそらく コンパイル時にあなたが意図したことを行わないだろうということです。バイト コンパイラーは、そのプログラム自身の(あなたが‘eval’でアクセスしたいと望 む)計算が発生しない、ローカル変数バインディングも存在しないプログラムの コンパイル時にマクロ定義を実行します。 この問題を避けるためには、*マクロ展開形の計算では引数式を評価しないで ください*。かわりにその式をマクロ展開形の中に置き換えれば、その値は展開 形の実行の一部として計算されます。これは、このチャプターの他の例が機能す る方法です。 13.5.5 マクロが展開される回数は? -------------------------------- 逐次解釈される関数で毎回マクロ呼び出しが展開されるが、コンパイルされた関 数では(コンパイル時の)1回だけしか展開されないという事実にもとづく問題が 時折発生します。そのマクロ定義が副作用をもつなら、そのマクロが何回展開さ れたかによって、それらのマクロは異なる動作をとるでしょう。 したがってあなたが何をしているか本当に判っていないのであれば、マクロ 展開形の計算での副作用は避けるべきです。 避けることのできない特殊な副作用が1つあります。それはLispオブジェクト の構築です。ほとんどすべてのマクロ展開形にはリストの構築が含まれます。リ スト構築はほとんどのマクロの核心部分です。これは通常は安全です。用心しな ければならないケースが1つだけあります。それは構築するオブジェクトがマク ロ展開形の中でクォートされた定数の一部となるときです。 そのマクロが1回だけ — コンパイル時 — しか展開されないなら、そのオブジ ェクトの構築もコンパイル時の1回です。しかし逐次実行では、そのマクロはマ クロ呼び出しが実行されるたびに展開され、これは毎回新たなオブジェクトが構 築されることを意味します。 クリーンなLispコードのほとんどでは、この違いは問題になりません。しか しマクロ定義によるオブジェクト構築の副作用を処理する場合には、問題になる かもしれません。したがって問題を避けるために、*マクロ定義によるオブジェ クト構築の副作用を避けてください*。以下は副作用により問題が起こる例です: (defmacro empty-object () (list 'quote (cons nil nil))) (defun initialize (condition) (let ((object (empty-object))) (if condition (setcar object condition)) object)) ‘initialize’が解釈されると、‘initialize’が呼び出されるたびに新しいリスト ‘(nil)’が構築されます。したがって各呼び出しの間において副作用は存続しま せん。しかし‘initialize’がコンパイルされると、マクロ‘empty-object’はコン パイル時に展開され、これは1つの定数‘(nil)’を生成し、この定数は ‘initialize’の呼び出しの各回で再利用、変更されます。 このような異常な状態を避ける1つの方法は、‘empty-object’をメモリー割り 当て構造ではなく一種の奇妙な変数と考えることです。‘'(nil)’のような定数に たいして‘setcar’を使うことはないでしょうから、当然‘(empty-object)’にも使 うことはないでしょう。 13.6 マクロのインデント ======================= マクロ定義ではマクロ呼び出しをがどのようにインデントすべきか指定す るために、‘declare’フォーム(*note Defining Macros::を参照)を使うことがで きます。インデント指定は以下のように記述します: (declare (indent INDENT-SPEC)) この‘lisp-indent-function’プロパティ内の結果はマクロの名前にセットされま す。 以下は利用できるINDENT-SPECです: ‘nil’ これはプロパティーを指定しない場合と同じ — 標準的なインデントパター ンを使用する。 ‘defun’ この関数を‘def’構文 — 2番目の行が“body”の開始 — と同様に扱う。 整数: NUMBER 関数の最初のNUMBER個の引数は“区別”され、残りは式のbodyと判断される 。その式の中の行は、最初の引数が区別されているかどうかにしたがって インデントされる。引数がbodyの一部なら、その行はこの式の先頭の開カ ッコ(open-parenthesis)よりも‘lisp-body-indent’だけ多い列にインデン トされる。引数が区別されていて1つ目か2つ目の引数なら、_2倍_余分にイ ンデントされる。引数が区別されていて1つ目か2つ目以外の引数なら、そ の行は標準パターンによってインデントされる。 シンボル: SYMBOL SYMBOLは関数名。この関数はこの式のインデントを計算するために呼び出 される関数。この関数は2つの引数をとる: POS その行のインデントが開始される位置。 STATE その行の開始まで解析したとき、‘parse-partial-sexp’(インデント とネスト深さの計算のためのLispプリミティブ)によりリターンされ る値。 これは数(その行のインデントの列数)、またはそのような数がcarであるよ うなリストをリターンすること。数とリストの違いは、数の場合は同じネ スト深さの後続のすべての行はこの数と同じインデントとなる。リストな ら、後続の行は異なるインデントを呼び出すかもしれない。これは ‘C-M-q’によりインデントが計算されるときに違いが生じる。値が数なら ‘C-M-q’はリストの終わりまでの後続の行のインデントを再計算する必要は ない。 14 カスタマイゼーション設定 *************************** EmacsのユーザーはCustomizeインターフェースにより、Lispコードを記述するこ となく変数とフェースをカスタマイズできます。*note (emacs)Easy Customization::を参照してください。このチャプターではCustomizeインターフ ェースを通じて、ユーザーとやりとりするための“カスタマイズアイテム( customization items)”を定義する方法を説明します。 カスタマイズアイテムには‘defcustom’マクロ (*note Variable Definitions::を参照)で定義されるカスタマイズ可能変数 ‘defface’(*note Defining Faces::で個別に説明)で定義されるカスタマイズ可能フェイス、およ び‘defgroup’ (*note Group Definitions::を参照)で定義される “カスタマイゼ ーショングループ(customization groups)”が含まれ、これは関連するカスタマ イゼーションアイテムのコンテナとして振る舞います。 14.1 一般的なキーワードアイテム =============================== 以降のセクションで説明するカスタマイゼーション宣言(customization declaration) — ‘defcustom’、‘defgroup’などはすべてさまざまな情報を指定す るためのキーワード引数(*note Constant Variables::を参照)を受け取ります。 このセクションではカスタマイゼーション宣言のすべての種類に適用されるキー ワードを説明します。 ‘:tag’以外のすべてのキーワードは、与えられたアイテムにたいして複数回 使用できます。キーワードの使用はそれぞれ独立した効果をもちます。例外は ‘:tag’で、これはすべての与えられたアイテムは1つの名前だけを表示できるか らです。 ‘:tag LABEL’ LABELを使用すると、カスタマイゼーションメニュー(customization menu)とカスタマイゼーションバッファー(customization buffer)のアイテ ムのラベルづけに、そのアイテムの名前のかわりに指定された文字列を使 用します。*混乱を招くのでそのアイテムの実際の名前と大きく異なる名前 は使用しないでください*。 ‘:group GROUP’ このカスタマイゼーションアイテムをグループGROUPにputする。カスタマ イゼーションアイテムからこのキーワードが欠落していると、アイテムは 最後に定義された同じグループ内に配置されるだろう。 ‘defgroup’内で‘:group’を使用すると、そのアイテムは新しいグループ (‘:group’のサブグループ)になる。 このキーワードを複数回使用すると、1つのアイテムを複数のグループに配 置することができる。それらのグループのいずれかを表示すると、このア イテムが表示される。煩雑になるので多用しないこと。 ‘:link LINK-DATA’ このアイテムのドキュメント文字列の後に外部リンクを含める。これは他 のドキュメントを参照するセンテンスを含んだボタンである。 LINK-DATAに使用できる複数の選択肢がある: ‘(custom-manual INFO-NODE)’ infoノードへのリンク。INFO-NODEは‘"(emacs)Top"’のような、ノー ド名を示す文字列である。このリンクはカスタマイゼーションバッフ ァーの‘[Manual]’に表示され、INFO-NODEにたいしてビルトインの infoリーダーを起動する。 ‘(info-link INFO-NODE)’ ‘custom-manual’と同様だが、カスタマイゼーションバッファーには そのinfoノード名が表示される。 ‘(url-link URL)’ ウェブページヘのリンク。URLはURLを指定する文字列である。カスタ マイゼーションバッファーに表示されるリンクは ‘browse-url-browser-function’で指定されたWWWブラウザーを呼び出 す。 ‘(emacs-commentary-link LIBRARY)’ ライブラリーのコメントセクション(commentary section)へのリンク 。LIBRARYはライブラリー名を指定する文字列である。*note Library Headers::を参照のこと。 ‘(emacs-library-link LIBRARY)’ Emacs Lispライブラリーファイルへのリンク。LIBRARYはライブラリ ー名を指定する文字列である。 ‘(file-link FILE)’ ファイルへのリンク。FILEはユーザーがこのリンクを呼び出したとき に‘find-file’でvisitするファイルの名前を指定する文字列である。 ‘(function-link FUNCTION)’ 関数のドキュメントへのリンク。FUNCTIONはユーザーがこのリンクを 呼び出したときに‘describe-function’で説明を表示する関数の名前 を指定する文字列である。 ‘(variable-link VARIABLE)’ 変数のドキュメントへのリンク。VARIABLEはユーザーがこのリンクを 呼び出したときに‘describe-variable’で説明を表示する変数の名前 を指定する文字列である。 ‘(custom-group-link GROUP)’ 他のカスタマイゼーショングループへのリンク。このリンクを呼び出 すことによりGROUPにたいする新たなカスタマイゼーションバッファ ーが作成される。 LINK-DATAの1つ目の要素の後に‘:tag NAME’を追加することにより、カスタ マイゼーションバッファーで使用するテキストを指定できます。たとえば ‘(info-link :tag "foo" "(emacs)Top")’は、そのバッファーで‘foo’と表 示されるEmacs manualへのリンクを作成します。 複数のリンクを追加するために、このキーワードを複数回使用することが できます。 ‘:load FILE’ そのカスタマイゼーションアイテムを表示する前にファイルFILEをロード する(*note Loading::を参照)。ロードは‘load’により行われ、そのファイ ルがまだロードされていないときだけロードを行う。 ‘:require FEATURE’ 保存したカスタマイゼーションがこのアイテム値をセットするとき、 ‘(require 'FEATURE)’が実行される。FEATUREはシンボル。 ‘:require’を使用するもっとも一般的な理由は、ある変数がマイナーモー ドのような機能を有効にするとき、そのモードを実装するコードがロード されていなければ、変数のセットだけでは効果がないからである。 ‘:version VERSION’ このキーワードはそのアイテムが最初に導入されたEmacsバージョン VERSIONか、そのアイテムのデフォルト値がそのバージョンで変更されたこ とを指定する。値VERSIONは文字列でなければならない。 ‘:package-version '(PACKAGE . VERSION)’ このキーワードはそのアイテムが最初に導入されたPACKAGEのバージョン VERSIONか、アイテムの意味(またはデフォルト値)が変更されたバージョン を指定する。このキーワードは‘:version’より優先される。 PACKAGEにはそのパッケージの公式名をシンボルとして指定すること(たと えば‘MH-E’)。VERSIONには文字列であること。パッケージPACKAGEが Emacsの一部としてリリースされたなら、PACKAGEとVERSIONの値は ‘customize-package-emacs-version-alist’の値に表示されるはずである。 Emacsの一部として配布された‘:package-version’キーワードを使用するパッ ケージは、‘customize-package-emacs-version-alist’変数も更新しなければな りません。 -- Variable: customize-package-emacs-version-alist これは‘:package-version’キーワード内でリストされたパッケージのバー ジョンに関連付けられたEmacsのバージョンにたいして、マッピングを提供 するalistである。このalistの要素は: (PACKAGE (PVERSION . EVERSION)...) それぞれのPACKAGE(シンボル)にたいして、パッケージバージョン PVERSIONを含む1つ以上の要素と、それに関連付けられるEmacsバージョン EVERSIONが存在する。これらのバージョンは文字列である。たとえば MH-Eパッケージは以下によりalistを更新する: (add-to-list 'customize-package-emacs-version-alist '(MH-E ("6.0" . "22.1") ("6.1" . "22.1") ("7.0" . "22.1") ("7.1" . "22.1") ("7.2" . "22.1") ("7.3" . "22.1") ("7.4" . "22.1") ("8.0" . "22.1"))) PACKAGEの値は一意である必要があり、‘:package-version’キーワード内に 現れるPACKAGEの値とマッチする必要がある。おそらくユーザーはエラーメ ッセージからこの値を確認するので、MH-EやGnusのようなパッケージの公 式名を選択するのがよいだろう。 14.2 カスタマイゼーショングループの定義 ======================================= Emacs Lispパッケージはそれぞれ、1つのメインのカスタマイゼーショングルー プ(main customization group)をもち、それにはすべてのオプションとフェイス 、そのパッケージ内の他のグループが含まれるべきです。そのパッケージに少数 のオプションとフェイスしかなければ、1つのグループだけを使用してその中に すべてを配置します。20以上のオプションやフェイスがあるなら、それらをサブ グループ内に構造化して、そのサブグループをメインのカスタマイゼーショング ループの下に配置します。そのパッケージ内の任意のオプションやフェイスを、 サブグループと並行してメイングループに配置しても問題はありません。 そのパッケージのメイングループ(または唯一のグループ)は、1つ以上の標準 カスタマイゼーショングループ(standard customization group)のメンバーであ るべきです(これらの完全なリストを表示するには‘M-x customize’を使用する )。それらの内から1つ以上(多すぎないこと)を選択して、‘:group’を使用してあ なたのグループをそれらに追加します。 新しいカスタマイゼーショングループは‘defgroup’で宣言します。 -- Macro: defgroup group members doc [keyword value]... MEMBERSを含むカスタマイゼーショングループとしてGROUPを宣言する。シ ンボルGROUPはクォートしない。引数DOCはそのグループにたいするドキュ メント文字列を指定する。 引数MEMBERSはそのグループのメンバーとなるカスタマイゼーションアイテ ムの初期セットを指定するリストである。しかしほとんどの場合は MEMBERSを‘nil’にして、メンバーを定義するときに‘:group’キーワードを 使用することによってそのグループのメンバーを指定する。 MEMBERSを通じてグループのメンバーを指定したければ、要素はそれぞれ ‘(NAME WIDGET)’という形式で指定すること。ここでNAMEはシンボル、 WIDGETはそのシンボルを編集するウィジェット型(widget type)である。変 数には‘custom-variable’、フェイスにはる‘custom-face’、グループには ‘custom-group’が有用なウィジェットである。 Emacsに新しいグループを導入するときは‘defgroup’内で‘:version’キーワ ードを使用する。そうすればグループの個別のメンバーにたいしてそれを 使用する必要がなくなる。 一般的なキーワード(*note Common Keywords::を参照)に加えて、 ‘defgroup’内では以下のキーワードも使用できる: ‘:prefix PREFIX’ グループ内のアイテムの名前がPREFIXで始まり、カスタマイズ変数 ‘custom-unlispify-remove-prefixes’が非‘nil’なら、そのアイテム のタグからPREFIXが省略される。グループは任意の数のプレフィクス をもつことができる。 -- User Option: custom-unlispify-remove-prefixes この変数が非‘nil’ならグループの‘:prefix’キーワードで指定されたプレ フィクスは、ユーザーがグループをカスタマイズするときは常にタグ名か ら省略される。 デフォルト値は‘nil’、つまりプレフィクス省略(prefix-discarding)の機 能は無効となる。これはオプションやフェイスの名前にたいするプレフィ クスの省略が混乱を招くことがあるからである。 14.3 カスタマイゼーション変数の定義 =================================== “カスタマイズ可能変数(customizable variable)”は“ユーザーオプション(user option)”とも呼ばれ、これはCustomizeインターフェースを通じてセットできる グローバルなLisp変数です。‘defvar’(*note Defining Variables::を参照)デ定 義される他のグローバル変数と異なり、カスタマイズ可能変数は‘defcustom’マ クロを使用して定義されます。サブルーチンとして‘defvar’を呼び出すことに加 えテ、‘defcustom’はCustomizeインターフェースでその変数が表示される方法や 、その変数がとることができる値などを明示します。 -- Macro: defcustom option standard doc [keyword value]... このマクロはユーザーオプション(かカスタマイズ可能変数)として OPTIONを宣言する。OPTIONはクォートしないこと。 引数STANDARDはOPTIONの標準値を指定する式である。‘defcustom’フォーム の評価によりSTANDARDが評価されるが、その値にそのオプションをバイン ドする必要はない。OPTIONがすでにデフォルト値をもつなら、それは変更 されずに残る。ユーザーがすでにOPTIONにたいするカスタマイゼーション を保存していれば、ユーザーによりカスタマイズされた値がデフォルト値 としてインストールされる。それ以外ならSTANDARDを評価した結果がデフ ォルト値としてインストールされる。 ‘defvar’と同様、このマクロは‘option’をスペシャル変数 — 常にダイナミ ックにバインドされることを意味する — としてマークする。OPTIONがすで にレキシカルバインドをもつなら、そのレキシカルバインドはバインディ ング構文を抜けるまで効果をもつ。*note Variable Scoping::を参照のこ と。 式STANDARDは別の様々な機会 — カスタマイゼーション機能がOPTIONの標準 値を知る必要があるときは常に — にも評価される可能性がある。そのため 任意回数の評価を行ても安全な式を使用するように留意されたい。 引数DOCはその変数にたいするドキュメント文字列を指定する。 ‘defcustom’が何も‘:group’を指定しなければ、同じファイル内で ‘defgroup’によって最後に定義されたグループが使用される。この方法で はほとんどの‘defcustom’は明示的な‘:group’が不必要になる。 Emacs Lispモードで‘C-M-x’(‘eval-defun’)で‘defcustom’フォームを評価 するとき、‘eval-defun’の特別な機能は変数の値がvoidかどうかテストせ ずに、無条件に変数をセットするよう段取りする(同じ機能は‘defvar’にも 適用される。*note Defining Variables::を参照)。すでに定義された defcustomで‘eval-defun’を使用することにより、(もしあれば)‘:set’関数 (以下参照)が呼び出される。 事前ロード(pre-loaded)されたEmacs Lispファイル(*note Building Emacs::を参照)に‘defcustom’を配置すると、ダンプ時にインストールされ た標準値は正しくない — たとえば依存している他の変数がまだ正しい値を 割り当てられていない — かもしれない。この場合はEmacs起動後に標準値 を再評価するために、以下で説明する‘custom-reevaluate-setting’を使用 する。 *note Common Keywords::にリストされたキーワードに加えて、このマクロに は以下のキーワードを指定できる ‘:type TYPE’ このオプションのデータ型としてTYPEを使用する。これはどんな値が適正 なのか、その値をどのように表示するかを指定する(*note Customization Types::を参照)。‘defcustom’はそれぞれこのキーワードにたいする値を指 定すること。 ‘:options VALUE-LIST’ このオプションに使用する適正な値のリストを指定する。ユーザーが使用 できる値はこれらの値に限定されないが、これらは便利な値の選択肢を提 示する。 これは特定の型にたいしてのみ意味をもち現在のところ‘hook’、‘plist’、 ‘alist’が含まれる。‘:options’を使用する方法は個別の型の定義を参照の こと。 ‘:set SETFUNCTION’ Customizeインターフェースを使用してこのオプションの値を変更する方法 としてSETFUNCTIONを指定する。関数SETFUNCTIONは2つの引数 — シンボル (オプション名)と新しい値 — を受け取り、このオプションにたいして正し く値を更新するために必要なことは何であれ行うこと(これはおそらく Lisp変数として単にオプションをセットすることを意味しない)。この関数 は引数の値を破壊的に変更しないことが望ましい。SETFUNCTIONのデフォル トは‘set-default’。 このキーワードを指定すると、その変数のドキュメント文字列には手入力 のLispコードで同じことを行う方法が記載されること。 ‘:get GETFUNCTION’ このオプションの値を抽出する方法としてGETFUNCTIONを指定する。関数 GETFUNCTIONは1つの引数(シンボル)を受け取り、カスタマイズがそのシン ボル(シンボルのLisp値である必要はない)にたいするカレント値としてそ れを使うべきかどうかをリターンすること。デフォルトは ‘default-value’。 ‘:get’を正しく使用するためには、Customの機能を真に理解する必要があ る。これは変数としてCustom内で扱われる値のためのものだが、実際には Lisp変数には格納されない。実際にLisp変数に格納されている値に GETFUNCTIONを指定するのは、ほとんどの場合は誤りである。 ‘:initialize FUNCTION’ FUNCTIONは‘defcustom’が評価されるときに変数を初期化するために使用さ れる関数であること。これは2つの引数 — オプション名(シンボル)と値を 受け取る。この方法での使用のために事前定義された関数がいくつかある: ‘custom-initialize-set’ 変数の初期化にその変数の‘:set’関数を使用するが、値がすでに非 voidなら再初期化を行わない。 ‘custom-initialize-default’ ‘custom-initialize-set’と同様だが、その変数の‘:set’のかわりに 関数‘set-default’を使用して変数をセットする。これは変数の ‘:set’関数がマイナーモードを有効または無効にする場合の通常の選 択である。この選択により変数の定義ではマイナーモード関数を呼び 出しは行わないが、変数をカスタマイズしたときはマイナーモード関 数を呼び出すだろう。 ‘custom-initialize-reset’ 変数の初期化に常に‘:set’関数を使用する。変数がすでに非voidなら 、(‘:get’メソッドでリターンされる)カレント値を使用して‘:set’関 数を呼び出して変数をリセットする。これはデフォルトの ‘:initialize’関数である。 ‘custom-initialize-changed’ 変数がすでにセットされている、またはカスタマイズされているなら 、変数の初期化のために‘:set’関数を使用して、それ以外なら単に ‘set-default’を使用する。 ‘custom-initialize-safe-set’ ‘custom-initialize-safe-default’ これらのn関数は‘custom-initialize-set’、 ‘custom-initialize-default’と同様に振る舞うがエラーをcatchする 。初期化中にエラーが発生したら、‘set-default’を使用して変数を ‘nil’にセットして、エラーのシグナルはしない。 これらの関数は事前ロードされたファイルで定義されたオプションの ためのものである(requireされた変数や関数がまだ定義されていない ため、STANDARD式はエラーをシグナルするかもしれない)。その値は 通常は‘startup.el’で更新され、‘defcustom’により計算された値は 無視される。startup後にその値をunsetして‘defcustom’を再評価す れば、エラーなしでSTANDARDは評価される。 ‘:risky VALUE’ その変数の‘risky-local-variable’プロパティーをVALUEにセットする (*note File Local Variables::を参照)。 ‘:safe FUNCTION’ その変数の‘safe-local-variable’プロパティーをFUNCTIONにセットします (*note File Local Variables::を参照)。 ‘:set-after VARIABLES’ 保存されたカスタマイゼーションに合わせて変数をセッティングするとき は、その前に変数VARIABLES確実にセット — つまりこれら他のものが処理 される後までセッティングを遅延 — すること。これら他の変数が意図され た値をもっていない場合に、この変数のセッティングが正しく機能しなけ れば‘:set-after’を使用すること。 特定の機能をオンに切り替えるオプションにたいしては、‘:require’キーワ ードを指定すると便利です。これはその機能がまだロードされていないときは、 そのオプションがセットされるとEmacsがその機能をロードするようにします。 *note Common Keywords::を参照してください。以下はライブラリー ‘saveplace.el’の例です: (defcustom save-place nil "Non-nil means automatically save place in each file..." :type 'boolean :require 'saveplace :group 'save-place) あるカスタマイゼーションアイテムが‘:options’がサポートする‘hook’や ‘alist’のような型をもつなら、‘custom-add-frequent-value’を呼び出すことに よって‘defcustom’宣言の外部から別途値を追加できます。たとえば ‘emacs-lisp-mode-hook’から呼び出されることを意図した関数 ‘my-lisp-mode-initialization’を定義する場合は、‘emacs-lisp-mode-hook’に たいする正当な値として、その定義を編集することなくその関数をリストに追加 したいと思うかもしれません。これは以下のようにして行うことができます: (custom-add-frequent-value 'emacs-lisp-mode-hook 'my-lisp-mode-initialization) -- Function: custom-add-frequent-value symbol value カスタマイズオプションSYMBOLにたいして正当な値のリストにVALUEを追加 する。 追加による正確な効果はSYMBOLのカスタマイズ型に依存する。 ‘defcustom’は内部的に、標準値にたいする式の記録にシンボルプロパティ ‘standard-value’、カスタマイゼーションバッファーでユーザーが保存した値の 記録に‘saved-value’、カスタマイゼーションバッファーでユーザーがセットし て未保存の値の記録に‘customized-value’を使用します。*note Symbol Properties::を参照してください。これらのプロパティは、carがその値を評価 する式であるようなリストです。 -- Function: custom-reevaluate-setting symbol この関数は‘defcustom’を通じて宣言されたユーザーオプションSYMBOLの標 準値を再評価する。変数がカスタマイズされたなら、この関数はかわりに 保存された値を再評価する。それからこの関数はその値に、(もし定義され ていればそのオプションの‘:set’プロパティーを使用して)ユーザーオプシ ョンをセットする。 これは値が正しく計算される前に定義されたカスタマイズ可能オプション にたいして有用である。たとえばstartupの間、Emacsは事前ロードされた Emacs Lispファイルで定義されたユーザーオプションにたいしてこの関数 を呼び出すが、これらの初期値は実行時だけ利用可能な情報に依存する。 -- Function: custom-variable-p arg この関数はARGがカスタマイズ可能変数なら非‘nil’をリターンする。カス タマイズ可能変数とは、‘standard-value’か‘custom-autoload’プロパティ ーをもつ(通常は‘defcustom’で宣言されたことを意味する)変数、または別 のカスタマイズ可能変数にたいするエイリアスのことである。 14.4 カスタマイゼーション型 =========================== ‘defcustom’でユーザーオプションを定義するときは、ユーザーオプションの“カ スタマイゼーション型(customization type)”を指定しなければなりません。こ れは(1)どの値が適正か、および(2)編集のためにカスタマイゼーションバッファ ーで値を表示する方法を記述するLispオブジェクトです。 カスタマイゼーション型は‘defcustom’内の‘:type’キーワードで指定します 。‘:type’の引数は評価されますが、‘defcustom’が実行されるときに1回だけ評 価されるので、さまざまな値をとる場合には有用でありません。通常はクォート された定数を使用します。たとえば: (defcustom diff-command "diff" "The command to use to run diff." :type '(string) :group 'diff) 一般的にカスタマイゼーション型は最初の要素が以降のセクションで定義さ れるカスタマイゼーション型の1つであるようなリストです。このシンボルの後 にいくつかの引数があり、それはそのシンボルに依存します。型シンボルと引数 の間にはオプションでkeyword-valueペアー(*note Type Keywords::を参照)を記 述できます。 いくつかの型シンボルは引数を使用しません。これらは“シンプル型(simple types)”と呼ばれます。シンプル型ではkeyword-valueペアーを使用しないなら、 型シンボルの周囲のカッコ(parentheses)を省略できます。たとえばカスタマイ ゼーション型として単に‘string’と記述すると、それは‘(string)’と等価です。 すべてのカスタマイゼーション型はウィジェットとして実装されます。詳細 は、*note Introduction: (widget)Top.を参照してください。 14.4.1 単純型 ------------- このセクションではすべてのシンプルデータ型を説明します。これらのカスタマ イゼーション型のうちのいくつかにたいして、カスタマイゼーションウィジェッ トは‘C-M-i’か‘M-’によるインライン補完を提供します。 ‘sexp’ 値はプリントと読み込みができる任意のLispオブジェクト。より特化した 型を使用するために時間をとりたくなければ、すべてのオプションにたい するフォールバックとして‘sexp’を使用することができる。 ‘integer’ 値は整数でなければならない。 ‘number’ 値は数(浮動小数点数か整数)でなければならない。 ‘float’ 値は浮動小数点数でなければならない。 ‘string’ 値は文字列でなければならない。カスタマイゼーションバッファーはその 文字列を区切り文字‘"’文字と‘\’クォートなしで表示する。 ‘regexp’ ‘string’文字と同様だがその文字列は有効な正規表現でなければならない 。 ‘character’ 値は文字コードでなければならない。文字コードは実際には整数だが、こ の型は数字を表示せずにバッファー内にその文字を挿入することにより値 を表示する。 ‘file’ 値はファイル名でなければならない。ウィジェットは補完を提供する。 ‘(file :must-match t)’ 値は既存のファイル名でなければならない。ウィジェットは補完を提供す る。 ‘directory’ 値はディレクトリー名でなければならない。ウィジェットは補完を提供す る。 ‘hook’ 値は関数のリストでなければならない。このカスタマイゼーション型はフ ック変数にたいして使用される。フック内で使用を推奨される関数のリス トを指定するために、フック変数の‘defcustom’内で‘:options’キーワード を使用できる。*note Variable Definitions::を参照のこと。 ‘symbol’ 値はシンボルでなければならない。これはカスタマイゼーションバッファ ー内でシンボル名として表示される。ウィジェットは補完を提供する。 ‘function’ 値はラムダ式か関数名でなければならない。ウィジェットは関数名にたい する補完を提供する。 ‘variable’ 値は変数名でなければならない。ウィジェットは補完を提供する。 ‘face’ 値はフェイス名のシンボルでなければならない。ウィジェットは補完を提 供する。 ‘boolean’ 値は真偽値 — ‘nil’か‘t’である。‘choice’と‘const’を合わせて使用する ことにより(次のセクションを参照)、値は‘nil’か‘t’でなければならない が、それら選択肢に固有の意味に適合する方法でそれぞれの値を説明する テキストを指定することもできる。 ‘key-sequence’ 値はキーシーケンス。カスタマイゼーションバッファーは‘kbd’関数と同じ 構文を使用してキーシーケンスを表示する。*note Key Sequences::を参照 のこと。 ‘coding-system’ 値はコーディングシステム名でなければならず、‘M-’で補完すること ができる。 ‘color’ 値は有効なカラー名でなければならない。ウィジェットはカラー名にたい する補完と、同様に‘*Colors*’バッファーに表示されるカラーサンプルと カラー名のリストからカラー名を選択するボタンを提供する。 14.4.2 複合型 ------------- 適切なシンプル型がなければ複合型(composite types)を使用することができま す。複合型は特定のデータにより、他の型から新しい型を構築します。指定され た型やデータは、その複合型の“引数(argument)”と呼ばれます。複合型は通常は 以下のようなものです: (CONSTRUCTOR ARGUMENTS...) しかし以下のように引数の前にkeyword-valueペアーを追加することもできます 。 (CONSTRUCTOR {KEYWORD VALUE}... ARGUMENTS...) 以下のテーブルに、コンストラクター(constructor)と複合型を記述するため にそれらを使用する方法を示します: ‘(cons CAR-TYPE CDR-TYPE)’ 値はコンスセルでなければならずCARはCAR-TYPE、CDRはCDR-TYPEに適合し ていなければならない。たとえば‘(cons string symbol)’は、‘("foo" . foo)’のような値にマッチするデータ型となる。 カスタマイゼーションバッファーでは、CARとCDRはそれぞれ特定のデータ 型に応じて個別に表示と編集が行われる。 ‘(list ELEMENT-TYPES...)’ 値はELEMENT-TYPESで与えられる要素と数が正確に一致するリストでなけれ ばならず、リストの各要素はそれぞれ対応するELEMENT-TYPEに適合しなけ ればならない。 たとえば‘(list integer string function)’は3つの要素のリストを示し、 1つ目の要素は整数、2つ目の要素は文字列、3つ目の要素は関数である。 カスタマイゼーションバッファーでは、各要素はそれぞれ特定のデータ型 に応じて個別に表示と編集が行われる。 ‘(group ELEMENT-TYPES...)’ これは‘list’と似ているが、Customバッファー内でのテキストのフォーマ ットが異なる。‘list’は各要素の値をそのタグでラベルづけするが、 ‘group’はそれを行わない。 ‘(vector ELEMENT-TYPES...)’ これは‘list’と似ているが、リストではなくベクターでなければならない 。各要素は‘list’の場合と同様に機能する。 ‘(alist :key-type KEY-TYPE :value-type VALUE-TYPE)’ 値はコンスセルのリストでなければならず、各セルのCARはカスタマイゼー ション型KEY-TYPEのキーを表し、同じセルのCDRはカスタマイゼーション型 VALUE-TYPEの値を表す。ユーザーはkey/valueペアーの追加や削除ができ、 各ペアのキーと値の両方を編集することができる。 省略された場合のKEY-TYPEとVALUE-TYPEのデフォルトは‘sexp’。 ユーザーは指定されたkey-typeにマッチする任意のキーを追加できるが、 ‘:options’(*note Variable Definitions::を参照)で指定することにより 、あるキーを優先的に扱うことができる。指定されたキーは、(適切な値と ともに)常にカスタマイゼーションバッファーに表示される。またalistに key/valueを含めるか、除外するか、それとも無効にするかを指定するチェ ックボックスも一緒に表示される。ユーザーは‘:options’キーワード引数 で指定された値を変更できない。 ‘:options’キーワードにたいする引数は、alist内の適切なキーにたいする 仕様のリストであること。これらは通常は単純なアトムであり、それらは 自身を意味します。たとえば: :options '("foo" "bar" "baz") これは名前が‘"foo"’、‘"bar"’、‘"baz"’であるような3つの既知のキーが あることを指定し、それらは常に最初に表示される。 たとえば‘"bar"’キーに対応する値を整数だけにするというように、特定の キーに対して値の型を制限したいときがあるかもしれない。これはリスト 内でアトムのかわりにリストを使用することにより指定することができる 。前述のように1つ目の要素はそのキー、2つ目の要素は値の型を指定する 。たとえば: :options '("foo" ("bar" integer) "baz") 最後にキーが表示される方法を変更したいときもあるだろう。デフォルト では‘:options’キーワードで指定された特別なキーはユーザーが変更でき ないので、キーは単に‘const’として表示される。しかしたとえばそれが関 数バインディングをもつシンボルであることが既知なら、 ‘function-item’のようにあるキーの表示のためにより特化した型を使用し たいと思うかもしれない。これはキーにたいしてシンボルを使うかわりに 、カスタマイゼーション型指定を使用することにより行うことができる。 :options '("foo" ((function-item some-function) integer) "baz") 多くのalistはコンスセルのかわりに2要素のリストを使用する。たとえば 、 (defcustom cons-alist '(("foo" . 1) ("bar" . 2) ("baz" . 3)) "Each element is a cons-cell (KEY . VALUE).") のかわりに以下を使用する (defcustom list-alist '(("foo" 1) ("bar" 2) ("baz" 3)) "Each element is a list of the form (KEY VALUE).") リストはコンスセルの最上位に実装されているため、上記の ‘list-alist’をコンスセルのalist(値の型が実際の値を含む1要素のリスト )として扱うことができる。 (defcustom list-alist '(("foo" 1) ("bar" 2) ("baz" 3)) "Each element is a list of the form (KEY VALUE)." :type '(alist :value-type (group integer))) ‘list’のかわりに‘group’を使用するのは、それが目的に適したフォーマッ トだという理由だけである。 同様に以下のようなトリックの類を用いることにより、より多くの値が各 キー連づけられたalistを得ることができる: (defcustom person-data '(("brian" 50 t) ("dorith" 55 nil) ("ken" 52 t)) "Alist of basic info about people. Each element has the form (NAME AGE MALE-FLAG)." :type '(alist :value-type (group integer boolean))) ‘(plist :key-type KEY-TYPE :value-type VALUE-TYPE)’ このカスタマイゼーション型は‘alist’(上記参照)と似ているが、(1)情報 がプロパティーリスト(*note Property Lists::を参照)に格納されていて 、(2)KEY-TYPEが省略された場合のデフォルトは‘sexp’ではなく‘symbol’に なる。 ‘(choice ALTERNATIVE-TYPES...)’ 値はALTERNATIVE-TYPESのうちのいずれかに適合しなければならない。たと えば‘(choice integer string)’では整数か文字列が許容される。 カスタマイゼーションバッファーでは、ユーザーはメニューを使用して候 補を選択して、それらの候補にたいして通常の方法で値を編集できる。 通常はこの選択からメニューの文字列が自動的に決定される。しかし候補 の中に‘:tag’キーワードを含めることにより、メニューにたいして異なる 文字列を指定できる。たとえば空白の数を意味する整数と、その通りに使 用したいテキストにたいする文字列なら、以下のような方法でカスタマイ ゼーション型を記述したいと思うかもしれない (choice (integer :tag "Number of spaces") (string :tag "Literal text")) この場合のメニューは‘Number of spaces’と‘Literal text’を提示する。 ‘const’以外の‘nil’が有効な値ではない選択肢には、‘:value’キーワード を使用して有効なデフォルト値を指定すること。*note Type Keywords::を 参照のこと。 複数の候補によりいくつかの値が提供されるなら、カスタマイズは適合す る値をもつ最初の候補を選択する。これは常にもっとも特有な型が最初で 、もっとも一般的な型が最後にリストされるべきことを意味する。以下は 適切な使い方の例である (choice (const :tag "Off" nil) symbol (sexp :tag "Other")) この使い方では特別な値‘nil’はその他のシンボルとは別に扱われ、シンボ ルは他のLisp式とは別に扱われる。 ‘(radio ELEMENT-TYPES...)’ これは‘choice’と似ているが、選択はメニューではなくラジオボタンで表 示される。これは該当する選択にたいしてドキュメントを表示できる利点 があるので、関数定数(‘function-item’カスタマイゼーション型)の選択に 適している場合がある。 ‘(const VALUE)’ 値はVALUEでなければならず他は許容されない。 ‘const’は主に‘choice’の中で使用される。たとえば‘(choice integer (const nil))’では整数か‘nil’が選択できる。 ‘choice’の中では‘:tag’とともに‘const’が使用される場合がある。たとえ ば、 (choice (const :tag "Yes" t) (const :tag "No" nil) (const :tag "Ask" foo)) これは‘t’がyes、‘nil’がno、‘foo’が“ask”を意味することを示す。 ‘(other VALUE)’ この選択肢は任意のLisp値にマッチできるが、ユーザーがこの選択肢を選 択したら値VALUEが選択される。 ‘other’は主に‘choice’の最後の要素に使用される。たとえば、 (choice (const :tag "Yes" t) (const :tag "No" nil) (other :tag "Ask" foo)) これは‘t’がyes、‘nil’がno、それ以外は“ask”を意味することを示す。ユ ーザーが選択肢メニューから‘Ask’を選択したら、値‘foo’が指定される。 しかしその他の値(‘t’、‘nil’、‘foo’を除く)なら‘foo’と同様に‘Ask’が表 示される。 ‘(function-item FUNCTION)’ ‘const’と同様だが値が関数のときに使用される。これはドキュメント文字 列も関数名と同じように表示する。ドキュメント文字列は‘:doc’で指定し た文字列かFUNCTION自身のドキュメント文字列。 ‘(variable-item VARIABLE)’ ‘const’と同様だが値が変数名のときに使用される。これはドキュメント文 字列も変数名と同じように表示する。ドキュメント文字列は‘:doc’で指定 した文字列かVARIABLE自身のドキュメント文字列。 ‘(set TYPES...)’ 値はリストでなければならず指定されたTYPESのいずれかにマッチしなけれ ばならない。 これはカスタマイゼーションバッファーではチェックリストとして表示さ れるので、TYPESはそれぞれ対応する要素を1つ、あるいは要素をもたない 。同じ1つのTYPESにマッチするような、異なる2つの要素を指定することは できない。たとえば‘(set integer symbol)’はリスト内で1つの整数、およ び/または1つのシンボルが許容されて、複数の整数や複数のシンボルは許 容されない。結果として‘set’内で‘integer’のような特化していない型を 使用するのは稀である。 以下のように‘const’型は‘set’内のTYPESでよく使用される: (set (const :bold) (const :italic)) alist内で利用できる要素を示すために使用されることもある: (set (cons :tag "Height" (const height) integer) (cons :tag "Width" (const width) integer)) これによりユーザーにオプションでheightとwidthの値を指定させることが できる。 ‘(repeat ELEMENT-TYPE)’ 値はリストでなければならず、リストの各要素は型ELEMENT-TYPEに適合し なければならない。カスタマイゼーションバッファーでは要素のリストと して表示され、‘[INS]’と‘[DEL]’ボタンで要素の追加や削除が行われる。 ‘(restricted-sexp :match-alternatives CRITERIA)’ これはもっとも汎用的な複合型の構築方法である。値はCRITERIAを満足す る任意のLispオブジェクト。CRITERIAはリストで、リストの各要素は以下 のうちのいずれかを満たす必要がある: • 述語 — つまり副作用をもたず引数は1つで、その引数に応じて ‘nil’か非‘nil’のどちらかをリターンする関数。リスト内での述語の 使用によりその述語が非‘nil’をリターンするようなオブジェクトが 許されることを意味する。 • クォートされた定数 — つまり‘'OBJECT’。リスト内でこの要素は OBJECT自身が許容される値であることを示す。 たとえば、 (restricted-sexp :match-alternatives (integerp 't 'nil)) これは整数、‘t’、‘nil’を正当な値として受け入れる。 カスタマイゼーションバッファーは適切な値をそれらの入力構文de表示し て、ユーザーはこれらをテキストとして編集できる。 以下は複合型でキーワード/値ペアーとして使用できるキーワードのテーブル です: ‘:tag TAG’ TAGはユーザーとのコミュニケーションのために、その候補の名前として使 用される。‘choice’内に出現する型にたいして有用。 ‘:match-alternatives CRITERIA’ CRITERIAは可能な値とのマッチに使用される。‘restricted-sexp’内でのみ 有用。 ‘:args ARGUMENT-LIST’ 型構築の引数としてARGUMENT-LISTの要素を使用する。たとえば‘(const :args (foo))’は‘(const foo)’と等価である。明示的に‘:args’と記述する 必要があるのは稀である。なぜなら最後のキーワード/値ペアーの後に続く ものは何であれ、引数として認識されるからである。 14.4.3 リストへのスプライス --------------------------- ‘:inline’機能により可変個の要素を、カスタマイゼーション型の‘list’や ‘vector’の途中にスプライス(splice: 継ぎ足す)することができます。‘list’や ‘vector’記述を含む型にたいして‘:inline t’を追加することによってこれを使 用します。 ‘list’や‘vector’型の仕様は、通常は単一の要素型を表します。しかしエン トリーが‘:inline t’を含むなら、マッチする値は含まれるシーケンスに直接マ ージされます。たとえばエントリーが3要素のリストにマッチするなら、全体が 3要素のシーケンスになります。これはバッククォート構文(*note Backquote::を 参照)の‘,@’に類似しています。 たとえば最初の要素が‘baz’で、残りの引数は0個以上の‘foo’か‘bar’でなけ ればならないようなリストを指定するには、以下のカスタマイゼーション型を使 用します: (list (const baz) (set :inline t (const foo) (const bar))) これは‘(baz)’、‘(baz foo)’、‘(baz bar)’、‘(baz foo bar)’のような値にマッ チします。 要素の型が‘choice’なら、‘choice’自身の中で‘:inline’を使用せずに、 ‘choice’の選択肢(の一部)の中で使用します。たとえば最初がファイル名で始ま り、その後にシンボル‘t’か2つの文字列を続けなければならないようなリストに マッチさせるには、以下のカスタマイゼーション型を使用します: (list file (choice (const t) (list :inline t string string))) 選択においてユーザーが選択肢の1つ目を選んだ場合はリスト全体が2つの要素を もち、2つ目の要素は‘t’になります。ユーザーが2つ目の候補を選んだ場合には リスト全体が3つの要素をもち、2つ目と3つ目の要素は文字列でなければなりま せん。 14.4.4 型キーワード ------------------- カスタマイゼーション型内の型名シンボルの後にキーワード/引数ペアーを指定 できます。以下は使用できるキーワードとそれらの意味です: ‘:value DEFAULT’ デフォルト値を提供する。 その候補にたいして‘nil’が有効な値でなければ、‘:value’に有効なデフォ ルトを指定することが必須となる。 ‘choice’の内部の選択肢として出現する型にたいしてこれを使用するなら 、ユーザーがカスタマイゼーションバッファー内のメニューでその選択肢 を選択したときに使用するデフォルト値を最初に指定する。 もちろんオプションの実際の値がこの選択肢に適合するなら、DEFAULTでは なく実際の値が表示される。 ‘:format FORMAT-STRING’ この文字列はその型に対応する値を記述するために、バッファーに挿入さ れる。FORMAT-STRING内では以下の‘%’エスケープが利用できる: ‘%[BUTTON%]’ ボタンとしてマークされたテキストBUTTONを表示する。‘:action’属 性はユーザーがそれを呼び出したときに、そのボタンが何を行うか指 定する。この属性の値は2つの引数 — ボタンが表示されるウィジェッ トとイベント — を受け取る関数である。 異なるアクションを行う2つの異なるボタンを指定する方法はない。 ‘%{SAMPLE%}’ ‘:sample-face’により指定されたスペシャルフェイス内のSAMPLEを表 示する。 ‘%v’ そのアイテムの値を代替えする。その値がどのように表示されるかは アイテムの種類と、(カスタマイゼーション型にたいしては)カスタマ イゼーション型にに依存する。 ‘%d’ そのアイテムのドキュメント文字列を代替えする。 ‘%h’ ‘%d’と同様だが、ドキュメント文字列が複数行なら、ドキュメント文 字列全体か最初の行だけかを制御するボタンを追加する。 ‘%t’ その位置でタグに置き換える。‘:tag’キーワードでタグを指定する。 ‘%%’ リテラル‘%’を表示する。 ‘:action ACTION’ ユーザーがボタンをクリックしたらACTIONを実行する。 ‘:button-face FACE’ ‘%[...%]’で表示されたボタンテキストにたいして、フェイスFACE(フェイ ス名、またはフェイス名のリスト)を使用する。 ‘:button-prefix PREFIX’ ‘:button-suffix SUFFIX’ これらはボタンの前か後に表示されるテキストを指定する。以下が指定で きる: ‘nil’ テキストは挿入されない。 文字列 その文字列がリテラルに挿入される。 シンボル そのシンボルの値が使用される。 ‘:tag TAG’ この型に対応する値(または値の一部)にたいするタグとしてTAG(文字列)を 使用する。 ‘:doc DOC’ この型に対応する値(か値の一部)にたいするドキュメント文字列として DOCを使用する。これが機能するためには‘:format’にたいする値を指定し て、その値にたいして‘%d’か‘%h’を使用しなければならない。 ある型にたいしてドキュメント文字列を指定するのは、‘:choice’内の選択 肢の型や、他の複合型の一部について情報を提供するのが通常の理由であ る。 ‘:help-echo MOTION-DOC’ ‘widget-forward’や‘widget-backward’でこのアイテムに移動したときに、 エコーエリアに文字列MOTION-DOCを表示する。さらにマウスの ‘help-echo’文字列としてMOTION-DOCが使用され、これには実際には」ヘル プ文字列を生成するために評価される関数かフォームを指定できる。もし 関数ならそれは1つの引数(そのウィジェット)で呼び出される。 ‘:match FUNCTION’ 値がその型にマッチするか判断する方法を指定する。対応する値 FUNCTIONは2つの引数(ウィジェットと値)を受け取る関数であり、値が適切 なら非‘nil’をリターンすること。 ‘:validate FUNCTION’ 入力にたいして検証を行う関数を指定する。FUNCTIONは引数としてウィジ ェットを受け取り、そのウィジェットのカレント値がウィジェットにたい して有効なら‘nil’をリターンすること。それ以外なら無効なデータを含む ウィジェットをリターンして、そのウィジェットの‘:error’プロパティに 、そのエラーを記述する文字列をセットすること。 14.4.5 新たな型の定義 --------------------- 前のセクションでは、‘defcustom’にたいして型の詳細な仕様を作成する方法を 説明しました。そのような型仕様に名前を与えたい場合があるかもしれません。 理解しやすいケースとしては、多くのユーザーオプションに同じ型を使用する場 合などです。各オプションにたいして仕様を繰り返すよりその型に名前を与えて 、‘defcustom’それぞれにその名前を使用することができます。他にもユーザー オプションの値が再帰的なデータ構造のケースがあります。あるデータ型がそれ 自身を参照できるようにするためには、それが名前をもつ必要があります。 カスタマイゼーション型はウィジェットとして実装されているめ、新しいカ スタマイゼーション型を定義するには、新たにウィジェット型を定義します。こ こではウィジェットインターフェイスの詳細は説明しません。*note Introduction: (widget)Top.を参照してください。かわりにシンプルな例を用い て、カスタマイゼーション型を新たに定義するために必要な最小限の機能につい て説明します。 (define-widget 'binary-tree-of-string 'lazy "A binary tree made of cons-cells and strings." :offset 4 :tag "Node" :type '(choice (string :tag "Leaf" :value "") (cons :tag "Interior" :value ("" . "") binary-tree-of-string binary-tree-of-string))) (defcustom foo-bar "" "Sample variable holding a binary tree of strings." :type 'binary-tree-of-string) 新しいウィジェットを定義するための関数は‘define-widget’と呼ばれます。 1つ目の引数は新たなウィジェット型にしたいシンボルです。2つ目の引数は既存 のウィジェットを表すシンボルで、新しいウィジェットではこの既存のウィジェ ットと異なる部分を定義することになります。新たなカスタマイゼーション型を 定義する目的にたいしては‘lazy’ウィジェットが最適です。なぜならこれは ‘defcustom’にたいするキーワード引数と同じ構文と名前でキーワード引数 ‘:type’を受け取るからです。3つ目の引数は新しいウィジェットにたいするドキ ュメント文字列です。この文字列は‘M-x widget-browse binary-tree-of-string ’コマンドで参照することができます。 これらの必須の引数の後にキーワード引数が続きます。もっとも重要なのは ‘:type’で、これはこのウィジェットにマッチさせたいデータ型を表します。上 記の例では‘binary-tree-of-string’は文字列、またはcarとcdrが ‘binary-tree-of-string’であるようなコンスセルです。この定義中でのウィジ ェット型への参照に注意してください。‘:tag’属性はユーザーインターフェイス でウィジェット名となる文字列、‘:offset’引数はカスタマイゼーションバッフ ァーでのツリー構造の外観で,子ノードと関連する親ノードの間に4つのスペー スを確保します。 ‘defcustom’は通常のカスタマイゼーション型に使用される方法で新しいウィ ジェットを表示します。 ‘lazy’という名前の由来は、他のウィジェットではそれらがバッファーでイ ンスタンス化されるとき、他の合成されたウィジェットが下位のウィジェットを 内部形式に変換するからです。この変換は再帰的なので、下位のウィジェットは _それら自身_の下位ウィジェットへと変換されます。データ構造自体が再帰的な ら、その変換は無限再帰(infinite recursion)となります。‘lazy’ウィジェット は、‘:type’引数を必要なときだけ変換することによってこの再帰を防ぎます。 14.5 カスタマイゼーションの適用 =============================== 以下の関数には変数とフェイスにたいして、そのユーザーのカスタマイゼーショ ン設定をインストールする役目をもちます。それらの関数はユーザーが Customizeインターフェイスで‘Save for future sessions’を呼び出したとき、 次回のEmacs起動時に評価されるように‘custom-set-variables’フォーム、およ び/または‘custom-set-faces’フォームがカスタムファイルに書き込まれること によって効果をもちます。 -- Function: custom-set-variables &rest args この関数はARGSにより指定された変数のカスタマイゼーションをインスト ールする。ARGS内の引数はそれぞれ、以下のようなフォームであること (VAR EXPRESSION [NOW [REQUEST [COMMENT]]]) VARは変数名(シンボル)、EXPRESSIONはカスタマイズされた値に評価される 式である。 この‘custom-set-variables’呼び出しより前にVARにたいして ‘defcustom’フォームが評価されたら即座にEXPRESSIONが評価されて、その 変数の値にその結果がセットされる。それ以外ならその変数の ‘saved-value’プロパティにEXPRESSIONが格納されて、これに関係する ‘defcustom’が呼び出されたとき(通常はその変数を定義するライブラリー がEmacsにロードされたとき)に評価される。 NOW、REQUEST、COMMENTエントリーは内部的な使用に限られており、省略さ れるかもしれない。NOWがもし非‘nil’なら、たとえその変数の ‘defcustom’フォームが評価されていなくても、その変数の値がそのときセ ットされる。REQUESTは即座にロードされる機能のリストである(*note Named Features::を参照)。COMMENTはそのカスタマイゼーションを説明す る文字列。 -- Function: custom-set-faces &rest args この関数はARGSにより指定されたフェイスのカスタマイゼーションをイン ストールする。ARGS内の引数はそれぞれ以下のようなフォームであること (FACE SPEC [NOW [COMMENT]]) FACEはフェイス名(シンボル)、SPECはそのフェイスにたいするカスタマイ ズされたフェイス仕様(*note Defining Faces::を参照)。 NOW、REQUEST、COMMENTエントリーは内部的な使用に限られており、省略さ れるかもしれない。NOWがもし非‘nil’なら、たとえ‘defface’フォームが評 価されていなくても、そのフェイス仕様がそのときセットされる。 COMMENTはそのカスタマイズを説明する文字列。 14.6 Customテーマ ================= “Customテーマ(Custom themes)”とはユニットとして有効や無効にできるセッテ ィングのコレクションです。*note (emacs)Custom Themes::を参照してください 。CustomテーマはそれぞれEmacs Lispソースファイルにより定義され、それらは このセクションで説明する慣習にしたがう必要があります(Customテーマを手作 業で記述するかわりに、Customize風のインターフェイスを使用して作成するこ ともできる。*note (emacs)Creating Custom Themes::を参照)。 Customテーマファイルは‘FOO-theme.el’のように命名すること。ここでFOOは テーマの名前。このファイルでの最初のLispフォームは‘deftheme’の呼び出しで 、最後のフォームは‘provide-theme’にすること。 -- Macro: deftheme theme &optional doc このマクロはCustomテーマの名前としてTHEME(シンボル)を宣言する。オプ ション引数DOCは、そのテーマを説明する文字列であること。この文字列は ユーザーが‘describe-theme’コマンドを呼び出したり、‘*Custom Themes*’バッファーで‘?’をタイプしたときに表示される。 2つの特別なテーマ名は禁止されている(使用するとエラーになる)。 ‘user’はそのユーザーの直接的なカスタマイズ設定を格納するためのダミ ーのテーマである。そし‘changed’はCustomizeシステムの外部で行われた 変更を格納するためのダミーのテーマである。 -- Macro: provide-theme theme このマクロは完全に仕様が定められたテーマ名THEMEを宣言する。 ‘deftheme’と‘provide-theme’の違いは、そのテーマセッティングを規定する Lispフォームです(通常は‘custom-theme-set-variables’の呼び出し、および/ま たは‘custom-theme-set-faces’の呼び出し)。 -- Function: custom-theme-set-variables theme &rest args この関数はCustomテーマTHEMEの変数のセッティングを規定する。THEMEは シンボル。ARGS内の各引数はフォームのリスト。 (VAR EXPRESSION [NOW [REQUEST [COMMENT]]]) ここでリストエントリーは‘custom-set-variables’のときと同じ意味をも つ。*note Applying Customizations::を参照のこと。 -- Function: custom-theme-set-faces theme &rest args この関数はCustomテーマTHEMEのフェイスのセッティングを規定する。 THEMEはシンボル。ARGS内の各引数はフォームのリスト。 (FACE SPEC [NOW [COMMENT]]) ここでリストエントリーは‘custom-set-faces’のときと同じ意味をもつ。 *note Applying Customizations::を参照のこと。 原則的にテーマファイルは他のLispフォームを含むこともでき、それらはそ のテーマがロードされるときに評価されるでしょうが、これは悪いフォームです 。悪意のあるコードを含むテーマのロードを防ぐために、最初に非ビルトインテ ーマをロードする前にEmacsはソースファイルを表示して、ユーザーにたいして 確認を求めます。 以下の関数は、テーマをプログラム的に有効または無効にするのに有用です: -- Function: custom-theme-p theme この関数はTHEME(シンボル)がCustomテーマの名前(たとえばそのテーマが 有効かどうかにかかわらず、CustomテーマがEmacsにロードされている)な ら非‘nil’をリターンする。それ以外は‘nil’をリターンする。 -- Variable: custom-known-themes この変数の値はEmacsにロードされたテーマのリストである。テーマはそれ ぞれLispシンボル(テーマ名)により表される。この変数のデフォルト値は 2つのダミーテーマ‘(user changed)’を含む。‘changed’テーマには Customテーマが適用される前に行われたセッティング(たとえばCustomの外 部での変数のセット)が格納されている。‘user’テーマにはそのユーザーが カスタマイズして保存したセッティングが格納されている。‘deftheme’マ クロで宣言されたすべての追加テーマは、このリストの先頭に追加される 。 -- Command: load-theme theme &optional no-confirm no-enable この関数はTHEMEという名前のCustomテーマを、変数 ‘custom-theme-load-path’で指定されたディレクトリーから探して、ソー スファイルからロードする。*note (emacs)Custom Themes::を参照のこと 。またそのテーマの変数とフェイスのセッティングが効果を及ぼすように テーマを“enables”にする(オプション引数NO-ENABLEが‘nil’の場合)。さら にオプション引数NO-CONFIRMが‘nil’なら、そのテーマをロードする前にユ ーザーに確認を求める。 -- Command: enable-theme theme この関数はTHEMEという名前のCustomテーマを有効にする。そのようなテー マがロードされていなければ、エラーをシグナルする。 -- Command: disable-theme theme この関数はTHEMEという名前のCustomテーマを無効にする。テーマはロード されたまま残るので、続けて‘enable-theme’を呼び出せばテーマは再び有 効になる。 15 ロード ********* Lispコードのファイルをロードすることは、その内容をLispオブジェクト形式で Lisp環境に取り込むことを意味します。Emacsはファイルを探してオープンして 、テキストを読み込んで各フォームを評価してから、そのファイルをクローズし ます。そのようなファイルは“Lispライブラリー(Lisp library)”とも呼ばれます 。 ‘eval-buffer’関数がバッファー内のすべての式を評価するのと同様に、 load関数はファイル内のすべての式を評価します。異なるのはEmacsバッファー 内のテキストではなく、load関数はディスク上で見つかったファイル内のテキス トを読み込んで評価することです。 ロードされたファイルは、ソースコードかバイトコンパイルされたコードと してLisp式を含んでいなければなりません。このファイル内の各フォームは“ト ップレベルフォーム(top-level form)”と呼ばれます。ロード可能なファイル内 のフォームにたいする特別なフォーマットはありません。ファイル内のフォーム はどれも同じように直接バッファーにタイプされて、そこで評価されるでしょう (実際ほとんどのコードはこの方法でテストされる)。多くの場合はそのフォーム は関数定義と変数定義です。 Emacsはコンパイルされたダイナミックモジュールも同様にロードできます。 これはEmacs Lispプログラム内で、Emacs Lispで記述されたパッケージと同様に 使用するための、追加機能を提供する共有ライブラリーです。ダイナミックモジ ュールのロード時に、Emacsはそのモジュールが実装する必要がある特殊な名前 の初期化関数を呼び出して、Emacs Lispプログラムに追加の関数と変数を公開し ます。 特定のEmacsプリミティブで必要となるとあらかじめ判明している外部ライブ ラリーのオンデマンドローディングについては、*note Dynamic Libraries::を 参照してください。 15.1 プログラムがロードを行う方法 ================================= Emacs Lispにはロードのためのインターフェイスがいくつかあります。たとえば ‘autoload’はファイル内で定義された関数にたいしてプレースホルダーとなるオ ブジェクトを作成します。この関数はオートロードされる関数を呼び出すために 、ファイルからその関数の実際の定義の取得を試みます(*note Autoload::を参 照)。‘require’はファイルがまだロードされていない場合にファイルをロードし ます(*note Named Features::を参照)。これらすべての関数は処理を行うために 最終的に‘load’を呼び出します。 -- Function: load filename &optional missing-ok nomessage nosuffix must-suffix この関数はLispコードのファイルを見つけてオープンして、その中のすべ てのフォームを評価してそのファイルをクローズする。 ‘load’はまずファイルを見つけるために、‘FILENAME.elc’という名前、つ まりFILENAMEに拡張子‘.elc’を足した名前のファイルを探す。このような ファイルが存在したらそれをロードする。Emacsがダイナミックモジュール (*note Dynamic Modules::を参照)のサポートつきでコンパイルされていれ ば、、次に‘load’は‘FILENAME.EXT’という名前のファイルを探す。ここで EXTは共有ライブラリーのシステム依存のファイル名拡張子である。その名 前のファイルが存在しなければ、‘load’は‘FILENAME.el’という名前のファ イルを探す。このファイルが存在したらそれをロードする。最後に、もし これらの名前がいずれも見つからなければ、‘load’は何も付け足さない FILENAMEという名前のファイルを探してそれが存在したらロードする (‘load’関数にFILENAMEを認識する賢さはない。‘foo.el.el’のような正し くない名前のファイルでも、‘(load "foo.el")’を評価してそれを見つけて しまうだろう)。 Auto Compressionモードが有効(残念ながらデフォルトでは有効)なら、 ‘load’は他のファイル名を試みる前に圧縮されたバージョンのファイル名 を探すのでファイルを見つけることができない。圧縮されたファイルが存 在したら、それを解凍してロードする。‘load’はファイル名に ‘jka-compr-load-suffixes’内の各サフィックスを足して圧縮されたバージ ョンを探す。この変数の値は文字列のリストでなければならない。標準的 な値は‘(".gz")’。 オプション引数NOSUFFIXが非‘nil’なら、‘load’はサフィックス‘.elc’と ‘.el’のロードを試みない。この場合はロードしたいファイルの正確な名前 を指定しなければならない。ただしAuto Compressionモードが有効なら ‘load’は圧縮されたバージョンを探すために、 ‘jka-compr-load-suffixes’を使用する。正確なファイル名を指定して、 NOSUFFIXに‘t’を使用することにより、‘foo.el.el’のような名前のファイ ルにたいするロードの試みを抑止できる。 オプション引数MUST-SUFFIXが非‘nil’の場合、ロードに使用されるファイ ルの名前に明示的にディレクトリー名が含まれていなければ、‘load’はフ ァイル名が‘.el’か‘.elc’、または共有ライブラリーの拡張子で終わること (もしかしたら圧縮による拡張子が付加されているかもしれない)を要求す る。 オプション‘load-prefer-newer’が非‘nil’なら、‘load’はサフィックスを 検索するとき、どんなファイル(‘.elc’、‘.el’等)であっても、もっとも最 近変更されたファイルのバージョンを選択する。 FILENAMEが‘foo’や‘baz/foo.bar’のような相対ファイル名なら、‘load’は 変数‘load-path’を使用してそのファイルを探す。これは‘load-path’内に リストされた各ディレクトリーにFILENAMEを追加して、最初に見つかった 名前のマッチするファイルをロードする。デフォルトディレクトリーを意 味する‘nil’が‘load-path’で措定されたときだけ、カレントデフォルトデ ィレクトリーを試みる。‘load’は‘load-path’内の最初のディレクトリーで 利用可能な3つのサフィックスすべてを試行してから、2つ目のディレクト リーで3つのサフィックスすべてを試行する、...というようにファイルを 探す。*note Library Search::を参照のこと。 最終的に見つかったファイル、およびEmacsがそのファイルを見つけたディ レクトリーが何であれ、Emacsはそのファイル名を変数‘load-file-name’の 値にセットする。 ‘foo.elc’が‘foo.el’より古いと警告されたら、それは‘foo.el’のリコンパ イルを考慮すべきことを意味する。*note Byte Compilation::を参照のこ と。 (コンパイルされていない)ソースファイルをロードしたとき、Emacsがファ イルをvisitしたときと同じように‘load’は文字セットの変換を行う。 *note Coding Systems::を参照のこと。 コンパイルされていないファイルをロードするとき、Emacsはそのファイル に含まれるすべてのマクロ(*note Macros::を参照)を展開する。わたした ちはこれを“eagerマクロ展開(eager macro expansion)”と呼んでいる。(関 連するコードを実行するまで展開を延期しないで)これを行うことにより、 コンパイルされていないコードの実行スピードが明らかに向上する。循環 参照によりこのマクロ展開を行うことができないときもある。これの一番 簡単な例は、ロードしようとしているファイルが他のファイルで定義され ているマクロを参照しているが、そのファイルはロードしようとしている ファイルを必要としている場合である。これは一般的には無害である。 Emacsは問題の詳細を与えるために警告(‘Eager macro-expansion skipped due to cycle...’)をプリントするが、単にその時点ではマクロを展開せず にそのファイルをロードする。あなたはこの問題が発生しないようにコー ドをリストラクチャーしたいと思うかもしれない。コンパイル済みファイ ルではマクロ展開はコンパイル時に行われるので、ロード時のマクロ展開 は行われない。*note Compiling Macros::を参照のこと。 NOMESSAGEが非‘nil’でなければ、エコーエリアに‘Loading foo...’や ‘Loading foo...done’のようなメッセージがロードの間に表示される。 ファイルをロードする間のハンドルされないエラーはロードを終了させる 。‘autoload’のためのロードの場合、ロードの間に定義された任意の関数 定義は元に戻される。 ‘load’がロードするファイルを見つけられいと、通常は(‘Cannot open load file FILENAME’メッセージとともに)エラー‘file-error’がシグナル される。しかしMISSING-OKが非‘nil’なら、‘load’は単に‘nil’をリターン する。 式の読み取りにたいして‘load’が‘read’のかわりに使用する関数を指定す るために、変数‘load-read-function’を使用できる。以下を参照されたい 。 ファイルが正常にロードされたら、‘load’は‘t’をリターンする。 -- Command: load-file filename このコマンドはファイルFILENAMEをロードする。FILENAMEが相対ファイル 名のなら、それはカレントデフォルトディレクトリーを指定したとみなさ れる。このコマンドは‘load-path’を使用せず、サフィックスの追加もしな い。しかし(Auto Compressionモードが有効なら)圧縮されたバージョンの 検索を行う。ロードするファイル名を正確に指定したければ、このコマン ドを使用すること。 -- Command: load-library library このコマンドはLIBRARYという名前のライブラリーをロードする。このコマ ンドは引数を読み取る方法がインタラクティブであることを除き‘load’と 同じ。*note (emacs)Lisp Libraries::を参照のこと。 -- Variable: load-in-progress この変数はEmacsがファイルをロード中なら非‘nil’、それ以外は‘nil’であ る。 -- Variable: load-file-name このセクションの最初に説明した検索でEmacsがファイルを見つけて、その ファイルをロード中のとき、この変数の値はそのファイルの名前である。 -- Variable: load-read-function この変数は‘load’と‘eval-region’が式を読み取るために、‘read’のかわり に使用する関数を指定する。指定する関数は‘read’と同様、引数が1つの関 数であること。 デフォルトではこの変数の値は‘read’。*note Input Functions::を参照の こと。 この変数を使用するかわりに別の新たな方法を使用するほうが明確である 。それは‘eval-region’のREAD-FUNCTION引数にその関数を渡す方法である 。*note Eval: Definition of eval-region.を参照のこと。 Emacsのビルドで‘load’がどのように使用されているかについての情報は、 *note Building Emacs::を参照のこと。 15.2 ロードでの拡張子 ===================== ここでは‘load’が試行するサフィックスについて、技術的な詳細を説明します。 -- Variable: load-suffixes これは(ソースまたはコンパイル済みの)Emacs Lispファイルを示すサフィ ックスのリストである。空の文字列が含まないこと。‘load’は指定された ファイル名にLispファイルのサフィックスを追加するときに、これらのサ フィックスを使用する。標準的な値は‘(".elc" ".el")’で、これは前のセ クションで説明した振る舞いとなる。 -- Variable: load-file-rep-suffixes これは同じファイルにたいして異なる表現を示すサフィックスのリストで ある。このリストは空の文字列から開始されること。‘load’はファイルを 検索するときは、他のファイルを検索する前にこのリストのサフィックス を順番にファイル名に追加する。 Auto Compressionモードを有効にすることにより ‘jka-compr-load-suffixes’のサフィックスがこのリストに追加され、無効 にすると再びリストから取り除かれる。‘load-file-rep-suffixes’の標準 的な値は、Auto Compressionモードが無効なら‘("")’。 ‘jka-compr-load-suffixes’の標準的な値が‘(".gz")’であることを考慮す ると、Auto Compressionモードが有効な場合の ‘load-file-rep-suffixes’の標準的な値は‘("" ".gz")’である。 -- Function: get-load-suffixes この関数はMUST-SUFFIX引数が非‘nil’のときは、‘load’が試みるべきすべ てのサフィックスを順番にしたがったリストでリターンする。この関数は ‘load-suffixes’と‘load-file-rep-suffixes’の両方を考慮する。 ‘load-suffixes’、‘jka-compr-load-suffixes’、 ‘load-file-rep-suffixes’がすべて標準的な値の場合、この関数はAuto Compressionモードが有効なら‘(".elc" ".elc.gz" ".el" ".el.gz")’、無 効なら‘(".elc" ".el")’をリターンする。 まとめると、‘load’は通常まず‘(get-load-suffixes)’の値のサフィックスを 試み、次に‘load-file-rep-suffixes’を試みる。NOSUFFIXが非‘nil’なら前者が スキップされ、MUST-SUFFIXが非‘nil’なら後者がスキップされる。 -- User Option: load-prefer-newer このオプションが非‘nil’なら、ファイルが見つかった最初のサフィックス で停止せずに、‘load’はすべてのサフィックスをテストして、一番新しい ファイルを使用する。 15.3 ライブラリー検索 ===================== EmacsがLispライブラリーをロードするときは、変数‘load-path’により指定され るディレクトリー内のライブラリーを検索します。 -- Variable: load-path この変数の値は‘load’でファイルをロードするときに検索するディレクト リーのリストである。リストの各要素は文字列(ディレクトリー名でなけれ ばなりません)、または‘nil’(カレントワーキングディレクトリーを意味す る)である。 Emacsは起動時にいくつかのステップにより‘load-path’の値をセットアップ する。最初にEmacsがコンパイルされたときのデフォルトロケーションセット (default locations set)を使用して、‘load-path’を初期化する。通常これは以 下のようなディレクトリーである "/usr/local/share/emacs/VERSION/lisp" (以降の例ではあなたがインストールしたEmacsのインストールプレフィクス に合うように‘/usr/local’を置き換えること。) これらのディレクトリーには、 Emacsとともにインストールされた標準的なLispファイルが含まれる。Emacsがこ れらを見つけられなければ正常に起動しないだろう。 Emacsをビルドしたディレクトリーから起動した場合 −−− つまり正式にイン ストールされた実行形式ではないEmacsを起動した場合 — 、Emacsはビルドされ たディレクトリーのソースの‘lisp’ディレクトリーを使用して‘load-path’を初 期化する。ソースとは別のディレクトリーでEmacsをビルドした場合は、ビルド したディレクトリーの‘lisp’ディレクトリーも追加する(いずれも要素は絶対フ ァイル名になる)。 ‘--no-site-lisp’オプションでEmacsを起動した場合を除き、‘load-path’の 先頭にさらに2つの‘site-lisp’を追加する。これらはローカルにインストールさ れたLispファイルで、通常は: "/usr/local/share/emacs/VERSION/site-lisp" および "/usr/local/share/emacs/site-lisp" の形式である。1つ目は特定のバージョンのEmacsにたいしてローカルにインスト ールされたものである。2つ目はインストールされたすべてのバージョンの Emacsが使用することを意図してローカルにインストールされたものである(イン ストールされたものでないEmacsが実行されると、もし存在すればソースディレ クトリーとビルドディレクトリーの‘site-lisp’ディレクトリーも追加される。 これらのディレクトリーは通常は‘site-lisp’ディレクトリーを含まない)。 環境変数‘EMACSLOADPATH’がセットされていたら、上述の初期化プロセスが変 更される。Emacsはこの環境変数の値にもとづいて‘load-path’を初期化する。 ‘EMACSLOADPATH’の構文は、‘PATH’で使用される構文と同様である。ディレク トリー名は‘:’(オペレーティングシステムによっては‘;’)で区切られる。 以下 は(‘sh’スタイルのシェルから)‘EMACSLOADPATH’変数をセットする例である: export EMACSLOADPATH=/home/foo/.emacs.d/lisp: 環境変数の値内の空の要素は、(上記例のような)末尾、先頭、中間のいずれ にあるかに関わらず、標準の初期化処理により決定される‘load-path’のデフォ ルト値に置き換えられる。そのような空要素が存在しなければ ‘EMACSLOADPATH’により‘load-path’全体が指定される。空要素、または標準の Lispファイルを含むディレクトリーへの明示的なパスのいずれかを含めなければ ならない。さもないとEmacsが関数を見つけられなくなる(‘load-path’を変更す る他の方法は、Emacs起動時にコマンドラインオプション‘-L’を使用する方法で ある。以下参照)。 ‘load-path’内の各ディレクトリーにたいして、Emacsはそのディレクトリー がファイル‘subdirs.el’を含むか確認して、もしあればそれをロードする。 ‘subdirs.el’ファイルは、‘load-path’のディレクトリーにたいして任意のサブ ディレクトリーを追加するためのコードが含まれており、Emacsがビルド/インス トールされたときに作成される。サブディレクトリーと複数階層下のレベルのサ ブディレクトリーの両方が直接追加される。ただし名前の最初が英数字でないデ ィレクトリー、名前が‘RCS’または‘CVS’のディレクトリー、名前が ‘.nosearch’というファイルを含むディレクトリーは除外される。 次にEmacsはコマンドラインオプション‘-L’(*note (emacs)Action Arguments::を参照)で指定したロードディレクトリーを追加する。もしあればオ プションパッケージ(*note Packaging Basics::を参照)がインストールされた場 所も追加する。 initファイル(*note Init File::を参照)で‘load-path’に1つ以上のディレク トリーを追加するコードを記述するのは一般的に行なわれている。たとえば: (push "~/.emacs.d/lisp" load-path) Emacsのダンプには‘load-path’の特別な値を使用する。ダンプされたEmacsを カスタマイズするために‘site-load.el’か‘site-init.el’を使用する場合、これ らのファイルが行った‘load-path’にたいする変更はすべてダンプ後に失われる 。 -- Command: locate-library library &optional nosuffix path interactive-call このコマンドはライブラリーLIBRARYの正確なファイル名を探す。‘load’と 同じ方法でライブラリーを検索を行い、引数NOSUFFIXも‘load’の場合と同 じ意味をもつ。LIBRARYに指定する名前にはサフィックス‘.elc’または ‘.el’を追加しないこと。 PATHが非‘nil’なら‘load-path’のかわりにそのディレクトリーのリストが 使用される。 ‘locate-library’がプログラムから呼び出されたときはファイル名を文字 列としてリターンする。ユーザーがインタラクティブに ‘locate-library’を実行したときは、引数INTERACTIVE-CALLが‘t’となり、 これは‘locate-library’にたいしてファイル名をエコーエリアに表示する よう指示する。 -- Command: list-load-path-shadows &optional stringp このコマンドは“シャドー(shadowed)”されたEmacs Lispファイルを表示す る。シャドーされたファイルとは、‘load-path’のディレクトリーに存在す るにも関わらず、‘load-path’のディレクトリーリスト内で前の位置にある 他のディレクトリーに同じ名前のファイルが存在するため、通常はロード されないファイルのことである。 たとえば以下のように‘load-path’がセットされていたとする ("/opt/emacs/site-lisp" "/usr/share/emacs/23.3/lisp") そして両方のディレクトリーに‘foo.el’という名前のファイルがあるとす る。この場合、‘(require 'foo)’は決して2つ目のディレクトリーのファイ ルをロードしない。このような状況はEmacsがインストールされた方法に問 題があることを示唆する。 Lispから呼び出されたると、この関数はシャドーされたファイルリストを バッファー内に表示するかわりに、それのメッセージをプリントする。オ プション引数‘stringp’が非‘nil’なら、かわりにシャドーされたファイル を文字列としてリターンする。 15.4 非ASCII文字のロード ======================== Emacs Lispプログラムが非ASCII文字の文字列定数を含むとき、Emacsはそれらを ユニバイト文字列かマルチバイト文字列のいずれかで表現する場合があります。 どちらの表現が使用されるかは、そのファイルがどのようにEmacsに読み込まれ たかに依存します。マルチバイト表現へのデコーディングとともに読み込まれた 場合、Lispプログラム内のテキストはマルチバイトのテキストとなり、ファイル 内の文字列定数はマルチバイト文字列になります。(たとえば)Latin-1文字を含 むファイルをデコーディングなしで読み込むと、そのプログラムのテキストはユ ニバイトのテキストとなり、ファイル内の文字列定数はユニバイト文字列になり ます。*note Coding Systems::を参照してください。 マルチバイト文字列がユニバイトバッファーに挿入されるときは自動的にユ ニバイトに変換されるため、大部分のEmacs Lispプログラムにおいて、マルチバ イト文字列が非ASCII文字列であるという事実を意識させないようにするべきで す。しかしこれが行われことにより違いが生じる場合には、ローカル変数セクシ ョンに‘coding: raw-text’と記述することにより、特定のLispファイルを強制的 にユニバイトとして解釈させることができます。この識別子により、そのファイ ルは無条件でユニバイトとして解釈されます。これは‘?vLITERAL’で記述された 非ASCII文字にキーバインドするとき重要になります。 15.5 autoload ============= “オートロード(autoload: 自動ロード)”の機能により、定義されているファイル をロードすることなく関数やマクロの存在を登録できます。関数の最初の呼び出 しで、実際の定義とその他の関連するコードをインストールするために適切なラ イブラリーを自動的にロードして、すべてがすでにロードされていたかのように 実際の定義を実行します。関数やマクロのドキュメントを参照することによって もオートロードが発生します(*note Documentation Basics::を参照)。 オートロードされた関数をセットアップするには2つの方法があります。それ は‘autoload’を呼び出す方法と、ソースの実際の定義の前に、“マジック”コメン トを記述する方法です。‘autoload’はオートロードのための低レベルのプリミテ ィブです。任意のLispプログラムが、任意のタイミングで‘autoload’を呼び出す ことができます。Emacsとともにインストールされるパッケージにとって、マジ ックコメントは関数をオートロードできるようににするための一番便利な方法で す。そのコメント自身は何も行いませんが、コマンド ‘update-file-autoloads’にたいするガイドの役目を果たします。このコマンド は‘autoload’の呼び出しを構築して、Emacsビルド時に実行されるようにアレン ジします。 -- Function: autoload function filename &optional docstring interactive type この関数はFILENAMEから自動的にロードされるように、FUNCTIONという名 前の関数(かマクロ)を定義する。文字列FILENAMEにはFUNCTIONの実際の定 義を取得するファイルを指定する。 FILENAMEがディレクトリー名、またはサフィックス‘.el’と‘.elc’のいずれ も含まなければ、この関数はこれらのサフィックスのいずれかを強制的に 追加して、サフィックスがないただのFILENAMEという名前のファイルはロ ードしない(変数‘load-suffixes’により要求される正確なサフィックスが 指定される)。 引数DOCSTRINGはその関数のドキュメント文字列である。‘autoload’の呼び 出しでドキュメント文字列を指定することにより、その関数の実際の定義 をロードせずにドキュメントを見ることが可能になる。この引数の値は通 常は関数定義のドキュメント文字列と等しいこと。もし等しくなければ、 その関数定義のドキュメント文字列がロード時に有効になる。 INTERACTIVEが非‘nil’なら、その関数はインタラクティブに呼び出すこと が可能になる。これによりFUNCTIONの実際の定義をロードせずに、‘M-x’に よる補完が機能するようになる。ここでは完全なインタラクティブ仕様は 与えられない。完全な仕様はユーザーが実際にFUNCTIONを呼び出すまで必 要ない。ユーザーが実際に呼び出したときに、実際の定義がロードされる 。 普通の関数と同様、マクロとキーマップをオートロードできる。 FUNCTIONが実際にはマクロならTYPEに‘macro’、キーマップのならTYPEに ‘keymap’を指定する。Emacsのさまざまな部分では、実際の定義をロードせ ずにこれらの情報を知ることが必要とされる。 オートロードされたキーマップは、あるプレフィクスキーがシンボル FUNCTIONにバインドされているとき、キーを探す間に自動的にロードされ る。そのキーマップにたいする他の類のアクセスではオートロードは発生 しない。特にLispプログラムが変数の値からそのキーマップを取得して ‘define-key’を呼び出した場合には、たとえその変数の名前がシンボル FUNCTIONと同じであってもオートロードは発生しない。 FUNCTIONが非voidのオートロードされたオブジェクトではない関数定義を もつなら、その関数は何も行わずに‘nil’をリターンする。それ以外ならオ ートロードされたオブジェクト(*note Autoload Type::を参照)を作成して 、それをFUNCTIONにたいする関数定義として格納する。オートロードされ たオブジェクトは以下の形式をもつ: (autoload FILENAME DOCSTRING INTERACTIVE TYPE) たとえば、 (symbol-function 'run-prolog) ⇒ (autoload "prolog" 169681 t nil) このような場合、‘"prolog"’はロードするファイルの名前、169681は ‘emacs/etc/DOC’ファイル(*note Documentation Basics::を参照)内のドキ ュメント文字列への参照で、‘t’はその関数がインタラクティブであること 、‘nil’はそれがマクロやキーマップでないことを意味する。 -- Function: autoloadp object この関数はOBJECTがオートロードされたオブジェクトなら非‘nil’をリター ンする。たとえば‘run-prolog’がオートロードされたオブジェクトかチェ ックするには以下を評価する (autoloadp (symbol-function 'run-prolog)) オートロードされたファイルは、通常は他の定義を含み1つ以上の機能を必要 としたり、あるいは提供するかもしれません。(内容の評価でのエラーにより)そ のファイルが完全にロードされていなければ、そのロードの間に行われた関数定 義や‘provide’の呼び出しはアンドゥされます。これはそのファイルからオート ロードされる関数にたいして再度呼び出しを試みたときに、そのファイルを確実 に再ロードさせるためです。こうしないと、そのファイル内のいくつかの関数は アボートしたロードにより定義されていて、それらはロードされない修正後のフ ァイルで提供される正しいサブルーチンを欠くため、正しく機能しないからです 。 オートロードされたファイルが意図したLisp関数またはマクロの定義に失敗 すると、データ‘"Autoloading failed to define function FUNCTION-NAME"’と ともにエラーがシグナルされます。 オートロードのマジックコメント(“autoload cookie”とも呼ばれる)は、オー トロード可能なソースファイル内の実際の定義の直前にある、 ‘;;;###autoload’だけの行から構成されます。コマンド‘M-x update-file-autoloads’は、対応する‘autoload’呼び出しを‘loaddefs.el’内に 書き込みます(autoload cookieとなる文字列と‘update-file-autoloads’で生成 されるファイルの名前は上述のデフォルトから変更可能です。以下参照)。 Emacsのビルドでは‘loaddefs.el’をロードするために‘autoload’を呼び出します 。‘M-x update-directory-autoloads’はより強力です。このコマンドはカレント ディレクトリー内のすべてのファイルにたいするオートロードを更新します。 このマジックコメントは任意の種類のフォームを‘loaddefs.el’内にコピーで きます。このマジックコメントに続くフォームはそのままコピーされます。しか しオートロード機能が特別に処理するフォームの場合は_除外_されます(たとえ ば‘autoload’内への変換)。以下はそのままコピーされないフォームです: 関数や関数風オブジェクトの定義: ‘defun’と‘defmacro’。‘cl-defun’と‘cl-defmacro’(*note (cl)Argument Lists::を参照)、および‘define-overloadable-function’ (‘mode-local.el’内のコメントを参照)も該当する。 メジャーモードとマイナーモードの定義: ‘define-minor-mode’、‘define-globalized-minor-mode’、 ‘define-generic-mode’、‘define-derived-mode’、 ‘easy-mmode-define-minor-mode’、‘easy-mmode-define-global-mode’、 ‘define-compilation-mode’、‘define-global-minor-mode’。 その他のタイプの定義: ‘defcustom’、‘defgroup’、‘defclass’ (*note EIEIO: (eieio)Top.を参照 )、および‘define-skeleton’ (‘skeleton.el’内のコメントを参照)。 ビルド時にそのファイル自身をロードするときにフォームを_実行しない_よ うにするためにマジックコメントを使用することもできます。これを行なうには マジックコメントと同じ行にフォームを記述します。これはコメントなのでソー スファイルをロードするときには何も行いません。ただし‘M-x update-file-autoloads’では、Emacsビルド時に実行されたものは‘M-x update-file-autoloads’にコピーします。 以下はマジックコメントによるオートロードのために‘doctor’を準備する例 です: ;;;###autoload (defun doctor () "Switch to *doctor* buffer and start giving psychotherapy." (interactive) (switch-to-buffer "*doctor*") (doctor-mode)) これにより以下が‘loaddefs.el’内に書き込まれます: (autoload (quote doctor) "doctor" "\ Switch to *doctor* buffer and start giving psychotherapy. \(fn)" t nil) ダブルクォートの直後のバックスラッシュと改行は、‘loaddefs.el’のようなプ リロードされた未コンパイルのLispファイルだけに使用される慣習です。これは ‘make-docfile’にたいして、ドキュメント文字列を‘etc/DOC’ファイルに配置す るよう指示します。*note Building Emacs::を参照してください。また ‘lib-src/make-docfile.c’内のコメントも参照してください。ドキュメント文字 列の使い方(usage part)の中の‘(fn)’は、種々のヘルプ関数(*note Help Functions::を参照)が表示するときに、その関数の名前に置き換えられます。 関数定義手法として既知ではなく、認められてもいないような、通常とは異 なるマクロにより関数定義を記述した場合、通常のオートロードのマジックコメ ントの使用によって定義全体が‘loaddefs.el’内にコピーされるでしょう。これ は期待した動作ではありません。かわりに以下を記述することにより、意図した ‘autoload’呼び出しを‘loaddefs.el’内に配置することができます。 ;;;###autoload (autoload 'foo "myfile") (mydefunmacro foo ...) autoload cookieとしてデフォルト以外の文字列を使用して、デフォルトの ‘loaddefs.el’とは異なるファイル内に対応するオートロード呼び出しを記述で きます。これを制御するためにEmacsは2つの変数を提供します: -- Variable: generate-autoload-cookie この変数の値はLispコメントの文法に準じた文字列である。‘M-x update-file-autoloads’はそのcookieの後のLispフォームを、cookieが生 成したオートロードファイル内にコピーします。この変数のデフォルト値 は‘";;;###autoload"’。 -- Variable: generated-autoload-file この変数の値は、オートロード呼び出しが書き込まれるEmacs Lispファイ ルを命名する。デフォルト値は‘loaddefs.el’だが、(たとえば‘.el’ファイ ル内のセクションLocal Variables))をオーバーライドできる。オートロー ドファイルは、フォームフィード文字で開始される終端を含んでいると仮 定される。 以下の関数はオートロードオブジェクトにより指定されたライブラリーを明 示的にロードするために使用されるかもしれません: -- Function: autoload-do-load autoload &optional name macro-only この関数はオートロードオブジェクトAUTOLOADにより指定されたロードを 処理する。オプション引数NAMEに非‘nil’を指定するなら、関数値が AUTOLOADとなるシンボルを指定すること。この場合、この関数のリターン 値がそのシンボルの新しい関数値になる。オプション引数MACRO-ONLYの値 が‘macro’なら、この関数は関数ではなくマクロのロードだけを有効にする 。 15.6 多重ロード =============== 1つのEmacsセッション内でファイルを複数回ロードできます。たとえばバッファ ーで関数定義を編集して再インストールした後に元のバージョンに戻したいとき があるかもしれません。これは元のファイルをリロードすることにより行なうこ とができます。 ファイルのロードやリロードを行う際、‘load’と‘load-library’関数は未コ ンパイルのファイルではなく、バイトコンパイルされた同名のファイルを自動的 にロードすることに留意してください。ファイルを再記述して保存後に再インス トールする場合には、新しいバージョンをバイトコンパイルする必要があります 。さもないとEmacsは新しいソースではなく、古いバイトコンパイルされたファ イルをロードしてしまうでしょう! この場合にはファイルロード時に表示される メッセージに、そのファイルのリコンパイルを促す‘(compiled; note, source is newer)’というメッセージが含まれます。 Lispライブラリーファイル内にフォームを記述するときは、そのファイルが 複数回ロードされるかもしれないことに留意してください。たとえば、そのライ ブラリーをリロードするときには、各変数が再初期化されるべきかどうか考慮し てください。。変数がすでに初期化されていれば、‘defvar’はその変数の値を変 更しません(*note Defining Variables::を参照)。 alistに要素を追加するもっともシンプルな方法は、以下のようなものでしょ う: (push '(leif-mode " Leif") minor-mode-alist) しかしこれはそのライブラリーがリロードされると、複数の要素を追加してしま うでしょう。この問題を避けるには‘add-to-list’(*note List Variables::を参 照)を使用します: (add-to-list 'minor-mode-alist '(leif-mode " Leif")) 時にはライブラリーが既にロード済みか、明示的にテストしたいときがある でしょう。そのライブラリーが‘provide’を使用して名前付きフィーチャ(named feature)を提供していれば、‘featurep’を使用して以前に‘provide’が実行され ているかテストすることができます。かわりに以下のようにすることもできます : (defvar foo-was-loaded nil) (unless foo-was-loaded EXECUTE-FIRST-TIME-ONLY (setq foo-was-loaded t)) 15.7 名前つき機能 ================= ‘provide’と‘require’は、‘autoload’にかわってファイルを自動的にロードする 関数です。これらは名前付きの“フィーチャ(feature: 機能)”という面で機能し ます。オートロードは特定の関数の呼び出しをトリガーにしますが、フィーチャ は最初は他のプログラムが名前により問い合わせたときにロードされます。 フィーチャ名とは関数や変数などのコレクションを表すシンボルです。これ らを定義するファイルは、そのフィーチャを“プロバイド(provide: 提供)”する べきです。これらのフィーチャを使用する他のプログラムは、その機能を“リク ワイア(require: 要求)”することによって、それらが定義されているか確認でき るでしょう。これは定義がまだロードされていなければ、定義ファイルをロード します。 フィーチャをリクワイアするには、フィーチャ名を引数として‘require’を呼 び出します。‘require’は意図する機能がすでにプロバイドされているか確認す るために、グローバル変数‘features’を調べます。もしプロバイドされていなけ れば、適切なファイルからそのフィーチャをロードします。このファイルはその フィーチャを‘features’に追加するために、トップレベルで‘provide’を呼び出 すべきです。これに失敗すると‘require’はエラーをシグナルします。 たとえば‘idlwave.el’内の‘idlwave-complete-filename’にたいする定義には 以下のコードが含まれます: (defun idlwave-complete-filename () "Use the comint stuff to complete a file name." (require 'comint) (let* ((comint-file-name-chars "~/A-Za-z0-9+@:_.$#%={}\\-") (comint-completion-addsuffix nil) ...) (comint-dynamic-complete-filename))) 式‘(require 'comint)’は‘comint.el’がまだロードされていなければ、 ‘comint-dynamic-complete-filename’が確実に定義されるようにそのファイルを ロードします。フィーチャは通常はそれらを提供するファイルにしたがって命名 されるため、‘require’にファイル名を与える必要はありません(‘require’命令 文が‘let’のbodyの外側にあるのが重要なことに注意。変数がletバインドされて いるライブラリーをロードすることにより、意図せぬ結果、つまりletをexitし た後にその変数がアンバインドされる)。 ‘comint.el’には以下のトップレベル式が含まれます: (provide 'comint) これは‘comint’をグローバルなリスト‘features’に追加するので、‘(require 'comint)’は今後何も行う必要がないことを知ることができます。 ファイルのトップレベルで‘require’が使用されたときは、それをロードした ときと同様、そのファイルをバイトコンパイル(*note Byte Compilation::を参 照)するときにも効果が表れます。これはリクワイアされたパッケージがマクロ を含んでいて、バイトコンパイラーがそれを知らなければならない場合です。こ れは‘require’によりロードされるファイルで定義される関数と変数にへのバイ トコンパイラーの警告も無効にします。 バイトコンパイルの間にトップレベルの‘require’が評価されるとしても、 ‘provide’呼び出しは評価されません。したがって以下の例のように‘provide’の 後に同じ機能にたいする‘require’を含めることにより、バイトコンパイル前に 定義しているファイルを確実にロードできます。 (provide 'my-feature) ; バイトコンパイラーには無視され ; ‘load’には評価される (require 'my-feature) ; バイトコンパイラーにより評価される。 コンパイラーは‘provide’を無視して、その後に対象のファイルをロードするこ とにより‘require’が処理されます。ファイルのロードは‘provide’呼び出しを実 行するので、後続の‘require’はファイルがロードされていれば何も行いません 。 -- Function: provide feature &optional subfeatures この関数はカレントEmacsセッションにFEATUREがロードされたこと、ある いはロードされつつあることをアナウンスする。これはFEATUREに関連する 機能が他のLispプログラムから利用可能できる、あるいは利用可能になる ことを意味する。 ‘provide’呼び出にによる直接的な効果は、リストFEATURE内にまだ追加さ れていなければFEATUREの先頭にそれを追加して、それを必要としている ‘eval-after-load’コードを呼び出すことである(*note Hooks for Loading::を参照)。引数FEATUREはシンボルでなければならない。 ‘provide’はFEATUREをリターンする。 SUBFEATURESが与えられたら、それはFEATUREの当該バージョンによりプロ バイドされる特定のサブフィーチャのセットを示すシンボルのリストであ ること。‘featurep’を使用して、サブフィーチャの存在をテストできる。 そのパッケージがロードされるかどうか、あるいは与えられるバージョン で存在するかどうか不明であるようなあるパッケージ(1つのFEATURE)にお いて、パッケージの種々の部分やパッケージ機能に命名することでそのパ ッケージを使いやすくするのが困難なほど複雑なときに使用するというの がサブフィーチャのアイデアである。*note Network Feature Testing::の 例を参照されたい。 features ⇒ (bar bish) (provide 'foo) ⇒ foo features ⇒ (foo bar bish) オートロードによりあるファイルがロードされて、その内容の評価エラー によりストップしたときは、そのロードの間に発生した関数定義や ‘provide’呼び出しはアンドゥされる。*note Autoload::を参照のこと。 -- Function: require feature &optional filename noerror この関数はカレントEmacsセッションにおいて、FEATUREが存在するかどう かを(‘(featurep FEATURE)’を使用する。以下参照)をチェックする。引数 FEATUREはシンボルでなければならない。 そのフィーチャが存在しなければ、‘require’は‘load’によってFILENAMEを ロードする。FILENAMEが与えられなければ、シンボルFEATUREの名前がロー ドするファイル名のベースとして使用される。しかしこの場合、 ‘require’はFEATUREを探すためにサフィックス‘.el’と‘.elc’の追加を強制 する(圧縮ファイルのサフィックスに拡張されるかもしれない)。名前がた だのFEATUREというファイルは使用されない(変数‘load-suffixes’は要求さ れるLispサフィックスを正確に指定する)。 NOERRORが非‘nil’なら、ファイルの実際のロードにおけるエラーを抑止す る。この場合はそのファイルのロードが失敗すると‘require’は‘nil’をリ ターンする。通常では‘require’はFEATUREをリターンする。 ファイルのロードは成功したがFEATUREをプロバイドしていなければ、 ‘require’は‘Required feature FEATURE was not provided’のようにエラ ーをシグナルする。 -- Function: featurep feature &optional subfeature この関数はカレントEmacsセッションでFEATUREがプロバイドされていれば (たとえばFEATUREが‘features’のメンバーなら)‘t’をリターンする。 SUBFEATUREが非‘nil’なら、この関数はサブフィーチャも同様にプロバイド されているとき(たとえばSUBFEATUREがシンボルFEATUREのプロパティ ‘subfeature’のメンバーのとき)だけ‘t’をリターンする。 -- Variable: features この変数の値はシンボルのリストであり、そのシンボルはカレントEmacsセ ッションにロードされたフィーチャである。シンボルはそれぞれ ‘provide’を呼び出すことにより、このリストにputされたものである。リ スト‘features’内の要素の順番に意味はない。 15.8 どのファイルで特定のシンボルが定義されているか =================================================== -- Function: symbol-file symbol &optional type この関数はSYMBOLを定義しているファイルの名前をリターンする。TYPEが ‘nil’なら、どのようなタイプの定義も受け入れる。TYPEが‘defun’なら関 数定義、‘defvar’は変数定義、‘defface’はフェイス定義だけを指定する。 値は通常は絶対ファイル名である。定義がどのファイルにも関係しなけれ ば‘nil’になることもある。SYMBOLがオートロード関数を指定するなら、値 が拡張子なしの相対ファイル名になることもある。 ‘symbol-file’は変数‘load-history’の値にもとづく。 -- Variable: load-history この変数の値はロードされたライブラリーファイルの名前を、それらが定 義する関数と変数の名前、およびそれらがプロバイドまたはリクワイアす るフィーチャに関連付けるalistである。 このalist内の各要素は、1つのロード済みライブラリー(スタートアップ時 にプリロードされたライブラリーを含む)を記述する。要素はCARがライブ ラリーの絶対ファイル名(文字列)であるようなリストである。残りのリス ト要素は以下の形式をもつ: ‘VAR’ シンボルVARが変数として定義された。 ‘(defun . FUN)’ 関数FUNが定義された。 ‘(t . FUN)’ 関数FUNはそのライブラリーが関数として再定義する前はオートロー ドとして定義されていた。後続の要素は常に‘(defun . FUN)’であり 、これはFUNを関数として定義する。 ‘(autoload . FUN)’ 関数FUNはオートロードとして定義された。 ‘(defface . FACE)’ フェイスFACEが定義された。 ‘(require . FEATURE)’ フィーチャFEATUREがリクワイアされた。 ‘(provide . FEATURE)’ フィーチャFEATUREがプロバイドされた。 ‘(cl-defmethod METHOD SPECIALIZERS)’ ‘cl-defmethod’を使用してスペシャライザーSPECIALIZERSとともに METHODという名前が定義された。 ‘(define-type . TYPE)’ 型TYPEが定義された。 ‘load-history’の値には、CARが‘nil’であるような要素が1つ含まれるかも しれない。この要素はファイルをvisitしていないバッファーで ‘eval-buffer’により作成された定義を記述する。 コマンド‘eval-region’は‘load-history’を更新しますが、要素を置き換えず に、visitされているファイルの要素にたいして定義されたシンボルを追加しま す。*note Eval::を参照してください。 15.9 アンロード =============== 他のLispオブジェクト用にメモリーを回収するために、ライブラリーによりロー ドされた関数や変数を破棄することができます。これを行うには関数 ‘unload-feature’を使用します: -- Command: unload-feature feature &optional force このコマンドはフィーチャFEATUREをプロバイドしていたライブラリーをア ンロードする。そのライブラリー内の‘defun’、‘defalias’、‘defsubst’、 ‘defmacro’、‘defconst’、‘defvar’、‘defcustom’によって定義されたすべ ての関数、マクロ、変数は未定義になる。その後に、それらのシンボルに たいして事前に関連付けられていたオートロードをリストアする(ロードは シンボルの‘autoload’プロパティにこれらを保存している)。 以前の定義をリストアする前に、特定のフックからそのライブラリー内の 関数を取り除くために、‘unload-feature’は‘remove-hook’を実行する。こ れらのフックには名前が‘-hook’(または廃止されたサフィックス ‘-hooks’)で終わる変数、加えて‘unload-feature-special-hooks’、同様に ‘auto-mode-alist’にリストされた変数も含まれる。これは重要なフックが すでに定義されていない関数を参照をすることにより、Emacsの機能が停止 することを防ぐためである。 標準的なアンロードアクティビティでは、そのライブラリー内の関数の ELPプロファイリング、そのライブラリーによりプロバイドされたフィーチ ャ、そのライブラリーで定義された変数に保持されたタイマーを取り消す 。 これらの基準が機能不全を防ぐのに十分でなければ、ライブラリーは ‘FEATURE-unload-function’という名前の明示的なアンローダーを定義でき る。そのシンボルが関数として定義されていたら、‘unload-feature’は何 かを行う前にまず引数なしでそれを呼び出す。これはライブラリーのアン ロードのために適切なすべてのことを行うことができる。これが‘nil’をリ ターンしたら、‘unload-feature’は通常のアンロードアクションを処理す る。それ以外ならアンロード処理は完了したとみなす。 ‘unload-feature’は通常は他のライブラリーが依存するライブラリーのア ンロードを拒絶する(ライブラリーBにたいする‘require’がライブラリー Aに含まれるなら、AはBに依存している)。オプション引数FORCEが非 ‘nil’なら依存関係は無視されて、どのようなライブラリーもアンロードで きる。 ‘unload-feature’関数はLispで記述されており、その動作は変数 ‘load-history’にもとづきます。 -- Variable: unload-feature-special-hooks この変数はライブラリー内で定義された関数を取り除くために、ライブラ リーをアンロードする前にスキャンするフックのリストを保持する。 15.10 ロードのためのフック ========================== 変数‘after-load-functions’を使用することにより、Emacsがライブラリーをロ ードするたびにコードを実行させることができます: -- Variable: after-load-functions このアブノーマルフック(abnormal hook)は、ファイルをロードした後に実 行される。フック内の各関数は1つの引数(ロードされたファイルの絶対フ ァイル名)で呼び出される。 _特定_のライブラリーのロード後にコードを実行したければ、マクロ ‘with-eval-after-load’を使用します: -- Macro: with-eval-after-load library body... このマクロはLIBRARYがロードされるたびに、ファイルLIBRARYのロードの 最後でBODYが評価されるよう準備する。LIBRARYがすでにロード済みなら即 座にBODYを評価する。 ファイル名LIBRARYにディレクトリーや拡張子を与える必要はない。通常は 以下のようにファイル名だけを与える: (with-eval-after-load "edebug" (def-edebug-spec c-point t)) どのファイルが評価をトリガーするか制限するには、ディレクトリーか拡 張子、またはその両方をLIBRARYに含める。実際のファイル名(シンボリッ クリンク名はすべて除外される)が、与えられた名前すべてにマッチするフ ァイルだけがマッチとなる。以下の例ではどこかのディレクトリー ‘..../foo/bar’にある‘my_inst.elc’や‘my_inst.elc.gz’は評価をトリガー するが、‘my_inst.el’は異なる。: (with-eval-after-load "foo/bar/my_inst.elc" ...) LIBRARYはフィーチャ(たとえばシンボル)でもよく、その場合には ‘(provide LIBRARY)’を呼び出す任意のファイルの最後にBODYが評価される 。 BODY内でのエラーはロードをアンドゥしないが、BODYの残りの実行を防げ る。 上手く設計されたLispプログラムは、通常は‘with-eval-after-load’を使用 するべきではありません。(外部からの使用を意図した)他のライブラリーで定義 された変数を調べたりセットする必要があるなら、それは即座に行うことができ ます −−− そのライブラリーがロードされるのを待つ必要はありまん。そのライ ブラリーで定義された関数を呼び出す必要があるならそのライブラリーをロード すべきであって、それには‘require’(*note Named Features::を参照)が適して います。 15.11 Emacs Dynamic Modules =========================== “ダイナミックEmacsミジュール(dynamic Emacs module)”とは、Emacs Lispで記 述されたパッケージのように、Emacs Lispプログラムで使用するための追加機能 を提供する共有ライブラリーです。 Emacs Lispパッケージをロードする関数は、ダイナミックモジュールのロー ドもできます。これらの関数はファイル名の拡張子、いわゆる“サフィックス”を 調べることによってダイナミックモジュールを認識します。 -- Variable: module-file-suffix この変数はモジュールファイルのファイル名拡張子の、システム依存な値 を保持する。Posixホストでは‘.so’、MS-Windowsでは‘.dll’。 すべてのダイナミックモジュールはCから呼び出し可能な ‘emacs_module_init’という名前の関数をエクスポートする必要があります。 ‘load’か‘require’でそのモジュールをロードする一部として、Emacsはその関数 を呼び出します。モジュールのコードがGPLまたはGPL互換のライセンスの下にリ リースされたことを示す、‘plugin_is_GPL_compatible’という名前のシンボルも エクスポートしてください。Emacsはこのようなシンボルをエクスポートしない モジュールのロードを拒絶するでしょう。 モジュールがEmacs関数を呼び出す必要があるなら、Emacsディストリビュー ションに含まれるヘッダーファイル‘emacs-module.h’で定義およびドキュメント されているAPIを通じてこれを行ってください。 モジュールは、そのモジールで定義されたC構造体にたいする埋め込みポイン ターである‘user-ptr’Lispオブジェクトを作成できます。これはモジュールで作 成されてそのモジュールの関数に渡される複雑なデータ構造を保持するために有 用です。user-ptrオブジェクトはそれに関連付けられる“ファイナライザー (finalizers)”をもつこともできます。ファイナライザーとはオブジェクトがガ ーベージコレクションされたときに実行される関数のことです。 -- Function: user-ptrp object この関数は引数が‘user-ptr’オブジェクトなら‘t’をリターンする。 Emacsのロード可能モジュールは、configure時にオプション ‘--with-modules’を使用することにより有効になります。 16 バイトコンパイル ******************* Emacs LispにはLispで記述された関数をより効率的に実行できる、“バイトコー ド(byte-code)”と呼ばれる特別な表現に翻訳する“コンパイラー(compiler)”があ ります。コンパイラーはLispの関数定義をバイトコードに置き換えます。バイト コード関数が呼び出されたとき、その定義は“バイトコードインタープリター (byte-code interpreter)”により評価されます。 バイトコンパイルされたコードは、(本当のコンパイル済みコードのように )そのマシンのハードウェアによって直接実行されるのではなく、バイトコンパ イラーによって評価されるため、バイトコードはリコンパイルしなくてもマシン 間での完全な可搬性を有します。しかし本当にコンパイルされたコードほど高速 ではありません。 一般的に任意のバージョンのEmacsはそれ以前のバージョンのEmacsにより生 成されたバイトコンパイル済みコードを実行できますが、その逆は成り立ちませ ん。 あるLispファイルを常にコンパイルせずに実行したい場合は、以下のように ‘no-byte-compile’をバインドするファイルローカル変数を配置します: ;; -*-no-byte-compile: t; -*- 16.1 バイトコンパイル済みコードのパフォーマンス =============================================== バイトコンパイルされた関数はCで記述されたプリミティブ関数ほど効率的では ありませんが、Lispで記述されたバージョンよりは高速に実行されます。以下は 例です: (defun silly-loop (n) "Return the time, in seconds, to run N iterations of a loop." (let ((t1 (float-time))) (while (> (setq n (1- n)) 0)) (- (float-time) t1))) ⇒ silly-loop (silly-loop 50000000) ⇒ 10.235304117202759 (byte-compile 'silly-loop) ⇒ [コンパイルされたコードは表示されない] (silly-loop 50000000) ⇒ 3.705854892730713 この例ではインタープリターによる実行には10秒を要しますが、バイトコン パイルされたコードは4秒未満です。これは典型的な結果例ですが、実際の結果 はさまざまでしょう。 16.2 バイトコンパイル関数 ========================= ‘byte-compile’により、関数やマクロを個別にバイトコンパイルできます。 ‘byte-compile-file’でファイル全体、‘byte-recompile-directory’または ‘batch-byte-compile’で複数ファイルをコンパイルできます。 バイトコンパイラーが警告、および/またはエラーメッセージを生成すること もあります(詳細は*note Compiler Errors::を参照)。これらのメッセージは Compilationモードが使用する‘*Compile-Log*’と呼ばれるバッファーに記録され ます。*note (emacs)Compilation Mode::を参照してください。 バイトコンパイルを意図したファイル内にマクロ呼び出しを記述する際には 注意が必要です。マクロ呼び出しはコンパイル時に展開されるので、そのマクロ はEmacsにロードされる必要があります(さもないとバイトコンパイラーが正しく 処理しないだろう)。これを処理する通常の方法は、必要なマクロ定義を含むフ ァイルを‘require’フォームで指定することです。バイトコンパイラーは通常は コンパイルするコードを評価しませんが、‘require’フォームは指定されたライ ブラリーをロードすることにより特別に扱われます。誰かがコンパイルされたプ ログラムを_実行_する際にマクロ定義ファイルのロードを回避するためには、 ‘require’呼び出しの周囲に‘eval-when-compile’を記述します(*note Eval During Compile::を参照)。詳細は*note Compiling Macros::を参照してくださ い。 インライン関数(‘defsubst’)はこれほど面倒ではありません。定義が判明す る前にそのような関数呼び出しをコンパイルした場合でも、その呼び出しは低速 になるだけで正しく機能するでしょう。 -- Function: byte-compile symbol この関数はSYMBOLの関数定義をバイトコンパイルして、以前の定義をコン パイルされた定義に置き換える。SYMBOLの関数定義は、その関数にたいす る実際のコードでなければならない。‘byte-compile’はインダイレクト関 数を処理しない。リターン値は、SYMBOLのコンパイルされた定義であるよ うなバイトコード関数ブジェクト(*note Byte-Code Objects::を参照)。 (defun factorial (integer) "INTEGERの階乗を計算する。" (if (= 1 integer) 1 (* integer (factorial (1- integer))))) ⇒ factorial (byte-compile 'factorial) ⇒ #[(integer) "^H\301U\203^H^@\301\207\302^H\303^HS!\"\207" [integer 1 * factorial] 4 "Compute factorial of INTEGER."] SYMBOLの定義がバイトコード関数オブジェクトなら、‘byte-compile’は何 も行わず‘nil’をリターンする。そのシンボルの関数セル内の(コンパイル されていない)オリジナルのコードはすでにバイトコンパイルされたコード に置き換えられているので、シンボルの定義の再コンパイルはしない。 ‘byte-compile’の引数として‘lambda’式も指定できる。この場合、関数は 対応するコンパイル済みコードをリターンするが、それはどこにも格納さ れない。 -- Command: compile-defun &optional arg このコマンドはポイントを含むdefunを読み取りそれをコンパイルして、結 果を評価します。実際に関数定義であるようなdefunでこれを使用した場合 は、その関数のコンパイル済みバージョンをインストールする効果があり ます。 ‘compile-defun’は通常は評価した結果をエコーエリアに表示するが、 ARGが非‘nil’なら、そのフォームをコンパイルした後にカレントバッファ ーに結果を挿入する。 -- Command: byte-compile-file filename &optional load この関数はFILENAMEという名前のLispコードファイルを、バイトコードの ファイルにコンパイルする。出力となるファイルの名前は、サフィックス ‘.el’を‘.elc’に変更することにより作成される。FILENAMEが‘.el’で終了 しない場合には、‘.elc’をFILENAMEの最後に付け足す。 コンパイルは入力ファイルから1つのフォームを逐次読み取ることにより機 能する。フォームが関数かマクロなら、コンパイル済みの関数かマクロが 書き込まれる。それ以外のフォームはまとめられて、まとめられたものご とにコンパイルされて、そのファイルが読まれたとき実行されるようにコ ンパイルされたコードが書き込まれる。入力ファイルを読み取る際には、 すべてのコメントは無視される。 このコマンドはエラーがなければ‘t’、それ以外は‘nil’をリターンする。 インタラクティブに呼び出されたときは、ファイル名の入力をもとめる。 LOADが非‘nil’なら、このコマンドはコンパイルした後にコンパイルしたフ ァイルをロードする。インタラクティブに呼び出された場合、LOADはプレ フィクス引数である。 $ ls -l push* -rw-r--r-- 1 lewis lewis 791 Oct 5 20:31 push.el (byte-compile-file "~/emacs/push.el") ⇒ t $ ls -l push* -rw-r--r-- 1 lewis lewis 791 Oct 5 20:31 push.el -rw-rw-rw- 1 lewis lewis 638 Oct 8 20:25 push.elc -- Command: byte-recompile-directory directory &optional flag force このコマンドはDIRECTORY(またはそのサブディレクトリー)内の、リコンパ イルを要するすべての‘.el’ファイルをリコンパイルする。‘.elc’ファイル が存在して、それが‘.el’より古いファイルは、リコンパイルが必要となる 。 ‘.el’ファイルに対応する‘.elc’ファイルが存在しない場合に何を行うかを FLAGで指定する。‘nil’なら、このコマンドはこれらのファイルを無視する 。FLAGが0なら、それらをコンパイルする。‘nil’と0以外なら、それらのフ ァイルをコンパイルするかユーザーに尋ねて、同様にそれぞれのサブディ レクトリーについても尋ねる。 インタラクティブに呼び出されると、‘byte-recompile-directory’は DIRECTORYの入力を求めて、FLAGはプレフィクス引数となる。 FORCEが非‘nil’なら、このコマンドは‘.elc’ファイルが存在するすべての ‘.el’ファイルをリコンパイルする。 リターン値は不定。 -- Function: batch-byte-compile &optional noforce この関数はコマンドラインで指定されたファイルにたいして ‘byte-compile-file’を実行する。この関数は処理が完了するとEmacsを killするので、Emacsのバッチ実行でのみ使用しなければならない。1つの ファイルでエラーが発生しても、それによって後続のファイルにたいする 処理が妨げられることはないが、そのファイルにたいする出力ファイルは 生成されず、Emacsプロセスは0以外のステータスコードで終了する。 NOFORCEが非‘nil’なら、この関数は最新の‘.elc’ファイルがあるファイル をリコンパイルしない。 $ emacs -batch -f batch-byte-compile *.el 16.3 ドキュメント文字列とコンパイル =================================== Emacsがバイトコンパイルされたファイルから関数や変数をロードする際、通常 はメモリー内にそれらのドキュメント文字列をロードしません。それぞれのドキ ュメント文字列は、必要なときだけバイトコンパイルされたファイルからダイナ ミック(dynamic: 動的)にロードされます。ドキュメント文字列の処理をスキッ プすることにより、メモリーが節約されてロードが高速になります。 この機能には欠点があります。コンパイル済みのファイルを削除や移動、ま たは(新しいバージョンのコンパイル等で)変更した場合、Emacsは以前にロード した関数や変数のドキュメント文字列にアクセスできなくなるでしょう。このよ うな問題は通常なら、あなた自身がEmacsをビルドしたときに、そのLispファイ ルを編集および/またはリコンパイルしたときだけ発生します。この問題は、リ コンパイル後にそれぞれのファイルをリロードするだけで解決します。 バイトコンパイルされたファイルからのドキュメント文字列のダイナミック ロードは、バイトコンパイルされたファイルごとにコンパイル時に解決されます 。これはオプション‘byte-compile-dynamic-docstrings’で無効にできます。 -- User Option: byte-compile-dynamic-docstrings これが非‘nil’なら、バイトコンパイラーはドキュメント文字列をダイナミ ックロードするようにセットアップしたコンパイル済みファイルを生成す る。 特定のファイルでダイナミックロード機能を無効にするには、以下のよう にヘッダー行でこのオプションに‘nil’をセットする(*note Local Variables in Files: (emacs)File Variables.を参照)。 -*-byte-compile-dynamic-docstrings: nil;-*- これは主として、あるファイルを変更しようとしていて、そのファイルを すでにロード済みのEmacsセッションがファイルを変更した際にも正しく機 能し続けることを望む場合に有用である。 内部的にはドキュメント文字列のダイナミックロードは、特殊なLispリーダ ー構文‘#@COUNT’とともにコンパイル済みファイルに書き込むことによって達成 される。この構文は次のCOUNT文字をスキップする。さらに‘#$’構文も使用され 、これはこのファイルの名前(文字列)を意味する。これらの構文をLispソースフ ァイル内で使用しないこと。これらは人間がファイルを読む際に明確であるよう にデザインされていない。 16.4 個別関数のダイナミックロード ================================= ファイルをコンパイルするとき、オプションで“ダイナミック関数ロード (dynamic function loading)”機能(“laxyロード(lazy loading)とも呼ばれる ”)を有効にできます。ダイナミック関数ロードでは、ファイルのロードでファイ ル内の関数定義は完全には読み込まれません。かわりに各関数定義にはそのファ イルを参照するプレースホルダーが含まれます。それぞれ関数が最初に呼び出さ れるときにそのプレースホルダーを置き換えるために、ファイルから完全な定義 が読み込まれます。 ダイナミック関数ロードの利点は、ファイルのロードがより高速になること です。ユーザーが呼び出せる関数を多く含むファイルにとって、それらの関数の うち1つを使用したら多分残りの関数も使用するというのでなければ、これは利 点になります。多くのキーボードコマンドを提供する特化したモードは、このパ ターンの使い方をする場合があります。ユーザーはそのモードを呼び出すかもし れませんが、使用するのはそのモードが提供するコマンドのわずか一部です。 ダイナミックロード機能には不利な点がいくつかあります: • ロード後にコンパイル済みファイルの削除や移動を行うと、Emacsはまだロ ードされていない残りの関数定義をロードできなくなる。 • (新しいバージョンのコンパイル等で)コンパイル済みファイルを変更した 場合に、まだロードされていない関数のロードを試みると通常は無意味な 結果となる。 このような問題は通常の状況でインストールされたEmacsファイルでは決して 発生しません。しかしあなたが変更したLispファイルでは発生し得ます。それぞ れのファイルをリコンパイルしたらすぐに新たなコンパイル済みファイルをリロ ードするのが、これらの問題を回避する一番簡単な方法です。 コンパイル時に変数‘byte-compile-dynamic’が非‘nil’なら、バイトコンパイ ラーはダイナミック関数ロード機能を使用します。ダイナミックロードが望まし いのは特定のファイルにたいしてだけなので、この変数をグローバルにセットし ないでください。そのかわりに、特定のソースファイルのファイルローカル変数 でこの機能を有効にしてください。たとえばソースファイルの最初の行に以下の テキストを記述することにより、これを行うことができます: -*-byte-compile-dynamic: t;-*- -- Variable: byte-compile-dynamic これが非‘nil’なら、バイトコンパイラーはダイナミック関数ロード用にセ ットアップされたコンパイル済みファイルを生成する。 -- Function: fetch-bytecode function FUNCTIONがバイトコード関数オブジェクトなら、それがまだ完全にロード されていなければ、バイトコンパイル済みのファイルからのFUNCTIONのバ イトコードのロードを完了させる。それ以外なら何も行わない。この関数 は常にFUNCTIONをリターンする。 16.5 コンパイル中の評価 ======================= これらの機能によりプログラムのコンパイル中に評価されるコードを記述できま す。 -- Special Form: eval-and-compile body... このフォームはそれを含むコードがコンパイルされるとき、および(コンパ イルされているかいないかに関わらず)実行されるときの両方でBODYが評価 されるようにマークする。 BODYを別のファイルに配置して、そのファイルを‘require’で参照すれば同 様の結果が得られる。これはBODYが大きいときに望ましい方法である。事 実上、‘require’は自動的に‘eval-and-compile’されて、そのパッケージは コンパイル時と実行時の両方でロードされる。 ‘autoload’も実際は‘eval-and-compile’される。これはコンパイル時に認 識されるので、そのような関数の使用により警告“not known to be defined”は生成されない。 ほとんどの‘eval-and-compile’の使用は、完全に妥当であると言えよう。 あるマクロがマクロの結果を構築するためのヘルパー関数をもち、そのマ クロがそのパッケージにたいしてローカルと外部の両方で使用される場合 には、コンパイル時と後の実行時にそのヘルパー関数を取得するために ‘eval-and-compile’を使用すること。 これは関数がプログラム的に(‘fset’で)定義されている場合には、コンパ イル時と実行時にプログラム的な定義を行わせてそれらの関数の呼び出し をチェックするためにも使用できる(“not known to be defined”の警告は 抑制される)。 -- Special Form: eval-when-compile body... このフォームはBODYがコンパイル時に評価され、コンパイルされたプログ ラムがロードされるときは評価されないようにマークする。コンパイラー による評価の結果はコンパイル済みのプログラム内の定数となる。ソース ファイルをコンパイルではなくロードすると、BODYは通常どおり評価され る。 生成するために何らかの計算が必要な定数があるなら、 ‘eval-when-compile’はコンパイル時にそれを行なうことができる。たとえ ば、 (defvar my-regexp (eval-when-compile (regexp-opt '("aaa" "aba" "abb")))) 他のパッケージを使用しているが、そのパッケージのマクロ(バイトコンパ イラーはそれらを展開します)だけが必要なら、それらを実行せずにコンパ イル用にロードさせるために‘eval-when-compile’を使用できる。たとえば 、 (eval-when-compile (require 'my-macro-package)) これらの事項は、マクロと‘defsubst’関数がローカルに定義されていて、 そのファイル内だけで使用されることを要求する。これらはそのファイル のコンパイルに必要だが、コンパイル済みファイルの実行には、ほとんど の場合必要ない。たとえば、 (eval-when-compile (unless (fboundp 'some-new-thing) (defmacro 'some-new-thing () (compatibility code)))) これは大抵は他のバージョンのEmacsとの互換性の保証のためのコードにた いしてのみ有用である。 *Common Lispに関する注意:* トップレベルでは、‘eval-when-compile’は Common Lispのイディオム‘(eval-when (compile eval) ...)’に類似する。 トップレベル以外では、Common Lispのリーダーマクロ‘#.’(ただし解釈時 を除く)が、‘eval-when-compile’と近いことを行う。 16.6 コンパイラーのエラー ========================= バイトコンパイルのエラーメッセージと警告メッセージは、‘*Compile-Log*’と いう名前のバッファーにプリントされます。これらのメッセージには、問題とな る箇所を示すファイル名と行番号が含まれます。これらのメッセージにたいして 、コンパイラー出力を操作する通常のEmacsコマンドが使用できます。 あるエラーがプログラムのシンタックスに由来する場合、バイトコンパイラ ーはエラーの正確な位置の取得に際して混乱するかもしれません。バッファー ‘ *Compiler Input*’.にスイッチするのは、これを調べ1つの方法です(このバッ ファー名はスペースで始まるので、Buffer Menuに表示されない)。このバッファ ーにはコンパイルされたプログラムと、バイトコンパイラーが読み取った箇所か らポイントがどれほど離れているかが含まれ、エラーの原因はその近傍の可能性 があります。シンタックスエラーを見つけるヒントについては、*note Syntax Errors::を参照してください。 定義されていない関数や変数の使用は、バイトコンパイラーにより報告され る警告のタイプとしては一般的です。そのような警告では、定義されていない関 数や変数を使用した位置ではなく、そのファイルの最後の行の行番号が報告され るので、それを見つけるには手作業で検索しなければなりません。 定義のない関数や変数の警告が間違いだと確信できる場合には、警告を抑制 する方法がいくつかあります: • 関数FUNCへの特定の呼び出しにたいする警告は、それを条件式‘fboundp’で テストすることで抑制できる: (if (fboundp 'FUNC) ...(FUNC ...)...) FUNCへの呼び出しは‘if’文のTHEN-FORM内になければならず、FUNCは ‘fboundp’呼び出し内でクォートされていなければならない(この機能は ‘cond’でも同様に機能する)。 • 同じように、変数VARIABLEの特定の使用についの警告を、条件式内の ‘boundp’テストで抑制できる: (if (boundp 'VARIABLE) ...VARIABLE...) VARIABLEへの参照は‘if’文のTHEN-FORM内になければならず、VARIABLEは ‘boundp’呼び出し内でクォートされていなければならない。 • コンパイラーに関数が‘declare-function’を使用して定義されていると告 げることができる。*note Declaring Functions::を参照のこと。 • 同じように、その変数が初期値なしの‘defvar’を使用して定義されている とコンパイラーに告げることができる(これはその変数を特別な変数として マークすることに注意。*note Defining Variables::を参照)。 ‘with-no-warnings’構文を使用して特定の式にたいするコンパイラーの任意 の警告をすべて抑制することもできます: -- Special Form: with-no-warnings body... これは実行時には‘(progn BODY...)’と等価だが、コンパイラーはBODYの中 で起こるいかなる事項にたいしても警告を発しない。 わたしたちは、あなたが抑制したいと意図する警告以外の警告を失わない ようにするために、可能な限り小さいコード断片にたいしてこの構文を使 用することを推奨する。 変数‘byte-compile-warnings’をセットすることにより、コンパイラーの警告 をより詳細に制御できます。詳細は変数のドキュメント文字列を参照してくださ い。 16.7 バイトコード関数オブジェクト ================================= バイトコンパイルされた関数は、“バイトコード関数オブジェクト(byte-code function objects)”という特別なデータ型をもちます。関数呼び出しとしてその ようなオブジェクトが出現したとき、Emacsはそのバイトコードを実行するため に、常にバイトコードインタープリターを使用します。 内部的にはバイトコード関数オブジェクトはベクターとよく似ています。バ イトコード関数オブジェクトの要素には‘aref’を通じてアクセスできます。バイ トコード関数オブジェクトのプリント表現(printed representation)はベクター と似ていて、開き‘[’の前に‘#’が追加されます。バイト関数オブジェクトは少な くとも4つの要素をもたねばならず、その要素数に上限はありません。しかし通 常使用されるのは最初の6要素です。これらは: ARGDESC 引数の記述子(descriptor)。これは*note Argument List::で説明されるよ うな引数のリスト、または要求される引数の個数をエンコードする整数の いずれかである。後者の場合、その記述子の値は0ビットから6ビットで引 数の最小個数、8ビットから14ビットで引数の最大個数を指定する。引数リ ストが‘&rest’を使用するなら7ビットがセットされて、それい以外ならク リアーされる。 ARGDESCがリストなら、そのバイトコード実行前に引数はダイナミックにバ インドされる。ARGDESCが整数なら、引数リストはそのバイトコード実行前 にバイトコーピンタープリンターのスタックにpushされる。 BYTE-CODE バイトコード命令を含む文字列。 CONSTANTS バイトコードにより参照されるLispオブジェクトのベクター。関数名と変 数名に使用されるシンボルが含まれる。 STACKSIZE この関数が要するスタックの最大サイズ。 DOCSTRING (もしあれば)ドキュメント文字列。それ以外は‘nil’。ドキュメント文字列 がファイルに格納されている場合、値は数字かリストかもしれない。本当 のドキュメント文字列の取得には、関数‘documentation’を使用する(*note Accessing Documentation::を参照)。 INTERACTIVE (もしあれば)インタラクティブ仕様。文字列かLisp式。インタラクティブ でない関数では‘nil’。 以下はバイトコード関数オブジェクトのプリント表現の例です。これはコマ ンド‘backward-sexp’の定義です。 #[256 "\211\204^G^@\300\262^A\301^A[!\207" [1 forward-sexp] 3 1793299 "^p"] バイトコードオブジェクトを作成するプリミティブな方法は ‘make-byte-code’です: -- Function: make-byte-code &rest elements この関数はELEMENTSを要素とするバイトコードオブジェクトを構築してリ ターンする。 あなた自身で要素を収集してバイトコード関数を構築しないでください。そ れらが矛盾する場合、その関数の呼び出しによりEmacsがクラッシュするかもし れません。これらのオブジェクトの作成は常にバイトコンパイラーにまかせてく ださい。(願わくば)バイトコンパイラーは要素を矛盾なく構築します。 16.8 逆アセンブルされたバイトコード =================================== 人はバイトコードを記述しません。それはバイトコンパイラーの仕事です。しか し好奇心を満たすために、わたしたちはディスアセンブラを提供しています。デ ィスアセンブラはバイトコードを人間が読めるフォームに変換します。 バイトコードインタープリターは、シンプルなスタックマシンとして実装さ れています。これは値を自身のスタックにpushして、計算で使用するためにそれ らをpopして取り出し、その結果を再びそのスタックにpushして戻します。バイ トコード関数がリターンするときは、スタックから値をpopして取り出し、その 関数の値としてリターンします。 それに加えてスタックとバイトコード関数は、値を変数とスタック間で転送 することにより、普通のLisp変数を使用したり、バインドやセットを行うことが できます。 -- Command: disassemble object &optional buffer-or-name このコマンドはOBJECTにたいするディスアセンブルされたコードを表示す る。インタラクティブに使用した場合、またはBUFFER-OR-NAMEが‘nil’か省 略された場合は、‘*Disassemble*’という名前のバッファーに出力します。 BUFFER-OR-NAMEが非‘nil’なら、それはバッファーもしくは既存のバッファ ーの名前でなければならない。その場合は、そのバッファーのポイント位 置に出力され、ポイントは出力の前に残りされる。 引数OBJECTには関数名、ラムダ式(*note Lambda Expressions::を参照)、 またはバイトコードオブジェクト(*note Byte-Code Objects::を参照)を指 定できる。ラムダ式なら‘disassemble’はそれをコンパイルしてから、その コンパイル済みコードをディスアセンブルする。 以下に‘disassemble’関数を使用した例を2つ示します。バイトコードと Lispソースを関連付ける助けとなるように、説明的なコメントを追加してありま す。これらのコメントは‘disassemble’の出力にはありません。 (defun factorial (integer) "Compute factorial of an integer." (if (= 1 integer) 1 (* integer (factorial (1- integer))))) ⇒ factorial (factorial 4) ⇒ 24 (disassemble 'factorial) ⊣ byte-code for factorial: doc: Compute factorial of an integer. args: (integer) 0 varref integer ; ‘integer’の値を取得して ; それをスタック上にpushする 1 constant 1 ; スタック上に1をpushする 2 eqlsign ; 2つの値をスタックからpopして取り出し、 ; それらを比較して結果をスタック上にpushする 3 goto-if-nil 1 ; スタックのトップをpopしてテストする ; ‘nil’なら1へ、それ以外はcontinue 6 constant 1 ; スタックのトップに1をpushする 7 return ; スタックのトップの要素をリターンする 8:1 varref integer ; ‘integer’の値をスタック上にpushする 9 constant factorial ; ‘factorial’をスタック上にpushする 10 varref integer ; ‘integer’の値をスタック上にpushする 11 sub1 ; ‘integer’をpopして値をデクリメントする ; スタック上に新しい値をpushする 12 call 1 ; スタックの最初(トップ)の要素を引数として ; 関数‘factorial’を呼び出す ; リターン値をスタック上にpushする 13 mult ; スタックのトップ2要素をpopして取り出し乗じ ; 結果をスタック上にpushする 14 return ; スタックのトップ要素をリターンする ‘silly-loop’は幾分複雑です: (defun silly-loop (n) "Return time before and after N iterations of a loop." (let ((t1 (current-time-string))) (while (> (setq n (1- n)) 0)) (list t1 (current-time-string)))) ⇒ silly-loop (disassemble 'silly-loop) ⊣ byte-code for silly-loop: doc: Return time before and after N iterations of a loop. args: (n) 0 constant current-time-string ; ‘current-time-string’を ; スタック上のトップにpushする 1 call 0 ; 引数なしで‘current-time-string’を呼び出し ; 結果をスタック上にpushする 2 varbind t1 ; スタックをpopして‘t1’にpopされた値をバインドする 3:1 varref n ; 環境から‘n’の値を取得して ; その値をスタック上にpushする 4 sub1 ; スタックのトップから1を減ずる 5 dup ; スタックのトップを複製する ; たとえばスタックのトップをコピーしてスタック上にpushする 6 varset n ; スタックのトップをpopして ; ‘n’をその値にバインドする ;; (要はシーケンス‘dup varset’はpopせずに ;; スタックのトップを‘n’の値にコピーする) 7 constant 0 ; スタック上に0をpushする 8 gtr ; スタックのトップ2値をpopして取り出し ; Nが0より大かテストし ; 結果をスタック上にpushする 9 goto-if-not-nil 1 ; ‘n’ > 0なら1へ ; (これはwhile-loopを継続する) ; それ以外はcontinue 12 varref t1 ; ‘t1’の値をスタック上にpushする 13 constant current-time-string ; ‘current-time-string’を ; スタックのトップにpushする 14 call 0 ; 再度‘current-time-string’を呼び出す 15 unbind 1 ; ローカル環境の‘t1’をアンバインドする 16 list2 ; スタックのトップ2要素をpopして取り出し ; それらのリストを作りスタック上にpushする 17 return ; スタックのトップの値をリターンする 17 Lispプログラムのデバッグ *************************** Emacs Lispプログラム内の問題を見つけて詳細に調べる方法がいくつかあります 。 • プログラム実行中に問題が発生した場合には、Lisp評価機能をサスペンド するためにビルトインのEmacs Lispデバッガを使用して評価機能の内部状 態の調査および/または変更を行なうことができる。 • Emacs LispにたいするソースレベルデバッガのEdebugを使用できる。 • 文法的な問題によりLispがプログラムを読むことさえできない場合には、 Lisp編集コマンドを使用して該当箇所を見つけることができる。 • バイトコンパイラーがプログラムをコンパイルするとき、コンパイラーに より生成されるエラーメッセージと警告メッセージを調べることができる 。*note Compiler Errors::を参照のこと。 • Testcoverパッケージを使用してプログラムのテストカバレッジを行なうこ とができる。 • ERTパッケージを使用してプログラムにたいするリグレッションテストを記 述できる。*note the ERT manual: (ert)Top.を参照のこと。 • プログラムをプロファイルしてプログラムをより効果的にするためのヒン トを取得できる。 入出力の問題をデバックする便利なその他のツールとして、ドリブルファイ ル(dribble file: *note Terminal Input::を参照)と、‘open-termscript’関数 (*note Terminal Output::)があります。 17.1 Lispデバッガ ================= 普通の“Lispデバッガ”は、フォーム評価のサスペンド機能を提供します。評価が サスペンド(一般的には“break”の状態として知られる)されている間、実行時ス タックを調べたり、ローカル変数やグローバル変数の値を調べたり変更すること ができます。breakは再帰編集(recursive edit)なので、Emacsの通常の編集機能 が利用可能です。デバッガにエンターするようにプログラムを実行することさえ 可能です。*note Recursive Editing::を参照してください。 17.1.1 エラーによるデバッガへのエンター --------------------------------------- デバッガに入るタイミングとして一番重要なのは、Lispエラーが発生したときで す。デバッガではエラーの直接原因を調査できます。 しかしデバッガへのエンターは、エラーによる通常の結末ではありません。 多くのコマンドは不適切に呼び出されたときにLispエラーをシグナルするので、 通常の編集の間にこれが発生するたびデバッガにエンターするのは、とても不便 でしょう。したがってエラーの際にデバッガにエンターしたいなら、変数 ‘debug-on-error’に非‘nil’をセットします(コマンド ‘toggle-debug-on-error’はこれを簡単に行う方法を提供する)。 -- User Option: debug-on-error この変数はエラーがシグナルされて、それがハンドルされていないときに デバッガを呼び出すかどうかを決定する。‘debug-on-error’が‘t’なら、 ‘debug-ignored-errors’(以下参照)にリストされているエラー以外の、す べての種類のエラーがデバッガを呼び出す。‘nil’ならデバッガを呼び出さ ない。 値にはエラー条件(*note Signaling Errors::を参照)のリストも指定でき る。その場合はこのリスト内のエラー条件だけによってデバッガが呼び出 される(‘debug-ignored-errors’にもリストされているエラー条件は除外さ れる)。たとえば‘debug-on-error’をリスト‘(void-variable)’にセットす ると、値をもたない変数に関するエラーにたいしてのみデバッガが呼び出 される。 ‘eval-expression-debug-on-error’がこの変数をオーバーライドするケー スがいくつかあることに注意(以下参照)。 この変数が非‘nil’のとき、Emacsはプロセスフィルター関数と番兵 (sentinel)の周囲にエラーハンドラーを作成しない。したがってこれらの 関数内でのエラーは、デバッガを呼び出す。*note Processes::を参照のこ と。 -- User Option: debug-ignored-errors この変数は‘debug-on-error’の値に関わらず、デバッガにエンターすべき でないエラーを指定する。変数の値はエラー条件のシンボルおよび/または 正規表現のリスト。エラーがこれら条件シンボルのいずれか、またはエラ ーメッセージが正規表現のいずれかにマッチすれば、そのエラーはデバッ ガにエンターしない。 この変数の通常の値には‘user-error’、および編集中に頻繁に発生するが Lispプログラムのバグに起因することは稀であるような、いくつかのエラ ーが含まれる。しかし“稀である”ことは“絶対ない”ということではない。 あなたのプログラムがこのリストにマッチするエラーによって機能しない なら、そのエラーをデバッグするためにこのリストの変更を試みるのもよ いだろう。通常は‘debug-ignored-errors’を‘nil’にセットしておくのが、 もっとも簡単な方法である。 -- User Option: eval-expression-debug-on-error この変数が非‘nil’値(デフォルト)なら、コマンド‘eval-expression’の実 行によって一時的に‘debug-on-error’が‘t’がバインドされる。*note Evaluating Emacs-Lisp Expressions: (emacs)Lisp Eval.を参照のこと。 ‘eval-expression-debug-on-error’が‘nil’なら、‘eval-expression’の間 も‘debug-on-error’の値は変更されない。 -- User Option: debug-on-signal ‘condition-case’でキャッチされたエラー、は通常は決してデバッガを呼 び出さない。‘condition-case’はデバッガがそのエラーをハンドルする前 にエラーをハンドルする機会を得る。 ‘debug-on-signal’を非‘nil’値に変更すると、‘condition-case’の存在如 何に関わらずすべてのエラーにおいてデバッガが最初に機会を得る(デバッ ガを呼び出すためには依然としてそのエラーが‘debug-on-error’と ‘debug-ignored-errors’で指定された条件を満たさなければならない)。 *警告:* この変数を非‘nil’にセットすると、芳しくない効果があるかもし れない。Emacsのさまざまな部分で処理の通常の過程としてエラーがキャッ チされており、そのエラーが発生したことに気づかないことさえあるかも しれない。‘condition-case’でラップされたコードをデバッグする必要が あるなら、‘condition-case-unless-debug’(*note Handling Errors::を参 照)の使用を考慮されたい。 -- User Option: debug-on-event ‘debug-on-event’をスペシャルイベント(*note Special Events::を参照 )にセットすると、Emacsは‘special-event-map’をバイパスしてこのイベン トを受け取ると即座にデバッガへのエンターを試みる。現在のところサポ ートされる値は、シグナル‘SIGUSR1’と‘SIGUSR2’に対応する値のみ(これが デフォルト)。これは‘inhibit-quit’がセットされていて、それ以外は Emacsが応答しない場合に有用かもしれない。 -- Variable: debug-on-message ‘debug-on-message’に正規表現をセットした場合は、それにマッチするメ ッセージがエコーエリアに表示されると、Emacsはデバッガにエンターする 。たとえばこれは特定のメッセージの原因を探すのに有用かもしれない。 initファイルロード中に発生したエラーをデバッグするには、オプション ‘--debug-init’を使用する。これはinitファイルロードの間に ‘debug-on-error’を‘t’にバインドして、通常はinitファイル内のエラーをキャ ッチする‘condition-case’をバイパスする。 17.1.2 無限ループのデバッグ --------------------------- プログラムが無限にループしてリターンできないとき、最初の問題はそのループ をいかに停止するかです。ほとんどのオペレーティングシステムでは、 (“quit”させる)‘C-g’でこれを行うことができます。*note Quitting::を参照し てください。 普通のquitでは、なぜそのプログラムがループしたかについての情報は与え られません。変数‘debug-on-quit’に非‘nil’をセットすることにより、より多く の情報を得ることができます。無限ループの途中でデバッガを実行すれば、デバ ッガからステップコマンドで先へ進むことができます。ループ全体をステップで 追えば、問題を解決するために十分な情報が得られるでしょう。 ‘C-g’によるquitはエラーとは判断されないので、‘C-g’のハンドルに ‘debug-on-error’は効果がありません。同じように‘debug-on-quit’はエラーに たいして効果がありません。 -- User Option: debug-on-quit この変数は‘quit’がシグナルされて、それがハンドルされていないときに デバッガを呼び出すかどうかを決定する。‘debug-on-quit’が非‘nil’なら 、quit(つまり‘C-g’をタイプ)したときは常にデバッガが呼び出される。 ‘debug-on-quit’が‘nil’(デフォルト)なら、quitしてもデバッガは呼び出 されない。 17.1.3 関数呼び出しによるデバッガへのエンター --------------------------------------------- プログラムの途中で発生する問題を調べるための有用なテクニックの1つは、特 定の関数が呼び出されたときデバッガにエンターする方法です。問題が発生した 関数にこれを行ってその関数をステップで追ったり、問題箇所の少し手前の関数 呼び出しでこれを行って、その関数をステップオーバーしてその後をステップで 追うことができます。 -- Command: debug-on-entry function-name この関数はFUNCTION-NAMEが呼び出されるたびにデバッガの呼び出しを要求 する。 Lispコードで定義された任意の関数とマクロは、インタープリターに解釈 されたコードかコンパイル済みのコードかに関わらず、エントリーに breakをセットできる。その関数がコマンドならLispから呼び出されたとき と、インタラクティブに呼び出されたときにデバッガにエンターする。(た とえばCで記述された)プリミティブ関数にもこの方法で ‘debug-on-entry’をセットできるが、そのプリミティブがLispコードから 呼び出されたときだけ効果がある。‘debug-on-entry’はスペシャルフォー ムにはセットできない。 ‘debug-on-entry’がインタラクティブに呼び出されたときは、ミニバッフ ァーでFUNCTION-NAMEの入力を求める。その関数がすでにエントリーでデバ ッガを呼び出すようにセットアップされていたら、‘debug-on-entry’は何 も行わない。‘debug-on-entry’は常にFUNCTION-NAMEをリターンする。 以下はこの関数の使い方を説明するための例である: (defun fact (n) (if (zerop n) 1 (* n (fact (1- n))))) ⇒ fact (debug-on-entry 'fact) ⇒ fact (fact 3) ------ Buffer: *Backtrace* ------ Debugger entered--entering a function: * fact(3) eval((fact 3)) eval-last-sexp-1(nil) eval-last-sexp(nil) call-interactively(eval-last-sexp) ------ Buffer: *Backtrace* ------ -- Command: cancel-debug-on-entry &optional function-name この関数はFUNCTION-NAMEにたいする‘debug-on-entry’の効果をアンドゥす る。インタラクティブに呼び出されたときは、ミニバッファーで FUNCTION-NAMEの入力を求める。FUNCTION-NAMEが省略または‘nil’なら、す べての関数にたいするbreak-on-entryをキャンセルする。エントリー時に breakするようセットアップされていない関数に ‘cancel-debug-on-entry’を呼び出したときは何も行わない。 17.1.4 明示的なデバッガへのエントリー ------------------------------------- プログラム内の特定箇所に式‘(debug)’を記述することによって、その箇所でデ バッガが呼び出されるようにできます。これを行うにはソースファイルを visitして、適切な箇所にテキスト‘(debug)’を挿入し、‘C-M-x’(Lispモードでの ‘eval-defun’にたいするキーバインド)をタイプします。*警告:* 一時的なデバ ッグ目的のためにこれを行なう場合には、ファイルを保存する前に確実にアンド ゥしてください! ‘(debug)’を挿入する箇所は追加フォームが評価されることができ、かつその 値を無視することができる箇所でなければなりません(‘(debug)’の値を無視しな いとプログラムの実行が変更されてしまうだろう!)。一般的にもっとも適した箇 所は、‘progn’または暗黙的な‘progn’(*note Sequencing::を参照)の内部です。 デバッグ命令を配置したいソースコード中の正確な箇所がわからないが、特 定のメッセージが表示されたときにバックトレースを表示したい場合には、意図 するメッセージにマッチする正規表現を‘debug-on-message’にセットできます。 17.1.5 デバッガの使用 --------------------- デバッガにエンターすると、その前に選択されていたウィンドウを1つのウィン ドウに表示して、他のウィンドウに‘*Backtrace*’という名前のバッファーを表 示します。backtraceバッファーには、現在実行されているLisp関数の各レベル が1行ずつ含まれます。このバッファーの先頭は、デバッガが呼び出された理由 を説明するメッセージ(デバッガがエラーにより呼び出された場合はエラーメッ セージや関連するデータなど)です。 backtraceバッファーは読み取り専用で、文字キーにデバッガコマンドが定義 されたDebuggerモードという特別なメジャーモードを使用します。通常の Emacs編集コマンドが利用できます。したがってエラー時に編集されていたバッ ファーを調べるためにウィンドウを切り替えたり、バッファーの切り替えやファ イルのvisit、その他一連の編集処理を行なうことができます。しかしデバッガ は再帰編集レベル(*note Recursive Editing::を参照)にあり、編集が終わった らそれはbacktraceバッファーに戻って、(‘q’コマンドで)デバッガをexitできま す。デバッガをexitすることによって再帰編集を抜け出し、backtraceバッファ ーはバリー(bury: 覆い隠す)されます(変数‘debugger-bury-or-killw’をセット することによってbacktraceバッファーで‘q’コマンドが何を行うかをカスタマイ ズできる。たとえばバッファーをバリーせずにkillしたいなら、この変数を ‘kill’にセットする。他の値については変数のドキュメントを調べてほしい)。 デバッガにエンターしたとき、‘eval-expression-debug-on-error’に一致す るように変数‘debug-on-error’が一時的にセットされます。変数 ‘eval-expression-debug-on-error’が非‘nil’なら、‘debug-on-error’は一時的 に‘t’にセットされます。これはデバッグセッション行っている間にさらにエラ ーが発生すると、(デフォルトでは)他のbacktraceがトリガーされることを意味 します。これが望ましくなければ、‘debugger-mode-hook’内で ‘eval-expression-debug-on-error’を‘nil’にセットするか、 ‘debug-on-error’を‘nil’にセットすることができます。 backtraceバッファーは実行されている関数と、その関数の引数の値を示しま す。そのフレームを示す行にポイントを移動して、スタックフレームを指定する こともできます(スタックフレームとは、Lispインタープリターがある関数への 特定の呼び出しを記録する場所のこと)。行ポイントがオンのフレームが、“カレ ントフレーム(current frame)”となります。デバッガコマンドのいくつかは、カ レントフレームを処理します。ある行がスター(star)で始まる場合は、そのフレ ームをexitすることによって再びデバッガが呼び出されることを意味します。こ れは関数のリターン値を調べるとき有用です。 関数名にアンダーラインが引かれている場合は、デバッガがその関数のソー スコードの位置を知っていることを意味します。その名前をマウスでクリックす るか、そこに移動してをタイプすれば、ソースコードをvisitできます。 デバッガはデバッガ自身のスタックフレーム数を想定するため、バイトコン パイルされて実行されなければなりません。デバッガがインタープリターに解釈 されて実行されているときは、これらの想定は正しくなくなります。 17.1.6 デバッガのコマンド ------------------------- (Debuggerモードの)debuggerバッファーでは、通常のEmacsコマンドに加えて特 別なコマンドが提供されます。デバッガでもっとも重要な使い方をするのは、制 御フローを見ることができるコードをステップ実行するコマンドです。デバッガ はインタープリターによって解釈された制御構造のステップ実行はできますが、 バイトコンパイル済みの関数ではできません。バイトコンパイル済み関数をステ ップ実行したいなら、同じ関数の解釈された定義に置き換えてください(これを 行なうにはその関数のソースをvisitして、関数の定義で‘C-M-x’とタイプする )。プリミティブ関数のステップ実行にLispデバッガは使用できません。 以下はDebuggerモードのコマンドのリストです: ‘c’ デバッガをexitして実行を継続する。これはあたかもデバッガにエンター しなかったかのようにプログラムの実行を再開する(デバッガ内で行った変 数値やデータ構造の変更などの副作用は除外)。 ‘d’ 実行を継続するが、次にLisp関数が何か呼び出されたときはデバッガにエ ンターする。これによりある式の下位の式をステップ実行して、下位の式 が計算する値や行うことを確認できる。 デバッガにエンターした関数呼び出しにたいして、この方法で作成された スタックフレームには自動的にフラグがつくため、そのフレームをexitす ると再びデバッガが呼び出される。このフラグは‘u’コマンドを使用してキ ャンセルできる。 ‘b’ カレントフレームにフラグをつけるので、そのフレームをexitするときデ バッガにエンターする。この方法でフラグがつけられたフレームは、 backtraceバッファーでスターのマークがつく。 ‘u’ カレントフレームをexitしたときデバッガにエンターしない。これはその フレームの‘b’コマンドをキャンセルする。目に見える効果としては backtraceバッファーの行からスターが削除される。 ‘j’ ‘b’と同じようにカレントフレームにフラグをつける。その後に‘c’のよう に実行を継続するが、‘debug-on-entry’によりセットアップされたすべて の関数にたいするbreak-on-entryを一時的に無効にする。 ‘e’ ミニバッファーのLisp式を読み取り、(関連するlexical環境が適切なら)そ れを評価してエコーエリアに値をプリントする。デバッガは特定の重要な 変数とバッファーを処理の一部として変更する。‘e’は一時的にデバッガの 外部からそれらの値をリストアするので、それらを調べて変更できる。こ れによりデバッガはより透過的になる。対照的にデバッガ内で‘M-:’は特別 なことを行わず、デバッガ内での変数の値を表示する。 ‘R’ ‘e’と同様だがバッファー‘*Debugger-record*’内の評価結果も保存する。 ‘q’ デバッグされているプログラムを終了して、Emacsコマンド実行のトップレ ベルにリターンする。 ‘C-g’によりデバッガにエンターしたが、実際はデバッグではなくquitした いときは‘q’コマンドを使用する。 ‘r’ デバッガから値をリターンする。ミニバッファーで式を読み取ってそれを 評価することにより値が計算される。 ‘d’コマンドは、(‘b’によるリクエストや‘d’によるそのフレームへのエン ターによる)Lisp呼び出しフレームからのexitでデバッガが呼び出されたと きに有用である。‘r’コマンドで指定された値は、そのフレームの値として 使用される。これは‘debug’を呼び出して、そのリターン値を使用するとき にも有用。それ以外は‘r’は‘c’と同じ効果をもち、指定されたリターン値 は問題とはならない。 エラーによりデバッガにエンターしたときは‘r’コマンドは使用できない。 ‘l’ 呼び出されたときにデバッガを呼び出す関数をリストする。これは ‘debug-on-entry’によりエントリー時にbreakするようセットされた関数の リストである。 ‘v’ カレントスタックフレームのローカル変数の表示を切り替える。 17.1.7 デバッガの呼び出し ------------------------- 以下ではデバッガを呼び出すために使用される関数‘debug’の完全な詳細を説明 します。 -- Command: debug &rest debugger-args この関数はデバッガにエンターする。この関数は‘*Backtrace*’(デバッガ への2回目以降の再帰エントリーでは‘*Backtrace*<2>、...’)という名前の バッファーにバッファーを切り替えて、Lisp関数呼び出しについての情報 を書き込む。それから再帰編集にエンターして、Debuggerモードで backtraceバッファーを表示する。 Debuggerモードのコマンド‘c’、‘d’、‘j’、‘r’は再帰編集をexitする。そ の後、‘debug’は以前のバッファーに戻って、‘debug’を呼び出したものが 何であれそこにリターンする。これは関数‘debug’が呼び出し元にリターン できる唯一の方法である。 DEBUGGER-ARGSを使用すると、‘debug’は‘*Backtrace*’の最上部に残りの引 数を表示するしてユーザーがそれらを確認できる。以下で説明する場合を 除いて、これはこれらの引数を使用する_唯一_の方法である。 しかし‘debug’への1つ目の引数にたいする値は、特別な意味をもつ(これら の値は通常は‘debug’を呼び出すプログラマーではなく、Emacs内部でのみ 使用される)。以下はこれら特別な値のテーブルである: ‘lambda’ 1つ目の引数が‘lambda’のなら、それは‘debug-on-next-call’が非 ‘nil’のときに関数にエントリーしたことによって‘debug’が呼び出さ れたことを意味する。デバッガはバッファーのトップのテキスト行に ‘Debugger entered--entering a function:’と表示する。 ‘debug’ 1つ目の引数が‘debug’なら、それはエントリー時にデバッグされるよ うにセットされた関数にエントリーしたことにって‘debug’が呼び出 されたことを意味する。デバッガは‘lambda’のときと同様、 ‘Debugger entered--entering a function:’を表示する。これはその 関数のスタックフレームもマークするので、exit時にデバッガが呼び 出される。 ‘t’ 1つ目の引数が‘t’なら、それは‘debug-on-next-call’が非‘nil’のと きに関数呼び出しの評価によって‘debug’が呼び出されたことを示す 。デバッガはバッファーのトップの行に‘Debugger entered--beginning evaluation of function call form:’と表示す る。 ‘exit’ 1つ目の引数が‘exit’のときは、exit時にデバッガを呼び出すよう以 前にマークされたスタックフレームをexitしたことを示す。この場合 は‘debug’に与えられた2つ目の引数がそのフレームからリターンされ た値になる。デバッガはバッファーのトップの行に‘Debugger entered--returning value:’とリターンされた値を表示する。 ‘error’ 1つ目の引数が‘error’のときは、ハンドルされていないエラーまたは ‘quit’がシグナルされてデバッガにエンターした場合であり、デバッ ガは‘Debugger entered--Lisp error:’とその後にシグナルされたエ ラーと‘signal’への引数を表示してそれを示す。たとえば、 (let ((debug-on-error t)) (/ 1 0)) ------ Buffer: *Backtrace* ------ Debugger entered--Lisp error: (arith-error) /(1 0) ... ------ Buffer: *Backtrace* ------ エラーがシグナルされた場合はおそらく変数‘debug-on-error’は非 ‘nil’で、‘quit’がシグナルされた場合はおそらく変数 ‘debug-on-quit’は非‘nil’である。 ‘nil’ 明示的にデバッガにエンターしたいときは、DEBUGGER-ARGSの1つ目の 引数に‘nil’を使用する。残りのDEBUGGER-ARGSはバッファーのトップ の行にプリントされる。メッセージ — たとえば‘debug’が呼び出され た条件を思い出すためのリマインダーとして — の表示にこの機能を 使用できる。 17.1.8 デバッガの内部 --------------------- このセクションではデバッガ内部で使用される関数と変数について説明します。 -- Variable: debugger この関数の値はデバッガを呼び出す関数呼び出しである。値には任意個数 の引数をとる関数、より具体的には関数の名前でなければならない。この 関数は何らかのデバッガを呼び出すこと。この変数のデフォルト値は ‘debug’。 関数にたいしてLispが渡す1つ目の引数は、その関数がなぜ呼び出されたか を示す。引数の慣習については‘debug’(*note Invoking the Debugger::)に 詳解がある。 -- Command: backtrace この関数は現在アクティブなLisp関数呼び出しのトレースをプリントする 。この関数は‘debug’が‘*Backtrace*’バッファーに書き込む内容を得るた めに使用される。どの関数呼び出しがアクティブか判断するためにスタッ クにアクセスしなければならないので、この関数はCで記述されている。リ ターン値は常に‘nil’。 以下の例ではLisp式で明示的に‘backtrace’を呼び出している。これはスト リーム‘standard-output’(この場合はバッファー‘backtrace-output’)に backtraceをプリントする。 backtraceの各行は、1つの関数呼び出しを表す。関数の引数が既知なら行 に値が表示され、まだ計算中の場合は行にその旨が示される。スペシャル フォームの引数は無視される。 (with-output-to-temp-buffer "backtrace-output" (let ((var 1)) (save-excursion (setq var (eval '(progn (1+ var) (list 'testing (backtrace)))))))) ⇒ (testing nil) ----------- Buffer: backtrace-output ------------ backtrace() (list ...computing arguments...) (progn ...) eval((progn (1+ var) (list (quote testing) (backtrace)))) (setq ...) (save-excursion ...) (let ...) (with-output-to-temp-buffer ...) eval((with-output-to-temp-buffer ...)) eval-last-sexp-1(nil) eval-last-sexp(nil) call-interactively(eval-last-sexp) ----------- Buffer: backtrace-output ------------ -- Variable: debug-on-next-call この変数が非‘nil’なら、それは次の‘eval’、‘apply’、‘funcall’の前にデ バッガを呼び出すよう指定する。デバッガへのエンターによって ‘debug-on-next-call’は‘nil’にセットされる。 デバッガの‘d’コマンドは、この変数をセットすることにより機能します。 -- Function: backtrace-debug level flag この関数はそのスタックフレームのLEVEL下位のスタックフレームの debug-on-exitフラグにFLAGに応じた値をセットする。FLAGが非‘nil’なら 、後でそのフレームをexitするときデバッガにエンターする。そのフレー ムを通じた非ローカルexitでも、デバッガにエンターする。 この関数はデバッガだけに使用される。 -- Variable: command-debug-status この変数はカレントのインタラクティブコマンドのデバッグ状態を記録す る。コマンドがインタラクティブに呼び出されるたびに、この変数は ‘nil’にバインドされる。デバッガは同じコマンドが呼び出されたときのデ バッガ呼び出しに情報を残すために、この変数をセットできる。 普通のグローバル変数ではなくこの変数を使用する利点は、そのデータが 後続のコマンド呼び出しに決して引き継がれないことである。 -- Function: backtrace-frame frame-number 関数‘backtrace-frame’はLispデバッガ内での使用を意図している。これは FRAME-NUMBERレベル下位のスタックフレームで、何の評価が行われている かに関する情報をリターンする。 そのフレームがまだ引数を評価していない、またはそのフレームがスペシ ャルフォームの場合、値は‘(nil FUNCTION ARG-FORMS...)’。 そのフレームが引数を評価して関数をすでに呼び出していたら、リターン 値は‘(t FUNCTION ARG-VALUES...)’。 リターン値のFUNCTIONは何であれ評価されたリストのCARとして提供される 。マクロ呼び出しの場合は‘lambda’式になる。その関数に‘&rest’引数があ ればリストARG-VALUESの末尾に示される。 FRAME-NUMBERが範囲外なら‘backtrace-frame’は‘nil’をリターンする。 17.2 Edebug =========== EdebugはEmacs Lispプログラムにたいするソースレベルデバッガです。これによ り以下のことができます: • 式の前後でストップして評価をステップで実行する。 • 条件付きまたは無条件のbreakpointのセット。 • 指定された条件がtrueならストップする(グローバルbreakpoint)。 • ストップポイントごとに停止したり、breakpointごとに簡単に停止して低 速または高速にトレースを行う。 • Edebug外部であるかのように式の結果を表示して、式を評価する。 • 式のリストを自動的に再評価して、Edebugがディスプレイを更新するたび にそれらの結果を表示する。 • 関数呼び出しとリターンのトレース情報を出力する。 • エラー発生時にストップする。 • Edebug自身のフレームを除外してbacktraceを表示する。 • マクロとフォームの定義で引数の評価を指定する。 • 初歩的なカバレッジテストと頻度数の取得。 以下の初めの3つのセクションは、Edebugの使用を開始するために十分な説明 を行います。 17.2.1 Edebugの使用 ------------------- EdebugでLispプログラムをデバッグするには、最初にデバッグしたいLispコード を“インストルメント(instrument: 計装)”しなければなりません。これを行なう もっともシンプルな方法は、関数またはマクロの定義に移動して‘C-u C-M-x’(プ レフィクス引数を指定した‘eval-defun’)を行います。コードをインストルメン トする他の手段については、*note Instrumenting::を参照してください。 一度関数をインストルメントすると、その関数にたいする任意の呼び出しに よってEdebugがアクティブになります。Edebugがアクティブになると、どの Edebug実行モードを選択したかに依存して、その関数をステップ実行できるよう に実行がストップされるか、ディスプレイを更新してデバッグコマンドにたいす るチェックの間、実行が継続されます。デフォルトの実行モードstepで、これは 実行をストップします。*note Edebug Execution Modes::を参照してください。 Edebugでは通常は、デバッグしているLispコードをEmacsバッファーで閲覧し ます。これを“ソースコードバッファー(source code buffer)”と呼び、バッファ ーは一時的に読み取り専用になります。 左フリンジの矢印は、その関数で実行されている行を示します。ポイントは 最初はその関数の実行されている行にありますが、ポイントを移動するとこれは 真ではなくなります。 以下は‘fac’の定義(以下を参照)をインストルメントして‘(fac 3)’を実行し た場合に通常目にするものです。ポイントは‘if’の前の開きカッコにあります。 (defun fac (n) =>★(if (< 0 n) (* n (fac (1- n))) 1)) 関数内でEdebugが実行をストップできる位置のことを、“ストップポイント (stop points)”と呼びます。ストップポイントはリストであるような部分式の前 後、および変数参照の後でも発生します。以下は関数‘fac’内のストップポイン トをピリオドで示したものです: (defun fac (n) .(if .(< 0 n.). .(* n. .(fac .(1- n.).).). 1).) Emacs Lispモードのコマンドに加えて、ソースコードバッファーでは Edebugのスペシャルコマンドが利用できます。たとえばEdebugコマンドで 次のストップポイントまで実行することができます。‘fac’にエントリーした後 に一度とタイプした場合は、以下のように表示されるでしょう: (defun fac (n) =>(if ★(< 0 n) (* n (fac (1- n))) 1)) 式の後でEdebugが実行をストップしたときは、エコーエリアにその式の値が 表示されます。 他にも頻繁に使用されるコマンドとして、ストップポイントにbreakpointを セットする‘b’、breakpointに達するまで実行する‘g’、Edebugをexitしてトップ レベルのコマンドループにリターンする‘q’があります。また‘?’とタイプすると すべてのEdebugコマンドがリストされます。 17.2.2 Edebugのためのインストルメント ------------------------------------- LispコードのデバッグにEdebugを使用するためには、最初にそのコードを“イン ストルメント(instrument: 計装)”しなければなりません。コードをインストル メントすると、適切な位置でEdebugを呼び出すために追加コードが挿入されます 。 関数定義でプレフィクス引数とともにコマンド‘C-M-x’ (‘eval-defun’)を呼 び出すと、それを評価する前にその定義をインストルメントします(ソースコー ド自体は変更しない)。変数‘edebug-all-defs’が非‘nil’ならプレフィクス引数 の意味を反転します。この場合は、‘C-M-x’はプレフィクス引数が_なければ_そ の定義をインストルメントします。‘edebug-all-defs’のデフォルト値は‘nil’で す。コマンド‘M-x edebug-all-defs’は変数‘edebug-all-defs’の値を切り替えま す。 ‘edebug-all-defs’が非‘nil’なら‘eval-region’、‘eval-current-buffer’、 ‘eval-buffer’もそれらが評価する定義をインストルメントします。同様に ‘edebug-all-forms’は、‘eval-region’が(非定義フォームさえ含む_あらゆる_フ ォームをインストルメントするべきかを制御します。これはミニバッファー内で のロードや評価には適用されません。コマンド‘M-x edebug-all-forms’はこのオ プションを切り替えます。 他にもコマンド‘M-x edebug-eval-top-level-form’が利用でき、これは ‘edebug-all-defs’や‘edebug-all-forms’の値に関わらずトップレベルのすべて のフォームをインストルメントします。‘edebug-defun’は ‘edebug-eval-top-level-form’のエイリアスです。 Edebugがアクティブのの間、コマンド‘I’(‘edebug-instrument-callee’)はポ イント後のリストフォームに呼び出される関数およびマクロ定義がまだインスト ルメントされていなければ、それらをインストルメントします。これはそのファ イルのソースの場所をEdebugが知っている場合だけ可能です。この理由により Edebugロード後は、たとえ評価する定義をインストルメントしない場合でも、 ‘eval-region’は評価するすべての定義の位置を記録します。インストルメント 済み関数呼び出しにステップインする‘i’コマンドも参照してください(*note Jumping::を参照)。 Edebugはすべての標準スペシャルフォーム、式引数をもつ‘interactive’フォ ーム、無名ラムダ式、およびその他の定義フォームのインストルメント方法を知 っています。しかしEdebugはユーザー定義マクロが引数にたいして何を行うかを 判断できないので、Edebug仕様を使用してその情報を与えなければなりません。 詳細は*note Edebug and Macros::を参照してください。 Edebugがセッション内で最初にコードをインストルメントしようとするとき は、フック‘edebug-setup-hook’を実行してからそれに‘nil’をセットします。使 おうとしているパッケージに結びつけてEdebug仕様をロードするためにこれを使 用できますが、それはEdebugを使用するときだけ機能します。 定義からインストルメントを削除するには、単にインストルメントを行わな い方法でその定義を再評価するだけです。フォームを絶対にインストルメントせ ずに評価するには2つの方法があります。それはファイルからの‘load’による評 価と、ミニバッファーからの‘eval-expression’(‘M-:’)による評価です。 Edebugがインストルメント中にシンタックスエラー(syntax error: 構文エラ ー)を検知した場合は、間違ったコードの箇所にポイントを残して ‘invalid-read-syntax’エラーをシグナルします。 Edebug内で利用可能な他の評価関数については、*note Edebug Eval::を参照 してください。 17.2.3 Edebugの実行モード ------------------------- Edebugはデバッグするプログラムの実行にたいして、いくつかの実行モードをサ ポートします。これらの実行モードを“Edebug実行モード(Edebug execution modes)”と呼びます。これらをメジャーモードやマイナーモードと混同しないで ください。カレントのEdebug実行モードは、プログラムをストップする前に Edebugがどれだけ実行を継続するか — たとえばストップポイントごとにストッ プ、あるいは次のbreakpointまで継続など — 、およびストップする前に Edebugがどれだけ進捗を表示するかを決定します。 Edebug実行モードは、通常はある特定のモードでプログラムを継続させるコ マンドをタイプすることによって指定します。以下はそれらのコマンドのテーブ ルです。‘S’以外のコマンドはプログラムの実行を再開して、少なくともある長 さの間だけは実行を継続します。 ‘S’ Stop(ストップ): これ以上プログラムを実行しないでEdebugのコマンドを 待つ(‘edebug-stop’)。 ‘’ Step(ステップ): 次のストップポイントでストップする (‘edebug-step-mode’)。 ‘n’ Next(次へ): 式の後にある次のストップポイントでストップする (‘edebug-next-mode’)。*note Jumping::の‘edebug-forward-sexp’も参照 のこと。 ‘t’ Trace(トレース): Edebugのストップポイントごとにpause(通常は1秒)する (‘edebug-trace-mode’)。 ‘T’ Rapid trace(高速でトレース):ストップポイントごとに表示を更新するが 、実際にpauseはしない(‘edebug-Trace-fast-mode’)。 ‘g’ Go(進む): 次のbreakpointまで実行する(‘edebug-go-mode’)。*note Breakpoints::を参照のこと。 ‘c’ Continue(継続): breakpointごとにpauseしてから継続する (‘edebug-continue-mode’)。 ‘C’ Rapid continue(高速で継続): ポイントを各breakpointへ移動するが pauseしない(‘edebug-Continue-fast-mode’)。 ‘G’ Go non-stop(ストップせず進む): breakpointを無視する (‘edebug-Go-nonstop-mode’)。まだ‘S’やその他の編集コマンドでプログラ ムをストップするのは可能。 一般的に上記リストの最初のほうにある実行モードは後のほうの実行モード に比べて、プログラムをより低速に実行するか、すぐにストップさせます。 新たなEdebugレベルにエンターしたとき、Edebugは通常は最初に遭遇したイ ンストルメント済みの関数でストップするでしょう。breakpointでのみストップ するか、(たとえばカバレッジデータ収集時など)ストップさせないようにするに は、‘edebug-initial-mode’の値をデフォルトの‘step’から‘go’か ‘Go-nonstop’、あるいはその他の値に変更してください(*note Edebug Options::を参照)。‘C-x C-a C-m’ (‘edebug-set-initial-mode’)でこれを容易 に行うことができます: -- Command: edebug-set-initial-mode ‘C-x C-a C-m’にバインドされるこのコマンドは‘edebug-initial-mode’を セットする。これはモードを示すキーの入力を求める。対応するモードを セットする上述8つのキーのいずれかを入力すること。 たとえば1つのコマンドからインストルメント済みの関数が複数回呼び出さ れたら、同じEdebugレベルに再エンターするかもしれないことに注意してくださ い。 実行中とトレース中は、任意のEdebugコマンドをタイプすることによって実 行をインタラプト(interrupt: 中断、割り込み)できます。Edebugは次のストッ プポイントでプログラムをストップしてからタイプされたコマンドを実行します 。たとえば実行中に‘t’をタイプすると、次のストップポイントでトレースモー ドに切り替えます。‘S’を使用すれば他に何も行わずに実行をストップできます 。 関数でたまたま読み取り入力が発生した場合には、実行のインタラプトを意 図してタイプされた文字は、かわりにその関数により読み取られます。そのプロ グラムが入力を欲するタイミングに注意を払うことで、そのような意図せぬ結果 を避けることができます。 このセクションのコマンドを含むキーボードマクロは、完全には機能しませ ん。プログラムを再開するためにEdebugからexitすると、キーボードマクロの追 跡記録は失われます。これに対処するのは簡単ではありません。またEdebug外部 でキーボードマクロを定義または実行しても、Edebug内部のコマンドに影響しま せん。通常これは利点です。*note Edebug Options::内の ‘edebug-continue-kbd-macro’オプションも参照してください。 -- User Option: edebug-sit-for-seconds このオプションはtraceモードとcontinueモードで実行ステップの間を何秒 待つか指定する。デフォルトは1秒。 17.2.4 ジャンプ --------------- このセクションで説明するコマンドは、指定された場所に達するまで実行を続け ます。‘i’を除くすべてのコマンドは、ストップ場所を確立するために一時的な breakpointを作成してからgoモードにスイッチします。意図されたストップポイ ントの前にある他のストップポイントに達した場合にも実行はストップします。 breakpointの詳細は、*note Breakpoints::を参照してください。 以下のコマンドでは、非ローカルexitはプログラムのストップを望む一時的 なbreakpointをバイパスできるので、期待どおり機能しないかもしれません。 ‘h’ ポイントがある場所の近くのストップポイントへ実行を進める (‘edebug-goto-here’)。 ‘f’ プログラムの式を1つ実行する(‘edebug-forward-sexp’)。 ‘o’ sexpを含む終端までプログラムを実行する(‘edebug-step-out’)。 ‘i’ ポイントの後のフォームから呼び出された関数かマクロにステップインす る(‘edebug-step-in’)。 ‘h’コマンドは一時的なbreakpointを使用してポイントのカレント位置、また はその後のストップポイントまで処理を進めます。 ‘f’コマンドは式を1つ飛び越してプログラムを実行します。より正確には ‘forward-sexp’により到達できる位置に一時的なbreakpointをセットしてから goモードで実行するので、プログラムはそのbreakpointでストップすることにな ります。 プレフィクス引数Nとともに使用すると、ポイントからN個の sexp(s-expression: S式)を超えた場所に一時的なbreakpointをセットします。 ポイントを含むリストがNより少ない要素で終わるような場合には、ストップ箇 所はポイントが含まれる式の後になります。 ‘forward-sexp’が見つける位置が、プログラムを実際にストップさせたい位 置なのかチェックしなければなりません。たとえば‘cond’内ではこれは正しくな いかもしれません。 ‘f’コマンドは柔軟性を与えるために、‘forward-sexp’をストップポイントで はなくポイント位置から開始します。_カレントのストップポイント_から1つの 式を実行したい場合には、まずそこにポイントを移動するために ‘w’(‘edebug-where’)をタイプして、それから‘f’をタイプしてください。 ‘o’コマンドは、式の外側で実行を継続します。これはポイントを含む式の最 後に一時的なbreakpointを配置します。ポイントを含むsexpが関数定義なら ‘o’はその定義内の最後のsexpの直前まで実行を継続します。もし定義内の最後 のsexpの直前にポイントがある場合は、その関数からリターンしてからストップ します。言い換えるとこのコマンドは最後のsexpの後にポイントがなければ、カ レントで実行中の関数からexitしません。 ‘i’コマンドは、ポイントの後のリストフォームに呼び出された関数やマクロ にステップインします。そのフォームは評価されようとしているものの1つであ る必要はないことに注意してください。しかしそのフォームが評価されようとし ている関数呼び出しなら、引数が何も評価されないうちにこのコマンドを使用し ないと、遅すぎることを覚えておいてください。 ‘i’コマンドはステップインしようとしている関数やマクロがまだインストル メントされていなければ、それらをインストルメントします。これは便利かもし れませんが、それらを明示的に非インストルメントしなければ、その関数やマク ロはインストルメントされたままになることを覚えておいてください。 17.2.5 その他のEdebugコマンド ----------------------------- ここではその他のEdebugコマンドを説明します。 ‘?’ Edebugのヘルプメッセージを表示する(‘edebug-help’)。 ‘C-]’ 1レベルを中断して以前のコマンドレベルへ戻る (‘abort-recursive-edit’)。 ‘q’ エディターのトップレベルのコマンドループにリターンする (‘top-level’)。これはすべてのレベルのEdebugアクティビティを含むすべ ての再帰編集レベルをexitする。しかしフォーム‘unwind-protect’か ‘condition-case’で保護されたインストルメント済みのコードはデバッグ を再開するかもしれない。 ‘Q’ ‘q’と同様だが、保護されたコードでもストップしない (‘edebug-top-level-nonstop’)。 ‘r’ エコーエリアにもっとも最近の既知のコマンドを再表示する (‘edebug-previous-result’)。 ‘d’ backtraceを表示するが、明確であるようにEdebug自身の関数は除外される (‘edebug-backtrace’)。 Edebugのbacktraceバッファーでは、標準デバッガ内のようにバッガコマン ドは使用できない。 実行を継続したときにbacktraceバッファーは自動的にkillされる。 Edebugから再帰的にEdebugをアクティブにするコマンドを呼び出すことがで きます。Edebugがアクティブなときは常に‘q’によトップレベルの終了、または ‘C-]’による再帰編集1レベルの中断ができます。‘d’によってすべての未解決な 評価のbacktraceを表示できます。 17.2.6 ブレーク --------------- Edebugのstepモードは、次のストップポイントに達したときに実行をストップし ます。一度開始されたEdebugの実行をストップするには、他に3つの方法があり ます。それはbreakpoint、グローバルbreak条件、およびソースbreakpointです 。 17.2.6.1 Edebugのブレークポイント ................................. Edebugを使用しているときは、テスト中のプログラム内に“breakpoint”を指定で きます。breakpointとは実行がストップされる場所のことです。*note Using Edebug::で定義されている任意のストップポイントにbreakpointをセットできま す。breakpointのセットと解除で影響を受けるストップポイントは、ソースコー ドバッファー内でポイント位置、またはポイント位置の後の最初のストップポイ ントです。以下はEdebugのbreakpoint用のコマンドです: ‘b’ ポイント位置、またはポイント位置の後のストップポイントに breakpointをセットする(‘edebug-set-breakpoint’)。プレフィクス引数を 使用すると、それは一時的なbreakpointとなり、プログラムが最初にそこ で停止したときに解除される。 ‘u’ (もしあれば)ポイント位置、またはポイント位置の後のストップポイント にあるbreakpointを解除(unset)する(‘edebug-unset-breakpoint’)。 ‘x CONDITION ’ CONDITIONを評価して非‘nil’値になる場合だけプログラムをストップする 条件付きbreakpointをセットする (‘edebug-set-conditional-breakpoint’)。プレフィクス引数を指定すると 一時的なbreakpointになる。 ‘B’ カレント定義内の次のbreakpointにポイントを移動する (‘edebug-next-breakpoint’)。 Edebug内では‘b’でbreakpointをセットして、‘u’でそれを解除できます。最 初に望ましいストップポイントにポイントを移動してから、そこにbreakpointを セットまたは解除するために‘b’または‘u’をタイプします。breakpointがない場 所でbreakpointを解除しても影響はありません。 ある定義の再評価や再インストルメントを行うと、以前のbreakpointはすべ て削除されます。 “条件付きbreakpoint(conditional breakpoint)”は、プログラムがそこに達 するたびに条件をテストします。条件を評価した結果エラーが発生した場合、エ ラーは無視されて結果は‘nil’になります。条件付きbreakpointをセットするに は‘x’を使用して、ミニバッファーで条件式を指定します。以前にセットされた 条件付きbreakpointがあるストップポイントに条件付きbreakpointをセットする と、以前の条件式がミニバッファーに配置されるのでそれを編集できます。 プレフィクス引数を指定してbreakpointをセットするコマンドを使用するこ とによって、“一時的”な条件付きbreakpoint、および無条件のbreakpointを作成 できます。一時的なbreakpointによりプログラムがストップしたとき、その breakpointは自動的に解除されます。 Go-nonstopモードを除き、Edebugは常にbreakpointでストップ、または pauseします。Go-nonstopモードではbreakpointは完全に無視されます。 breakpointがどこにあるか探すには‘B’コマンドを使用します。このコマンド は同じ関数内からポイント以降にある次のbreakpoint(ポイント以降に breakpointが存在しなければ最初のbreakpoint)にポイントを移動します。この コマンドは実行を継続せずに、単にバッファー内のポイントを移動します。 17.2.6.2 グローバルなブレーク条件 ................................. “グローバルbreak条件(global break condition)”は指定された条件が満たされ たとき、それがどこで発生したかによらず、実行をストップします。Edebugは、 すべてのストップポイントでグローバルbreak条件を評価します。これが非 ‘nil’値に評価された場合は、あたかもそのストップポイントにbreakpointがあ ったかのように、実行をストップまたはpauseします(実行モードによる)。条件 の評価でエラーを取得した場合は、実行をストップしません。 条件式は‘edebug-global-break-condition’に格納されます。Edebugがアクテ ィブなときにソースバッファーから‘X’コマンドを使用するか、Edebugがロード されている間は任意のバッファーから任意のタイミングで‘C-x X X’(‘edebug-set-global-break-condition’)を使用して新たな式を指定できます 。 グローバルbreak条件は、コード内のどこでイベントが発生したかを見つける もっともシンプルな方法ですが、コードの実行は遅くなります。そのため使用し ないときは条件を‘nil’にリセットするべきです。 17.2.6.3 ソースブレークポイント ............................... 定義内のすべてのbreakpointは、それをインストルメントするたびに失われます 。breakpointが失われないようにしたければソースコード内で単に関数 ‘edebug’を呼び出す“ソースbreakpoint(source breakpoint)”を記述できます。 もちろんそのような呼び出しを条件付きすることにもできます。たとえば ‘fac’関数内に以下のような行を1行目に挿入して、引数が0になったときストッ プさせることができます: (defun fac (n) (if (= n 0) (edebug)) (if (< 0 n) (* n (fac (1- n))) 1)) ‘fac’の定義がインストルメントされて呼び出されたとき、‘edebug’呼び出し はbreakpointとして振る舞います。実行モードに応じてEdebugはそこでストップ またはpauseします。 ‘edebug’が呼び出されたときにインストルメント済みのコードが実行されて いなければ、この関数は‘debug’を呼び出します。 17.2.7 エラーのトラップ ----------------------- エラーがシグナルされて、それが‘condition-case’でハンドルされていないとき 、Emacsは通常はエラーメッセージを表示します。Edebugがアクティブでインス トルメント済みコードの実行中は、ハンドルされていないエラーには通常は Edebugが対応します。オプション‘edebug-on-error’と‘edebug-on-quit’でこれ をカスタマイズできます。*note Edebug Options::を参照してください。 Edebugがエラーに対応するときは、エラー発生箇所の前にある最後のストッ プポイントを表示します。この場所はインストルメントされていない関数の呼び 出しであったり、その関数内で実際にエラーが発生したのかもしれません。バイ ンドされていない変数に関するエラーの場合は、最後の既知のストップポイント は、その不正な変数参照から遠く離れた場所にあるかもしれません。そのような 場合には完全なbacktraceを表示したいと思うでしょう(*note Edebug Misc::を 参照)。 Edebugがアクティブの間に‘debug-on-error’か‘debug-on-quit’を変更すると 、それらの変更はEdebugが非アクティブになったとき失われます。さらに Edebugの再帰編集の間、これらの変数はEdebugの外部でもっていた値にバインド されます。 17.2.8 Edebugのビュー --------------------- これらのEdebugコマンドは、Edebugにエントリーする前のバッファーの外観とウ ィンドウの状態を調べるコマンドです。外部のウィンドウ構成はウィンドウのコ レクションとその内容であり、それらは実際にはEdebugの外部にあります。 ‘v’ 外部のウィンドウ構成ビューに切り替える(‘edebug-view-outside’)。 Edebugにリターンするには‘C-x X w’をタイプする。 ‘p’ 一時的に外部のカレントバッファーを表示して、ポイントもその外部の位 置になる(‘edebug-bounce-point’)。Edebugにリターンする前に1秒 pauseす る。プレフィクス引数Nを指定すると、かわりにN秒 pauseする。 ‘w’ ソースコードバッファー内のカレントストップポイントにポイントを戻す (‘edebug-where’)。 このコマンドを同じバッファーを表示する異なるウィンドウで使用すると 、そのウィンドウは将来カレント定義を表示するために代用される。 ‘W’ Edebugが外部のウィンドウ構成の保存とリストアを行うかどうかを切り替 える(‘edebug-toggle-save-windows’)。 プレフィクス引数を指定すると、‘W’は選択されたウィンドウの保存とリス トアだけを切り替える。ソースコードバッファーを表示していないウィン ドウを指定するには、グローバルキーマップから‘C-x X W’を使用しなけれ ばならない。 ‘v’、または単に‘p’でカレントバッファーにポイントを反跳させれば、たと え通常は表示されないウィンドウでも外部のウィンドウ構成を調べることができ ます。 ポイントを移動した後にストップポイントに戻りたいときがあるかもしれま せん。これはソースコードバッファーから‘w’で行うことができます。どのバッ ファーにいても‘C-x X w’を使用すれば、ソースコードバッファー内のストップ ポイントに戻ることができます。 保存を_オフ_にするために‘W’を使用するたびに、Edebugは外部のウィンドウ 構成を忘れます。そのためたとえ保存を_オン_に戻しても、(プログラムを実行 することによって)次にEdebugをexitしたとき、カレントウィンドウ構成は変更 されないまま残ります。しかし十分な数のウィンドウをオープンしていない場合 には、‘*edebug*’と‘*edebug-trace*’の再表示があなたが見たいバッファーと競 合するかもしれません。 17.2.9 評価 ----------- Edebug内では、まるでEdebugが実行されていないかのように式を評価できます。 式の評価とプリントに際して、Edebugは不可視になるよう試みます。副作用をも つ式の評価は、Edebugが明示的に保存とリストアを行うデータへの変更を除いて 期待したとおり機能するでしょう。このプロセスの詳細は、*note The Outside Context::を参照してください。 ‘e EXP ’ Edebugのコンテキスト外で式EXPを評価する(‘edebug-eval-expression’)。 つまり、Edebugはその式への干渉を最小限にしようと努める。 ‘M-: EXP ’ Edebug自身のコンテキスト内で式EXPを評価する(‘eval-expression’)。 ‘C-x C-e’ Edebugのコンテキスト外でポイントの前の式を評価する (‘edebug-eval-last-sexp’)。 Edebugは‘cl.el’内の構文(‘lexical-let’、‘macrolet’、 ‘symbol-macrolet’)によって作成された、レキシカル(lexical)にバインドされ たシンボルへの参照を含む式の評価をサポートします。 17.2.10 評価 List Buffer ------------------------ 式をインタラクティブに評価するために、‘*edebug*’と呼ばれる“評価リストバ ッファー(evaluation list buffer)”を使用できます。Edebugがディスプレイを 更新するたびに自動的に評価される、式の“評価リスト(evaluation list)”もセ ットアップできます。 ‘E’ 評価リストバッファー‘*edebug*’に切り替える (‘edebug-visit-eval-list’)。 ‘*edebug*’バッファーでは、以下の特別なコマンドと同様にLisp Interactionモード(*note (emacs)Lisp Interaction::を参照)のコマンドも使用 できます。 ‘C-j’ ポイントの前の式をコンテキスト外で評価して、その値をバッファーに挿 入する(‘edebug-eval-print-last-sexp’)。 ‘C-x C-e’ Edebugのコンテキスト外でポイントの前の式を評価する (‘edebug-eval-last-sexp’)。 ‘C-c C-u’ バッファー内のコンテンツから新たに評価リストを構築する (‘edebug-update-eval-list’)。 ‘C-c C-d’ ポイントのある評価リストグループを削除する (‘edebug-delete-eval-item’)。 ‘C-c C-w’ ソースコードバッファーに切り替えてカレントストップポイントに戻る (‘edebug-where’)。 評価リストウィンドウ内では、‘*scratch*’にいるときと同様に‘C-j’や‘C-x C-e’で式を評価できますが、それらはEdebugのコンテキスト外で評価されます。 インタラクティブに入力した式(と結果)は、実行を継続すると失われます。 しかし実行がストップされるたびに評価されるように、式から構成される“評価 リスト”をセットアップできます。 これを行なうには、評価リストバッファー内で1つ以上の“評価リストグルー プ(evaluation list group)”を記述します。評価リストグループは1つ以上の Lisp式から構成されます。グループはコメント行で区切られます。 コマンド‘C-c C-u’(‘edebug-update-eval-list’)はバッファーをスキャンし て、各グループの最初の式を使用して評価リストを再構築します(これはグルー プの2つ目の式は以前に計算、表示されている値だという発想からである)。 Edebugにエントリーするたびに、評価リストの各式(および式の後に式のカレ ント値)をバッファーに挿入して再表示します。これはコメント行も挿入するの で、各式はそのグループの一員となります。したがってバッファーのテキストを 変更せずに‘C-c C-u’とタイプすると、評価リストは実際には変更されません。 評価リストからの評価の間にエラーが発生すると、それが式の結果であるか のようにエラーメッセージが文字列で表示されます。したがってカレントで無効 な変数を使用する式によって、デバッグが中断されることはありません。 以下はいくつかの式を評価リストウィンドウに追加したとき、どのように見 えるかの例です: (current-buffer) # ;--------------------------------------------------------------- (selected-window) # ;--------------------------------------------------------------- (point) 196 ;--------------------------------------------------------------- bad-var "Symbol's value as variable is void: bad-var" ;--------------------------------------------------------------- (recursion-depth) 0 ;--------------------------------------------------------------- this-command eval-last-sexp ;--------------------------------------------------------------- グループを削除するにはグループ内にポイントを移動して‘C-c C-d’をタイプ するか、単にグループのテキストを削除して‘C-c C-u’で評価リストを更新しま す。評価リストに新たな式を追加するには、適切な箇所にその式を挿入して新た なコメント行を挿入してから‘C-c C-u’をタイプします。コメント行にダッシュ を挿入する必要はありません — 内容は関係ないのです。 ‘*edebug*’を選択した後に‘C-c C-w’でソースコードバッファーにリターンで きます。‘*edebug*’は実行を継続したときにkillされて、次回必要となったとき に再作成されます。 17.2.11 Edebugでのプリント -------------------------- プログラム内の式が循環リスト構造(circular list structure)を含む値を生成 する場合は、Edebugがそれをプリントしようとしたときエラーとなるかもしれま せん。 循環構造への対処の1つとして、‘print-length’と‘print-level’にプリント の切り詰めをセットする方法があります。Edebugは変数 ‘edebug-print-length’と‘edebug-print-level’の値(非‘nil’値なら)を、これら の変数にバインドします。*note Output Variables::を参照してください。 -- User Option: edebug-print-length 非‘nil’なら結果をプリントするときEdebugは‘print-length’をこの値にバ インドする。デフォルト値は‘50’。 -- User Option: edebug-print-level 非‘nil’なら結果をプリントするときEdebugは‘print-level’をこの値にバ インドする。デフォルト値は‘50’。 ‘print-circle’を非‘nil’値にバインドして、循環構造や要素を共有する構造 にたいして、より参考になる情報をプリントするよういにすることもできます。 以下は循環構造を作成するコードの例です: (setq a '(x y)) (setcar a a) カスタムプリントはこれを‘Result: #1=(#1# y)’のようにプリントします。 ‘#1=’という表記はその後の構造をラベル‘1’とラベル付けして、‘#1#’表記はそ の前にラベル付けされた構造を参照しています。この表記はリストとベクターの 任意の共有要素に使用されます。 -- User Option: edebug-print-circle 非‘nil’なら結果をプリントするときEdebugは‘print-circle’をこの値にバ インドする。デフォルト値は‘t’。 他のプログラムもカスタムプリントを使用できます。詳細は ‘cust-print.el’を参照してください。 17.2.12 トレースバッファー -------------------------- Edebugは実行トレースを‘*edebug-trace*’という名前のバッファーに格納して記 録できます。実行トレースとは関数呼び出しとリターンのログのことで関数名と 引数、および値を確認できます。トレースレコードを有効にするには、 ‘edebug-trace’を非‘nil’値にセットしてください。 トレースバッファーの作成は実行モードのトレースの使用(*note Edebug Execution Modes::を参照)と同じではありません。 トレースレコードが有効なときは、関数へのエントリーとexitのたびにトレ ースバッファーに行が追加されます。関数エントリーレコードは‘::::{’、およ び関数名と引数の値によって構成されます。関数のexitレコードは‘::::}’、お よび関数名と関数の結果によって構成されます。 ‘:’の数は関数エントリーの再帰レベルを表します。トレースバッファーでは 関数呼び出しの開始と終了の検索に‘{’と‘}’を使用できます。 関数‘edebug-print-trace-before’と‘edebug-print-trace-after’を再定義す ることによって、関数エントリーと関数exitのトレースレコードをカスタマイズ できます。 -- Macro: edebug-tracing string body... このマクロはBODYフォームの実行活動にたいして追加のトレース情報をリ クエストする。引数STRINGはトレースバッファーに配置する‘{’と‘}’の後 のテキストを指定する。すべての引数は評価されて、‘edebug-tracing’は BODY内の最後のフォームの値をリターンする。 -- Function: edebug-trace format-string &rest format-args この関数はトレースバッファーにテキストを挿入する。テキストは‘(apply 'format FORMAT-STRING FORMAT-ARGS)’によって計算される。エントリー間 の区切りとして改行も付け加える。 ‘edebug-tracing’と‘edebug-trace’は、たとえEdebugが非アクティブでも、 呼び出されたときは常にトレースバッファーに行を挿入します。トレースバッフ ァーへのテキストの追加により、挿入された最後の行が見えるようにウィンドウ もスクロールします。 17.2.13 カバレッジテスト ------------------------ Edebugは基本的なカバレッジテスト(coverage test)と実行頻度(execution frequency)の表示を提供します。 カバレッジテストは、すべての式の結果と以前の結果を比較することによっ て機能します。プログラム内の各フォームがカレントEmacsセッション内でカバ レッジテストを開始して以降に、2つの異なる値をリターンしたら、それらのフ ォームはカバーされたと判断されます。したがってプログラムにカバレッジテス トを行なうには、そのプログラムをさまざまなコンディション下で実行して、プ ログラムが正しく振る舞うかに注目します。異なるコンディション下で十分にテ ストして、すべてのフォームが異なる2つの値をリターンしたとき、Edebugはそ のことを告げるでしょう。 カバレッジテストにより実行速度が低下するので、 ‘edebug-test-coverage’が非‘nil’のときだけカバレッジテストが行なわれます 。頻度計数(frequency count)はたとえ実行モードがGo-nonstopでも、カバレッ ジテストが有効か無効かに関わらずすべての式にたいして行われます。 定義にたいするカバレッジ情報と頻度数の両方を表示するには‘C-x X =’ (‘edebug-display-freq-count’)を使用します。単に‘=’ (‘edebug-temp-display-freq-count’)とすると、他のキーをタイプするまでの間 だけ一時的に同様の情報を表示します。 -- Command: edebug-display-freq-count このコマンドはカレント定義の各行の頻度数を表示する。 このコマンドはコードの各行の下にコメント行として頻度数を挿入する。 1回の‘undo’コマンドですべての挿入をアンドゥできる。頻度数は式の前の ‘(’か式の後の‘)’、または変数の最後の文字の下に表示される。表示をシ ンプルにするために同一行にたいして式の以前頻度数と頻度数が同じ場合 は表示しない。 ある式にたいする頻度数の後に文字‘=’がある場合は、その式が評価される たびに同じ値を毎回リターンしていることを表す。言い換えるとカバレッ ジテストの目的からは、その式はまだカバーされていないということであ る。 ある定義にたいして頻度数とカバレッジデータを明確にするには、単に ‘eval-defun’で再インストルメントすればよい。 たとえばソースのbreakpointで‘(fac 5)’を評価した後に ‘edebug-test-coverage’を‘t’にセットすると、breakpointに達したときの頻度 データは以下のようになります: (defun fac (n) (if (= n 0) (edebug)) ;#6 1 = =5 (if (< 0 n) ;#5 = (* n (fac (1- n))) ;# 5 0 1)) ;# 0 コメント行は‘fac’が6回呼び出されたことを表しています。最初の‘if’命令 は毎回同じ結果を5回リターンしています。同じ結果という意味では2つ目の ‘if’の条件にも当てはまります。‘fac’の再帰呼び出しは結局リターンしません 。 17.2.14 コンテキスト外部 ------------------------ Edebugはデバッグ中のプログラムにたいして透過的であろうと努めますが完全に は達成されません。Edebugは‘e’や評価リストバッファーで式を評価するときに も、一時的に外部のコンテキストをリストアして透明化を試みます。このセクシ ョンではEdebugがリストアするコンテキストと、Edebugが完全に透過的になるの に失敗する理由を正確に説明します。 17.2.14.1 停止するかどうかのチェック .................................... Edebugにエンターするときは常に特定のデータの保存とリストアを行なう必要が あり、それはトレース情報を作成するか、あるいはプログラムを停止するかを決 定する前に行なう必要があります。 • ‘max-lisp-eval-depth’と‘max-specpdl-size’は、Edebugがスタックに与え る影響の低減効果を高める。しかしそれでもEdebug使用時にスタック空間 を使い切ってしまうことがあり得る。 • キーボードマクロの実行状態の保存とリストアが行われる。Edebugがアク ティブの間、‘edebug-continue-kbd-macro’が‘nil’なら ‘executing-kbd-macro’が‘nil’にバインドされる。 17.2.14.2 Edebugの表示の更新 ............................ (たとえばtraceモードなどで)Edebugが何かを表示する必要があるときは、 Edebugの外部からカレントウィンドウ構成(*note Window Configurations::を参 照)を保存します。Edebugをexitするときに、以前のウィンドウ構成がリストア されます。 Emacsはpause時だけ再表示を行います。通常は実行を継続すると、そのプロ グラムはbreakpointかステップ実行後にEdebugに再エンターして、その間に pauseや入力の読み取りはありません。そのような場合、Emacsが外部の構成を再 表示する機会は決してありません。結果としてユーザーが目にするウィンドウ構 成は、前回Edebugが中断なしでアクティブだったときのウィンドウ構成と同じに なります。 何かを表示するためにEdebugにエントリーすることにより、(たとえこれらの うちのいくつかは、エラーやquitがシグナルされたときは故意にリストアしない データだとしても)以下のデータも保存とリストアが行われます。 • カレントバッファー、およびカレントバッファー内のポイントとマークの 位置が保存およびリストアされる。 • ‘edebug-save-windows’が非‘nil’なら、外部のウィンドウ構成の保存とリ ストアが行われる(*note Edebug Options::を参照)。 エラーやquitではウィンドウ構成はリストアされないが、 ‘save-excursion’がアクティブなら、たとえエラーやquitのときでも外部 の選択されたウィンドウが_再選択される_。‘edebug-save-windows’の値が リストなら、それにリストされたウィンドウだけが保存およびリストアさ れる。 ただしソースコードバッファーのウィンドウの開始位置と水平スクロール はリストアされないので、表示はEdebug内で整合性が保たれたままとなる 。 • ‘edebug-save-displayed-buffer-points’が非‘nil’なら、表示されている それぞれのバッファー内のポイント値は保存およびリストアされる。 • 変数‘overlay-arrow-position’と‘overlay-arrow-string’は保存とリスト アが行われるので、同じバッファー内の他の場所の再帰編集から安全に Edebugを呼び出せる。 • ‘cursor-in-echo-area’は‘nil’にローカルにバインドされるのでカーソル はそのウィンドウ内に現れる。 17.2.14.3 Edebugの再帰編集 .......................... Edebugにエンターしてユーザーのコマンドが実際に読み取られるとき、Edebugは 以下の追加データを保存(および後でリストア)します: • カレントマッチデータ。*note Match Data::を参照のこと。 • 変数‘last-command’、‘this-command’、‘last-command-event’、 ‘last-input-event’、‘last-event-frame’、‘last-nonmenu-event’、 ‘track-mouse’。Edebug内のコマンドはEdebug外部のこれらの変数に影響を あたえない。 Edebug内でのコマンド実行は‘this-command-keys’によりリターンされるキ ーシーケンスを変更でき、Lispからそのキーシーケンスをリセットする方 法はない。 Edebugは‘unread-command-events’の値の保存とリストアができない。この 変数が重要な値をもつときにEdebugにエンターすると、デバッグ中のプロ グラムの実行に干渉する可能性がある。 • Edebug内で実行された複雑なコマンドは変数‘command-history’に追加され る。これは稀に実行に影響を与える。 • Edebug内では再帰の深さがEdebug外部の再帰の深さより1つ深くなる。これ は自動的に更新される評価リストウィンドウでは異なる。 • ‘standard-output’と‘standard-input’は、‘recursive-edit’によって ‘nil’にバインドされるがEdebugは評価の間それらを一時的にリストアする 。 • キーボードマクロ定義の状態は保存およびリストアされる。Edebugがアク ティブの間、‘defining-kbd-macro’は‘edebug-continue-kbd-macro’にバイ ンドされる。 17.2.15 Edebugとマクロ ---------------------- Edebugが正しくマクロを呼び出す式をインストルメントするには、いくつかの特 定な配慮が必要になります。このサブセクションでは、その詳細を説明します。 17.2.15.1 マクロ呼び出しのインストルメント .......................................... EdebugがLispマクロを呼び出す式をインストルメントするときは、正しくインス トルメントを行なうために、そのマクロに関して追加の情報が必要になります。 これはマクロ呼び出しのどの部分式(subexpression)が評価されるフォームなの か推測する方法がないからです(評価はマクロのbodyで明示的に発生するかもし れないし、展開結果が評価されるとき、または任意のタイミングで行われるかも しれない)。 したがってEdebugが処理するかもしれないすべてのマクロにたいして、その マクロの呼び出しフォーマットを説明するためのEdebug仕様(Edebug specification)を定義しなければなりません。これを行なうにはマクロ定義に ‘debug’宣言を追加します。以下はマクロ例‘for’(*note Argument Evaluation::を参照)にたいする簡単な仕様の例です。 (defmacro for (var from init to final do &rest body) "Execute a simple \"for\" loop. For example, (for i from 1 to 10 do (print i))." (declare (debug (symbolp "from" form "to" form "do" &rest form))) ...) このEdebug仕様はマクロ呼び出しのどの部分が評価されるフォームなのかを 示しています。単純なマクロにたいするEdebug仕様は、そのマクロ定義の正式な 引数リストに酷似している場合がありますが、Edebug仕様はマクロ引数に比べて より汎的です。‘declare’フォームの詳細は*note Defining Macros::を参照して ください。 コードをインストルメントするときには、Edebugに仕様が確実に解るように 注意してください。マクロ定義を含む他のファイルを要求するために ‘eval-when-compile’を使用するファイルから関数をインストルメントする場合 には、そのファイルを明示的にロードする必要があるかもしれません。 ‘def-edebug-spec’によりマクロ定義から個々のマクロにたいしてEdebug仕様 を定義することもできます。Lispで記述されたマクロ定義にたいしては ‘debug’宣言を追加するほうが好ましく便利でもありますが、 ‘def-edebug-spec’ではCで実装されたスペシャルフォームにたいしてEdebug仕様 を定義することが可能になります。 -- Macro: def-edebug-spec macro specification マクロMACRO呼び出しのどの式が評価される式かを指定する。 SPECIFICATIONはEdebug仕様である。どちらの引数も評価されない。 引数MACROには単なるマクロ名ではない、任意の実シンボルを指定できる。 以下はSPECIFICATIONに指定できるシンボルと、引数を処理する方法のテーブ ルです。 ‘t’ すべての引数は評価のためにインストルメントされる。 ‘0’ 引数はインストルメントされない。 シンボル そのシンボルはかわりに使用されるEdebug仕様をもたなければならない。 このインダイレクションは他の種類の仕様が見つかるまで繰り返される。 これによって他のマクロの仕様を継承できる。 リスト リストの要素はフォーム呼び出しの引数の型を記述する。仕様リストに指 定できる要素については以降のセクションを参照のこと。 マクロがEdebug仕様をもたなければ、‘debug’宣言および ‘def-edebug-spec’呼び出しのどちらを介しても、変数 ‘edebug-eval-macro-args’が効果を発揮します。 -- User Option: edebug-eval-macro-args これはEdebugが明示的なEdebug仕様をもたないマクロ引数を扱う方法を制 御する。‘nil’(デフォルト)なら引数は評価のためにインストルメントされ ない。それ以外ばらすべての引数がインストルメントされる。 17.2.15.2 仕様リスト .................... あるマクロ呼び出しにおいて、いくつかの引数は評価されても、それ以外の引数 は評価されないような場合には、Edebug仕様のために“仕様リスト (specification list)”が必要となります。仕様リスト内のいくつかの要素は1つ 以上の引数にマッチしますが、それ以外の要素は以降に続くすべての引数の処理 を変更します。後者は“仕様キーワード(specification keywords)”と呼ばれ、 (‘&optional’のように)‘&’で始まるシンボルです。 仕様リストはそれ自身がリストであるような引数にマッチする部分リスト (sublist)、あるいはグループ化に使用されるベクターを含むかもしれません。 したがって部分式とグループは仕様リストをレベル階層に細分化します。仕様キ ーワードは部分式やグループを含むものの残りに適用されます。 仕様リストに選択肢や繰り返しが含まれる場合は、実際のマクロの呼び出し のマッチでバックトラックが要求されるかもしれません。詳細は*note Backtracking::を参照してください。 Edebug仕様はバランスのとれたカッコで括られた部分式へのマッチ、フォー ムの再帰処理、インダイレクト仕様を通じた再帰等の、正規表現によるマッチン グとコンテキストに依存しない文法構成を提供します。 以下は仕様リストに使用できる要素と、その意味についてのテーブルです(使 用例は*note Specification Examples::を参照): ‘sexp’ 評価されない単一のLispオブジェクト。インストルメントされない。 ‘form’ 評価される単一のLispオブジェクト。インストルメントされる。 ‘place’ 汎変数(generalized variable)。*note Generalized Variables::を参照の こと。 ‘body’ ‘&rest form’の省略形。以下の‘&rest’を参照のこと。 ‘function-form’ 関数フォーム。クォートされた関数シンボル、クォートされたラムダ式、 または(関数シンボルかラムダ式に評価される)フォームのいずれか。これ はラムダ式のbodyをいずれかの方法でインストルメントするので、 ‘function’よりも‘quote’でクォートされたラムダ式の引数にたいして有用 。 ‘lambda-expr’ クォートされないラムダ式。 ‘&optional’ 仕様リスト内の後続の要素はオプション。マッチしない要素が出現すると Edebugはこのレベルのマッチングを停止する。 後続が非オプションの要素であるような数個の要素をオプションにするだ けなら、‘[&optional SPECS...]’を使用する。複数の要素すべてのマッチ や非マッチを指定するには、‘&optional [SPECS...]’を使用する。 ‘defun’の例を参照のこと。 ‘&rest’ 仕様リスト内の後続のすべての要素は0回以上繰り返される。しかし最後の 繰り返しでは、仕様リスト内のすべての要素にたいするマッチングの前に 式が終了しても問題はない。 数個の要素を繰り返すには‘[&rest SPECS...]’を使用する。各繰り返しに おいてすべてマッチしなければならない複数要素を指定するには、‘&rest [SPECS...]’を使用する。 ‘&or’ 仕様リスト内の後続の各要素は選択肢である。選択肢の1つがマッチしなけ ればならず、マッチしなければ‘&or’仕様は失敗する。 ‘&or’に続く各リスト要素は単一の選択肢である。複数のリスト要素を単一 の選択肢にグループ化するには、それらを‘[...]’で括る。 ‘¬’ 後続の各要素は‘&or’が使用されたときのように選択肢にマッチするが、要 素がマッチしたら失敗となる。マッチする要素がなければ何もマッチされ ないが‘¬’仕様は成功となる。 ‘&define’ フォーム定義にたいする仕様であることを示す。フォーム定義自体はイン ストルメントされない(つまりEdebugはフォーム定義の前後でストップしな い)が、フォーム内部は通常はインストルメントされるであろう。 ‘&define’キーワードはリスト仕様の最初の要素であること。 ‘nil’ カレント引数レベルでマッチさせる引数が存在しなければ成功し、それ以 外は失敗する。部分リスト仕様とバッククォートの例を参照のこと。 ‘gate’ 引数はマッチされないがgateを通じたバックトラックは、このレベルの仕 様の残りをマッチングする間は無効にされる。これは主に特定の構文エラ ーメッセージを一般化するために使用される。詳細は*note Backtracking::、および‘let’の例も参照のこと。 ‘OTHER-SYMBOL’ 仕様リスト内のその他の要素は、述語(predicate)かインダイレクト仕様 (indirect specification)である。 シンボルがEdebug仕様をもつなら、“インダイレクト仕様(indirect specification)”はシンボル位置に使用されるリスト仕様か、引数を処理す るための関数のいずれかである。この仕様はマクロにたいする ‘def-edebug-spec’のように定義される。‘defun’の例を参照のこと。 それ以外ならシンボルは述語(predicate)である。述語は引数とともに呼び 出されて‘nil’をリターンしたら、その仕様は失敗して引数はインストルメ ントされない。 適切な述語としては‘symbolp’、‘integerp’、‘stringp’、‘vectorp’、 ‘atom’が含まれる。 ‘[ELEMENTS...]’ 要素のベクターは要素を単一の“グループ仕様(group specification)”にグ ループ化する。このグループ仕様はベクター自体には何も行わない。 ‘"STRING"’ 引数はSTRINGという名前のシンボルである。この仕様はSYMBOLの名前が STRINGであるようなクォートされたシンボル‘'SYMBOL’と等価だが、文字列 形式のほうが好ましい。 ‘(vector ELEMENTS...)’ 引数は要素が仕様内のELEMENTSにマッチするようなベクターである。バッ ククォートの例を参照のこと。 ‘(ELEMENTS...)’ 他のリストは“部分リスト仕様(sublist specification)”であり、引数は要 素が仕様のELEMENTSにマッチするリストでなければならない。 部分リスト仕様はドットリスト(dotted list)かもしれず、その場合対応す るリスト引数はドットリストである。かわりにドットリスト仕様の最後の CDRが、(グループ化やインダイレクト仕様による)他の部分リスト仕様かも しれない(たとえば要素が非ドットリストにマッチする‘(spec . [(more specs...)])’))。これはバッククォートの例のような再帰仕様に有用。こ のような再帰を終了させるには上述の‘nil’仕様も参照のこと。 ‘(specs . nil)’のように記述された部分リスト仕様は‘(specs)’、‘(specs . (sublist-elements...))’は‘(specs sublist-elements...)’と等価であ ることに注意。 以下は‘&define’の後だけに出現する追加仕様のリストです。‘defun’の例を 参照してください。 ‘name’ 引数(シンボル)は定義フォームの名前。 定義フォームは名前フィールドをもつ必要はなく、複数の名前フィールド をもつかもしれない。 ‘:name’ この構文は引数に実際のマッチは行わない。‘:name’の後の要素はシンボル であり、その定義の追加の名前要素として使用される。定義名に一意で静 的な要素を加えるためにこれを使用できる。複数回使用できる。 ‘arg’ 引数(シンボル)は定義フォームの引数の名前である。しかしlambda-listキ ーワード(‘&’で始まるシンボル)は許されない。 ‘lambda-list’ これはラムダリスト(ラムダ式の引数リスト)にマッチする。 ‘def-body’ 引数は定義内のコードのbodyである。これは上述の‘body’と似ているが、 定義のbodyはその定義に関連する情報を照会する別のEdebug呼び出しでイ ンストルメントされていなければならない。定義内のより高位レベルのフ ォームリストには‘def-body’を使用する。 ‘def-form’ 引数は定義内のもっとも高位レベルの単一フォームである。これは ‘def-body’と似ているが、フォームリストではなく単一フォームのマッチ に使用される。特別なケースとして‘def-form’はフォームが実行されると きトレース情報を出力しないことも意味する。‘interactive’の例を参照の こと。 17.2.15.3 仕様でのバックトレース ................................ あるポイント位置で仕様がマッチに失敗しても、構文エラーがシグナルされると は限りません。そのかわり“バックトラッッキング(backtracking)”が開始されま す。バックトラックはすべての選択肢をマッチングするまで行なわれます。最終 的に引数リストのすべての要素は仕様内の要素のいずれかとマッチしなければな らず、仕様内の必須要素は引数のいずれかとマッチしなければなりません。 構文エラーが検出されてもその時点では報告されず、より高位レベルの選択 肢のマッチングが終わった後、実際のエラー箇所から離れたポイント位置でエラ ーが報告されるかもしれません。しかしエラー発生時にバックトラックが無効な らエラーは即座に報告されるでしょう。ある状況ではバックトラックも自動的に 再有効化されることに注意してください。‘&optional’、‘&rest’、‘&or’により 新たな選択肢が設定されたとき、または部分リスト、グループ、インダイレクト 仕様が開始されたときはバックトラックが自動的に有効になります。バックトラ ックを有効、または無効にした場合の影響は、現在処理中のレベルの残り要素と 低位レベルに限定されます。 何らかのフォーム仕様(すなわち‘form’、‘body’、‘def-form’、 ‘def-body’)をマッチングする間、バックトラックは無効になっています。これ らの仕様は任意のフォームにマッチするので、何らかのエラーが発生するとした らそれは高位レベルではなく、そのフォーム自体の内部でなければなりません。 バックトラックはクォートされたシンボルや文字列仕様とのマッチに成功し た後にも無効になります。なぜなら通常これは構文成が認識されたことを示すか らです。しかし同じシンボルで始まる一連の選択肢構文がある場合には、たとえ ば‘["foo" &or [first case] [second case] ...]’のように、通常は選択肢の外 部にそのシンボルをファクタリングすることによりこの制約に対処できます。 ほとんどのニーズは、バックトラックを自動的に無効にする、これら2つの方 法で満足させることができますが、‘gate’仕様を使用して明示的にバックトラッ クを無効にするほうが便利なときもあります。これは高位に適用可能な選択肢が 存在しないことが分かっている場合に有用です。‘let’仕様の例を参照してくだ さい。 17.2.15.4 仕様の例 .................. 以下で提供する例から学ぶことにより、Edebug仕様の理解が容易になるでしょう 。 スペシャルフォーム‘let’は、バインディングとbodyのシーケンスをもちます 。各バインディングはそシンボル、またはシンボルとオプションの部分リストで す。以下の仕様では部分リストを見つけたらバックトラックを抑止するために、 部分リスト内の‘gate’があることに注目してください。 (def-edebug-spec let ((&rest &or symbolp (gate symbolp &optional form)) body)) Edebugは‘defun’および関連する引数リスト、‘interactive’仕様にたいして 以下の仕様を使用します。式の引数はその関数bodyの外部で実際に評価されるの で、interactiveフォームは特別に処理する必要があります。(‘defmacro’にたい する仕様は‘defun’にたいする仕様と酷似するが‘declare’命令文が許される) (def-edebug-spec defun (&define name lambda-list [&optional stringp] ; ドキュメント文字列が与えられた場合はマッチする。 [&optional ("interactive" interactive)] def-body)) (def-edebug-spec lambda-list (([&rest arg] [&optional ["&optional" arg &rest arg]] &optional ["&rest" arg] ))) (def-edebug-spec interactive (&optional &or stringp def-form)) ; ‘def-form’に注目 以下のバッククォートにたいする仕様はドットリストにマッチさせる方法と 、‘nil’を使用して再帰を終了させる方法を説明するための例です。またベクタ ーのコンポーネントをマッチさせる方法も示しています(Edebugにより定義され る実際の仕様は少し異なり、失敗するかもしれない非常に深い再帰を引き起こす ためドットリストについてはサポートしない)。 (def-edebug-spec \` (backquote-form)) ; 単なる明確化用エイリアス (def-edebug-spec backquote-form (&or ([&or "," ",@"] &or ("quote" backquote-form) form) (backquote-form . [&or nil backquote-form]) (vector &rest backquote-form) sexp)) 17.2.16 Edebugのオプション -------------------------- 以下のオプションはEdebugの動作に影響を与えます: -- User Option: edebug-setup-hook Edebugが使用される前に呼び出される関数。この関数は毎回新たな値をセ ットする。Edebugはこれらの関数を一度呼び出したら、その後に ‘edebug-setup-hook’を‘nil’にリセットする。使用するパッケージに関係 するEdebug仕様をロードするために使用でdきるがそれはEdebugを使用す るときだけである。*note Instrumenting::を参照のこと。 -- User Option: edebug-all-defs これが非‘nil’の場合に‘defun’や‘defmacro’のような定義フォームの普通 に評価すると、Edebug用にインストルメントされる。これは ‘eval-defun’、‘eval-region’、‘eval-buffer’、and ‘eval-current-buffer’に適用される。 このオプションの切り替えにはコマンド‘M-x edebug-all-defs’を使用する 。*note Instrumenting::を参照のこと。 -- User Option: edebug-all-forms これが非‘nil’の場合には‘eval-defun’、‘eval-region’、‘eval-buffer’、 ‘eval-current-buffer’はたとえフォームが何も定義していなくても、すべ てのフォームをインストルメントする。これはロードとミニバッファー内 の評価には適用されない。 このオプションの切り替えにはコマンド‘M-x edebug-all-forms’を使用す る。*note Instrumenting::を参照のこと。 -- User Option: edebug-save-windows これが非‘nil’なら、Edebugはウィンドウ構成の保存とリストアを行なう。 これにはある程度の時間を要するので、ウィンドウ構成に何が起こっても プログラムに関係なければ、この変数を‘nil’にセットしたほうがよい。 値がリストならリストされたウィンドウだけが保存およびリストアされる 。 Edebug内ではこの変数をインタラクティブに変更するために‘W’コマンドを 使用できる。*note Edebug Display Update::を参照のこと。 -- User Option: edebug-save-displayed-buffer-points これが非‘nil’ならEdebugは表示されているすべてのバッファー内のポイン トを保存およびリストアする。 選択されていないウィンドウ内に表示されているバッファーのポイントを 変更するコードをデバッグしている場合は、他のバッファーのポイントを 保存およびリストアする必要がある。その後にEdebugまたはユーザーがそ のウィンドウを選択した場合は、そのバッファー内のポイントはそのウィ ンドウのポイント値に移動される。 すべてのバッファー内のポイントの保存とリストアは、それぞれのウィン ドウを2回選択する必要があり高価な処理なので、必要なときだけ有効にす る。*note Edebug Display Update::を参照のこと。 -- User Option: edebug-initial-mode この変数が非‘nil’なら、Edebugが最初にアクティブになったときの Edebugの最初の実行モードを指定する。指定できる値は‘step’、‘next’、 ‘go’、‘Go-nonstop’、‘trace’、‘Trace-fast’、‘continue’、 ‘Continue-fast’。 デフォルト値は‘step’。この変数は‘C-x C-a C-m’でインタラクティブにセ ットできる。*note Edebug Execution Modes::を参照のこと。 -- User Option: edebug-trace これが非‘nil’なら各関数のエントリーとexitをトレースする。トレース出 力は関数のエントリーとexitを行ごとに、再帰レベルにしたがって ‘*edebug-trace*’という名前のバッファーに表示される。 *note Trace Buffer::の‘edebug-tracing’も参照されたい。 -- User Option: edebug-test-coverage 非‘nil’ならEdebugはデバッグされるすべての式のカバレッジをテストする 。*note Coverage Testing::を参照のこと。 -- User Option: edebug-continue-kbd-macro 非‘nil’ならEdebug外部で実行されている任意のキーボードマクロの定義ま たは実行を継続する。これはデバッグされないので慎重に使用すること。 *note Edebug Execution Modes::を参照されたい。 -- User Option: edebug-unwrap-results 非‘nil’ならEdebugは式の結果を表示するときに、その式自体のインストル メント結果の削除を試みる。マクロをデバッグするときは、式の結果自体 がインストルメントされた式になるということに関連するオプションであ る。実際的な例ではないが、サンプル例の関数‘fac’がインストルメントさ れたとき、そのフォームのマクロを考えてみるとよい。 (defmacro test () "Edebug example." (if (symbol-function 'fac) ...)) ‘test’マクロをインストルメントしてステップ実行すると、デフォルトで は‘symbol-function’呼び出しは多数の‘edebug-after’フォームと ‘edebug-before’フォームをもつことになり、それにより実際の結果の確認 が難しくなり得る。‘edebug-unwrap-results’が非‘nil’ならEdebugは結果 からこれらのフォームの削除を試みる。 -- User Option: edebug-on-error ‘debug-on-error’が以前‘nil’だったら、Edebugは‘debug-on-error’をこの 値にバインドする。*note Trapping Errors::を参照のこと。 -- User Option: edebug-on-quit ‘debug-on-quit’の以前の値が‘nil’なら、Edebugは‘debug-on-quit’にこの 値をバインドする。*note Trapping Errors::を参照のこと。 Edebugがアクティブな間に‘edebug-on-error’か‘edebug-on-quit’の値を変更 したら、_次回_に新たなコマンドを通じてEdebugが呼び出されるまでこれらの値 は使用されない。 -- User Option: edebug-global-break-condition 非‘nil’なら、値はすべてのステップポイントでテストされる式である。式 の結果が‘nil’ならbreakする。エラーは無視される。*note Global Break Condition::を参照のこと。 17.3 無効なLisp構文のデバッグ ============================= Lispリーダーは無効な構文(invalid syntax)について報告はしますが実際の問題 箇所は報告しません。たとえばある式を評価中のエラー‘End of file during parsing’は、開カッコまたは開角カッコ(open parenthese or open square bracket)が多いことを示しています。Lispリーダーはこの不一致をファイル終端 で検出しましたが、本来閉カッコがあるべき箇所を解決することはできません。 同様に‘Invalid read syntax: \")\"’は開カッコの欠落を示していますが、欠落 しているカッコが属すべき場所は告げません。ならばどうやって変更すべき箇所 を探せばよいのでしょうか? 問題が単なるカッコの不一致でない場合の便利なテクニックは、各defunの先 頭で‘C-M-e’とタイプして、そのdefunの最後と思われる箇所に移動するか確認す る方法です。もし移動しなければ、問題はそのdefunの内部にあります。 マッチしないカッコがLispにおいてもっとも一般的な構文エラーなので、こ れらのケースにたいしてさらにアドバイスすることができます(Show Parenモー ドを有効にしてコードにポイントを移動するだけでカッコの不一致を探しやすく なるだろう)。 17.3.1 過剰な開カッコ --------------------- カッコがマッチしないdefunを探すことが最初のステップです。過剰な開カッコ が存在する場合は、ファイルの終端に移動して‘C-u C-M-u’とタイプします。こ れによってカッコがマッチしない最初のdefunの先頭に移動するでしょう。 何が間違っているのか正確に判断するのが次のステップです。これを確実に 行なうには、そのプログラムを詳しく調べる以外に方法はありませんが、カッコ があるべき箇所を探すのに既存のインデントが手掛かりになることが多々ありま す。‘C-M-q’で再インデントして何が移動されるか確認するのが、この手掛かり を使用するもっとも簡単な方法です。*しかし、行うのはちょっと待ってくださ い!* まず続きを読んでからにしましょう。 これを行なう前にdefunに十分な閉カッコがあるか確認します。十分な閉カッ コがなければ‘C-M-q’がエラーとなるか、そのdefunからファイル終端までの残り すべてが再インデントされます。その場合はdefunの最後に移動して、そこに閉 カッコを挿入します。そのdefunのカッコの釣り合いがとれるまでは、defunの最 後に移動するのに‘C-M-e’は失敗するでしょうから使用できません。 これでdefunの先頭に移動して‘C-M-q’とタイプすることができます。通常は 一定のポイントからその関数の最後までのすべての行が、右へとシフトされるで しょう。これはおそらくそのポイント付近で閉カッコが欠落しているか不要な開 カッコがあります(しかしこれを真実と決め付けずコードを詳しく調べてること )。不一致箇所を見つけたら、元のインデントはおそらく意図されたカッコに適 しているはずなので、‘C-_’で‘C-M-q’をアンドゥしてください。 問題をfixできたと思った後に、再度‘C-M-q’を使用します。実際に元のイン デントが意図したカッコのネストに適合していて、足りないカッコを追加してい たら、‘C-M-q’は何も変更しないはずです。 17.3.2 過剰な閉カッコ --------------------- 過剰な閉カッコへの対処は、まずファイルの先頭に移動してから、カッコのマッ チしないdefunを探すために‘C-u -1 C-M-u’をタイプします。 それからそのdefunの先頭で‘C-M-f’をタイプして、実際にマッチする閉カッ コを探します。これによりそのdefunの終端より幾分手前の箇所に移動するはず です。その付近に間違った閉カッコが見つかるでしょう。 そのポイントに問題が見つからなければ、そのdefunの先頭で‘C-M-q’をタイ プするのが次のステップです。ある行範囲はおそらく左へシフトするでしょう。 その場合には欠落している開カッコまたは間違った閉カッコは、おそらくそれら の行の1行目付近にあるでしょう (しかしこれを真実と決め付けずコードを詳し く調べること)。不一致箇所を見つけたら、元のインデントはおそらく意図され たカッコに適しているはずなので、‘C-_’で‘C-M-q’をアンドゥしてください。 問題をfixできたと思った後に再度‘C-M-q’を使用します。実際に元のインデ ントが意図したカッコのネストに適合していて、足りないカッコを追加していた ら、‘C-M-q’は何も変更しないはずです。 17.4 カバレッジテスト ===================== ‘testcover’ライブラリーをロードしてコマンド‘M-x testcover-start FILE ’でコードをインストルメントすることにより、Lispコードのファイ ルにたいしてカバレッジテストを行なうことができます。コードを1回以上呼び 出すことによってテストが行なわれます。コマンド‘M-x testcover-mark-all’を 使用すれば、カバレッジが不十分な箇所が色付きでハイライト表示されます。コ マンド‘M-x testcover-next-mark’は次のハイライトされた箇所へポイントを前 方に移動します。 赤くハイライトされた箇所は通常はそのフォームが完全に評価されたことが 一度もないことを示し、茶色でハイライトされた箇所は常に同じ値に評価された (その結果にたいして少ししかテストされていない)ことを意味します。しかし ‘error’のように完全に評価するのが不可能なフォームにたいしては、赤いハイ ライトはスキップされます。‘(setq x 14)’のように常に同じ値に評価されるこ とが期待されるフォームにたいしては、茶色のハイライトはスキップされます。 難しいケースではテストカバレッジツールにアドバイスを与えるために、コ ードにdo-nothingマクロを追加することができます。 -- Macro: 1value form FORMを評価してその値をリターンするが、テストカバレッジにたいして FORMが常に同じ値だという情報を与える。 -- Macro: noreturn form FORMを評価してFORMが決してリターンしないという情報をカバレッジテス トに与える。もしリターンしたらrun-timeエラーとなる。 Edebugにもカバレッジテスト機能があります(*note Coverage Testing::を参 照)。これらの機能は部分的に重複しており、組み合わせることで明確になるで しょう。 17.5 プロファイリング ===================== プログラムは正常に機能しているものの、より高速または効率的に実行させたい 場合にまず行うべきは、そのプログラムがリソースをどのように使用するか知る ためにコードを“プロファイル(profile)”することです。ある特定の関数の実行 が、実行時間のうち無視できない割り合いを占めるようなら、その部分を最適化 する方法を探すことを開始できます。 このためにEmacsにはビルトインのサポートがあります。プロファイリングを 開始するには‘M-x profiler-start’をタイプします。プロファイルはプロセッサ ー使用(processor usage)とメモリー使用(memory usage)、またはその両方を選 択できます。何らかの処理を行った後に‘M-x profiler-report’とタイプすると 、プロファイルに選択した各リソースがsummaryバッファーに表示されます。 reportバッファーの名前にはそのレポートが生成された時刻が含まれるので、前 の結果を消去せずに後で他のレポートを生成できます。プロファイリングが終了 したら‘M-x profiler-stop’とタイプしてください(プロファイリングに関連する 多少のオーバーヘッドがあるため)。 profiler reportバッファーでは各行に呼び出された関数と、その後にプロフ ァイリングが開始されてから使用したリソース(プロセッサーまたはメモリー)の 絶対時間とパーセンテージ時間が表示されます。左側にシンボル‘+’のある行で はをタイプして行を展開して、高位レベルの関数に呼び出された関数を確 認できます。もう一度をタイプすると、元の状態へと行が折り畳まれます 。 ‘j’か‘mouse-2’を押下すると関数の定義にジャンプします。‘d’を押下すると 関数のドキュメントを閲覧できます。‘C-x C-w’を使用してプロファイルをファ イルに保存できます。‘=’を使用すれば2つのプロファイルを比較することができ ます。 ‘elp’ライブラリーは別のアプローチを提案します。使い方は‘elp.el’を参照 してください。 ‘benchmark’ライブラリーを使用してEmacs Lispフォームのスピードを個別に チェックできます。‘benchmark.el’内の関数‘benchmark-run’と ‘benchmark-run-compiled’を参照してください。 ‘configure’のオプションに‘--enable-profiling’を使用してビルドすること により、EmacsをCコードのレベルでプロファイルすることができます。こうして ビルドされたEmacsは、Emacsをexitするときに‘gprof’ユーティリティを使用し て検証できるファイル‘gmon.out’を生成します。この機能は主にEmacsのデバッ グに有用です。このEmacsは実行状態から上述の‘M-x profiler-...’コマンドに よりLispレベルで実際にストップします。 18 Lispオブジェクトの読み取りとプリント *************************************** “プリント(print)”と“読み取り(read)”はLispオブジェクトからテキスト形式へ の変換、またはその逆の変換を行なう操作です。これらは*note Lisp Data Types::で説明したプリント表現(printed representation)と入力構文(read syntax)を使用します。 このチャプターでは読み取りとプリントのためのLisp関数について説明しま す。このチャプターではさらに“ストリーム(stream)”についても説明します。ス トリームとは、(読み取りでは)テキストがどこから取得されるか、(プリントで は)テキストをどこに出力するかを指定します。 18.1 読み取りとプリントの概念 ============================= Lispオブジェクトの“読み取り”とは、テキスト形式のLisp式をパース(parse: 解 析)して、対応するLispオブジェクトを生成することを意味します。これは LLispプログラムがLispコードファイルからLispに取得される方法でもあります 。わたしたちはそのテキストのことを、そのオブジェクトの“入力構文(read syntax)”と呼んでいます。たとえばテキスト‘(a . 5)’は、CARが‘a’でCDRが数字 の5であるようなコンスセルにたいする入力構文です。 Lispオブジェクトの“プリント”とは、あるオブジェクトをそのオブジェクト の“プリント表現(printed representation)”に変換することによって、そのオブ ジェクトを表すテキストを生成することを意味します(*note Printed Representation::を参照)。上述のコンスセルをプリントするとテキスト‘(a . 5)’が生成されます。 読み取りとプリントは概ね逆の処理といえます。あるテキスト断片を読み取 った結果として生成されたオブジェクトをプリントすると、多くの場合は同じテ キストが生成され、あるオブジェクトをプリントした結果のテキストを読み取る と、通常は同じようなオブジェクトが生成されます。たとえばシンボル‘foo’を プリントするとテキスト‘foo’が生成されて、そのテキストを読み取るとシンボ ル‘foo’がリターンされます。要素が‘a’と‘b’のリストをプリントするとテキス ト‘(a b)’が生成されて、そのテキストを読み取ると、(同じリストではないが )要素が‘a’と‘b’のリストが生成されます。 しかし、これら2つの処理は互いにまったく逆の処理とい