car、cdr、cons
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
car、cdr、cons
car、cdr、cons"へのコメント(無し)
Lispでは、car、cdr、consは基本関数である。 関数consはリストの作成、 関数carとcdrはリストの分解に使う。
関数copy-region-as-killのウォークスルーでは、 consに加えてcdrの変形であるsetcdrとnthcdrを 見ることになる (See 節 8.5 copy-region-as-kill)。
関数consの名前は不合理ではなく、 単語「construct(作り上げる)」の省略である。 一方、carとcdrの名前の由来は、難解である。 carは「Contents of the Address part of the Register」の頭文字であり、 cdr(「クダー」と読む)は 「Contents of the Decrement part of the Register」の頭文字である。 これらの語句は、最初のLispが開発された初期のコンピュータの ハードウェアの一部を指す。 この語句は古くて意味がないばかりでなく、 Lispに関していえば、25年間以上にもわたってこれらの語句は無意味であった。 研究者の一部には、これらの関数に対する合理的な名称を使う人もいるが、 それにもかかわらず、これらの名称は使われ続けている。 特に、これらはEmacs Lispのソースコードでも使われているので、 本書でもこれにならう。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
carとcdr
carとcdr"へのコメント(無し)
リストのcarは、簡単にいえば、リストの先頭要素である。 したがって、リスト(rose violet daisy buttercup)のcarは、 roseである。
GNU EmacsのInfoで読んでいる場合には、つぎを評価するとわかる。
(car '(rose violet daisy buttercup)) |
式を評価すると、エコー領域にroseと表示される。
明らかに、関数carのもっと合理的な名称はfirstであり、 しばしばそのように提案されている。
carは、リストからその先頭要素を取り除くのではない。 先頭要素が何であるかを返すだけである。 リストにcarを適用したあとでも、リストはそれ以前と同じである。 専門用語では、carは「非破壊的(non-destructive)」であるという。 この機能は重要なことがあとでわかる。
リストのcdrは、リストの残りである。 つまり、関数cdrは、リストの最初の要素のあとに続く部分を返す。 したがって、リスト'(rose violet daisy buttercup)のcarは、 roseであるが、cdrが返すリストの残りは (violet daisy buttercup)である。
いつものようにつぎの式を評価すればわかる。
(cdr '(rose violet daisy buttercup)) |
これを評価すると、エコー領域には(violet daisy buttercup)と表示される。
carと同様に、cdrもリストから要素を取り除くことはない。 リストの第2要素以降が何であるかを返すだけである。
上の例では、花のリストをクオートしていた。 クオートしないと、Lispインタープリタは、関数としてroseを呼び リストを評価しようとする。 この例では、そのようにはしたくないのである。
明らかに、関数cdrのより合理的な名称はrestであろう。
(つぎのことに注意してほしい: 新しい関数に名前を付けるときには、何をしているかを注意深く考えてほしい。 というのは、予想以上に長期間にわたって使われることもあるからである。 本書で、(carやcdrのような)これらの名称を使い続けるのは、 Emacs Lispのソースコードでこれらを使っているからである。 同じ名称を使わないと、読者がコードを読む際に苦労するであろう。 合理的な名称を使えば、あとに続く人々に感謝されるはずである。)
(pine fir oak maple)のようなシンボルだけから成るリストに carやcdrを適用すると、関数carが返す リストの要素は周りに括弧のないシンボルpineである。 pineは、リストの先頭要素である。 一方、つぎの式を評価してみるとわかるように、 リストのcdrはリストであり、(fir oak maple)である。
(car '(pine fir oak maple)) (cdr '(pine fir oak maple)) |
ところが、リストのリストでは、先頭要素は、それ自体、リストである。 carはリストの先頭要素をリストとして返す。 たとえば、3つのリスト、肉食獣のリスト、草食獣のリスト、海棲哺乳類のリスト から成るリストを考える。
(car '((lion tiger cheetah)
(gazelle antelope zebra)
(whale dolphin seal)))
|
この場合、第1要素、つまり、リストのcarは、 肉食獣のリスト(lion tiger cheetah)であり、リストの残りの部分は ((gazelle antelope zebra) (whale dolphin seal))である。
(cdr '((lion tiger cheetah)
(gazelle antelope zebra)
(whale dolphin seal)))
|
再度指摘するが、carとcdrは、非破壊的である。 つまり、これらをリストに適用したあとでも、 リストは変更されていないことに注意してほしい。 これらの使い方において、この性質はとても重要である。
第1章のアトムに関する説明で、Lispにおいては、 「配列などのある種のアトムは分解できるが、 その機構はリストを分解する機構とは異なる。 Lispでは、リストのアトムを分解することはできない」と述べた (See 節 1.1.1 Lispのアトム)。 関数carとcdrは、リストを分解するために使い、 Lispにとって基本的であると考えられている。 これらの関数では、 配列を分解したりその一部を参照できないので、配列はアトムと考えられる。 逆に、別の基本関数consはリストを作り上げるが、配列を作ることはできない (配列は、配列専用の関数で処理する。 @xref{Arrays})。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
cons
cons"へのコメント(無し)
関数consはリストを作り、carとcdrの逆操作である。 たとえば、3要素リスト(fir oak maple)から4要素リストを作るのに consを使う。
(cons 'pine '(fir oak maple)) |
このリストを評価すると、つぎのリスト
(pine fir oak maple) |
がエコー領域に表示される。 consは、リストの先頭に新たな要素を置く、あるいは、要素をリストに繋ぐ。
consには繋ぐべきリストが必要である。 (3) 何もないところから始めることはできない。 リストを作るときには、最初は少なくとも空リストを与える必要がある。 花のリストを作る一連のconsをつぎに示す。 GNU EmacsのInfoで読んでいる場合には、いつものように各式を評価できる。 値は「の評価結果は」と読める`=>'のうしろに記しておく。
(cons 'buttercup ())
=> (buttercup)
(cons 'daisy '(buttercup))
=> (daisy buttercup)
(cons 'violet '(daisy buttercup))
=> (violet daisy buttercup)
(cons 'rose '(violet daisy buttercup))
=> (rose violet daisy buttercup)
|
最初の例では、空リストを()で表し、buttercupに空リストが続く リストを作成している。 見ればわかるように、作成したリスト内の空リストは表示されない。 (buttercup)とだけ表示される。 空リストには何も含まれないので、空リストをリストの要素としては数えない。 一般に、空リストは見えない。
2番目の例では、(cons 'daisy '(buttercup))により、 buttercupのまえにdaisyを置いて新たに2要素リストを作る。 3番目の例では、daisyとbuttercupのまえにvioletを置いて 3要素リストを作っている。
7.2.1 リストの長さ: lengthHow to find the length of a list.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
length
length"へのコメント(無し)
Lisp関数lengthを使うとリスト内の要素の個数を調べることができる。 たとえば、つぎの式、
(length '(buttercup))
=> 1
(length '(daisy buttercup))
=> 2
(length (cons 'violet '(daisy buttercup)))
=> 3
|
3番目の例では、関数consを用いて3要素リストを作り、 それを関数lengthの引数として渡している。
空リストの要素数を数える場合にもlengthを使える。
(length ())
=> 0
|
予想どおりに、空リストの要素数は0である。
リスト以外の長さを調べようとするとどうなるであろうか? lengthに空リストさえも与えずに引数なしで呼んでみよう。
(length ) |
これを評価すると、つぎのエラーメッセージを得る。
Wrong number of arguments: # |
これは、関数が予想する引数の個数とは異なる、 0個の引数を受け取ったことを意味する。 この場合は、関数lengthが長さを調べる引数として1個必要である (リストの要素数がいくつであろうが、 1つのリストは1つの引数である)。
エラーメッセージの`#lengthはEmacs LispではなくCで書かれた基本操作関数であることを 意味する (`subr'は、「subroutine(サブルーティン)」の略)。 サブルーティンについてより 詳しくは、See 節 `What Is a Function?' in
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
nthcdr
nthcdr"へのコメント(無し)
関数nthcdrは、関数cdrに関連しており、 リストのcdrを繰り返し取る。
リスト(pine fir oak maple)のcdrを取ると、 リスト(fir oak maple)を得る。 同じことを返された値に適用するとリスト(oak maple)を得る (もちろん、もとのリストにcdrを繰り返し適用しても、 関数はリストを変更しないので、同じ結果を得るだけである。 cdrのcdrのように評価する必要がある)。 これを続けると、最終的には空リストを得ることになるが、 ()のかわりにnilと表示される。
一連のcdrを繰り返して適用してみよう。 それぞれの結果は、`=>'のうしろに記しておく。
(cdr '(pine fir oak maple))
=>(fir oak maple)
(cdr '(fir oak maple))
=> (oak maple)
(cdr '(oak maple))
=>(maple)
(cdr '(maple))
=> nil
(cdr 'nil)
=> nil
(cdr ())
=> nil
|
一連のcdrのあいだで値を表示しない場合には、つぎのようにする。
(cdr (cdr '(pine fir oak maple)))
=> (oak maple)
|
この例では、Lispインタープリタはもっとも内側のリストを最初に評価する。 もっとも内側のリストはクオートしてあるので、そのままもっとも内側のcdrに 渡される。 このcdrはリストの2番目以降の要素で構成されたリストを もっとも外側のcdrに渡し、それはもとのリストの3番目以降の要素で 構成されたリストを返す。 この例では、関数cdrを繰り返し、もとのリストの先頭と2番目の要素を 除いた要素で構成されたリストを返す。
関数nthcdrは、cdrを繰り返し呼んで同じことを行う。 つぎの例では、引数2とリストを関数nthcdrに渡し、 先頭と第2要素を除いたリストを得る。 つまり、リストに対してcdrを2回繰り返したのと同じである。
(nthcdr 2 '(pine fir oak maple))
=> (oak maple)
|
もとの4要素リストを使って、0、1、5などの数値引数を nthcdrに渡すとどうなるかを見てみよう。
;; リストはそのまま
(nthcdr 0 '(pine fir oak maple))
=> (pine fir oak maple)
;; 第1要素を除いたコピーを返す
(nthcdr 1 '(pine fir oak maple))
=> (fir oak maple)
;; 最初の3つの要素を除いたリストのコピーを返す
(nthcdr 3 '(pine fir oak maple))
=> (maple)
;; 4つの要素すべてを除いたものを返す
(nthcdr 4 '(pine fir oak maple))
=> nil
;; すべての要素を除いたものを返す
(nthcdr 5 '(pine fir oak maple))
=> nil
|
重要なことは、cdrと同様にnthcdrも もとのリストを変更しないことである。 つまり、関数は非破壊的である。 これは、関数setcarやsetcdrとは大きく異なる。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
setcar
setcar"へのコメント(無し)
名前から予想できるように、関数setcarやsetcdrは、 リストのcarやcdrに新しい値を設定する。 もとのリストを変更しないcarやcdrと異なり、 これらはもとのリストを変更する。 実際にその動作を試してみよう。 まず、関数setcarから始めよう。
最初に、リストを作り、関数setqを使って変数の値 としてそのリストを設定する。 ここでは動物のリストを作ろう。
(setq animals '(giraffe antelope tiger lion)) |
GNU EmacsのInfoで読んでいる場合には、いつものように 式の直後にカーソルを置いてC-x C-eとタイプすれば、この式を評価できる (筆者もこのようにして執筆している。 これは、計算環境にインタープリタがあることの1つの利点である)。
変数animalsを評価すると、リスト(giraffe antelope tiger lion)に 束縛されていることがわかる。
animals
=> (giraffe antelope tiger lion)
|
いいかえれば、変数animalsはリスト(giraffe antelope tiger lion) を指しているのである。
つぎに、変数animalsとクオートしたシンボルhippopotamusを 2つの引数として関数setcarを評価する。 それには、3要素リスト(setcar animals 'hippopotamus)を書いて、 いつものようにそれを評価する。
(setcar animals 'hippopotamus) |
この式を評価してから、変数animalsを再度評価する。 動物のリストが変化したことがわかるはずである。
animals
=> (hippopotamus antelope tiger lion)
|
リストの先頭要素がgiraffeからhippopotamusに変更された。
つまり、setcarは、consのようにはリストに新たな要素を 追加しないことがわかる。 setcarは、giraffeをhippopotamusで置き換え、 リストを変更したのである。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
setcdr
setcdr"へのコメント(無し)
関数setcdrは関数setcarに似ているが、 リストの先頭要素ではなく2番目以降の要素を変更する。
この動作をみるために、つぎの式を評価して変数に 家畜動物のリストを設定する。
(setq domesticated-animals '(horse cow sheep goat)) |
このリストを評価すると、リスト(horse cow sheep goat)を得る。
domesticated-animals
=> (horse cow sheep goat)
|
つぎに、リストを値として持つ変数の名前とリストのcdrに設定する リストの2つの引数でsetcdrを評価する。
(setcdr domesticated-animals '(cat dog)) |
この式を評価すると、エコー領域にリスト(cat dog)と表示される。 これは関数が返した値である。 ここで興味があるのは「副作用」であり、変数domesticated-animalsを 評価するとわかる。
domesticated-animals
=> (horse cat dog)
|
つまり、リストは(horse cow sheep goat)から(horse cat dog)へと 変更された。 リストのcdrが、(cow sheep goat)から (cat dog)に変わったのである。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
consを使った数個の式を評価して、鳥のリストを作ってみよ。 リストにそのリスト自身をconsするとどうなるかを調べてみよ。 鳥の4要素リストの先頭要素を魚に置き換えてみよ。 さらに、そのリストの残りの部分を他の魚で置き換えてみよ。
| [ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |