| [ < ] | [ > ] | [ << ] | [ 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はリストを作り、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: #<subr length>, 0 |
これは、関数が予想する引数の個数とは異なる、
0個の引数を受け取ったことを意味する。
この場合は、関数lengthが長さを調べる引数として1個必要である
(リストの要素数がいくつであろうが、
1つのリストは1つの引数である)。
エラーメッセージの`#<subr length>'の部分は、関数名を表す。
これは特別な記法`#<subr'で書かれており、
関数lengthはEmacs LispではなくCで書かれた基本操作関数であることを
意味する
(`subr'は、「subroutine(サブルーティン)」の略)。
サブルーティンについてより
詳しくは、See 節 `What Is a Function?' in
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
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や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は関数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要素リストの先頭要素を魚に置き換えてみよ。
さらに、そのリストの残りの部分を他の魚で置き換えてみよ。
| [ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |