[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

1. List 処理

訓練を受けてない人にとっては、Lisp は奇妙なプログラミング言語である。 Lisp のコードの中には到る所に括弧が見受けられる。中には、Lisp と言う名前 は、`Lots of Isolated Silly Parentheses' を表わしているのだとほざく人ま でいる。しかし、これは根拠のない主張である。Lisp は LISt Processing を表 わし、リスト (や、リストのリスト) を両側を括弧で狭んで扱うプログ ラミング言語である。括弧は、リストの境界を示している。時々、リストの頭に アポストロフィ、即ち引用符 `'' が付いていたりすることもある。リスト は Lisp の基礎である。

1.1 Lisp のリスト  リストってなあに?
1.2 プログラムの実行  Lisp のリストはすぐに実行出来る
1.3 エラーメッセージの出力  
1.4 シンボルの名前と関数定義  
1.5 Lisp インタプリタ  
1.6 評価  
1.7 変数  
1.8 引数  
1.9 変数の値の設定  
1.10 まとめ  
1.11 練習問題  


1.1 Lisp のリスト

Lisp の中では、リストは '(rose violet daisy buttercup) という格好 をしている。このリストの頭には一つのアポストロフィが付いている。これは、 よりあなたが親しんでいるであろう次のような形のリストに書くことも出来る。
 
'(rose 
  violet 
  daisy 
  buttercup)

このリストの要素は四つの異なる花の名前であり、各々が空白で区切られ、 括弧に囲まれている。これは石の壁に囲まれた広場の花を連想させる。

リストはまた、要素として数値を持つことも出来る。例えば、(+ 2 2) なんかがそうだ。このリストはプラスの符号 `+' とその後に続く二つの `2' を持っていて、各々は空白で区切られている。 Lisp の中では、データとプログラムの両方が同じ様に表現される。つまり、こ れらは共に、空白で区切られ括弧で囲まれた単語や数値や他のリストからなるリ ストである。(プログラムがデータと似ているために、あるプログラムは容易に 他のプログラムのデータとして役に立つ; これが Lisp の極めて強力な特徴の一 つである.) (ついでに言っておくと、この直前の括弧で囲まれた補足は Lisp の リストではない。というのも、これらは `;'`.' という 句読点を中に含んでいるからだ。) (訳註:日本語としては変かもしれないが、つじつまを合わせるために `;'`.' を使った。) ここで別のリストの例を挙げよう。今度は中にリストを含んでいる。
 
'(this list has (a list inside of it))

このリストの構成要素は、`this', `list', `has', という単語 と、`(a list inside of it)' というリストである。内部にある方のリス トは、`a', `list', `inside', `of', `it' という 単語からなっている。

1.1.1 Lisp のアトム  分解不可能な存在
1.1.2 リストの中の空白  
1.1.3 GNU Emacs によるリストのタイプの支援  


1.1.1 Lisp のアトム

Lisp の中では、今まで単語と呼んで来たものは、atom (アトム) と 呼ばれる。この言葉は歴史的には「これ以上分解出来ない」という意味を表わす 単語アトムから来ている。Lisp について話している限りは、我々がリストの中 で使ってきた単語は、それ以上小さなプログラムのパートには分解出来ない。そ れは数字や `+' みたいな一つの文字からなる記号でも同じである。一方リ ストは、アトムと違って幾つかの部分に分解することが出来る。(car cdr & cons:基本関数, 参照。) リストの中では各々のアトムは互いに空白で区切られている。また括弧のすぐ 隣りに位置することが出来る。

技術的な言い方をすると、Lisp のリストとは、空白で区切られた、いくつかの アトムないしは他のリスト、あるいはアトムと他のリストの両方を括弧でくくっ たものである。中には一つしかアトムが無くても良いし、全く無くても良い。中 に何も入っていないリストというのは () であるが、これは空リス ト (null list) と呼ばれる。空リストは同時にアトムでもリストでもあ る唯一のものである。

アトムやリストを表示したものは、symbolic-expression, もしくはもっ と簡単にS式と呼ばれる。Expression という単語自身は、表示さ れた表現か、あるいは計算機の内部に保持されているアトムやリストのどちらか を表わす。Expression という言葉はしばしば曖昧に用いられる。(また、 多くの本では形式 (form) という言葉が expression の同意語とし て使われている。)

(訳註:この訳では以下、expression を式、ないしはS式と訳していることが多 い。)

ついでだが、我々の世界を構成しているアトム(原子)がそう名付けられたのは、 それらが分割不可能と考えられていた時のことである。しかしその後、物理で 言うアトムは分割不可能ではないことが発見された。核分裂を起こして、小原 子を放出したり、ほぼ同じサイズの二つの原子に分裂したりもするのだ。物理 で言うアトムはまだその本当の性質が解明されぬままに時期尚早にして名付け られてしまった。Lisp の中では、ある種のアトム、例えば配列 (array) は、 複数の部分に分解出来る。しかし、そのメカニズムはリストを分解する時のメ カニズムとは違う。リストの操作に関する限り、リストの中のアトムは分解不 可能である。

英語におけるのと同様、Lisp のアトムを構成する文字要素は、一つの単語とし ての文字とは違う。例えば、南アメリカナマケモノ (South American sloth) を 表わす `ai'`a'`i' という二つの単語とは全く異なる。 自然の中には沢山の種類のアトムがあるが、Lisp の中には数種類のアトムしか ない。例えば、37、511、1729といった数値 (number)、そして `+'`foo'、や `forward-line' といったシンボル (symbol) なんかである。今まで挙げたリストの例の中に出てきた単語は 全てシンボルである。普段の Lisp の会話では、アトムという単語はあまり使わ れないが、それはプログラマは普通今使っているアトムがどの種類のアトムかを より限定的にみようとしているからである。Lisp のプログラミングはもっぱら リストの中のシンボル (と、ときどき数値) についてのものである。 それに加えて、二つの二重引用符で挟まれたテキストは---それが一つの文であ ろうと、あるいはパラグラフであろうとも---アトムである。例を挙げておこう。

 
     '(this list includes "text between quotation marks.")

Lisp では句読点が入っていようが空白があろうが、全ての二重引用符で囲まれ た文は一つのアトムである。この種のアトムは 文字列 (string) と呼ばれ、計算機が人間が読める形でメッセージを出す用途等に使われる。文字 列は数値ともシンボルとも違う種類のアトムであり、異なる使い方をされる。


1.1.2 リストの中の空白

 
'(this list
   looks like this)

は、次のリストと全く同じである。

 
'(this list looks like this)

どちらの例も、Lisp にとっては `this'`list'`looks'`like'、そして `this' というシンボルがこの順番で並んでいる全く 同じリストである。

余分な空白と改行は、人間がよりリストを見やすいようにするために用いられて いる。Lisp が表現を読み取る時は、全ての余分な空白を除いてしまう(ただし、 最低一個の空白は残している。そうでないとアトムとアトムの区切りが分らなく なってしまう)。

奇妙に思えるかもしれないが、今まで見てきた例で殆どの Lisp のリストがカバー されてしまう。Lisp における他の全てのリストは、多かれ少なかれ、これらの 例のどれかに似ている。まあ、もっと長くなったり、複雑になったりはするが。 簡単にまとめると、リストは括弧に挟まれ、文字列は二重引用符に挟まれ、シン ボルとは単語のようなもので、数値とは数のようなものである。(ある場合には、 角括弧や、ドット、それに他の特別な文字が使われたりもする。しかし、それら が無くても我々はかなり先まで進める。)


1.1.3 GNU Emacs によるリストのタイプの支援

もし、あなたが Lisp のS式を GNU Emacs の Lisp Interaction mode もしくは Emacs Lisp mode の中でタイプしているなら、幾つかのコマンドを使って、Lisp のS式を整形し読みやすいものにすることが出来る。例えば TAB キーを 押すと、カーソルがある行を自動的に適当な分だけインデントしてくれる。リー ジョン内のコードをきちんとインデントしてくれるコマンドは、M-C-\ に バインドされている。インデントは、あなたが、どの要素がどのリストに属して いるか見やすいように---サブリストの要素がそれを含んでいるようなリストの 要素よりも深くインデントされるように---設計されている。

更に、あなたが括弧を閉じる時に、Emacs は一時的にその括弧にマッチする開き 括弧の位置にジャンプする。そうすることでどの括弧に対応するかを確かめるこ とが出来る。これは大変便利である。というのも、あなたが Lisp でタイプする 全てのリストについて、開き括弧には必ず閉じ括弧がきちんと対応していなけれ ばならないからである。(section `Major Modes' in The GNU Emacs Manual, に Emacs の mode のより詳しい説明が書かれている。)


1.2 プログラムの実行

Lisp におけるリストは、たとえどんなリストであってもそのまま走らせることが 出来る。実際に走らせてみると (Lisp の専門用語では 評価 (evaluate) するという)、計算機は次の3つのうちいずれかの動作をする。 まずは、そのリストをそのまま返すだけで何もしない、もしくは、あなたにエラー メッセージを返す、あるいは、リストの先頭のシンボルをコマンドとして扱い、 なにがしかのことをする。(普通は勿論、最後の動作があなたの求めるものであ ろう。)

以前のセクションで挙げた例の中での、リストの頭に付けた単独のアポストロフィ ' は、引用符ないしは クウォート (quote) と呼ば れる。これがリストの頭に付いている場合は、何も付いていない場合と異なり、 Lisp はこのリストについては何もしなくていいんだなと解釈する。しかし、リ ストの頭に引用符が付いていない場合は、リストの最初の項が特別な意味を持つ。 つまり、これは計算機が従うべき命令になるのだ。(Lisp ではこれらの命令は 関数 (function) と呼ばれる。) 以前挙げた (+ 2 2) と いうリストには頭に引用符は付いていないので、Lisp は + が残りのリ ストに関する何らかの命令であると解釈する。この場合は、残りの数を加えると いうものである。

もし、この文章を GNU Emacs の Info の中で読んでいるのなら、次のようにし てこのようなリストを評価することが出来る。カーソルを次のリストの右側の括 弧のすぐ後に持ってきて、C-x C-e とタイプするのである。

 
(+ 2 2)

エコー領域に 4 が見えたはずだ。(専門用語では、このことを「リスト を評価する」と言う。エコー領域というのはスクリーンの最下行のことであり、 テキストを表示ないしは「エコー」する。) さて、同じことを頭に引用符が付 いたリストでやってみよう。カーソルを下のリストのすぐ後に移動させ、 C-x C-e とタイプする。

 
'(this is a quoted list)

この場合はエコー領域に (this is a quoted list) という文が見えたは ずだ。

いずれの場合にも、あなたはコマンドを GNU Emacs の Lisp インタプリタ (Lisp interpreter) と呼ばれるプログラムに渡している。つまり、イン タプリタにそのS式を評価しろという命令を与えているのだ。Lisp インタプリ タという名前は、S式の意味を提供する---つまりそれを解釈する---という人間 の仕事を表わす単語から来ている。

リストの一部ではない、単独のアトム---括弧に囲まれていないもの---を評価す ることも出来る。この場合も Lisp インタプリタは人間が読むことの出来るS式 から、計算機の内部の言語への翻訳をする。しかし、これについて議論する前に (変数, 参照)、まず我々がエラーを犯した場合に Lisp イン タプリタが何をするかを見ていこう。


1.3 エラーメッセージの出力

まず、Lisp インタプリタがエラーメッセージを出すようなコマンドを与えてみ よう。偶然このようなことをしてしまっても何も心配することはない。これは、 無害な操作である。実際、我々はしばしば意図的にエラーメッセージを出させる ことがある。一旦専門用語を理解してしまえば、エラーメッセージから多くの情 報を得ることが出来る。そういう意味ではエラーメッセージと言うよりはヘルプ メッセージと言うべきだろう。言わば他の国から来た旅行者にとっての道標のよ うなものである。解読するのは大変だけれども、一度理解してしまえば正しい道 を教えてくれるというわけだ。

我々が行うのは、頭に引用符が付いていないにもかかわらず、先頭の要素が意味 あるコマンドでないようなリストを評価することである。ここに、我々がさっき 使ったリストとほぼ同じだが、頭に引用符が付いていないものがある。これの直 後にカーソルを持っていって、C-x C-e とタイプしてみよう。

 
(this is an unquoted list)

今回は次のようなメッセージがエコー領域に表示されたはずだ。

 
Symbol's function definition is void: this

(それと、端末がビープ音を鳴らしたかもしれない---鳴らすものもあるし、鳴ら さないものもある。ランプが点滅したりするのもある。これは注意を引くための 装置である。) メッセージは他のキーをタイプするとすぐに消えてしまう。たと えそれが単にカーソルを移動しただけであってもだ。

我々が既に知っていることを基にして、このエラーメッセージをほぼ解読するこ とが出来る。我々は `シンボル' という単語の意味を知っている。今の場 合、リストの最初のアトムである単語 `this' のことを指している。 `関数' という単語は前に一度出て来た。これは極めて重要な単語である。 ここでは 関数 (function) とは計算機に何かやらせるための命令 の集まりである、とでも定義しておけば十分だろう。(技術的には、シンボルは 計算機に何処で命令を見つければよいかを教えていることになる。しかし、この 辺の事情は複雑だし、しばらくは無視して構わない。)

以上で、`Symbol's function definition is void: this' というエラー メッセージを理解することが出来るようになる。これは、シンボル (つまり、 `this' という単語) には計算機が実行出来るような命令セットは定義され ていないということである。

この、`function definition is void' というちょっと奇妙なメッセージ は Emacs Lisp の実装の仕方を表わすように書かれている。つまり、そのシンボ ルに対して関数が定義されていない場合、命令が含まれているはずの場所が空 (void) だというわけである。一方で、(+ 2 2) を評価した時にちゃんと 2に2を加えることが出来るということから、シンボル + は計算機が従う べき命令セットを持っており、それらの命令は + に続く数を加えるとい う物であるに違いないと推測出来る。


1.4 シンボルの名前と関数定義

我々は今まで議論してきたことに基づいて、別の Lisp の特徴---それも極めて 大切な特徴---を述べることが出来る。即ち + のようなシンボルはそれ 自身は計算機が実行出来るような命令ではないということである。その代わりシ ンボルは、多分、一時的に、命令の定義ないしは命令の集まりの位置を見つける 方法として使うことが出来る。我々に見えているのは、それを通して命令を見つ けることの出来る名前なのである。人間の名前も同じ働きをする。私は `Bob' として言及される。しかしながら、私は `B'`o'`b' という文字ではなく、一貫して特定の生命体に付随しているある意識 である。名前は私そのものではないが、私を示すのに使われる。

Lisp では、ある命令セットには幾つかの名前が付随している。例えば、数を 加えろという計算機の命令には、+ というシンボルと同様、plus というシンボルも付随させることが出来る。(他の幾つかの Lisp の方言では実 際にそうなっている。) 人間の場合でも、私は `Bob' だけではなく `Robert' として呼ばれることもあるし、他の呼び方をされることもある。

一方で、一つのシンボルは、一度にただ一つの関数定義しか持つことが出来ない。 そうでないと、計算機がどの定義を使うべきか迷ってしまうからだ。仮にこれが 人間の場合であれば、`Bob' という名前は世界で一人の人間にしかつけて はいけないということである。ただし、名前に付随する関数定義は簡単に変更 することが出来る。(関数定義のインストール, 参照。)

Emacs Lisp はでかいので、関数が属する Emacs のパートが識別出来るようなシ ンボルの名前を付け方をするのが普通である。というわけで、Texinfo を扱うため の関数の名前は全て `texinfo-' で始まっているし、メールを読むための (reading mail) 関数の名前は `rmail-' で始まっている。


1.5 Lisp インタプリタ

これまでに見てきたことから、リストを評価するよう命令した時に Lisp インタ プリタが何をするかを理解することが出来る。まずインタプリタは、list の頭 に、引用符が付いているか見る。もし付いていれば、単にそのリストを我々に渡 す。一方、付いていない場合はリストの先頭の要素を見に行き、それが関数の定 義を持っているかどうかを調べる。定義されている場合はその定義にある命令を 実行し、そうでなければエラーメッセージを表示する。

これが Lisp の動作である。単純だ。これに加えて若干複雑なこともある。それ についてはすぐ後で説明するが、基本はこれだけである。勿論、Lisp のプログ ラムを書く際には、関数の定義を書いたりそれに名前を付けるにはどうすればい いか、あるいはそういうことをあなた自身や計算機が混乱しないように行うには どうすればいいか等、他にも知らなければいけないことはあるが。

さて、さっき述べた「複雑なこと」の一つ目は、Lisp インタプリタは、リスト に加えて引用符が付いておらず、括弧に囲まれてもいない単独のシンボルも評価 することが出来るということである。この場合、Lisp インタプリタはそのシン ボルの値を変数 (variable) として評価しようとする。このこと については変数のセクションで説明することにする。(変数, 参照。)

複雑なことの二つ目は、幾つかの関数はその特殊性のために普通のやり方ではう まく働かないことによる。このような関数は 特殊形式 (special form) と呼ばれる。これらは、例えば関数を定義したりといった特別な用途を 持つ。次の幾つかのセクションでもっと大切な特殊形式を紹介するつもりだ。

三つ目の、そして最後の複雑なことはこうだ。もし現在 Lisp インタプリタが見 ている関数が特殊形式ではなく、しかもそれがリストの一部であるなら、Lispイ ンタプリタはリストが内部にリストを含んでいないかどうかを見る。もし内部に リストを含んでいれば、それがその内部でどういうことをするかを見る。そして、 その後にその外側のリストを処理する。また、もしその内部のリストが更に他の リストを中に含んでいれば、まず、それを先に処理してからということになる。 即ち、Lisp インタプリタは常に一番内部のリストから処理していく。これは、 内部のリストの結果をそれを包む外側のS式で使う場合、内部のリストの結果が どうなるかを見てから外側のリストを処理しなければいけないためである。

それ以外の場合は、インタプリタは左から右へS式を一つずつ処理していく。

1.5.1 バイトコンパイル  


1.5.1 バイトコンパイル

解釈の仕方について、もう一つ興味深いことがある。 Lisp インタプリタは二つ の種類の物を解釈出来るということである。一つは人間が読めるコードであり、 我々は専らこれに焦点を当てている。もう一つは特別なプロセスコードであり、 これはバイトコンパイル (byte compile) されたコードで、人間に 読める代物ではない。バイトコンパイルされたコードは我々が読めるコードより も速く走らせることが出来る。

人間が読めるコードをバイトコンパイルされたコードに変換することも出来る。 これは byte-compile-file 等の、コンパイルコマンドを走らせることに よって行う。バイトコンパイルされたコードは普通、`.el' ではなく、 `.elc' という拡張子で終わるファイルに保存される。`emacs/lisp' などといったディレクトリィを覗けば、この両方の種類のファイルが見つかる。 我々が読むのは `.el' という拡張子の付いた方である。

実際問題として、あなたが Emacs をカスタマイズしたり拡張したりする場合に は大抵はバイトコンパイルする必要はない。そして私もここではこの話題につい ては述べない。section `Byte Compilation' in The GNU Emacs Lisp Reference Manual, を見ればバイトコンパイルについての全ての情 報が載っている。


1.6 評価

Lisp インタプリタがあるS式を処理している時、その動作は 評価 (evaluation) と呼ばれる。つまり、「インタプリタはS式を評価する」 のように言うわけだ。私はこの用語を以前にも何回か用いた。この単語はこの言 葉の日常会話での意味、Webster's New Collegiate Dictionary (及び、 小学館 『プログレッシブ英和中辞典』) によれば「価値や量を確かめる、概算 する、見積もる」という意味、から来ている。

Lisp インタプリタはS式を評価した後、大抵その関数の定義の中にある計算機 に対する命令を実行した時に出力する値を 返し (return)、そうで ない場合は、おそらくその関数を処理するのを止めてエラーメッセージを出力す る。(インタプリタは、急に別の関数を渡されることもあり得るし、あるいは、 いわゆる「無限ループ」に入って現在の処理を何回でも繰り返そうとするかもし れない。が、これらの動作をすることはそれ程多くはないので、我々はこれらの 場合は無視することにする。) つまり殆どの場合、インタプリタは値を返す。

インタプリタは値を返すと同時に、カーソルを動かしたり、ファイルをコピー したりといった他の動作も行う。このような動作は、副作用 (side effect) と呼ばれる。例えば結果を表示するとか、我々人間が重要 と考える動作もしばしば、Lisp インタプリタにとっては「副作用」でしかない。 この専門用語には異和感があるかもしれないが、副作用自体の使い方を学ぶのは かなり簡単だということがその内分るだろう。

以上まとめると、Lisp インタプリタはS式を評価した時に、まず殆どの場合あ る値を返し、大抵はある副作用を実行する。そして、そうでない場合はエラーを 出力する、ということになる。

1.6.1 内部のリストの評価  


1.6.1 内部のリストの評価

もしあるリストの内部のリストが評価された場合、その外側のリストが評価され る時には最初に内部のリストを評価した時に返った値を情報として使うことが出 来る。このことから、何故内部のリストが先に評価されるかが説明出来る。即ち、 内部のS式が返す値を外部のS式が用いるためである。

我々はこのプロセスを次のような例で実際に確かめることが出来る。次の式の直後 にカーソルを持っていき、C-x C-e とタイプしてみよう。

 
(+ 2 (+ 3 3))

数字の8がエコー領域に表示されたはずだ。

この例で起こったことはこうだ。まず Lisp インタプリタは内部のS式である (+ 3 3) を評価する。それに対して値6が返される。それによって外部 のS式があたかも (+ 2 6) であるように評価され、値8を返すことにな る。これらを内部に含む評価すべきS式はこれ以上存在しないので、インタプ リタはこの値をエコー領域に表示する。

さて、ここまでくれば C-x C-e で引き起こされるコマンドの名前を理解 することは簡単だ。このコマンドの名前は eval-last-sexp となってい る。この中の sexp という文字は `symbolic expression' (S式) の略 である。ということで、これは直前のS式を評価する (evaluate last symbolic expression) ことを表している。

実験的に、次に挙げる例のS式の直後の行の先頭やS式の内部にカーソルを持っ ていってS式を評価してみることも出来る。

これがその例である。

 
(+ 2 (+ 3 3))

もしカーソルをS式の直後の空行の先頭に置いて C-x C-e とタイプした なら、その場合もやはり8がエコー領域に表示されることだろう。では次に、 カーソルをS式の内部に持っていこう。最後の括弧のすぐ右側の括弧の後にカー ソルを持っていって (つまりカーソルは最後の括弧の上に見えている) そこで評 価したならば、エコー領域には6と表示される。これはコマンドが (+ 3 3) という式を評価したからである。

今度はカーソルを数字の直後に持っていこう。C-x C-e とするとその数字 そのものが返される。Lisp の中では、数字を評価した場合、その値自身が返さ れる。これが数字がシンボルと違うところである。もし + みたいなシン ボルを先頭に持つリストの直後でS式を評価したなら、返された値はその名前に 付随する関数の定義の中の命令を計算機が実行した結果が返ってくる。が、もし シンボルそのものが評価されたなら別のことが起きる。これについては次のセク ションで見ることにしよう。


1.7 変数

Lisp の中では、シンボルは、関数定義を持つことが出来るのと同様に、ある値 を持つことが出来る。これら二つは全く別の物である。関数定義は計算機が従う べき命令の集まりであるのに対し、値は数値とか名前のように、何か変化し得る ものである。(このようなシンボルが変数と呼ばれるのはこのためである。) ど んな Lisp のS式もシンボルの値になり得る。例えばシンボルや数値、リスト、 文字列なんかが値となることが出来る。値としてのシンボルはしばしば変 数 (variable) と呼ばれる。

シンボルは、関数定義と値の両方を同時に持つことが出来る。この二つは分離さ れているのだ。これは、ケンブリッジという名前がマサチューセッツの中のある 都市そのものを表わすのと同様に、その都市名に付随するなんらかの情報、例え ば、「大きなプログラミングセンター」であるといった情報をも持ち得るのと似 ている。

このことを理解するもう一つの考え方を述べよう。それはシンボルを整理棚であ ると見倣す方法である。関数定義はその引き出しの一つに入っており、値はまた 別の引き出しに入っている... という具合である。値の入っている引き出しの中 身は同じ棚の関数定義の入っている引き出しの中身に影響を与えることなく変え ることが可能だし、逆も同様である。

変数 fill-column を例にとって、値を持つシンボルというものを説明し てみよう。GNU Emacs の全てのバッファにおいて、このシンボルはある値にセッ トされている。大抵は72か70であるが、他の値を持つこともある。この値が何か を見るにはこのシンボル自身を評価してみれば良い。もしこの文章を GNU Emacs の Info で読んでいるなら、カーソルを次のシンボルの直後に持っていって C-x C-e とタイプするだけである。

 
fill-column

私が今 C-x C-e とタイプしてみたところ、Emacs はエコー領域に72と表 示した。これが、私がこの文章を書いている時に fill-column にセット されている値である。あなたの Info のバッファではまた別の値が設定されてい るかもしれない。変数として評価されて返す値は、ある関数が命令を実行した時 に値を返す時と全く同じように表示されることに注意しよう。Lisp インタプリ タの立場からすると、どちらも返り値であることに違いはない。どんな種類のS 式から来ているかということは一旦その値が分ってしまえば重要ではないのだ。

シンボルはどんな値も持つことが出来る。あるいは専門用語を使っていうなら我々 は変数に72といった数値や "such as this" といった文字列や、 (spruce pine oak)バインド (bind) (束縛とも言う) す ることが出来る。変数に関数定義をバインドすることすら可能である。

シンボルに値を設定するには幾つか方法がある。その方法の一つについては、 変数の値の設定, を参照のこと。

fill-column という単語には周りに括弧がついていないことに注意しよ う。これは我々がこのシンボルを関数の名前としては使わなかったためである。 fill-column がリストの最初、もしくは唯一の要素であった場合、Lisp インタプリタはこれに付随する関数定義を見つけようとする。しかし、 fill-column は関数定義を持ってはいない。例えば次を評価してみよう。

 
(fill-column)

 
すると次のようなエラーメッセージが出るはずだ。

Symbol's function definition is void: fill-column

1.7.1 値のないシンボルに対するエラーメッセージ  


1.7.1 値のないシンボルに対するエラーメッセージ

もし、値を持たないシンボルを評価しようとしたなら、エラーメッセージが返さ れるはずだ。2に2を加える例で実験してみよう。次のS式のなかで最初の2の直 前にある + の直後にカーソルを持っていって C-x C-e とタイプ してみよう。

 
(+ 2 2)

次のようなエラーメッセージを受けとるはずだ。

 
Symbol's value as variable is void: +

これは我々が見た最初のエラーメッセージとは違っている。さっきは `Symbol's function definition is void: this' (シンボルの関数定義 が空です: this) というものだった。今回のケースではシンボルは変数として の値を持っておらず、もう一方のケースではシンボル (`this' という単語) は関数定義を持たなかった。

この + を使った実験の中でやったことは Lisp インタプリタに + を 評価させ、関数定義では無く変数の値を探させることである。そのためにカーソ ルを前のようにリストを囲む括弧の後では無く、シンボルのすぐ右隣に持っていっ たのだった。結果として Lisp インタプリタは直前のS式、この場合は + そ のもの、を評価したのだ。

+ は関数定義は持っていても値は持ってはいないので、エラーメッセー ジはシンボルの変数としての値が空だよと報告したのである。


1.8 引数

どうしたら情報が関数に渡されるかを見るために、これまで何回か使ってきた2 に2を足す例をもう一度見てみよう。

 
(+ 2 2)

もしこのS式を評価したなら数字の4がエコー領域に表示される。Lisp インタプ リタがやったことは + の後に続く数字を加えることである。

+ によって加えられた数字は関数 +引数 (argument) と呼ばれる。これらの数字は関数に与えられる、もしくは 渡される (pass される) 情報である。

「argument」という単語は数学での用法から来ているもので、二人の人が議論す るという意味ではない。そうではなく関数、今の場合でいうと + という 関数、に与えられる情報のことを指している。Lisp の中では関数に対する引数 は関数に続くアトムもしくはリストである。これらのアトムやリストを評価して 返された値が関数に渡されることになる。異なる関数は異なる数の引数を取り得 る。全く引数を取らない関数もある。(1)

1.8.1 引数のデータ型  
1.8.2 引数には変数の値やリストも使える  
1.8.3 可変な数の引数  
                                ものもある
1.8.4 関数に間違った型の引数を与えると  
1.8.5 関数 message  メッセージを表示する時に便利な関数


1.8.1 引数のデータ型

関数に渡されるデータの型はその関数がどんな種類の情報を必要としているかに よる。+ のような関数の引数は数値でなければならない。というのも + は数値を加える命令だからである。他の関数は引数として他の種類の データを取る。

例えば、concat という関数は二つ以上のテキストの文字列を連結な いしは合成して一つの文字列を作る。この場合の引数は文字列である。 二つの文字文字列 abc, def を連結 (concatenate) すると、 abcdef という一つの文字列が出来る。これは次の例で確かめられる。

 
(concat "abc" "def")

このS式を評価すると "abcdef" という値が返される。

substring のような関数は引数として文字列と数値の両方を取る。これ は文字列である最初の引数の部分文字列を返す関数である。引数の数値は 3つである。最初の引数は文字からなる文字列である。二番目と三番目の引 数は部分文字列の初めと終りの場所を示す数値であり、文字列の最初から の(スペースや句読点も含めた)文字数を指定する。

例えば次を評価してみよう。

 
(substring "The quick brown fox jumped." 16 19)

substring に渡される文字列は、たとえスペースで区切られた複数の単 語からなっていたとしても一つのアトムであることに注意しよう。Lisp は二つ の二重引用符の間に狭まれる全てを空白も含めて文字列の一部と数える。 substring という関数は一種の「アトム粉砕機 (atom smasher)」だと見 倣すことが出来る。というのもこいつは他の方法では分離できないアトムを取り 込み、その一部を抽出するからである。しかしながら、substring は文 字列である引数から部分文字列を抽出することが出来るだけで、他の種類の例え ば数値とかシンボルであるアトムについてはそのようなことは出来ない。


1.8.2 引数には変数の値やリストも使える

引数は評価された時に値を返すシンボルであることも可能だ。例えば fill-column というシンボルそれ自身が評価された場合には、数値が返 される。この数値は加法演算にも使える。次のS式の後にカーソルを持っていっ て C-x C-e とタイプしてみよう。

 
(+ 2 fill-column)

返される値は fill-column を単独で評価した場合に得られる数に2を加 えたものになるはずだ。私には74が返される。何故なら fill-column の 値が72であるからだ。

たった今見た様に、引数は評価された時に値を返すシンボルであり得る。それに 加えて、引数は評価された時に値を返すリストでもあっても良い。例えば次のS 式の中では関数 concat の引数は "The ", "red foxes." そしてリスト (+ 2 fill-column) である。

 
(concat "The " (+ 2 fill-column) " red foxes.")

もしこのS式を評価したならエコー領域には "The 74 red foxes." が表 示されるはずだ。(最後の文字列を表示させるには `The' という単語 の後と `red' という単語の前に空白を入れなければならないことに注意。) (訳註:74の所は勿論場合に応じて変わる。)


1.8.3 可変な数の引数

例えば concat, +, * といった幾つかの関数では引数の 数は固定されていない。(* は乗法を表わすシンボルである。) これは次 に挙げる各々のS式を評価してみると分る。エコー領域に表示されるべき結果は このテキストの中の `=>' の後に書かれている。これは「を評価し た結果は」と読み換えられる。

最初は引数がない場合である。

 
(+)       => 0

(*)       => 1

こちらは一つだけ引数を持っている。

 
(+ 3)     => 3

(* 3)     => 3

次では、各々三つずつ引数を持っている。

 
(+ 3 4 5) => 12

(* 3 4 5) => 60


1.8.4 関数に間違った型の引数を与えると

ある関数に間違った型の引数が与えられると、Lisp インタプリタはエラーメッ セージを返す。例えば、+ という関数は引数の値として数値を要求する。 実験的に、これに数値ではなく hello というシンボルを与えてみよう。 次のS式の後にカーソルを持っていって C-x C-e とタイプしてみよう。

 
(+ 2 'hello)

そうするとエラーメッセージが表示される。この場合に起きたことはこうだ。 + は2に 'hello を評価して返される値を加えようとした。しか し返された値は hello というシンボルであり数値ではない。だが数値で ないものを足すことは出来ない。そのために、+ は加算を実行すること が出来なかったのである。

普通、エラーメッセージは助けになるように、つまりあなたが一度読み方を修得 した後は意味が分るようなものであろうとする。この場合は

 
Wrong type argument: number-or-marker-p, hello

と言ってくる。(訳註:Emacs version 18 では integer-or-marker-p で ある。Emacs version 19 からは浮動小数点数も扱うようになった。) エラーメッ セージの最初の部分は極めて直接的だ。`Wrong type argument' (引数の型 が違う) である。次に来るのはミステリアスな専門用語 `number-or-marker-p' である。この言葉はあなたに + という 関数が要求する引数のタイプを教えようとしている。

number-or-marker-p というシンボルの名前は、Lisp インタプリタが与 えられた情報 (引数の値) が数値 (number) もしくはマーカ (marker) (バッファ での位置を表わす特殊なオブジェクト) であるかどうかを判定することを意味し ている。今の場合、実際に行うのは + にちゃんと加えるべき整数が与え られたかどうかのテストである。そして、マーカと呼ばれる特殊オブジェクトか どうかもテストされる。これは Emacs Lisp 固有のものである。(Emacs の中で はバッファ内での位置がマーカとして記録される。C-@ あるいは C-SPC というコマンドによってマークがセットされるとその位置が マーカとして保持されるのである。このマークは数---バッファの始まりからそ の位置までの文字数---と考えることが出来る。) Emacs Lisp では + は マーカの位置を示す数を数値として加えるのにも使うことが出来るわけである。

number-or-marker-p の中の `p' は Lisp プログラミングの初期の 頃に始まったある試みの具体化である。`p' は「述語 (predicate)」を表 わしている。初期の Lisp 研究者の間で用いられていた専門用語では「述語」は ある属性が正しいかどうかを判定する関数のことを指していた。従って、 `p'number-or-marker-p が、与えられた引数が数値 (number) もしくは マーカであるかどうかを判定する関数の名前であることを意味してい る。他の Lisp のシンボルで `p' で終わるものに zerop がある。 これは引数が0という値であるかどうかを判定する関数である。同様に listp は引数がリストかどうかを判定する関数である。

さて、いよいよエラーメッセージの最後の部分 hello であるが、これ は + に渡された引数の値である。もし加法演算に正しい型のオブジェク トが与えられていたとするなら、渡される値は hello のようなシンボル ではなく、例えば37のような数値であったはずである。もっとも、その場合は、 エラーメッセージは出ないけれども。


1.8.5 関数 message

+ と同様、message という関数も引数の数は固定されていない。 この関数はユーザにメッセージを送るものだが、今説明したように、いろいろと 役に立つものである。

メッセージはエコー領域に表示される。例えば次のリストを評価することで、あ なたのエコー領域にメッセージを表示させてみよう。

 
(message "This message appears in the echo area!")

二重引用符に狭まれた文字列全体が一つの引数になっており、その全体が表示さ れる。(この例ではメッセージそれ自身が二重引用符の中に入っていることに注 意しよう。これは、message という関数が返す値そのものがそうなって いるからである。あなたが書くプログラムの中での普通の message の使 い方ではエコー領域に表示されるのは副作用によるものである。それには、二重 引用符は付いていない。このような例については インタラクティブな multiply-by-seven, を参照のこと。)

しかしながら、もし `%s' が二重引用符に挟まれた文字文字列に入ってい たとしたら、関数 message`%s' をそのまま表示したりはせず に、文字列に続く引数を見にいく。まずは二番目の引数を評価し、その結果を `%s' のある所に代入して表示する。

次のS式の最後にカーソルを持っていって C-x C-e とタイプしてみるこ とで、このことが確かめられる。

 
(message "The name of this buffer is: %s." (buffer-name))

Info の中では、"The name of this buffer is: *info*." という文がエ コー領域に表示されたことだろう。buffer-name という関数は現在のバッ ファ名を文字列として返す。message はそれを %s の位置に挿入 したのだ。

値を10進数として表示するには、`%s' の代わりに `%d' という関数 を同じように使う。例えば、現在の fill-column の値をエコー領域に表 示させるには次を評価すれば良い。

 
(message "The value of fill-column is %d." fill-column)

私の使っているシステムでは、このリストを評価した時点ではエコー領域には "The value of fill-column is 72." と表示された。

もし一つ以上の `%s' が二重引用符で狭まれた文字列の中にあったとする と、最初の `%s' の位置にはその文字列に続く最初の引数の値が表示され、 二番目の `%s' の位置には二番目の引数の値が表示され... という具合に なる。例として次を評価してみよう。

 
(message "There are %d %s in the office!"
         (- fill-column 14) "pink elephants")

かなり風変わりなメッセージが表示されたことと思う。私のシステムでは、 "There are 58 pink elephants in the office!"。のように表示された。

ここでは (- fill-column 14) というS式が評価され、その結果として 返された値が `%d' の位置に挿入される。そして二重引用符で狭まれた "pink elephants" という文字列が一つの引数として扱われて、 `%s' の位置に置かれる。 (つまり、二重引用符で挟まれた文字列は、評価 された時に、数値と同じ様にそれ自身を値として返すわけである。)

最後に、単なる数の計算ではなく、S式の中にS式を置き、`%s' で置き換 えることで文章を作る例として、ちょっと複雑なものを挙げておこう。

 
(message "He saw %d %s"
         (- fill-column 34)
         (concat "red "
                 (substring
                  "The quick brown foxes jumped." 16 21)
                 " leaping."))

この例では、message は3つの引数を取る。"He saw %d %s" と いう文字列と、(- fill-column 32) というS式と、concat とい う関数で始まるS式である。(- fill-column 32) を評価して得られた結 果は `%d' の位置に挿入され、concat から始まるS式を評価して 返される値は `%s' の位置に挿入される。

私がこの式を評価したところ、エコー領域には "He saw 38 red foxes leaping." というメッセージが表示された。

(訳註:日本語の例も挙げておく。Mule の内部では2バイトコードは3バイトで表 現されるため一文字3ポイントの勘定になる。)

 
(message "彼は %d 匹の%sのを見た。"
         (- fill-column 34)
         (concat "赤い"
                 (substring
                  "すばしこい茶色のきつねが跳んだ。" 24 33)
                 "が飛び跳ねる"))


1.9 変数の値の設定

変数に値を格納する方法は幾つかある。一つの方法は、set もしくは setq という関数を使うことである。また別の方法として、let を使うというのもある。(3.6 let, 参照). (このプロセスのことを専門用語で は、変数に値をバインド (束縛) すると言う。)

この後のセクションでは setsetq の働きを説明するだけで はなく、引数の渡され方の様子も述べることにする。

1.9.1 set の利用  値の設定
1.9.2 setq の利用  引用符をつけずに値を設定するには
1.9.3 カウント  カウンタとしての setq の利用


1.9.1 set の利用

シンボル flowers の値としてリスト '(rose violet daisy buttercup) をセットするために、次のS式の後にカーソルを持っていって C-x C-e とタイプして、この式を評価してみよう。

 
(set 'flowers '(rose violet daisy buttercup))

すると (rose violet daisy buttercup) というリストがエコー領域に表 示されたはずだ。これは関数 set によって返された (return さ れた) 値である。その副作用としてシンボル flowers が、このリストに バインドされる。つまり、シンボル flowers は、変数とみなすことが出 来て、その値がこのリストであるようになったということである。(因みにこの プロセスは、値をセットするという Lisp インタプリタにとっては副作用でしか ないことが、我々人間にとっては主効果たりうるということの説明になっている。 Lisp インタプリタはエラーが出ない限り必ず値を返すが、副作用の方は、 そのように意図しないと出ないものなのである。)

さっきの set を使ったS式を評価した後では、flowers という シンボルを評価することが出来るようになっている。そして、たった今、我々が セットした値を返す。実際に次のシンボルの後にカーソルを持っていって C-x C-e とタイプしてみよう。

 
flowers

flowers を評価すると、エコー領域にリスト (rose violet daisy buttercup) が現れたはずだ。

もし間違えて頭に引用符を付けた 'flowers の方を評価したとすると、 エコー領域に現れるのはこのシンボル flowers それ自身になる。引用符 を付けたやつを次に挙げておくので、評価してみて欲しい。

 
'flowers

もう一つ注意すべきことは、set を利用する時は、set の引数に は、それらを評価しようとする場合を除いて、必ず引用符を付ける必要があると いうことである。先の例の場合では、引数 flowers 及び (rose violet daisy buttercup) のどちらも評価したくはなかったので、両方に引用 符を付けている。(set の一番目の引数に引用符を付けなかった場合、ま ずまっ先にその引数が評価される。これを flowers に対してやったとし て、もしこのシンボルがまだ値を持っていなかったとすると、`Symbol's value as variable is void' というエラーメッセージが表示される。一方、も し flowers がある値を返したとすると、set はそいつにある値を 設定しようとする。勿論こうなるのが正しい動作である状況もあるが、そういう ことは稀である。)


1.9.2 setq の利用

実際問題として、大抵の場合は第一引数には引用符をつけることになるだろう。 set と引用符付きの第一引数の組み合わせというのは極めてよく使われ るために、それに対して名前がついている。それが特殊形式 setq であ る。この特殊形式はほぼ set と同じなのだが、第一引数に自動的に引用 符を付けてくれる所だけが違う。従ってあなた自身で引用符を付ける必要はない。 また、そのことに加えて、setq には、一つのS式の中で複数の異なる変 数に対して各々に異なる値をセットすることが出来る、という便利さもある。

setq を使って carnivores という変数に '(lion tiger leopard) というリストをセットするには次のS式が使われる。

 
(setq carnivores '(lion tiger leopard))

これは set を使ったものと、第一引数に引用符が付いていないことを除 き、全く同じである。(`setq'qquote を意味して いる。) set を使うと上のS式は次のようになる。

 
(set 'carnivores '(lion tiger leopard))

また、setq は異なる変数の各々に異なる値をセットすることも出来ると 書いた。この場合には一番目の引数には二番目の引数の値がバインドされ、三番 目の引数には四番目の引数の値がバインドされ、という風になる。例えば次のS 式を使うとシンボル tree には樹木のリストを、シンボル herbivores には草食動物のリストをセットすることが出来る。

 
(setq trees '(pine fir oak maple) 
      herbivores '(gazelle antelope zebra))

(このS式は一行で書いても構わないのだが、そうなるとページの大きさには合 わなってしまう。人間が読む場合には、こういう形式の方が読みやすいことがお 分りいただけると思う。)

(訳註:因みに、日本語なら次のようになる。)

 
(setq 樹木 '(松 モミ 樫 楓) 
      草食動物 '(ガゼル レイヨウ シマウマ))

これまで何度か「セットする」という言葉を使ってきたけれども、set とか setq の働きにはもう一つの捉え方がある。それは setsetq はそのシンボルを特定のリストへのポイントにする、とい うものだ。このような考え方は大変一般的であり、この後の章で、少なくとも一 つ、名前の中にそのポインタを持つシンボルに出逢うことになる。そういう名前 が付けられたのはそのシンボルに、ある値、より詳しくはリスト、がセットされ ているためである。もう一つの言い方では、そのシンボルはそのリストへのポイ ンタに設定されているという風に表現出来る。


1.9.3 カウント

次の例は、setq をカウンタとして利用する方法を説明してくれる。これ は、プログラムの中である部分が何回繰り返されたかを数える場合に使うもので ある。最初に変数を0に設定し、その後、プログラムの中でその部分が現われる ごとに一つずつ足していくのである。そのためにはカウンタとなる一つの変数に 加えて二つのS式を用意する必要がある。初めにこのカウンタを0にセットする ための setq 式と、評価されるごとにその変数を一ずつ増やすための setq 式である。

 
(setq counter 0)                ; これをイニシャライザと呼ぼう。

(setq counter (+ counter 1))    ; これはインクリメンタと呼ぶ。

counter                         ; これはカウンタ。

(`;' に続く文はコメントである。関数定義の変更, 参照。)

もし最初のイニシャライザであるS式 (setq counter 0) を評価してか ら三番目の counter というS式を評価したなら、エコー領域には数値0 が表示される。次に二番目のインクリメンタのS式 (setq counter (+ counter 1)) を評価すると、カウンタは一つ増える。従って、ここでもう一度 counter を評価するとエコー領域には数字1が表示される。その後も二番 目のS式を評価するごとに値は増えていく。

インクリメンタ (setq counter (+ counter 1)) が評価されると Lisp インタプリタは最初にもっとも内側にあるリストを評価する。このリストを評価 するために、まず counter, 次に1が評価される。変数 counter を評価すると、その時の値が返される。それが数 1 とともに + に渡されて足し合わされる。で、その和は内側のリストの値として setq に渡され、カウンタに新しい値が設定されることになる。以上のようにして、変 数 counter の値が変更される。


1.10 まとめ

Lisp を学ぶことは、最初の道が急な坂であるような丘を登るようなものである。 あなたが現在登っているのはもっとも急な部分である。先に進むに従って、より 楽に進めるようになるはずだ。

まとめると


1.11 練習問題

幾つか単純な練習問題を挙げておく。


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Matsuda Shigeki on April, 10 2002 using texi2html