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

18. Lispオブジェクトの読み取りと表示

表示(printing)とは Lispオブジェクトをテキスト表現へ変換する操作であり, 読み取り(reading)は逆の変換操作です. これには, Lispのデータ型で述べた表示表現と入力構文を用います.

本章では, 読み取りや表示を行うLisp関数について述べます. また, (読み取るときに)テキストをどこから得たり, (表示するときに)どこへ出すかを指定する ストリーム(stream)についても述べます.


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

18.1 読み取りと表示の概要

Lispオブジェクトの読み取りとは, テキスト表現のLisp式を解析して対応するLispオブジェクトを生成することを 意味します. これにより, プログラムはLispコードのファイルからLispへ取り込まれます. テキストをオブジェクトの入力構文(read syntax)と呼びます. たとえば, テキスト‘(a . 5)’は, CARaでありCDRが数5であるコンスセルの入力構文です.

Lispオブジェクトの表示とは, オブジェクトを表現するテキストを生成することを意味します. つまり, オブジェクトをその表示表現 (see section 表示表現と入力構文)に変換します. 上に述べたコンスセルを表示するとテキスト‘(a . 5)’を生成します.

読み取りと表示は, 多かれ少なかれ, 逆操作です. 与えられたテキスト断片を読み取ることで得られたオブジェクトを表示すると, しばしば, 同じテキストを生成します. オブジェクトを表示することによって得られたテキストを読み取ると, 通常, 似たようなオブジェクトを生成します. たとえば, シンボルfooを表示するとテキスト‘foo’を生成し, そのテキストを読み取るとシンボルfooが返されます. 要素がabであるリストを表示すると テキスト‘(a b)’を生成し, そのテキストを読み取ると 要素がabである(もとと同じではないが)リストを生成します.

しかし, これら2つの操作は厳密には逆操作ではありません. 3種類の例外があります.


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

18.2 入力ストリーム

テキストを読み取るほとんどのLisp関数は, ストリーム(stream)を引数として受け付けます. 入力ストリームは, 読み取るべきテキストの文字をどこからどのように得るのかを 指定します. 入力ストリームとして使える型は以下のとおりです.

buffer

入力文字はバッファbufferのポイント直後の文字から読み取られる. 1文字読むごとにポイントを進める.

marker

入力文字は, マーカmarkerがあるバッファの マーカ直後の文字から読み取られる. 1文字読むごとにマーカ位置を進める. ストリームがマーカであるときには, バッファのポイント位置は影響しない.

string

入力文字は文字列stringの先頭文字から必要な文字数だけ読み取られる.

function

入力文字は関数functionが生成する. この関数は2種類の呼び出し方を扱える必要がある.

t

ストリームとしてtを使うと, ミニバッファから読み取ることを意味する. 実際には, ミニバッファを表示しユーザーが指定したテキストから成る文字列を作り, それを入力ストリームとして使う.

nil

入力ストリームとしてnilを指定すると, standard-inputの値をかわりに使うことを意味する. その値はデフォルト入力ストリームであり, nil以外の入力ストリームであること.

symbol

入力ストリームとしてのシンボルは, そのシンボルの(あれば)関数定義と等価である.

バッファであるストリームからの読み取りの例を 読み取り前後のポイント位置を含めて示します.

 
---------- Buffer: foo ----------
This∗ is the contents of foo.
---------- Buffer: foo ----------
(read (get-buffer "foo"))
     ⇒ is
(read (get-buffer "foo"))
     ⇒ the
---------- Buffer: foo ----------
This is the∗ contents of foo.
---------- Buffer: foo ----------

最初の読み取りでは空白を読み飛ばしていることに注意してください. 読み取りでは, 意味あるテキストのまえにある白文字はいくつでも読み飛ばします.

