| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
慣れていない人の目には、Lispは不可思議なプログラミング言語である。 Lispのコードには、いたるところに括弧がある。 「Lots of Isolated Silly Parentheses(奇妙な括弧が多い)」の略であると 批判する人達もいる。 しかし、この批判は不当である。 LispはLISt Processingの略であり、括弧で囲んだリスト(list) (および、リストのリスト)を扱うプログラミング言語である。 括弧はリストの境界を表す。 リストの直前にアポストロフィ、つまり、引用符`''を付ける場合もある。 リストはLispの基本である。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispでは、リストを'(rose violet daisy buttercup)のように書く。
このリストの直前にはアポストロフィが1つ付いている。
これはつぎのように書くこともでき、
こちらの書き方にも慣れてほしい。
'(rose violet daisy buttercup) |
このリストの各要素は異なる4つの花の名前である。 個々の要素を空白で区切り、庭の花を石で囲うように括弧で囲む。
Numbers, Lists inside of Lists List have numbers, other lists, in them. 1.1.1 Lispのアトム (2004/01/16) Elemental entities. 1.1.2 リスト内の空白 (2004/01/16) Formating lists to be readable. 1.1.3 GNU Emacsのリスト入力補佐機能 (2004/03/28) How GNU Emacs helps you type lists.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispでは、データとプログラムのどちらも同じ方法で表現する。 つまり、どちらも、単語や数やリストを空白で区切って括弧で囲んだリストである (プログラムはデータのようにも見えるので、 プログラムは容易に他のプログラムのデータとなりえる。 これは、Lispの強力な機能である)。
(原文の2つの括弧書き
(Since a program looks like data, one program may easily serve as data for another; this is a very powerful feature of Lisp.) |
(Incidentally, these two parenthetical remarks are not Lisp lists, because they contain `;' and `.' as punctuation marks.) |
つぎもリストの例であり、リストの中にリストを含む。
'(this list has (a list inside of it)) |
このリストの要素は、`this'、`list'、`has'の単語と、 リスト`(a list inside of it)'である。 内側のリストは、`a'、`list'、`inside'、`of'、 `it'の単語からできている。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispでは、これまで単語と呼んできたものをアトム(atoms)と呼ぶ。
この用語はアトム(原子)の歴史的な意味からきており、
「不可分」ということである。
Lispに関していえば、リストに用いてきた単語はそれ以上には小さく分割できず、
プログラムの一部として同じものを意味する。
数や`+'のような1文字の記号についてもそうである。
一方、アトムとは異なり、リストは部分に分割できる
(See 節 7. 基本関数 car、cdr、cons (2004/08/07))。
リストでは、アトムを空白で区切る。 アトムは、括弧のすぐ隣にあってもよい。
技術的には、Lispのリストは、空白で区切ったアトムを囲む括弧、
リストを囲む括弧、アトムやリストを囲む括弧から成る。
リストは、たった1個のアトムを含むだけでも、まったく含まなくてもよい。
何も含まないリストは()のように書き、空リスト(empty list)と呼ぶ。
空リストは、それ以外のものとは異なり、アトムであると同時にリストでもある。
アトムやリストを表示したものをシンボリック式(symbolic expressions)、 あるいは、より簡素にはS式(s-expressions)と呼ぶ。 用語「式(expression)」そのものでは、表示したアトムやリスト、あるいは、 コンピュータ内部に格納したアトムやリストを意味する。 しばしば、これらを区別せずに用語「式(expression)」を使う (さらに、多くの書籍では式の同義語として用語「フォーム(form)」を使う)。
われわれの宇宙を構成するアトム(原子)は、 それらが不可分であると考えられた時代に命名されたものであるが、 物質原子は不可分ではないことが知られている。 原子の一部を分割したり、ほぼ同じ大きさに分裂させたりできる。 物質原子は、その真の性質が発見される以前に命名されたのである。 Lispでは、配列などのある種のアトムは構成部分に分割できるが、 この分割機構はリストを分割する機構とは異なる。 リスト操作に関する限り、リストのアトムは分割できない。
英語の場合と同様に、Lispのアトムを構成する文字は、 単語を作り上げる個々の文字とは異なる。 たとえば、南米のナマケモノを表す単語`ai'(ミツユビナマケモノ)は、 2つの単語`a'と`i'とはまったく異なる。
自然界には多種類の原子が存在するが、Lispには数種類のアトムしかない。
たとえば、37、511、1729などの数(numbers)、
`+'、`foo'、`forward-line'などの
シンボル(symbols)である。
これまで例にあげた単語はすべてシンボルである。
Lispの日常の習慣では、用語「アトム」をあまり使わない。
というのは、プログラマは扱っているアトムの種類を特定しようとするからである。
Lispのプログラミングでは、リスト内のシンボル(やときには数)を扱う
(括弧書き「(やときには数)」の原文(and sometimes numbers)は、
アトムを空白で区切って括弧で囲んであり、
しかも、Lispの句読点記号以外は含まないのでLispのリストである)。
さらに、二重引用符で囲まれたテキストは(文であろうと段落であろうと) アトムである。 つぎに例を示す。
'(this list includes "text between quotation marks.") |
Lispでは、句読点記号や空白を含みこのように囲まれたテキストは単一のアトムである。 この種のアトムは文字列(string)と呼ばれ、 コンピュータが人間向けに出力するメッセージに使う。 文字列は、数やシンボルとは異なる別の種類のアトムであり、使い方も異なる。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
リスト内の空白の個数はいくつでもよい。 Lisp言語の視点からすれば、
'(this list looks like this) |
は、つぎとまったく同等である。
'(this list looks like this) |
どちらの例もLispにとっては同じリストであり、 `this'、`list'、`looks'、`like'、 `this'のシンボルからこの順に構成されたリストである。
余分な空白や改行は人間がリストを見やすくするためのものである。 Lispが式を読み取るとき、余分な空白をすべて取り除く (ただし、アトムとアトムのあいだには、 これらを区切るために少なくとも1つの空白が必要である)。
奇妙に思えるかもしれないが、これまでの例で、 Lispのすべてのリストがどのようなものであるかを見てきた。 Lispのリストは多かれ少なかれこれまでの例のようなものであり、 もっと長かったり複雑なだけである。 要約すれば、リストは括弧で囲まれたものであり、 文字列は二重引用符で囲まれたものであり、シンボルは単語のようなものであり、 数は数字列である (鈎括弧やドットや特別な数種の文字を使う場合もあるが、 しばらくはこれらを使わない)。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
GNU EmacsのLisp InteractionモードやEmacs LispモードでLispの式を 入力する時には、Lispの式を読みやすく整形するためのコマンドを利用できる。 たとえば、TABキーを押すと、カーソルを置いた行を自動的に字下げする。 あるリージョンのコードを正しく字下げするコマンドは、 慣習的にM-C-\にバインドされている。 リストの各要素が、どのリストに属するかがわかりやすくなるように字下げする。 つまり、内側のリストの各要素は、 そのリストを囲むリストの要素よりも字下げされる。
さらに、閉じ括弧をタイプするとEmacsは対応する開き括弧に一時的に カーソルを移動して、対応関係がわかるようにする。 Lispに与える個々のリストは括弧の対応が取れている必要があるので、 これはとても便利である (See 節 `Major Modes' in
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispのどんなリストも実行できるプログラムである。 それを実行する(Lispの専門用語では評価(evaluate)する)と、 コンピュータは、つぎの3つのうちの1つを行う。 リストそのものを返して、それ以外のことは何もしない。 エラーメッセージを出す。 リストの先頭シンボルをなんらかのコマンドとして扱う (もちろん、普通は3番目の動作を望む!)。
前節の例においてリストの直前に付けた1つのアポストロフィ'を
クオート(quote)と呼ぶ。
リストの直前にこれを付けると、
そのリストに関しては何もせずに字面どおりに扱うことをLispに指示する。
リストの直前にクオートがない場合には、リストの先頭要素を特別扱いし、
コンピュータが従うべきコマンドとなる
(Lispでは、これらのコマンドを関数(functions)と呼ぶ)。
まえにあげたリスト(+ 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)と呼ばれるプログラムに指令、 つまり、式を評価せよという指令を与えたのである。 Lispインタープリタという名称は、表現の意味を追いかける人間の仕事、 つまり、通訳(interpreter)からきている。
リストの一部ではないアトム、つまり、 括弧に囲まれていないアトムを評価することもできる。 この場合もLispインタープリタは人間が読める表現をコンピュータの言語に変換する。 このこと(see 節 1.7 変 数 (2004/01/17))を説明するまえに、 まちがいを起こした場合にLispインタープリタがどうするかを説明しよう。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
偶然引き起こした場合は気にしないでよいが、ここでは エラーメッセージを生成するようなコマンドをLispインタープリタに与えてみる。 これは無害であり、意図してエラーメッセージを生成することにある。 専門用語を理解すれば、エラーメッセージは有益でさえある。 「エラー」メッセージと呼ぶよりは「ヘルプ」メッセージと呼ぶべきであろう。 これらは、見知らぬ国を訪れた旅行者のための道標のようなものである。 読みこなすのはたいへんであろうが、 いったん理解してしまえば道を指し示してくれる。
エラーメッセージはEmacsに組み込まれたデバッガにより表示されます。
リストの先頭要素に意味のあるコマンドもなく、クオートもしていないリストを 評価してみよう。 上で用いたリストとほとんど同じであるが、その直前には引用符を付けない。 このリストの直後にカーソルを置いてC-x C-eとタイプする。
(this is an unquoted list) |
どのように結果が表示されるかはEmacsのバージョンに依存します。Emacs 21 ではEmacs 20やそれ以前のものよりも多くの情報が表示されます。まず、最近 のEmacsでのエラーを、次にEmacs 20でのエラーを紹介します。
Emacs 21では `*Backtrace*' ウィンドウが開き、そこに以下のようなメッ セージが表示されます。
---------- Buffer: *Backtrace* ---------- Debugger entered--Lisp error: (void-function this) (this is an unquoted list) eval((this is an unquoted list)) eval-last-sexp-1(nil) eval-last-sexp(nil) call-interactively(eval-last-sexp) ---------- Buffer: *Backtrace* ---------- |
カーソルはこのウィンドウへ移ります (表示されるまで数秒かかるかもしれま せん)。デバッガを停止させ、そこから抜けるために、以下を入力します。
q |
すぐに q を押してください。そうすれば、デバッガから抜けることが できます。それから、C-x C-e で再度評価すると、またデバッガに入り ます。
すでに知っていることをもとにすれば、このエラーメッセージのほとんどを理 解できるでしょう。
`*Backtrace*' バッファは下から読んでいきます。それがEmacsが実行し
た順番になるのです。C-x C-e を入力すると、eval-last-sexp
を対話的に呼び出します。evalは「evaluate(評価する)」の、
sexp は「symbolic expression(シンボリック式)」の略称です。結局、
このコマンドは「最後のシンボリック式を評価する」ことを意味します。つま
り、カーソルの直前の式が評価されるのです。
各行は次にどの式が評価されるかを表しています。Emacsが行ったことを逆方 向に(エラーのある行から遡って)確認することができることから、このバッファ を`*Backtrace*'バッファと呼んでいます。`*Backtrace*'
`*Backtrace*' バッファの最初の行は以下のようになっています。
Debugger entered--Lisp error: (void-function this) |
Lispインタープリタはリストのアトムである`this'という単語を評価し ようとします。これにより、`void-function this' (this という関数は 未定義)というエラーメッセージが表示されます。
このメッセージには`void-function' と `this' という単語が含ま れます。
`function' は以前に取り上げました。 これは重要な用語である。 ここでは、関数(function)とは、コンピュータに何かを行わせるための コンピュータに対する一連の命令であると定義しよう
これで`void-function this' というエラーメッセージを理解することが できます。関数(つまり、`this'という単語) にはコンピュータに実行させ るための命令が定義されていないということです。
メッセージ`function definition is void'の単語の使い方が 少々変わってるのは、Emacs Lispの実装方法に準じているのである。 つまり、シンボルに関数定義が与えられていない場合には、 命令列を格納する場所は「void(空)」になるのである。
一方で、(+ 2 2)を評価すると2に2を加算できるので、
シンボル+にはコンピュータが実行すべき命令列が与えられており、
しかも、それらは+に続く数を加算する命令であると推理できる。
Emacs 20やそれ以前のバージョンでは、以下のような1行のエラーメッセージ しか表示されません。そして、このメッセージはエコー領域に以下のように表 示されます。
Symbol's function definition is void: this |
このメッセージはカーソルを移動したり、何かキーをタイプするだけでメッセー ジは消えてしまいます。
用語`Symbol'の意味は知っている。 これは、リストの先頭要素である単語`this'を意味する。 用語`関数(function)'はコンピュータがすべき命令であると述べた。 (技術的には、シンボルは一連の命令を探す場所を指定するのであるが、 ここではその詳細は無視できる)。
エラーメッセージ`Symbol's function definition is void: this' の意味を理解できるはずである。 シンボル(つまり単語`this')には、 コンピュータが実行すべき、命令が欠けているのである。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
これまでに説明してきたことをもとに、Lispの別の特徴を明確にすることができる。
+のようなシンボルは、それ自身はコンピュータが実行すべき命令列
ではないという重要な特徴である。
そのかわりに、定義、すなわち、命令列を探すために
シンボルを(一時的に)使うのである。
われわれが見ているものは、命令列を探すための名前である。
人の名前も同じように使われる。
筆者は`Bob'と呼ばれているが、私は`B'、`o'、`b'の3文字
ではなく、意識のある生命体である。
名前そのものは私ではなく、私を指すために名前を使うのである。
Lispでは、1つの命令列に複数の名前を結び付けることができる。
たとえば、コンピュータの加算命令列をシンボルplusにも+にも
結び付けられる(そのようなLispの方言もある)。
人間の世界でも、筆者を`Bob'と呼んだり`Robert'と呼んだりでき、
それ以外の単語でもよい。
その一方で、シンボルには1つの関数定義しか結び付けられない。 さもなければ、どちらの定義を採用すべきかコンピュータが混乱する。 これを人間の世界に当てはめると、 `Bob'という名前を持つ人は世界中で1人に限られることになる。 しかし、名前が参照する関数定義を変更するのは容易である (See 節 3.2 関数定義のインストール)。
Emacs Lispは巨大なので、関数が属するEmacsの構成部分がわかるように シンボルを命名する慣習がある。 したがって、Texinfoを扱うすべての関数の名前は`texinfo-'で始まり、 メールを読むことに関連する関数の名前は`rmail-'で始まる。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
これまでの説明をもとに、リストの評価をLispインタープリタに命じると Lispインタープリタが何をするかを理解することができる。 まず、リストの直前にクオートがあるかどうかを調べる。 クオートがあれば、リストを返すだけである。 一方、クオートがなければ、インタープリタはリストの先頭要素を調べ、 それに関数定義があるかどうかを調べる。 関数定義があれば、インタープリタはその関数定義内の命令列を実行する。 さもなければ、インタープリタはエラーメッセージを出力する。
これがLispの動作であり、単純である。 すぐに説明するが、これに加えて複雑なことがらもあるが、以上が基本である。 当然、Lispプログラムを書くには、関数定義の書き方、名前への結び付け方、 および、これらを読者やコンピュータに混乱のないように行う方法を知る必要がある。
Complications Variables, Special forms, Lists within. 1.5.1 バイトコンパイル Specially processing code for speed.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
では、複雑なことがらの最初のことを説明しよう。 Lispインタープリタは、リストに加えて、 クオートもせず括弧で囲まれてもいないシンボルを評価できる。 Lispインタープリタは、 変数(variable)としてのシンボルの値を決定しようとする。 これについては変数に関する節で説明する (See 節 1.7 変 数 (2004/01/17))。
複雑なことがらの2番目は、特殊な関数があり、 これらは普通の方式のように動作しないことである。 これらをスペシャルフォーム(special forms)と呼ぶ。 関数の定義などの特殊なことを行うものであり、それらの個数は多くはない。 以下のいくつかの章では、重要なスペシャルフォームのいくつかを紹介する。
3番目で最後の複雑なことがらはつぎのとおりである。 Lispインタープリタが探しあてた関数がスペシャルフォームでなく、 しかも、それがリストの一部である場合には、 Lispインタープリタはリストの内側にリストがあるかどうかを調べる。 内側にリストがあれば、Lispインタープリタは内側のリストを処理してから、 外側のリストを処理する。 内側のリストの内側にもリストが含まれている場合には、 それを処理してから内側のリストを処理する。 つねに、もっとも内側のリストを最初に処理する。 インタープリタはもっとも内側のリストの結果を得るためにそれを処理する。 その結果はそれを含む式で使われる。
そうでない場合には、インタープリタは左から右への順で式を1つずつ処理する。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
インタープリタには別の側面もある。 Lispインタープリタは2種類のものを解釈できる。 本書で取り上げている人が読める形式のコードと、 特別な処理を施し人には読めない形式の バイトコンパイル(byte compiled)コードである。 バイトコンパイルしたコードは、人間向けのコードに比べて実行が速い。
byte-compile-fileのようなコンパイルコマンドを実行すれば、
人間向けのコードをバイトコンパイルコードに変換できる。
バイトコンパイルコードは、拡張子`.el'ではなく拡張子`.elc'で
終わるファイルに収容するのが一般的である。
ディレクトリ`emacs/lisp'には両方の種類のファイルがあるが、
読むのは拡張子`.el'のファイルである。
実用上は、Emacsを調整したり拡張したりするのがほとんどであろうから、 バイトコンパイルする必要はなく、ここではこれ以上取り上げない。 バイトコンパイルについて詳しくはSee 節 `Byte Compilation' in
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispインタープリタが式を処理することを評価する(evaluation)と呼ぶ。 インタープリタが「式を評価する」という。 これまでにも、この用語を何度か使ってきた。 この用語は日常の言葉使いからきている。 つまり、
式を評価し終えると、Lispインタープリタは、関数定義で与えられた命令列を コンピュータが実行した結果を返す(return)のが一般的であるが、 関数の処理を諦めてエラーメッセージを生成する場合もある (インタープリタ自体を別の関数に渡したり、 「無限ループ」と呼ばれる無制限に同じことを繰り返すこともある。 これらの動作は一般的ではないので、ここでは無視することにする)。 ほとんどの場合、インタープリタは値を返す。
インタープリタは、値を返すと同時に、 カーソルを移動したりファイルをコピーしたりなどの別の動作も行う。 この種の別の動作は、副作用(side effect)と呼ばれる。 結果の表示などの人間が重要と考える動作は、 Lispインタープリタによっては「副作用」であることが多い。 この用語は奇妙に聞こえるかもしれないが、 副作用の使い方を学ぶのはかなり簡単である。
まとめると、Lispインタープリタは、 シンボリック式を評価するとほとんどの場合は値を返すが、副作用を伴うこともある。 あるいは、エラーを生成する。
1.6.1 内側のリストの評価 (2004/01/17) Lists within lists...
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
内側にリストを含んだリストを評価するときには、 内側のリストを評価して得られた値を、 外側のリストを評価するときの情報として使う場合がある。 そのために、内側の式を最初に評価するのである。 それが返した値を外側の式で使用する。
このような評価の過程を、加算の例題で調べることにしよう。 つぎの式の直後にカーソルを置いてC-x C-eとタイプする。
(+ 2 (+ 3 3)) |
エコー領域には数8が表示される。
Lispインタープリタで行われることは、
まず内側の式(+ 3 3)を評価することであり、これは値6を返す。
続いて、外側の式を(+ 2 6)であるかのように評価し、これは値8を返す。
評価すべき外側の式はもうないので、
インタープリタはこの値をエコー領域に表示する。
さて、キー列C-x C-eで起動されるコマンドの名前を理解するのは容易である。
コマンドの名前はeval-last-sexpである。
sexpは「シンボリック式(symbolic expression)」の略、
evalは「評価(evaluate)」の略である。
コマンドの意味は、「直前のシンボリック式を評価する」である。
式の直後の行の先頭や式の内側にカーソルを置いても式を評価できるかどうか 試してみよう。
つぎの式で試してみる。
(+ 2 (+ 3 3)) |
式の直後の空行の先頭にカーソルを置いてC-x C-eとタイプしても、
エコー領域には値8が表示される。
今度は、式の内側にカーソルを置いて試してみる。
最後の括弧のまえに(つまり、表示上は最後の括弧の上に)
カーソルを置いて評価すると、エコー領域には値6が表示される!
コマンドは式(+ 3 3)を評価したからである。
今度は、数の直後にカーソルを置いてみる。
C-x C-eとタイプすると数そのものを得る。
Lispでは、数を評価するとその数そのものを得るのである。
これが数とシンボルの違いである。
+のようなシンボルで始まるリストを評価すると、
+に結び付けた関数定義の命令列を実行した結果の値を得る。
つぎの節で説明するように、シンボルそのものを評価すると別のことが起こる。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Emacs Lispでは、シンボルに関数定義を結び付けるように、 シンボルに値を結び付けることもできる。 これらの2つは異なるものである。 関数定義はコンピュータが遂行する命令列である。 一方で、値は、数や名前などの何かであり、変更できる (これが、そのようなシンボルが変数と呼ばれる理由である)。 シンボルの値としては、シンボル、数、リスト、文字列などの Lispの任意の式を取れる。 値を持つシンボルをしばしば変数(variable)と呼ぶ。
シンボルには関数定義と値の両方を同時に結び付けておくことができる。あるいは一方か 他方だけを結び付けておくことができる. これら2つは別々である。 これは、ケンブリッジという名称が、マサチューセッツの都市を表すと同時に、 「偉大なプログラミングセンター」のような名前の付加属性を持つことができるのに 似ている。
あるいは、シンボルは箪笥であると考えてほしい。 関数定義はある引き出しに入れてあり、値は別の引き出しに入れてあるのである。 関数定義を収めた引き出しの中身を変えることなく、 値を収めた引き出しの中身を変更でき、その逆もそうである。
fill-column, an Example Variable1.7.1 Error Message for a Symbol Without a Function (2004/01/17) The error message for a symbol without a function. 1.7.2 値のないシンボルに対するエラーメッセージ (2004/01/17) The error message for a symbol without a value.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
fill-column, an Example Variable fill-column,anExampleVariable"へのコメント(無し)
値を持つシンボルの例として変数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インタープリタの視点からは、どちらも返された値である。
値が求まってしまえば、それがどのような式から得られたかは関係ないのである。
シンボルにはどのような値でも結び付けることができる。
専門用語を使えば、変数には、72のような数、"such as this"のような文字列、
(spruce pine oak)のようなリストを束縛(bind)できる。
変数に関数定義を束縛することもできる。
シンボルに値を束縛する方法は何通りかある。 1つの方法は、See 節 1.9 変数への値の設定 (2004/01/17)。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
fill-column を変数としての値を知るために評価するときには,単語の前後に括
弧は付けない.これは,関数として使うわけではないからである.
fill-columnがリストの先頭要素だとすると、
Lispインタープリタはこれに結び付けられた関数定義を探そうとする。
しかし、fill-columnには関数定義はない。
つぎを評価してみよう。
(fill-column) |
Emacs 21では,`*Backtrace*'バッファが表れ,以下のように表示されるだろう.
---------- Buffer: *Backtrace* ---------- Debugger entered--Lisp error: (void-function fill-column) (fill-column) eval((fill-column)) eval-last-sexp-1(nil) eval-last-sexp(nil) call-interactively(eval-last-sexp) ---------- Buffer: *Backtrace* ---------- |
(デバッガを停止させ、そこから抜けるために、`*Backtrace*' バッファで q を入力することを思い出しなさい)
Emacs20やそれ以前もののでは、つぎのエラーメッセージを得る。
Symbol's function definition is void: fill-column |
(カーソルキーや他のキーを入力するとこのメッセージはすぐに消えてしまいます)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
値が束縛されていないシンボルを評価すると、エラーメッセージを得る。
たとえば、2足す2の例を用いて調べてみよう。
つぎの式で、最初の数2のまえの+の直後にカーソルを置いて
C-x C-eをタイプすると、
(+ 2 2) |
Emacs 21では`*Backtrace*'バッファに以下のように表示されます.
---------- Buffer: *Backtrace* ---------- Debugger entered--Lisp error: (void-variable +) eval(+) eval-last-sexp-1(nil) eval-last-sexp(nil) call-interactively(eval-last-sexp) ---------- Buffer: *Backtrace* ---------- |
(他の時同様,デバッガに入った時には,`*Backtrace*'バッファでq を入力 することでデバッガから出ることができます)
これはまえに見た`Symbol's function definition is void: this'とは 違うエラーメッセージである。 ここでは、シンボルには変数としての値がないのである。 まえの場合は、(`this'という)シンボルには関数定義がなかったのである。
+で試したことは、Lispインタープリタに+を評価させて、
関数定義ではなく変数の値を探させたのである。
そうするために、式を閉じる括弧の直後にカーソルを置くかわりに、
シンボルの直後にカーソルを置いた。
そのため、Lispインタープリタは直前のS式、つまり、
+そのものを評価したのである。
+には、関数定義はあるが、値は束縛されていないので、
変数としてのシンボルの値は空(void)である旨のエラーメッセージが
報告されたのである。
Emacs 20やそれ以前のものでは,つぎのエラーメッセージを得る。
Symbol's value as variable is void: + |
意味はEmacs 21と同じである.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
どのように関数に情報が伝えられるかを見るために、 お馴染みの2足す2の例を使ってみよう。 Lispでは、つぎのように書く。
(+ 2 2) |
この式を評価すると、エコー領域には数4が表示される。
Lispインタープリタが行ったことは、+のあとに続く数の加算である。
+が加算した数のことを、関数+の引数(arguments)と呼ぶ。
これらの数は、関数に与えられた、つまり、渡された情報である。
「argument(引数)」という用語は数学での用法からきており、
2人のあいだの議論のことではない。
ここでは、+という関数へ与えられた情報を意味する。
Lispでは、関数に対する引数は、その関数のあとに続くアトムやリストである。
これらのアトムやリストを評価して返された値が関数へ渡される。
関数が異なれば、必要な引数の個数も異なり、
引数をまったく必要としない関数もある。
(2)
1.8.1 引数のデータ型 (2004/01/16) Types of data passed to a function. 1.8.2 引数としての変数やリストの値 (2004/01/17) An argument can be the value of a variable or list. 1.8.3 可変個数の引数 (2004/01/17) Some functions may take a variable number of arguments. 1.8.4 引数に誤った型のオブジェクトを指定 (2004/01/17) Passing an argument of the wrong type to a function. 1.8.5 関数 message(2004/01/17)A useful function for sending messages.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
関数に渡すデータの型は、関数が使用する情報の種類に応じて決まる。
+は数を加算するので、
+のような関数への引数は数値である必要がある。
別の関数では別の種類のデータの引数が必要である。
たとえば、関数concatは複数の文字列を繋いで1つの文字列を生成する。
したがって、引数は文字列である。
文字列abcとdefを繋ぐ(concatinate)と、
1つの文字列abcdefが作られる。
これはつぎの式を評価するとわかる。
(concat "abc" "def") |
この式を評価して得られる値は"abcdef"である。
substringのような関数は、引数として文字列と数を取る。
この関数は文字列の一部分、つまり、第1引数の部分文字列を返す。
この関数は3つの引数を取る。
第1引数は文字列であり、第2引数と第3引数は数で、
部分文字列の開始位置と終了位置を示す。
これらの数は、文字列の先頭からの(空白や句読点を含む)文字の個数である。
たとえば、つぎを評価すると、
(substring "The quick brown fox jumped." 16 19) |
エコー領域には"fox"と表示される。
引数は、1つの文字列と2つの数である。
substringに渡された文字列は、
空白で区切られた複数の単語ではあるが1つのアトムである。
Lispは、2つの二重引用符のあいだにあるものを、
たとえ空白があいだにあっても文字列として扱う。
関数substringは、他の方法では不可分なアトムから一部分を切り出すので、
「アトム粉砕機」の一種と考えてもよい。
しかし、substringは、文字列である引数から部分文字列を切り出すことが
できるだけであり、数やシンボルなどのそれ以外の種類のアトムは扱えない。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
引数は、評価したときに値を返すシンボルでもよい。
たとえば、シンボルfill-columnそのものを評価すると数を返す。
この数は加算に使える。
つぎの式のうしろにカーソルを置いてC-x C-eとタイプする。
(+ 2 fill-column) |
fill-columnを単独で評価した値より2だけ大きな値が得られる。
筆者の場合には、fill-columnの値は72なので、結果は74である。
このように、評価すると値を返すシンボルを引数に使えるのである。
さらに、評価すると値を返すリストを引数に使うこともできる。
たとえば、つぎの式では、関数concatへの引数は、
文字列"The "と" red foxes."、
さらに、リスト(number-to-string (+ 2 fill-column))である。
(concat "The " (number-to-string (+ 2 fill-column)) " red foxes.") |
この式を評価すると、(もし私のEmacsのように fill-column が 72 になっていれ
ば) エコー領域には"The 74 red foxes."と表示される
(最終結果の文字列に空白が含まれるように、
単語`The'の直後と単語`red'の直前には空白が必要である.関数
number-to-string は加算関数が返す整数を文字に変換する.
number-to-string は int-to-string でも同じである)。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
concat、+、*などのある種の関数は、任意個数の引数を取る
(*は乗算のシンボルである)。
これは、以下の式のおのおのを通常の方法で評価するとわかる。
`=>'のあとのテキストがエコー領域に表示され、
`=>'は「の評価結果は」と読めばよい。
まず、関数に引数がない場合にはつぎのとおりである。
(+) => 0 (*) => 1 |
つぎは、関数に引数が1つの場合である。
(+ 3) => 3 (* 3) => 3 |
今度は、関数に引数が3つある場合である。
(+ 3 4 5) => 12 (* 3 4 5) => 60 |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
誤った型の引数を関数へ渡すと、Lispインタープリタはエラーメッセージを生成する。
たとえば、関数+は引数として数を仮定する。
数のかわりにクオートしたシンボルhelloを与えてみよう。
つぎの式の直後にカーソルを置いてC-x C-eとタイプする。
(+ 2 'hello) |
そうすると、エラーメッセージを得る。
何が起こったかというと、+は数2に'helloが返す値を
加算しようとしたのだが、'helloが返した値はシンボルhelloであり、
数ではない。
加算できるのは数のみである。
したがって、+は加算を実行できなかったのである。
Emacs 21では `*Backtrace*' に以下のように表示される.
---------- Buffer: *Backtrace* ----------
Debugger entered--Lisp error:
(wrong-type-argument number-or-marker-p hello)
+(2 hello)
eval((+ 2 (quote hello)))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp)
---------- Buffer: *Backtrace* ----------
|
普通、エラーメッセージは有用なようになっているので、 読み方がわかれば意味がわかる。
エラーメッセージの最初の部分は簡単で、
`Wrong type argument'、つまり、引数の型がまちがっているである。
つぎは、不思議な専門用語で`number-or-marker-p'である。
これは、+が仮定する引数の種類を伝えようとしているのである。
シンボルnumber-or-marker-pの意味は、Lispインタープリタが
渡された情報(引数の値)が整数(数)かマーカー(バッファ内の位置を表す
特殊なオブジェクト)かどうかを調べようとしているということである。
つまり、加算すべき整数が+に与えられたかどうかを検査したのである。
このとき、引数が、Emacs Lispに特有の機能であるマーカーかどうかも検査する
(Emacsでは、バッファ内の位置をマーカーで記録する。
コマンドC-@やC-SPCでマークを設定すると、
その位置はマーカーとして保存される。
マークは数として扱うことができ、
バッファの先頭からのその位置までの文字の個数である)。
Emacs Lispでは、+でマーカー位置の数値を数として加算できる。
number-or-marker-pの`p'は、
Lispプログラミングの早い時期に始まった慣習である。
`p'は「述語(predicate)」の略である。
初期のLisp研究者が用いた専門用語では、
述語とはある性質が真か偽か調べる関数を意味する。
したがって、`p'があることで、number-or-marker-pは、
与えられた引数が数であるかマーカーであるかの真偽を調べる関数の名前である
ことがわかる。
`p'で終わるそのほかのLispシンボルには、
引数の値が0であるかを調べる関数zerop、
引数がリストであるかを調べる関数listpがある。
エラーメッセージの最後の部分はシンボルhelloである。
これは+に渡された引数の値である。
正しい型のオブジェクトが加算に渡されれば、
渡された値はhelloのようなシンボルではなく37のような数であるはずである。
そうであったならば、エラーメッセージが表示されることはなかったのである。
Emacs 20やそれ以前のものでは,エコー領域に以下のように表示されます.
Wrong type argument: number-or-marker-p, hello |
これは,言い方を変えると,`*Backtrace*' バッファの先頭行のようなものと言う ことができる.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
message (2004/01/17) message"へのコメント(無し)
+のように、関数messageは任意個数の引数を取る。
この関数はユーザーにメッセージを表示するために使うので、
ここで説明しておくのがよいであろう。
メッセージはエコー領域に表示される。 たとえば、つぎのリストを評価すると、エコー領域にメッセージが表示される。
(message "This message appears in the echo area!") |
二重引用符のあいだの文字列は1つの引数であり、一塊で表示される
(ここでの例では、エコー領域に表示されるメッセージは二重引用符で囲まれる。
これは、関数messageが返した値を見ているからである。
プログラム内でのmessageのほとんどの使い方では、
副作用としてエコー領域にテキストが表示されるので、
表示には引用符は含まれない。
このような例について詳しくは、
See 節 3.3.1 対話的 multiply-by-seven)。
さて、文字列の中に`%s'がある場合、
関数messageは`%s'をそのとおりに表示することはなく、
文字列のあとに続く引数を調べる。
第2引数を評価し、その値を文字列の`%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の位置に挿入する。
値を整数として表示するには`%s'のかわりに`%d'を使う。
たとえば、fill-columnの値を含んだメッセージをエコー領域に表示するには
つぎの式を評価する。
(message "The value of fill-column is %d." fill-column) |
筆者のシステムでは、このリストを評価するとエコー領域に
"The value of fill-column is 72."と表示される(3)。
文字列の中に複数の`%s'があれば、 文字列のあとに続く最初の引数の値が最初の`%s'の位置に表示され、 2番目の引数の値が2番目の`%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)が評価され、
その結果の数が`%d'の位置に挿入される。
二重引用符の中の文字列"pink elephants"は
1つの引数として扱われて`%s'の位置に挿入される
(つまり、数と同様に、二重引用符で囲まれた文字列を評価するとそれ自身となる)。
最後は少々複雑な例で、数の計算を示すとともに、 `%s'と置き換わるテキストを生成する式を式の内側で使う方法を示す。
(message "He saw %d %s"
(- fill-column 34)
(concat "red "
(substring
"The quick brown foxes jumped." 16 21)
" leaping."))
|
この例では、messageには、文字列"He saw %d %s"、
式(- fill-column 32)、関数concatで始まる式の3つの引数がある。
(- fill-column 32)を評価した結果は`%d'の位置に挿入され、
concatで始まる式の値は`%s'の位置に挿入される。
筆者の場合、この式を評価するとエコー領域に
"He saw 38 red foxes leaping."と表示される。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
変数に値を与える方法はいくつかある。
1つの方法は関数setか関数setqを使うことである。
別の方法はlet(see 節 3.6 let (2004/03/28))を使うことである
(この過程を専門用語では変数を値に束縛(bind)するという)。
つぎの節ではsetとsetqの動作を説明するとともに、
引数がどのように渡されるかも説明する。
1.9.1 setの使い方 (2004/01/17)Setting values. 1.9.2 setqの使い方 (2004/01/17)Setting a quoted value. 1.9.3 数え上げ (2004/01/17) Using setqto count.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
setの使い方 (2004/01/17) setの使い方"へのコメント(無し)
シンボルflowersの値としてリスト'(rose violet daisy buttercup)を
設定するには、つぎの式の直後にカーソルを移動してC-x C-eとタイプして
式を評価する。
(set 'flowers '(rose violet daisy buttercup)) |
エコー領域には、リスト(rose violet daisy buttercup)が表示される。
これは、関数setが返したものである。
副作用として、シンボルflowersにリストが束縛される。
つまり、シンボルflowersは変数とみなされ、
その値としてリストが与えられるのである
(値を設定するこの過程は、Lispインタープリタにとっては副作用であるが、
人間にとっては興味のある主要な効果である。
各Lisp関数はエラーがなければ値を返す必要があるが、
関数の目的は副作用だけの場合もある)。
set式を評価したあとは、シンボルflowersを評価することができ、
設定した値が返される。
つぎのシンボルの直後にカーソルを置いてC-x C-eとタイプする。
flowers |
flowersを評価すると、
エコー領域にリスト(rose violet daisy buttercup)が表示される。
'flowers、つまり、直前にクオートを置いた変数を評価すると、
エコー領域にはシンボルflowersそのものが表示される。
つぎの例を試してみよう。
'flowers |
setを使う場合、いずれの引数も評価してほしくない場合には、
両方の引数をクオートする必要があることに注意してほしい。
上の例では、変数flowersもリスト(rose violet daisy buttercup)も
評価したくないので、両者をクオートした
(setの第1引数をクオートせずに使うと、まず最初に第1引数が評価される。
上の例でこれを行うと、flowersには値がないので、
`Symbol's value as variable is void'というエラーメッセージを得る。
一方、flowersを評価して値が返される場合には、
setはその返された値に値を設定しようとする。
このように関数を動作させたい場面もあるが、そのような場面は少ない)。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
setqの使い方 (2004/01/17) setqの使い方"へのコメント(無し)
実用上、setの第1引数をほとんどつねにクオートするはずである。
setで第1引数をクオートする組み合わせは多用されるので、
スペシャルフォームsetqが用意してある。
このスペシャルフォームはsetとほとんど同じであるが、
第1引数を自動的にクオートするので、引用符をタイプする必要はない。
さらに、便利なように、setqでは、複数の異なる変数に異なる値を
1つの式で設定することもできる。
setqを用いて、変数carnivoresの値を
リスト'(lion tiger leopard)とするには、つぎの式を使う。
(setq carnivores '(lion tiger leopard)) |
これはsetを用いた場合とほぼ同じであるが、
setqでは第1引数を自動的にクオートするのが異なる(setqの`q'は、
クオート(quote)を意味する)。
setを用いる場合は、つぎのようにする。
(set 'carnivores '(lion tiger leopard)) |
さらに、setqは、複数の異なる変数に異なる値を代入するためにも使える。
第1引数には第2引数の値を束縛し、第3引数には第4引数の値を束縛し、……と続く。
たとえば、シンボルtreesに樹木名のリストを、
シンボルherbivoresにハーブの名前のリストを代入するにはつぎのようにする。
(setq trees '(pine fir oak maple)
herbivores '(gazelle antelope zebra))
|
(式を1行に書いてもよいが、ページの幅に収まらない。 人間には、適切にフォーマットしたリストは読みやすいものである。)
「代入」という用語を用いたが、setやsetqの動作を考える
別の方法もある。
つまり、setやsetqは、シンボルがリストを指す(point)
ようにするのである。
この考え方は非常によく使われ、うしろの章では、名前の一部に「pointer」がある
シンボルを見ることになる。
この名称は、シンボルには特にリストなどの値が結び付けらている、
あるいは、シンボルがリストを「指す」ように設定されていることに由来する。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
ここではカウンタでsetqを使う方法を示そう。
プログラムのある部分を何回繰り返したかを数え上げるために使える。
まず、変数に0を設定する。
そして、プログラムを繰り返すたびに1を加える。
そのためには、カウンタの役割を果たす変数と2つの式、つまり、
カウンタ変数を0に初期化するsetqを用いた式と、
評価するたびにカウンタを増加するsetqを用いた式が必要である。
(setq counter 0) ; 初期化式 (setq counter (+ counter 1)) ; 増加式 counter ; カウンタ |
(`;'以降のテキストは注釈である。 See 節 3.2.1 関数定義の変更。)
これらの最初の式、つまり、初期化式(setq counter 0)を評価してから
3番目の式counterを評価すると、エコー領域には数0が表示される。
続いて、2番目の式、増加式(setq counter (+ counter 1))を評価すると、
カウンタの値は1になる。
そのため、ふたたびcounterを評価すると
エコー領域には数1が表示される。
2番目の式を評価するたびに、カウンタの値は増加する。
増加式(setq counter (+ counter 1))を評価するとき、
Lispインタープリタは、もっとも内側のリスト、つまり、加算を最初に評価する。
このリストを評価するには、変数counterと数1を評価する必要がある。
変数counterを評価するとその現在値が得られる。
この値と数1が+に渡され加算される。
この合計値がもっとも内側のリストの値として返され、
さらに、変数counterにこの新しい値を設定するsetqへ渡される。
したがって、変数counterの値が変更されるのである。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Lispを学ぶことは、登り始めがもっとも険しい小山を登るようなものである。 読者はもっとも困難な部分を登り終えたのであり、 あとは、先へ進むほど楽になる。
本章をまとめるとつぎのようになる。
forward-paragraphのような複数個の文字のシンボル、
+のような1文字のシンボル、二重引用符で囲った文字列、あるいは、数である。
'は、Lispインタープリタに対して続く式を字面のとおりに
返すことを指示し、引用符がない場合のようには評価しない。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
簡単な演習問題をあげておく。
| [ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |