car
、cdr
、cons
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
car
、cdr
、cons
(2004/08/07)
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
(2004/08/08))。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
関数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
(2004/08/07)
car
とcdr
"へのコメント(無し)
The CAR of a list is, quite simply, the first item in the list. Thus the CAR of the list (rose violet daisy buttercup)
is rose
. リストの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)
のCDRは、 rose
であるが、cdr
が返すリストの残りは (violet daisy buttercup)
である。
いつものようにつぎの式を評価すればわかる。
(cdr '(rose violet daisy buttercup)) |
これを評価すると、エコー領域には(violet daisy buttercup)
と表示される。
car
と同様に、cdr
もリストから要素を取り除くことはない。 リストの第2要素以降が何であるかを返すだけである。
上の例では、花のリストをクオートしていた。 クオートしないと、Lispインタープリタは、関数としてrose
を呼び リストを評価しようとする。 この例では、そのようにはしたくないのである。
明らかに、関数cdr
のより合理的な名称はrest
であろう。
(つぎのことに注意してほしい: 新しい関数に名前を付けるときには、何をしているかを注意深く考えてほしい。 というのは、予想以上に長期間にわたって使われることもあるからである。 本書で、(car
やcdr
のような)これらの名称を使い続けるのは、 Emacs Lispのソースコードでこれらを使っているからである。 同じ名称を使わないと、読者がコードを読む際に苦労するであろう。 合理的な名称を使えば、あとに続く人々に感謝されるはずである。)
When car
and cdr
are applied to a list made up of symbols, such as the list (pine fir oak maple)
, the element of the list returned by the function car
is the symbol pine
without any parentheses around it. pine
is the first element in the list. However, the CDR of the list is a list itself, (fir oak maple)
, as you can see by evaluating the following expressions in the usual way: (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のアトム (2004/01/16))。 関数car
とcdr
は、リストを分解するために使い、 Lispにとって基本的であると考えられている。 これらの関数では、 配列を分解したりその一部を参照できないので、配列はアトムと考えられる。 逆に、別の基本関数cons
はリストを作り上げるが、配列を作ることはできない (配列は、配列専用の関数で処理する。 @xref{Arrays})。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
cons
(2004/08/07)
cons
"へのコメント(無し)
関数cons
はリストを作り、car
とcdr
の逆操作である。 たとえば、3要素リスト(fir oak maple)
から4要素リストを作るのに cons
を使う。
(cons 'pine '(fir oak maple)) |
このリストを評価すると、つぎのリスト
(pine fir oak maple) |
がエコー領域に表示される。 cons
は、リストの先頭に新たな要素を置く、あるいは、要素をリストに繋ぐ。
Build a list 7.2.1 リストの長さ: length
How to find the length of a list.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
cons
には繋ぐべきリストが必要である。 (7) 何もないところから始めることはできない。 リストを作るときには、最初は少なくとも空リストを与える必要がある。 花のリストを作る一連の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要素リストを作っている。
[ < ] | [ > ] | [ << ] | [ 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 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
nth
nth
"へのコメント(無し)
nthcdr
はリストの CDR を繰り返し取ります.nth
は nthcdr
により得られた結果の CAR を取ります.結果として,リ ストの N 番目の要素を返します.
そういうわけで,速度のことを考えずに C で定義しなければ,nth
の 定義は下記のようになるだろう.
(defun nth (n list) "Returns the Nth element of LIST. N counts from zero. If LIST is not that long, nil is returned." (car (nthcdr n list))) |
(もともと,nth
は `subr.el' で Emacs Lisp により定義されて いました.しかし,1980年代に C で再定義されました.)
nth
はリストの要素を1つ返します.これはとても便利な機能です.
要素は1からではなく,0から始まります.つまり,リストの最初の要素, CAR で取り出せるものは 0 番目の要素なのです.これは"0基準"の数え 方であり,しばしば"1基準"であるリストの最初の要素が1番目から始まる数え 方に慣れていると戸惑うことでしょう.
例を示します.
(nth 0 '("one" "two" "three")) => "one" (nth 1 '("one" "two" "three")) => "two" |
重要なことは,nth
は nthcdr
や cdr
のようにもとの リストを変更しないことである.つまり,この関数は非破壊的なのである.こ れは,setcar
や setcdr
とは大きく異なる.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
setcar
setcar
"へのコメント(無し)
名前から予想できるように、関数setcar
やsetcdr
は、 リストのcar
やcdr
に新しい値を設定する。 もとのリストを変更しないcar
やcdr
と異なり、 これらはもとのリストを変更する。 実際にその動作を試してみよう。 まず、関数setcar
から始めよう。
最初に、リストを作り、関数setq
を使って変数の値 としてそのリストを設定する。 ここでは動物のリストを作ろう。
(setq animals '(antelope giraffe lion tiger)) |
GNU EmacsのInfoで読んでいる場合には、いつものように 式の直後にカーソルを置いてC-x C-eとタイプすれば、この式を評価できる (筆者もこのようにして執筆している。 これは、計算環境にインタープリタがあることの1つの利点である)。
変数animals
を評価すると、リスト(giraffe antelope tiger lion)
に 束縛されていることがわかる。
animals => (antelope giraffe lion tiger) |
いいかえれば、変数animals
はリスト (antelope giraffe lion tiger)
を指しているのである。
つぎに、変数animals
とクオートしたシンボルhippopotamus
を 2つの引数として関数setcar
を評価する。 それには、3要素リスト(setcar animals 'hippopotamus)
を書いて、 いつものようにそれを評価する。
(setcar animals 'hippopotamus) |
この式を評価してから、変数animals
を再度評価する。 動物のリストが変化したことがわかるはずである。
animals => (hippopotamus giraffe lion tiger) |
The first element on the list, antelope
is replaced by hippopotamus
. リストの先頭要素がantelope
から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要素リストの先頭要素を魚に置き換えてみよ。 さらに、そのリストの残りの部分を他の魚で置き換えてみよ。
[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |