第8回 リスト(6月8日)

今日の課題

■ ペア(コンス)

2つのデータをまとめたもの.通常は後述するリストを使用することが好まれる.

コンスに対する基本操作を以下にまとめる:

例題1

以下を Definitions に記述し,Run してみよう.
(define p1 (cons 100 200))
(car p1)
(cdr p1)

■リスト

コンスを組み合わせて,データを並べたものがリストである.

リストに対する基本操作を以下にまとめる:

例題2

リストの個々のデータを扱う関数を作ってみよう.

  1. 数を要素とするリストを入力し,その数の総和を求める関数 sum-list

    実行方法: (sum-list (list 1 2 3 4 5))
    (define sum-list (lambda (lst)
      (if (null? lst)
          0
          (+ (car lst) (sum-list (cdr lst))))))    
    

  2. リストの個々のデータを表示する関数 print-list

    実行方法: (print-list (list 1 2 3 4 5))
    (define print-list (lambda (lst)
      (if (not (null? lst))
          ((lambda ()                      ここの lambda は
            (write '<)                     これら write と print-list の順接を
            (write (car lst))              実行するために
    	(write '>)                     使われている.
            (print-list (cdr lst)))))))    lambda の前に括弧が2つあることに注意
    

  3. 数を要素とするリストを入力し,最大値を求める関数 max-list

    実行方法: (max-list (list 1 2 4 5 3))
    (define max-list (lambda (lst)
      (if (= 1 (length lst))          もし長さが1のリストならば,
          (car lst)                   リストの先頭の数値が最大値
          ((lambda (m n)              そうでなければ,2つの数値 m と n について
             (if (> m n) m n))        大きい数値を返す関数に,
           (car lst)                  リストの先頭の数値と
           (max-list (cdr lst))))))   リストの2番目以降の最大値を比較してもらう.
    

    上記の max-list の意味がわからない人は以下の定義をまず考えてみよう.
    (define max-list01 (lambda (m n)
      (if (> m n) m n)))
    
    (define max-list (lambda (lst)
      (if (= 1 (length lst))
          (car lst)
          (max-list01 (car lst) (max-list (cdr lst))))))
    

    以下の定義でも動作するが,無駄が多い? 関数型言語ならば,引数が同じ関数呼出しは同じ答えが得られるはずであり,無駄な計算はないので,無駄にはならない?
    (define max-list (lambda (lst)
      (if (= 1 (length lst))
          (car lst)
          (if (> (car lst) (max-list (cdr lst)))
              (car lst)
              (max-list (cdr lst))))))
    

上記の例は,リストから値を抽出する関数であった.次にリストを加工してみよう.

例題3

  1. append 関数を cons を用いて定義しよう.自分で作った append 関数を my-append とする.

    実行方法: (my-append (list 1 2 3) (list 4 5 6))
    (define my-append (lambda (lst1 lst2)
      (if (null? lst1)
          lst2
          (cons (car lst1) (my-append (cdr lst1) lst2)))))
    

  2. リスト内の各数値を 2 倍にする関数 double を定義しよう.

    実行方法: (double (list 1 2 3 4 5))
    (define double (lambda (lst)
      (if (null? lst)
          (list )
          (cons (* 2 (car lst)) (double (cdr lst))))))
    

  3. 数を格納したリストから次のようなリストを作る関数 add-next を定義しよう:新しいリストは,入力のリストの隣り合う2つの数値の和とする.たとえば,(1 1) を入力すると (2) を返し,(1 2 1) を入力すると (3 3)を返す.また,入力のリストは要素数が2未満のときは空のリストとする.

    実行方法: (add-next (list 1 2 1))
    (define add-next (lambda (lst)
      (if (< (length lst) 2)
          (list )
          (cons (+ (car lst) (car (cdr lst))) (add-next (cdr lst))))))
    

■ 練習問題

練習1:A4用紙のサイズ

よく使うプリンタの用紙は,A4サイズである(210mm x 297mm).この用 紙は,長辺を半分に折るとA5サイズの用紙になる(小数点は切り捨て. truncate関数).A0サイズは,841mm x 1189mm という大きさである.では, nを変数として Anサイズの2辺の長さをペアの値で返す関数 (paper-a-size n) を定義せよ.

ヒント:ペアのデータ1を短辺,データ2を長辺とすることにする.

ヒント:ペアのデータを入力として,長辺を折り曲げて新しい短辺とし,短辺を新しい長辺としてペアの値を返す関数を作ってみる.

練習2:パスカルの三角形

問題を説明する前に,次の式を見てみよう.

(a+b)1 = a + b
(a+b)2 = a2 + 2ab + b2
(a+b)3 = a3 + 3a2b + 3ab2 + b3
(a+b)4 = a4 + 4a3b + 6a2b2 + 4ab3 + b4
(a+b)5 = a5 + 5a4b + 10a3b2 + 10a2b3 + 5ab4 + b5
:

右辺の係数に注目すると,次の図のような関係が見えてくる.この図をパスカルの三角形という.

(a+b)の羃乗の数ごとに,係数をリストで表すと,次のようになる.

(a+b)1のとき (list 1 1)
(a+b)2のとき (list 1 2 1)
(a+b)3のとき (list 1 3 3 1)
(a+b)4のとき (list 1 4 6 4 1)

(a+b)nのときの係数のリストを返す関数 pascal-list を定義せよ.

ヒント:pascal-list では,n = 1 のとき,「(list 1 1)を返すこと」として,そのほかのとき,「先頭が 1 であり,そして,残りが n - 1 の pascal-list の返り値について add-next のような計算をしたもの」とするとよい.つまり,(1 3 3 1)を求める計算では,(1 2 1)から(3 3)を求めて,前後に (1) を append すると実現できる.

できた人は,append を使わずに,cons を使って関数 pascal-list を定義しよう.


(c) 2006.6.5 by tokuhisa