6. リスト処理関数の設計(発展版) プログラミング論 I 例題1. ベクトルの内積 同じ長さのリストとして表現された2つのベクトル データ x, y の内積を求める関数 product を作る ※ただし,ベクトルx, yの要素数(長さ)は等しいとする. product 入力は, 2つのリスト (list 1 2 3) (list 4 5 6) 出力は数値 32 リスト処理の再帰関数のテンプレート リスト処理関数一般のテンプレート (define (fun_for_list a_list) (cond [(empty? a_list) …] [else … (first a_list) … … (fun_for_list (rest a_list)) …])) 今回の関数 product のテンプレート 今回は2つのリスト (ベクトル)を処理 する必要がある (define (product x y) (cond [(empty? x) …] [else … (first x) … (first y) … … (product (rest x) (rest y)) …])) 例題1.ベクトルの内積 1. リストが空ならば: (終了条件) 解は 0 (自明な解) 2. そうでなければ: – 「 2つのリストの先頭の積」と、「2つのリス トの rest 部の内積」の「和」 が求める解 (empty? x) Yes 0 が自明な解 No (+ (* (first x) (first y)) (product (rest x)(rest y))) 例題1.ベクトルの内積 ;; product: list list -> number ;; inner product of two vectors ;; (product (list 1 2 3) (list 4 5 6)) = 32 (define (product x y) 今回は2つのリスト (cond 終了条件 自明な解 (ベクトル)を処理 [(empty? x) 0] する必要がある [else (+ (* (first x) (first y)) first部の処理 (product (rest x) (rest y)))])) 結果とrest部の 処理結果を足す product の内部に product (再帰による繰り返し) 例: (product (list 1 2 3) (list 4 5 6)) = (+ (* 1 4)(product (list 2 3)(list 5 6))) 練習1.ベクトルの内積 (product (list 1 2 3 4) (list 5 6 7 8)) の 計算過程の概要と結果を示せ。 例題2. 構造体のリスト リストの要素は単純値以外でもよい → 構造体やリストも可 AddressNote 構造体のリストから名前(name) AddressNote のリストを得る関数 select_name を作る name –(参考: AddressNote は第4回目の資料の personal_info と同様の構造体) age address –構造体1つで1人分 → 構造体のリストで複数人分 入力は AddressNote 構造体のリスト Select_name (list (make-AddressNote "Ken" 35 "Fukuoka") (make-AddressNote "Bill" 30 "Saga") (make-AddressNote "Mike" 28 "Nagasaki")) 出力は文字列 (name)のリスト (list "Ken" "Bill" "Mike") リスト処理の再帰関数のテンプレート リスト処理関数一般のテンプレート (define (fun_for_list a_list) (cond [(empty? a_list) …] [else … (first a_list) … … (fun_for_list (rest a_list)) …])) 今回の関数 select_name のテンプレート (define (select_name a_list) 先頭要素が構造体データ なのでさらにセレクタ を使 (cond いフィールドデータを参照 [(empty? a_list) …] [else … (AddressNote-name (first a_list)) … (select_name (rest a_list)) …])) 例題2. 構造体のリスト (define-struct AddressNote 構造体定義 (name age address)) ;; select_name: ;; a list of AddressNote -> a list of string ;; to select name from an AddressNote list (define (select_name a_list) 要素が構造体のリストなの (cond 終了条件 自明な解 でさらにセレクタを適用し [(empty? a_list) empty] フィールドデータを参照 [else (cons (AddressNote-name (first a_list)) first部の処理結果 (select_name (rest a_list)))])) とrest部の処理結果 リストをconsで結合し て結果リストを作る C言語 練習2. 構造体のリスト AddressNote 構造体のリストから年齢(age) のリストを得る関数 select_age を作る 入力は AddressNote 構造体のリスト select_age (list (make-AddressNote "Ken" 35 "Fukuoka") (make-AddressNote "Bill" 30 "Saga") (make-AddressNote "Mike" 28 "Nagasaki")) AddressNote name age address 出力は数(age) のリスト (list 35 30 28) 次に AddressNote 構造体のリストから,年齢(age)の 平均を求める関数 average_age を作る 例題3. 自然数処理 n から 1 までの自然数を要素に持つリスト を生成する関数 naturals を作る naturals 入力は 1つの自然数 3 出力は自然数 のリスト1つ (list 3 2 1) 自然数を定義する(再帰的データ) 任意数の要素を持つリストを再帰により定式化した → 同様に自然数も定式化してみる (自然数もその要素が任意の大きさであるデータの一つ) 列挙記述 0, 1, 2, … から非形式的 “...” を取るため自己参照で記述 0 は自然数である。 もし n が自然数なら、n より1だけ大きいものも、そうである。 ↓ Scheme-style のデータ記述 自然数とはいずれかである 1. 0 または 2. (add1 n)、もし n も自然数なら。 ここで演算 add1 は 自然数に 1 を加えるもの。 (リスト同様、上記の 2 は自己参照を持つが、1は持たない) 自然数を定義する(再帰的データ) データ定義から例を組み立てる 0 は最初の自然数。 (add1 0) は次の自然数。 (add1 (add1 0)) はその次の自然数。以下同様… これは、リスト構築プロセスと類似: • empty から始め cons で要素を加えることでリストを作った。 ⇒ 今度は 0 から始めて 1 を add することで自然数を作る。 (自然数は省略記法を持つ:(add1 0) は 1、(add1 (add1 0)) は 2、など) リスト処理では cons されたリストを抽出するのに rest を使った (rest (cons a_value a_list)) = a_list 自然数でこの “extraction” を行う演算は sub1 (次の法則を満足) (sub1 (add1 n)) = n 再帰関数のテンプレート リストを処理する関数のテンプレート (define (fun_for_list a_list) (cond [(empty? a_list) …] [else … (first a_list) … … (fun_for_list (rest a_list)) …])) 自然数を処理する関数のテンプレート (define (fun-for-natural n) リスト処理の empty? に相当。意味は (= n 0) と同じ (cond [(zero? n) …] [else … n … (fun-for-natural (sub1 n)) …])) リスト処理の rest に相当。意味は (- n 1) と同じ 例題3. 自然数処理 1. n が 0 ならば(終了条件) :empty (自明な解) 2. そうでなければ: – (sub1 n) に対する結果の先頭に n を加える – リストの先頭に n をつなげるために cons を使う ;;naturals: number -> list-of-numbers ;; to create a list of numbers (n to 1) ;; (naturals 3) = (list 3 2 1) (define (naturals n) (cond [(zero? n) empty] [else (cons n (naturals (sub1 n)))])) naturals の中に naturals (再帰で繰り返し) 例:(naturals 3) = (cons 3 (naturals 2)) (naturals 3) から (list 3 2 1) が得られる過程の概略 (naturals = … = (cons 3 = … = (cons 3 = … = (cons 3 = … = (cons 3 = (list 3 3) 最初の式 (naturals 2)) (cons 2 (naturals 1))) (cons 2 (cons 1 (naturals 0)))) (cons 2 (cons 1 empty))) コンピュータ内部での計算 2 1) 実行結果 練習3. 自然数処理 1から n までの自然数を要素に持つリスト を生成する関数 naturals2 を作る naturals2 入力は 1つの自然数 3 出力は自然数 のリスト1つ (list 1 2 3) ヒント 自然な考え方 • 2つの引数を取る補助関数naturals2-subを定 義する. ;; naturals2-sub : number number -> (listof number) ;; to produce a list of natural numbers, whose elements start i to n. When i becomes greater than n, return the list produced. (define (naturals2-sub i n) ... ) 少々難問! 別の観点から • ある数をリストの末尾に置くmake-last-itemを 補助関数として定義し,これを利用する. ;;make-last-item : num (listof num)->(listof num) ;;(make-last-item 3 (list 1 2)) should produce (list 1 2 3) n=3の時,(- n 1)は 2なので,(list 1 2) ;;(naturals2 n) ;; もし n=1 ならば (list n) ;; そうでなければ,nを,(naturals2 (- n 1))で作るリスト の最後に置く. 例題4. 要素の挿入 • ソート(整列)されたリストに,整列関係を保って 要素を挿入する関数 insert を作る – この例では要素が大きい順に整列されているとする insert 入力は,数と 整列済みのリスト 出力は要素挿入 後の整列済リスト (list 80 40 21 10 7 5 4) 40 (list 80 21 10 7 5 4) 例題4. 要素の挿入 リスト処理のテンプレートをもとに具体化 1. リストが空ならば(終了条件) : (cons n empty) ; 要素が1つのリスト 2. そうでない場合: 整列関係を保つ位置にnを挿入 – 先頭要素 ≦ n なら→ nが最大なので先頭に挿入 (cons n alon) が整列済みの結果 – 先頭要素 > n なら→ nの挿入は尾部のどこか rest部への再帰。その結果と先頭要素を結合 (rest部への再帰的な呼出も意図通りに機能するという前提) 例題4. 要素の挿入 (empty? lon) Yes No (cond [(<= (first lon) n) (cons n lon)] [(> (first lon) n) (cons (first lon) (insert n (rest lon)))]) (<= (first lon) n) No Yes (cons n lon) が解 (cons n empty) が解 再帰 (insert n (rest lon)) でrest部への挿入を試み、その 結果と (first lon)を結合 例題4. 要素の挿入 ;;insert: number list-of-numbers->list-of-numbers ;;create a list of numbers from n and numbers on alon that ;; is sorted in descending order; alon is already sorted ;;insert: number list-of-numbers(sorted) ;; -> list-of-numbers(sorted) (define (insert n alon) (cond [(empty? alon) (cons n empty)] [else (cond [(<= (first alon) n) (cons n alon)] [(> (first alon) n) (cons (first alon) (insert n (rest alon)))])])) insert の定義内に insert の適用(再帰による繰り返し) 例: (insert 40 (list 80 21 10 7 5 4)) = (cons 80 (insert 40 (list 21 10 7 5 4))) (insert 20 (list 80 21 10 7 5 4)) から (list 80 21 20 10 7 5 4)) を得る過程の概略 (insert 20 (list 80 21 10 7 5 4)) = … = (cons 80 (insert 20 (rest (list 80 21 10 7 5 4)))) = (cons 80 (insert 20 (list 21 10 7 5 4))) = … = (cons 80 (cons 21 (insert 20 (list 10 7 5 4))) = … = (cons 80 (cons 21 (list 20 10 7 5 4))) = (list 80 21 20 10 7 5 4) 例題5. インサーションソート 例題4の insert を補助関数として使い、リストを ソートする関数 sort を作る – ここでは,大きい順にソートする 入力はリスト 出力はソート済みリスト sort (list 3 5 1 4) → (list 5 4 3 1) (list 1 3 5 7 10 21 4 80) → (list 80 21 10 7 5 4 3 1) 例題5.インサーションソート ソート済リストに insert を使って要素を挿入したも のもまたソート済リスト 1.リストが空なら(終了条件) : empty が解 2.そうでなければ: 要素を1つずつ取り出しなが らその時点での整列済みリストを作る → rest部への再帰呼出の結果に (それが意 図通り整列済みリストであるという前提で) first 要素を補助関数 insert で挿入する 例題5.インサーションソート ;; sort: list-of-numbers -> list-of-numbers (define (sort lon) (cond [(empty? lon) empty] [else (insert (first lon)(sort (rest lon)))])) No (empty? lon) Yes empty (insert (first lon) (sort (rest lon))) sort の定義内部に sort の関数適用(再帰的繰り返し) 例:(sort (list 3 5 1 4)) = (insert 3 (sort (list 5 1 4))) (sort (list 3 5 1 4)) の計算過程の概略 (sort (list 3 5 1 4)) … = (insert 3 (sort (list 5 1 4))) … = (insert 3 (insert 5 (sort (list 1 4)))) … = (insert 3 (insert 5 (insert 1 (sort (list 4))))) … = (insert 3 (insert 5 (insert 1 (insert 4 (sort empty))))) … = (insert 3 (insert 5 (insert 1 (insert 4 empty)))) … = (insert 3 (insert 5 (cons 4 (insert 1 (rest (list 4)))))) = (insert 3 (insert 5 (cons 4 (insert 1 empty)))) … = (insert 3 (insert 5 (cons 4 (cons 1 empty)))) = … = (insert 3 (cons 5 (list 4 1))) = … = (cons 5 (insert 3 (list 4 1))) = … = (cons 5 (cons 4 (insert 3 (list 1))) = … = (cons 5 (cons 4 (cons 3 (list 1))) = (list 5 4 3 1) 練習4. インサーションソート 例題5(および例題4)を参考に リストを、今度は小 さい順に、ソートする関数 sort2 を作る 入力はリスト 出力はソート済みリスト sort2 (list 3 5 1 4) → (list 1 3 4 5) (list 7 1 80 3 21 5 10 4) → (list 1 3 4 5 7 10 21 80) 課題 課題1. 奇数のリスト • 自然数 n から,「1番目からn番目の奇数のリ スト」を作る関数 odd_list を作成せよ。 – 例えば (odd-list 4) = (7 5 3 1) Odd-list ;; odd-list: number -> (listof number) ;; (odd-list 4) -> (list 7 5 3 1) (define (odd-list n) (cond [(= n 0) empty] [else (cons (- (* 2 n) 1) (odd-list (- n 1)))])) 課題2. 構造体リスト処理(フィルタリング) AddressNote 構造体(例題2)についての問題 – 年齢 an_ageとAddressNote 構造体のリスト a_list を 入力とし,年齢が an_age に一致する人のリストを出力 する関数 select_by_age を作成せよ。 例えば (select-by-age 35 (list (make-address-note (make-address-note (make-address-note = (list (make-address-note (make-address-note “Kaneko” 35 “Hakozaki”) “Tanaka” 34 “Kaizuka”) “Saito” 35 “Tenjin”))) “Kaneko” 35 “Hakozaki”) “Saito” 35 “Tenjin”)) select_by_age ;;(select-by-age 35 ;; (list (make-address-note “Kaneko” 35 “Hakozaki”) ;; (make-address-note “Tanaka” 34 “Kaizuka”) ;; (make-address-note “Saito” 35 “Tenjin”) ) ;;= (list (make-address-note “Kaneko” 35 “Hakozaki”) ;; (make-address-note “Saito” 35 “Tenjin”)) ) ;; select_by_age: age (listof address-node) => (listof address_node) (define (select_by_age aloAN age) (cond [(empty? aloAN) empty] [(= (address-node-age (first aloAN)) age) (cons (first aloAN) (select_by_age (rest aloAN) age))] [else (select_by_age (rest aloAN) age) ] ))
© Copyright 2024 ExpyDoc