(a b c d) (23 45 17) ((Norah 17) (Molly 14) (Anna 11) (Lauren 11) (Charlotte 8))のようなリストが作れる. 記号を含むリストはわれわれの言語の式:
(* (+ 23 45) (+ x 9)) (define (fact n) (if (= n 1) 1 (* n (fact (- n 1)))))のように見える.
記号を操作するためには, 言語に新しい要素: データオブジェクトをクォートする (quote)能力が必要である. リスト(a b)が作りたかったとする. (list a b)で作るわけにはいかない. なぜかといえば, この式は記号ではなく, aとbの値のリストを作るからである. この問題は 自然言語の方面ではよく知られている. 自然言語では, 単語や文は意味の対象として見られる時もあり, また文字列(構文の対象)として見られる時もある. 自然言語の通常のやり方は, 単語や文が文字列として字面通り扱われることを示すのに引用符を使う. 例えば「John」の最初の文字は確かに「J」である. 誰かに「あなたの名前を大声でいって下さい」というと, その人の名前が聞こえると考える. しかし, 誰かに「『あなたの名前』を大声でいって下さい」というと「あなたの名前」が聞こえると考える. 誰か他の人がいったことを記述するには引用符を入れ子にしなければならなくなることに注意しよう.32
リストや記号を評価される式としてではなく, データオブジェクトとして扱うことを示すのに, 同じやり方に従おう. しかしクォートの形式は自然言語のそれと違い, 引用符(伝統的に一重引用記号 ')を, クォートするオブジェクトの前にだけ置く. Schemeの構文ではオブジェクトの終りが空白やかっこで分るので, これで十分である. つまり一重引用符の意味は, 続くオブジェクトをクォートすることである.33
(define a 1) (define b 2) (list a b) (1 2) (list 'a 'b) (a b) (list 'a b) (a 2)
通常のリストの印字表現を使って合成オブジェクトを入力するのにクォートを使うことが出来る:34
(car '(a b c)) a (cdr '(a b c)) (b c)この方式でいくと, '()の評価で空リストが得られ, そこで変数 nilがいらなくなる.
記号を操作するのに使うもう一つの基本的要素は, 引数として二つの記号をとり, それらが同じであるかどうかをテストする eq?である.35 eq?を使うとmemqという有用な手続きが実装出来る. これは記号とリストの二つの引数をとる. その記号がリストに含まれていない(つまり, リストのどの項ともeq?でない)なら, memqは偽を返す. そうでなければ, リストの, 最初に現れたその記号から始る部分リストを返す:
(define (memq item x) (cond ((null? x) false) ((eq? item (car x)) x) (else (memq item (cdr x)))))例えば
(memq 'apple '(pear banana prune))の値は偽であり,
(memq 'apple '(x (apple sauce) y apple pear))の値は(apple pear)である.
(list 'a 'b 'c) (list (list 'george)) (cdr '((x1 x2) (y1 y2))) (cadr '((x1 x2) (y1 y2))) (pair? (car '(a short list))) (memq 'red '((red shoes) (blue socks))) (memq 'red '(red shoes blue socks))
(equal? '(this is a list) '(this is a list))は真である. しかし
(equal? '(this is a list) '(this (is a) list))は偽である. より正確には, 基本となるeq?等価を使い, aと bが記号であって両者がeq?であるか, あるいは両者が, (car a) が(car b)にequal?であり, (cdr a)が(cdr b)にequal?であるようなリストであると, equal?を再帰的に定義出来る. この考えを使い, equal?を実装せよ.36
(car ''abracadabra)と入力した. 驚いたことに解釈系はquoteと印字してきた. なぜか.
32
言語にクォートを許すと, 等しいものは等しいもので置き換えてよいという考えを破壊するので, 単純な言葉で言語を理解する能力を台無しにする. 例えば3は2足す1である. しかし「3」の単語は「2+1」という句ではない.
クォートは, (4章で解釈系を書く時に分るように,)他の表現を操作する表現を構成する方法をもたらすが故に強力である. しかし言語の中の他の文について語る言語の中の文は, 「等しいものは等しいものに置き換えてよい」ということが意味する首尾一貫の原則の維持を困難にする. 例えば
宵の明星は明けの明星であることを知っていれば,「宵の明星は金星である」という文から「明けの明星は金星である」と簡約出来る. しかし「Johnは宵の明星は金星であることを知っている」から「Johnは明けの明星が金星であることを知っている」は推論出来ない.
33
一重引用符は, 印字すべき文字列を囲むのに使った
二重引用符とは別のものである. 一重引用符はリストや記号を表記するのに使うが, 二重引用符は文字列と共にだけ使う. 本書では文字列は印字すべきものとしてだけ使う.
34
厳密にいえば, 引用符の使い方は, われわれの言語では合成式はすべてかっこで囲まれ, リストのように見えなければならないという, 一般法則に違反している. この統一性を, 引用符と同じ目的の特殊形式
quoteを使って補正出来る. つまり'aの代りに(quote a),
'(a b c)の代りに(quote (a b c))と入力するのである. これは解釈系がやっている方法に外ならない. 引用符は次の完全な式をquote
を使って包み, (quote 〈expression〉)の形にする一文字省略形なのである. これは解釈系が見たどの式もデータオブジェクトとして操作されるという原則を維持するので, 重要である. 例えば(car '(a b c))
(これは(car (quote (a b c)))と同じ)は(list 'car (list 'quote '(a b c)))を評価して構成することが出来る.
35
二つの記号は同じ順に同じ文字で出来ているなら「同じ」と考える. この定義は, プログラム言語における, 「同じであること」の意味に答えることが出来ていないという深い問題を惹き起す. このことには3
章(3.1.3節)で戻ることにしよう.
36
実際にはプログラマはequal?を記号だけでなく数値を含むリストを比較するにも使う. 数値は記号とは考えない.
二つの数値的に等しい数(=でテストする.)がeq?でもあるかという問題は, 実装に大きく依存する. (Schemeで基本としているもののように)
equal?の更によい定義は, aとbが共に数値であれば, 両者が数値的に等しい時, aとbはequal?であると規定することであろう.