つぎは, マーカをストリームとして読み取る例です. マーカの初期位置は下に示したバッファの先頭にあります. 読み取った値はシンボルThisです.

 
---------- Buffer: foo ----------
This is the contents of foo.
---------- Buffer: foo ----------
(setq m (set-marker (make-marker) 1 (get-buffer "foo")))
     ⇒ #<marker at 1 in foo>
(read m)
     ⇒ This
m
     ⇒ #<marker at 5 in foo>   ;; 最初の空白の直前

つぎは文字列の内容から読み取ります.

 
(read "(When in) the course")
     ⇒ (When in)

以下の例は, ミニバッファから読み取ります. プロンプトは‘Lisp expression: ’です. (ストリームtから読むとつねにこのプロンプトが使われる. ) ユーザーの入力はプロンプトに続けて示してあります.

 
(read t)
     ⇒ 23
---------- Buffer: Minibuffer ----------
Lisp expression: 23 <RET>
---------- Buffer: Minibuffer ----------

最後は, useless-streamという名前の関数をストリームにした例です. このストリームを使うまえに, 変数useless-listを文字のリストで初期化します. そうすると, 関数useless-streamを呼び出すたびに リスト内のつぎの文字を返すか, リストの先頭に追加して文字を読み戻します.

 
(setq useless-list (append "XY()" nil))
     ⇒ (88 89 40 41)
(defun useless-stream (&optional unread)
  (if unread
      (setq useless-list (cons unread useless-list))
    (prog1 (car useless-list)
           (setq useless-list (cdr useless-list)))))
     ⇒ useless-stream

つぎのようにしてストリームを使って読み取ります.

 
(read 'useless-stream)
     ⇒ XY
useless-list
     ⇒ (40 41)

リストには開き括弧と閉じ括弧が残っていることに注意してください. Lispリーダが開き括弧に出会うとこれで入力を終えると決定し, それを読み戻すのです. この時点で読み取りを試みると, ‘()’を読み取ってnilを返します.

Function: get-file-char

この関数は, 関数loadで開いた入力ファイルから読み取るための 入力ストリームとして内部的に使われる. 読者はこの関数を使ってはならない.


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

18.3 入力関数

本節では読み取りに関係するLisp関数や変数について述べます.

以下の関数では, streamは入力ストリーム(前節参照)を表します. streamnilであったり省略すると, standard-inputの値をデフォルトにします.

読み取り中に閉じていないリストやベクトル, 文字列に出会うと, エラーend-of-fileを通知します.

Function: read &optional stream

この関数はstreamから1つのLisp式のテキスト表現を読み取り, それをLispオブジェクトとして返す. これは基本的なLisp入力関数である.

Function: read-from-string string &optional start end

この関数はstringのテキストから先頭のLisp式のテキスト表現を読み取る. 読み取った式をCAR, 文字列に残っているつぎの文字 (つまり読み取っていない最初の文字)の位置を表す整数をCDR とするコンスセルを返す.

startが指定してあると, 文字列のstartで添字付け (先頭文字の添字は0)されるところから読み始める. endを指定すると, その添字位置の直前で読み取りを終らせ, 文字列には残りの文字がないかのように扱う.

例:

 
(read-from-string "(setq x 55) (setq y 5)")
     ⇒ ((setq x 55) . 11)
(read-from-string "\"A short string\"")
     ⇒ ("A short string" . 16)
;; 最初の文字から読み始める
(read-from-string "(list 112)" 0)
     ⇒ ((list 112) . 10)
;; 2番目の文字から読み始める
(read-from-string "(list 112)" 1)
     ⇒ (list . 5)
;; 7番目の文字から読み始め, 9番目の文字で読み終える
(read-from-string "(list 112)" 6 8)
     ⇒ (11 . 8)
Variable: standard-input

この変数はデフォルトの入力ストリーム, つまり, 引数streamnilである場合にreadが使う ストリームを保持する.


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

18.4 出力ストリーム

出力ストリームは表示で生成した文字群をどのように扱うかを指定します. ほとんどの表示関数は省略可能な引数として出力ストリームを受け付けます. 出力ストリームとして使える型は以下のとおりです.

buffer

出力文字はバッファbufferのポイント位置に挿入される. 文字が挿入されるたびにポイント位置は進められる.

marker

出力文字はマーカmarkerがあるバッファのマーカ位置に挿入される. 文字を挿入するたびにマーカ位置は進められる. ストリームがマーカであるときには, バッファのポイント位置は影響せず, この種の表示ではポイントを移動しない.

function

出力文字は関数functionに渡され, この関数が文字の保存に責任を持つ. 1つの文字を引数として出力する文字数の回数だけ呼び出され, 読者の望む場所に文字を保存する責任を持つ.

t

出力文字はエコー領域に表示される.

nil

出力ストリームとしてnilを指定すると, standard-outputの値をかわりに使うことを意味する. その値はデフォルト出力ストリームであり, nil以外であること.

symbol

出力ストリームとしてのシンボルは, そのシンボルの(あれば)関数定義と等価である.

正当な出力ストリームの多くは, 入力ストリームとしても正当です. 入力ストリームと出力ストリームの違いは, オブジェクト型の違いというよりは, 読者がLispオブジェクトをどのように使うかです.

バッファを出力ストリームとして使った例を示します. ポイントの初期位置は以下に示すように‘the’の‘h’の直前にあります. 終了後でも, ポイントは同じ‘h’の直前に位置しています.

 
---------- Buffer: foo ----------
This is t∗he contents of foo.
---------- Buffer: foo ----------
(print "This is the output" (get-buffer "foo"))
     ⇒ "This is the output"

---------- Buffer: foo ----------
This is t
"This is the output"
∗he contents of foo.
---------- Buffer: foo ----------

つぎは, マーカを出力ストリームとして用いた例です. バッファfooのマーカの初期位置は, 単語‘the’の‘t’と‘h’のあいだにあります. 終了後には, マーカは挿入したテキストを越えて同じ‘h’の直前に位置します. ポイント位置はなんの影響もないことに注意してください.

 
---------- Buffer: foo ----------
This is the ∗output
---------- Buffer: foo ----------
(setq m (copy-marker 10))
     ⇒ #<marker at 10 in foo>
(print "More output for foo." m)
     ⇒ "More output for foo."
---------- Buffer: foo ----------
This is t
"More output for foo."
he ∗output
---------- Buffer: foo ----------
m
     ⇒ #<marker at 34 in foo>

つぎは, エコー領域への出力の例です.

 
(print "Echo Area output" t)
     ⇒ "Echo Area output"
---------- Echo Area ----------
"Echo Area output"
---------- Echo Area ----------

最後は, 関数を出力ストリームとして使った例を示します. 関数eat-outputは与えられた文字を受け取り, それをリストlast-outputの先頭にコンスします (see section コンスセルとリストの構築). 終了後には, リストがすべての出力文字を保持していますが逆順です.

 
(setq last-output nil)
     ⇒ nil
(defun eat-output (c)
  (setq last-output (cons c last-output)))
     ⇒ eat-output
(print "This is the output" 'eat-output)
     ⇒ "This is the output"
last-output
     ⇒ (10 34 116 117 112 116 117 111 32 101 104 
    116 32 115 105 32 115 105 104 84 34 10)

リストの順番を逆にすれば正しい順序の出力になります.

 
(concat (nreverse last-output))
     ⇒ "
\"This is the output\"
"

concatを呼び出してリストを文字列に変換し, 内容を読みやすいようにしました.


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

18.5 出力関数

本節ではLispオブジェクトを表示する, つまり, オブジェクトを表示表現に変換するLisp関数について述べます.

Emacsの表示関数のなかには, 正しく読み取れるように クォート文字を出力に追加するものがあります. 使用されるクォート文字は‘"’と‘\’です. これらは, 文字列とシンボルを区別したり, 文字列やシンボル内の句読点文字を読み取るときに区切り文字として扱うこと を防ぎます. 詳しくはSee section 表示表現と入力構文. 出力関数を選べば, クォートのありなしを指定できます.

テキストをLispシステムへ読み取る意図がある場合には, 曖昧さを避けるためにクォート文字付きで表示するべきです. Lispプログラマに対してLispオブジェクトを明確に記述する場合も同様です. しかし, 人間向けの見やすい出力が目的であれば, クォートせずに表示するのが普通はよいでしょう.

Lispオブジェクトはそれ自身を参照できます. 自己参照しているオブジェクトを普通の方法で表示するには 無限のテキストが必要であり, そのような試みは無限再帰をもたらします. Emacsはそのような再帰を検出し, すでに表示したオブジェクトを再帰的に表示するかわりに ‘#level’を表示します. たとえば, ‘#0’は, 現在の表示操作においてレベル0のオブジェクトを 再帰的に参照することを示します.

 
(setq foo (list nil))
     ⇒ (nil)
(setcar foo foo)
     ⇒ (#0)

以下の関数では, streamは出力ストリームを表します. (出力ストリームについては前節を参照. ) streamnilであったり省略すると, standard-outputの値をデフォルトにします.

Function: print object &optional stream

関数printは便利な表示方法である. オブジェクトobjectの表示表現を ストリームstreamに出力し, objectの前後に改行を1つずつ表示する. クォート文字を使う. printobjectを返す. たとえばつぎのとおり.

 
(progn (print 'The\ cat\ in)
       (print "the hat")
       (print " came back"))
     -| 
     -| The\ cat\ in
     -| 
     -| "the hat"
     -| 
     -| " came back"
     -| 
     ⇒ " came back"
Function: prin1 object &optional stream

この関数はオブジェクトobjectの表示表現を ストリームstreamに出力する. printのようには出力を区切る改行を表示しないが, printと同様にクォート文字を用いる. objectを返す.

 
(progn (prin1 'The\ cat\ in) 
       (prin1 "the hat") 
       (prin1 " came back"))
     -| The\ cat\ in"the hat"" came back"
     ⇒ " came back"
Function: princ object &optional stream

この関数はオブジェクトobjectの表示表現を ストリームstreamに出力する. objectを返す.

この関数は, readではなく人が読みやすい出力を意図しており, クォート文字を挿入せず, 文字列を囲むダブルクォートも出力しない. 空白も追加しない.

 
(progn
  (princ 'The\ cat)
  (princ " in the \"hat\""))
     -| The cat in the "hat"
     ⇒ " in the \"hat\""
Function: terpri &optional stream

この関数はストリームstreamに改行を出力する. 関数名は『terminate print』の略.

Function: write-char character &optional stream

この関数は文字characterをストリームstreamへ出力する. characterを返す.

Function: prin1-to-string object &optional noescape

この関数は同じ引数に対してprin1が表示するであろう テキストから成る文字列を返す.

 
(prin1-to-string 'foo)
     ⇒ "foo"
(prin1-to-string (mark-marker))
     ⇒ "#<marker at 2773 in strings.texi>"

noescapenil以外であると, 出力にはクォート文字を使わない. (この引数は, Emacs 19版以降で使える. )

 
(prin1-to-string "foo")
     ⇒ "\"foo\""
(prin1-to-string "foo" t)
     ⇒ "foo"

文字列としてのLispオブジェクトの表示表現を得るための別の方法については, 文字と文字列の変換formatを参照.

Macro: with-output-to-string body...

このマクロは, standard-outputを文字列への出力と設定して フォームbodyを実行する. そして, その文字列を返す.

たとえば, カレントバッファの名前が‘foo’であると

 
(with-output-to-string
  (princ "The buffer is ")
  (princ (buffer-name)))

"The buffer is foo"を返す.



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

この文書は新堂 安孝によって2009年9月22日texi2html 1.82を用いて生成されました。