carcdrcons
[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7. 基本関数 carcdrcons

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=car%20cdr%20&%20cons"
"intro/基本関数carcdrcons"へのコメント(無し)

Lispでは、carcdrconsは基本関数である。 関数consはリストの作成、 関数carcdrはリストの分解に使う。

関数copy-region-as-killのウォークスルーでは、 consに加えてcdrの変形であるsetcdrnthcdrを 見ることになる (See 節 8.5 copy-region-as-kill)。

Strange Names

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=Strange%20Names"
"intro/StrangeNames"へのコメント(無し)

関数consの名前は不合理ではなく、 単語「construct(作り上げる)」の省略である。 一方、carcdrの名前の由来は、難解である。 carは「Contents of the Address part of the Register」の頭文字であり、 cdr(「クダー」と読む)は 「Contents of the Decrement part of the Register」の頭文字である。 これらの語句は、最初のLispが開発された初期のコンピュータの ハードウェアの一部を指す。 この語句は古くて意味がないばかりでなく、 Lispに関していえば、25年間以上にもわたってこれらの語句は無意味であった。 研究者の一部には、これらの関数に対する合理的な名称を使う人もいるが、 それにもかかわらず、これらの名称は使われ続けている。 特に、これらはEmacs Lispのソースコードでも使われているので、 本書でもこれにならう。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7.1 carcdr

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=car%20&%20cdr"
"intro/carcdr"へのコメント(無し)

リストの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であろう。

(つぎのことに注意してほしい: 新しい関数に名前を付けるときには、何をしているかを注意深く考えてほしい。 というのは、予想以上に長期間にわたって使われることもあるからである。 本書で、(carcdrのような)これらの名称を使い続けるのは、 Emacs Lispのソースコードでこれらを使っているからである。 同じ名称を使わないと、読者がコードを読む際に苦労するであろう。 合理的な名称を使えば、あとに続く人々に感謝されるはずである。)

(pine fir oak maple)のようなシンボルだけから成るリストに carcdrを適用すると、関数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)))

再度指摘するが、carcdrは、非破壊的である。 つまり、これらをリストに適用したあとでも、 リストは変更されていないことに注意してほしい。 これらの使い方において、この性質はとても重要である。

第1章のアトムに関する説明で、Lispにおいては、 「配列などのある種のアトムは分解できるが、 その機構はリストを分解する機構とは異なる。 Lispでは、リストのアトムを分解することはできない」と述べた (See 節 1.1.1 Lispのアトム)。 関数carcdrは、リストを分解するために使い、 Lispにとって基本的であると考えられている。 これらの関数では、 配列を分解したりその一部を参照できないので、配列はアトムと考えられる。 逆に、別の基本関数consはリストを作り上げるが、配列を作ることはできない (配列は、配列専用の関数で処理する。 @xref{Arrays})。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7.2 cons

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=cons"
"intro/cons"へのコメント(無し)

関数consはリストを作り、carcdrの逆操作である。 たとえば、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番目の例では、daisybuttercupのまえにvioletを置いて 3要素リストを作っている。

7.2.1 リストの長さ:length    How to find the length of a list.



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7.2.1 リストの長さ:length

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=length"
"intro/リストの長さ: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

これは、関数が予想する引数の個数とは異なる、 0個の引数を受け取ったことを意味する。 この場合は、関数lengthが長さを調べる引数として1個必要である (リストの要素数がいくつであろうが、 1つのリストは1つの引数である)。

エラーメッセージの`#'の部分は、関数名を表す。 これは特別な記法`#'で書かれており、 関数lengthはEmacs LispではなくCで書かれた基本操作関数であることを 意味する (`subr'は、「subroutine(サブルーティン)」の略)。 サブルーティンについてより 詳しくは、See 節 `What Is a Function?' in

GNU Emacs Lispリファレンスマニュアル



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7.3 nthcdr

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=nthcdr"
"intro/nthcdr"へのコメント(無し)

関数nthcdrは、関数cdrに関連しており、 リストのcdrを繰り返し取る。

リスト(pine fir oak maple)cdrを取ると、 リスト(fir oak maple)を得る。 同じことを返された値に適用するとリスト(oak maple)を得る (もちろん、もとのリストにcdrを繰り返し適用しても、 関数はリストを変更しないので、同じ結果を得るだけである。 cdrcdrのように評価する必要がある)。 これを続けると、最終的には空リストを得ることになるが、 ()のかわりに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も もとのリストを変更しないことである。 つまり、関数は非破壊的である。 これは、関数setcarsetcdrとは大きく異なる。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7.4 setcar

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=setcar"
"intro/setcar"へのコメント(無し)

名前から予想できるように、関数setcarsetcdrは、 リストのcarcdrに新しい値を設定する。 もとのリストを変更しないcarcdrと異なり、 これらはもとのリストを変更する。 実際にその動作を試してみよう。 まず、関数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は、giraffehippopotamusで置き換え、 リストを変更したのである。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7.5 setcdr

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=setcdr"
"intro/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 ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

7.6 演習問題

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=eintro&node=cons%20Exercise"
"intro/演習問題"へのコメント(無し)

consを使った数個の式を評価して、鳥のリストを作ってみよ。 リストにそのリスト自身をconsするとどうなるかを調べてみよ。 鳥の4要素リストの先頭要素を魚に置き換えてみよ。 さらに、そのリストの残りの部分を他の魚で置き換えてみよ。


[ << ] [ >> ]           [表紙] [目次] [索引] [検索] [上端 / 下端] [?]