ペアとは2つのデータをまとめた構造体のことである。ドットペアと呼ばれることもある。
ペアに対する基本操作は以下の3つである:
| 意味 | 関数名 | 形式 |
|---|---|---|
| ペアの作成 | cons | (cons 〈データ1〉 〈データ2〉) |
| ペアの左側の抽出 | car | (car 〈ペア〉) |
| ペアの右側の抽出 | cdr | (cdr 〈ペア〉) |
以下を Definitions に記述し、Run してみよう。
(define p1 (cons 100 200)) (car p1) (cdr p1) |
ペアを組み合わせて、データを並べた構造体をリストと呼ぶ。
たとえば、次のようにペアを使うと、数字のリストになる。
(define p2 (cons 1 (cons 2 (cons 3 (cons 4 ()))))) |
() は空を表す
このように、リストの正体は、ペアの集りであるが、分りにくい。そこで、次のように書くことができる。
(define p3 (list 1 2 3 4)) または (define p3 '(1 2 3 4)) |
演習では印刷上のわかりやすさから、クオートの表記はせず、list の表記を用いる。
equal? を使って p2 と p3 が等しいことを確認しよう。
> (equal? p2 p3) |
リストに対する基本操作を以下にまとめる:
| 意味 | 関数名 | 形式 |
|---|---|---|
| リストの作成 | list | (list 〈データ1〉 〈データ2〉 … 〈データn〉) |
| リストの先頭に要素を追加 | cons | (cons 〈要素〉 〈リスト〉) |
| リストから先頭要素を抽出 | car | (car 〈リスト〉) |
| リストから2番目以降のリストを抽出 | cdr | (cdr 〈リスト〉) |
| リストが空かどうかの検査 | null? | (null? 〈リスト〉) |
幾つかリスト操作を確認しよう。
> (list 1 2 3) > (cons 1 (list 5 4 3)) > (car (list 1 2 3)) > (cdr (list 1 2 3)) > (null? (list 1 2 3)) > (null? (list )) > (cdr (list 1)) > (null? (cdr (list 1))) ※ この方法で、リストの要素数が1つかどうかの検査が可能 > (define x (list 1 2 3)) ※ 名前を付けることができる > (car x) > (cdr x) |
よく使う組み込みの関数
下記の動作を確認しよう。
> (length (list 1 2 3 4 5)) > (append (list 1 2 3) (list 4 5 6)) |
リストを扱う関数を作ってみよう。
実行方法: (list-sum (list 1 2 3 4 5))
予想結果: 15
簡単な解答例:
*** 解1 ***
(define list-sum
(lambda (lst)
(if (null? lst)
0
(+ (car lst) (list-sum (cdr lst))))))
|
末尾再帰関数として作る場合:
*** 解2 ***
(define list-sum2
(lambda (lst n)
(if (null? lst)
n
(list-sum2 (cdr lst) (+ n (car lst))))))
(define list-sum
(lambda (lst)
(list-sum2 lst 0)))
|
解2を変更して、関数定義を内部に持たせる場合:
*** 解3 ***
(define list-sum
(lambda (lst)
(define list-sum2
(lambda (lst n)
(if (null? lst)
n
(list-sum2 (cdr lst) (+ n (car lst))))))
(list-sum2 lst 0)))
|
※ 解2は、Interactions 上で list-sum2 を実行することができるが、解3は、実行できない。
実行方法: (list-max (list 1 2 4 5 3))
予想結果: 5
簡単だけど愚かな解答例:
*** 解1 ***
(define list-max
(lambda (lst)
(if (null? (cdr lst)) 要素数が1つか?
(car lst) 先頭要素を返す
(if (> (car lst) (list-max (cdr lst)) 先頭要素と2番目以降の最大値を比較
(car lst) 先頭要素を返す
(list-max (cdr lst)))))) 2番目以降の最大値を返す
|
2つめの if 文で、list-max の計算をするが、もし、その値のほうが大きいとき、2つ下の式が評価されるのだが、再び同じ計算をすることになる。
無駄な計算を省いた解答例:
*** 解2 ***
(define greater
(lambda (m n)
(if (> m n) m n)))
(define list-max
(lambda (lst)
(if (null? (cdr lst))
(car lst)
(greater (car lst) (list-max (cdr lst))))))
|
最後の行で、リストの2番目以降の最大値を、まず求めており、その結果が greater で使われているので、同じ計算を繰り返すことはない。
一方、末尾再帰関数として作成してみると、もっと簡単になる。
*** 解3 ***
(define list-max2
(lambda (lst n) これから計算すべきリスト lst、これまでの最大値 n
(if (null? lst) リストが空のとき、
n これまでの最大値 n が全体の最大値
(if (> n (car lst)) そうでなければ、先頭要素とこれまでの最大値を比較
(list-max2 (cdr lst) n) n が大きいとき
(list-max2 (cdr lst) (car lst)))))) 先頭要素が大きいとき
(define list-max
(lambda (lst)
(list-max2 (cdr lst) (car lst))))
先頭要素をこれまでの最大値とし、2番目以降を処理
|
list-max2 の定義を内部に持たせる場合:
*** 解4 ***
(define list-max
(lambda (lst)
(define list-max2
(lambda (lst n)
(if (null? lst)
n
(if (> n (car lst))
(list-max2 (cdr lst) n)
(list-max2 (cdr lst) (car lst))))))
(list-max2 (cdr lst) (car lst))))
|
例題2では、リストから値を抽出する関数であった。次にリストを返す関数を作ってみよう。
実行方法: (my-append (list 1 2 3) (list 4 5 6))
予想結果: (1 2 3 4 5 6)
(define my-append
(lambda (lst1 lst2)
(if (null? lst1)
lst2
(cons (car lst1) (my-append (cdr lst1) lst2)))))
|
※ append は、末尾再帰関数が簡単には作れません。
実行方法: (replace-sanbai (list 1 2 3 4 5 6 7 8 9 10))
予想結果: (1 2 '* 4 5 '* 7 8 '* 10)
(define replace-sanbai
(lambda (lst)
(if (null? lst)
(list) ← 空のリスト
(if (= (modulo (car lst) 3) 0)
(cons '* (replace-sanbai (cdr lst)))
(cons (car lst) (replace-sanbai (cdr lst)))))))
|
実行方法: (list-double (list 1 2 3 4 5))
予想結果: (2 4 6 8 10)
(define list-double
(lambda (lst)
(if (null? lst)
(list) ← 空のリスト
(cons (* 2 (car lst)) (list-double (cdr lst))))))
|
リスト操作と再帰呼出しに慣れるために、以下の関数を作ろう。
よく使うプリンタの用紙は、A4サイズである(210mm x 297mm)。この用紙は、長辺を半分に折るとA5サイズの用紙になる(小数点の切り捨てはtruncate関数を使用)。A0サイズは、841mm x 1189mm という大きさである。では、nを変数として Anサイズの2辺の長さをペアの値で返す関数 (paper-a-size n) を定義せよ。
動作例:(paper-a-size 4) ⇒ (210 . 297)
表1には連分数の表記とその具体的な式の関係が示されている。たとえば、(list 2 4)は、2 + 1 / 4 という式の略記であり、(list 3 6 9) は、3 + 1 / (6 + 1 / 9) という式の略記である。そこで、入力される数のリストに対して連分数を計算する関数 contfrac を定義せよ。
表1 連分数の表記と計算式
