変数と関数の鍛錬をしたばかりですが、 ここからは Scheme のリストの霧靄立ち籠める湿地に足を踏み入れます。
リストについてあれこれ話す前にアトム値とリストの何が違うのか知っていただく必要があります。
アトム値についてはこれまでの講義で変数を初期化するときにご覧に入れました。 アトム値とは単独値のことです。 ですからたとえばつぎの構文で変数「x
」に単なる値 8 を代入できます。
(let* ( (x 8) ) x)
この例では最後にもう一度 x
とだけ書いた式をつけ加えました。 これは変数 x
に最終的に与えられた値を表示させるのが目的なのですが、 普段ははこんなことをしなくても済みます。 というのも let*
でも最後の構文は得た値を返すようになっているためこの式自体が関数のようにふるまえるからです。
変数は単なる値のみならず一連の他の値をまとめたリストを指すこともできます。 値 1、 3、 5 からなるリストを変数 x
に代入するにはこのように書きます。
(let* ( (x '(1 3 5))) x)
先の構文とこの構文をおのおの Script-Fu コンソールに打ち込んでどんな結果が返されるか試してみましょう。 先の構文を打ち込んだ場合はこのような簡単な答えになります。
8
ところがもう一方の構文を打ち込んだ場合にはつぎのような結果を返します。
(1 3 5)
返された値が 8 の例は変数 x
がアトム値 8 をもつことを示しています。 もう一方の (1 3 5)
が返された例では変数 x
には単なる値ではなく一連の値のリストが入っていたことを示します。 リストをつくるときコンマで区切るようなことはありませんし、 返ってきた結果にも空白以外の区切り文字がないことに注目しましょう。
リストを定義する構文は、
'(a b c)
のように書く規則になっており、 a
、 b
、 c
はリテラル (値の直接表記) です。 これらをまとめた丸括弧の前にアポストロフィ「'
」をつけるとその括弧は関数や式ではなくリテラルだけで構成されたリストであることを示します。
空のリストもつぎのようにすれば定義できます。
'()
もしくは単純に、
()
と書いてもよろしい。 リストにはアトム値だけでなく他のリストも入れられます。
(let* ( (x '("GIMP" (1 2 3) ("is" ("great" () ) ) ) ) ) x )
ちなみに最初にアポストロフィをつけてしまえば、 その内部でさらにリストをつくるときにアポストロフィを省略できます。 それでは直ちに Script-Fu コンソールに上記のリストを写してどんな結果がでるか見てみましょう。
返される値が単なるアトム値のリストではないことに注意してください。 ここではリテラル "GIMP"
やリスト (1 2 3)
などからなるリストができています。
リストを「先頭」と「後続」からできているととらえるのはうまい方法です。 先頭とはリストの最初の要素のことであり、 後続はリストの残りの要素のことです。 リストから要素をとりだしたり加えたりする方法を学ぶなかでこうしたとらえ方の重要性が解るはずです。
今後使うことになる関数のなかでも cons
は多く目にすることになる関数です。 この関数がとる変数のひとつはリストであり、 第 2 引数として与えます。 さきほどリストのことを先頭の要素と後続の要素群としてとらえてみるよう勧めました。 それが cons
のやっていることです。 この関数は要素をリストの先頭に差し込みます。 それではつぎのようにリストを作ってみましょう。
(cons 1 '(2 3 4) )
結果的にリストは (1 2 3 4)
となります。
同様にして要素がひとつだけのリストも作れます。
(cons 1 () )
先に定義してあった変数が、 あらゆるリテラルの代わりに思い通りに使えます。
リテラルとあらかじめ定義しておいた変数を組み合わせて、 list
関数を使ってリストを定義できます。
(list 5 4 3 a b c)
この式は変数 a
、 b
、 c
それぞれがもつ値を含むリストを組み立てて返します。 たとえば、
(let* ( (a 1) (b 2) (c 3) ) (list 5 4 3 a b c) )
この式はリスト (5 4 3 1 2 3)
を作成します。
リストの中にある値を取り出すときは car
関数と cdr
関数を使います。 前者がリストの先頭の要素を返し、 後者はその残りを返します。 先に述べたとおり両関数はリストを先頭と後続の構成に分解します。
car
はリストの最初の要素 (リストの先頭) を返します。 リストが空ではいけません。 つぎの式はリストの先頭の要素を返します。
(car '("first" 2 "third"))
すなわちこれはつぎの値と同じです。
"first"
cdr
はリストから先頭の値を除外した残りの要素のリスト (リストの後続) を返します。 リストにひとつしか要素が入っていなかった場合は空のリストが返されます。
(cdr '("first" 2 "third"))
この式が返すのはつぎのとおりです。
(2 "third")
また、
(cdr '("たったひとつ"))
この式が返すのはつぎのとおりです。
()
はい、 もう十分です。 リストから先頭の要素も後続の要素たちも取り出せるようになりました。 それではリストの 2 番目や 3 番目などほかの要素はどんなふうにしたら取り出せるのでしょう。 ここで使える 便利 な関数があります。 たとえば先頭の先頭を引き出すには caadr
が、 後続から後続の要素群を引き出すには cddr
が使えるというふうになっています。
根本的に命名規則は簡単です。 a や d がそれぞれリストの先頭や後続を表しているのです。 ですから
(car (cdr (car x) ) )
という式はつぎのように書き直せます。
(cadar x)
リスト用引き出し関数の練習のためつぎのスクリプトを書いてみましょう。 コンソール上で試すときは 1 行につづけて書きます。 そうしたら car
と cdr
の両関数をいろいろ組み合わせてリスト内の要素群を取り出す式を作ってみましょう。
(let* ( (x '( (1 2 (3 4 5) 6) 7 8 (9 10) ) ) ) ; この行以降にご自分なりの式を car や cdr を駆使して書きます )
両関数だけをいろいろ組み合わせて、 リストから数字の 3 を引き出す式を書きましょう。 これができるならあなたは Script-Fu の達人になりつつあるのです。
注記 | |
---|---|
Scheme 言語ではセミコロン「 |