510 likes | 606 Views
関数の組み立てと 補助関数. 今日は,複数の関数の組み合わせを学ぶ 関数 sort 関数 insert. リスト処理の再帰関数の本体定義. 基底ケース 再帰を含まない cond 節 定式化が容易 ,あるいは「例」により既知 ※ 「例」とは設計レシピでの「例」 自己参照のケース 関数の再帰的な適用 結果値の生成 部分 的な 結果 ( r est 部を使った自己適用 の結果) を使い, 最終結果を得る. この資料では,結果値の生成で, 補助関数を使う例を示す. ソート. ソート前 ( list 2 5 1 3 4 ) ソート後
E N D
今日は,複数の関数の組み合わせを学ぶ 関数 sort 関数 insert
リスト処理の再帰関数の本体定義 • 基底ケース • 再帰を含まないcond節 • 定式化が容易,あるいは「例」により既知 ※ 「例」とは設計レシピでの「例」 • 自己参照のケース • 関数の再帰的な適用 • 結果値の生成 部分的な結果(rest部を使った自己適用 の結果)を使い,最終結果を得る この資料では,結果値の生成で, 補助関数を使う例を示す
ソート • ソート前 (list2 5 1 3 4) • ソート後 (list5 4 3 2 1) 要素の並び替え ※降順でのソートの場合 降順:大→小,昇順:小→大
問題の分析とデータの定義 入力: 数のリスト 出力 数のリスト (ソート済み) 入力と出力の関係: 入力(数のリスト)の各要素を,降順に 並べなおしたものが出力 数のリスト(list of numbers)の定義 1.empty (空リスト) あるいは 2. (conse L) 但し, eは数(number)で, Lは数のリスト (list of numbers)
入力: 数のリスト 出力数のリスト(ソート済み) ※ 関数名は好きに付けてよいが,今回は sortとしている ;; sort : (listof number) ;; -> (listof number) (sorted) ;; alon 内の数から,降順にソートされた数のリストを生成 ;; (sort empty) -> empty ;; (sort (list 2 5 1 3 4)) ;; -> (list 5 4 3 2 1) (define (sort alon) ...)
数のリストを扱う再帰関数のテンプレート 基底ケース部分の定義を行う・・・ 基底ケース empty 入力が空リスト (empty) のとき, 出力も空リスト (empty) (define (sort alon) (cond [(empty?alon) ... ] [else ... (firstalon) ... (sort (restalon)) ... ]))
[else ... (firstalon) ... (sort (restalon)) ... ])) 入力が空リスト (empty) で無い場合 例えば,入力が (list2 5 1 3 4) の場合 ■ 欲しい最終結果 (list5 4 3 2 1) 要素の並び替え これで,関数定義を考える (define (sort alon) (cond [(empty?alon) empty ] [else ... (firstalon) ... (sort (restalon)) ... ]))
[else ... (firstalon) ... (sort (restalon)) ... ])) 例えば,入力が (list2 5 1 3 4)の場合 ■ 中間結果(sort (restalon)) は (restalon) -> (list5 1 3 4) になるので, (sort (restalon)) -> (list5 4 3 1) になるはず ■ 欲しい最終結果 (list5 4 3 2 1) 中間結果を使って, どう,最終結果を得るか (firstalon) -> 2 最終結果を得るには,(firstalon)の値 2 を中間結果 (list5 4 3 1)に挿入
[else ... (firstalon) ... (sort (restalon)) ... ])) [else (insert (firstalon) (sort (restalon))) ])) 関数定義の完成 ※ (first alon) を,降順でソート済みの数のリスト(sort (rest alon))の適切な場所に挿入 → そのために,補助関数 (Auxiliary Function) を定義 (ここでは,関数名 insert) (firstalon) -> 2 最終結果を得るには,(firstalon)の値 2 を中間結果 (list5 4 3 1)に挿入
補助関数 insert を用いた,関数 sort の定義 ;; sort : (listof number) -> (listof number) (sorted) ;; alon 内の数から,降順にソートされた数のリストを生成 ;; (sort empty) -> empty ;; (sort (list 2 5 1 3 4)) ;; -> (list 5 4 3 2 1) (define (sort alon) (cond [(empty?alon) empty] [else (insert (firstalon) (sort (restalon)))])) 基底ケース 補助関数を使用 これで,関数 sortの定義は終わり
主関数と補助関数 主関数 sort 処理を頼む! 結果 呼び出す側 必要なデータ 補助関数 insert 呼び出される側 複数の関数に「分割」
主関数 sortの定義は終わったので,insertに移る • 関数 insertの定義では consが登場する
問題の分析とデータの定義今度は関数 insert 入力: 数と 数のリスト (ソート済み) 出力 数のリスト (ソート済み) 入力と出力の関係: 数を,降順でソート済みの数のリストに挿入し,新しい数のリストを作る 入力として与えられる数のリストは, すでに降順でソート済みである と考える
入力: 数と 数のリスト(ソート済み) 出力数のリスト(ソート済み) ※ 関数名は好きに付けてよいが,今回は insert としている ;; insert : number (listof number)(sorted) ;; -> (listof number)(sorted) ;; 数 n と,降順にソート済みの数のリスト alon から, ;; 降順にソート済みの数のリストを生成する ;; (insert 5 empty) -> (list 5) ;; (insert 2 (list 5 4 3 1)) ;; -> (list 5 4 3 2 1) (define (insert n alon) ...)
ヘッダ (define (insert n alon) ... ) 数のリストを扱う再帰関数のテンプレート テンプレートは,ヘッダで 定めた関数名 insert と, パラメータ名 n, alon にあわ せて調整 基底ケース 自己適用を含む Scheme 式 ※ セレクタrestの結果値へ自己適用 (define (insert n alon) (cond [(empty?alon) ...] [else ... (firstalon) ... (insert n (restalon)) ... ]))
数のリストを扱う関数のテンプレートをもとに数のリストを扱う関数のテンプレートをもとに 基底ケース部分の定義を行う 基底ケース (cons n empty) 入力が空リスト (empty) のとき, 出力は, (cons n empty) (define (insert n alon) (cond [(empty?alon) ... ] [else ... (firstalon) ... (insert n (restalon)) ... ]))
[else ... (firstalon) ... (insert n (restalon)) ... ])) 数のリストを扱う関数のテンプレートをもとに 基底ケース部分の定義を行う 基底ケース alonが空リストで無い場合 結果値は,nも含み,alonの全要素を含む数のリスト(降順でソート済み) (cons n empty) 入力が空リスト (empty) のとき, 出力は, (cons n empty) (define (insert n alon) (cond [(empty?alon) ... ] [else ... (firstalon) ... (insert n (restalon)) ... ]))
nが alonの全要素よりも大きいとき n = 7, alon = (list6 5 4)のとき ■ 最終結果 (insert nalon) -> (list7 6 5 4) ⇒ これは (consn alon) 最終結果 ※ n を先頭要素として,新しいリストを作る • nが alonの全要素よりも大きいか? • 全要素を調べる必要はない • nが (firstalon)より大きいか,等しいかを調べるだけ. alonは降順にソート済みだから
nよりも大きな数を alonが含むとき n = 3, alon = (list6 2 1 -1)のとき ■ 最終結果 (insert n alon)-> (list6 3 2 1 -1) • これは数のリスト.nを含むとともに,alonの全要素を含む • nを, 先頭要素の前に挿入してはいけない • nを,alon内のどこに挿入するかを探す処理が必要
nよりも大きな数を alonが含むとき n = 3, alon = (list6 2 1 -1)のとき ■ 最終結果 (insert n alon)-> (list6 3 2 1 -1) • nは (restalon)内のどこかに挿入される • (cons (firstalon) (insertn (restalon))) (firstalon)-> 6 (insertn (restalon)) -> (list3 2 1 -1) 中間結果 最終結果 nが (firstalon)よりも小さい場合の処理
nが (firstalon)より大きいか,等しい場合 • nが (firstalon)よりも小さい場合 場合分けには,Scheme の cond文を使用 (cond [(>=n (firstalon)) ...] [(<n (firstalon)) ...])
;; insert: number (listof number)(sorted) ;; -> (listof number)(sorted) ;; 数 n と,降順にソート済みの数のリスト alon から, ;; 降順にソート済みの数のリストを生成する ;; (insert 5 empty) -> (list 5) ;; (insert 2 (list 5 4 3 1)) -> (list 5 4 3 2 1) (define (insert n alon) (cond [(empty?alon) (consnempty)] [else (cond [(>=n (firstalon)) (consn alon)] [(<n (firstalon)) (cons (firstalon) (insert n (restalon)))])]))
複数の関数の組み合わせ • 関数 sort • 関数 insert ;; sort : (listof number) ;; -> (listof number) (sorted) (define (sort alon) (cond [(empty?alon) empty] [else (insert (firstalon) (sort (restalon)))])) 規約に合致 ;; insert: number (listof number)(sorted) ;; -> (listof number)(sorted) ※ 規約に合致しなければ実行時エラー
実習の進め方 資料を熟読 「実習」と書いてあるページでは,実習の手順に従って,各自で実行する.理解を深める それぞれの「実習」では,実行結果を付けているので,確認しておく 26
DrScheme の使用 DrScheme の起動 プログラム → PLT Scheme → DrScheme 今日の演習では「Intermediate Student」 に設定 Language → Choose Language → Intermediate Student → Run ボタン 27
例題1.要素の挿入 • ソート済み(整列)のリストに,整列関係を保ったままで,要素を挿入する関数 insert • ここでは,要素が降順にソート済みとする • 挿入を行うために,consを使う 降順にソート済みのリスト 要素 (80 21 10 7 5 4 3 1) 40 (80 40 21 10 7 5 4 3 1)
入力と出力 (list 80 21 10 7 5 4) 40 (list 80 40 21 10 7 5 4) insert 出力 入力 入力は,数と ソート済みの数のリスト 出力は要素挿入後の ソート済みの数のリスト
実習1 • 「定義ウインドウ」で,次の関数定義を行いなさい • 関数名 insert, パラメータ n, alon • 入力した後に,Run ボタンを押す ;;insert : number (listof number) (sorted) ;; -> (listof number) (sorted) ;; 数 n と,降順にソート済みの数のリスト alon から, ;; 降順にソート済みの数のリストを生成する ;; (insert 5 empty) -> (list 5) ;; (insert 2 (list 5 4 3 1)) -> (list 5 4 3 2 1) (define (insert n alon) (cond [(empty?alon) (consnempty)] [else (cond [(>=n (firstalon)) (consn alon)] [(<n (firstalon)) (cons (firstalon) (insert n (restalon)))])])) 2. その後,次の式を「対話ウインドウ」で評価させなさい (insert40 (list80 21 10 7 5 4))
関数 insert 評価結果 31
実習2 • 「定義ウインドウ」の中身を次のように書き換えて,もう1度評価させなさい • 入力した後に,Run ボタンを押す 実習1と同じ ;;insert : number (listof number) (sorted) ;; -> (listof number) (sorted) ;; 数 n と,降順にソート済みの数のリスト alon から, ;; 降順にソート済みの数のリストを生成する ;; (insert 5 empty) -> (list 5) ;; (insert 2 (list 5 4 3 1)) -> (list 5 4 3 2 1) (define (insert n alon) (cond [(empty?alon) (consnempty)] [else (cond [(>=n (firstalon)) (consn alon)] [(<n (firstalon)) (cons (firstalon) (insert n (restalon)))])])) (insert20 (list80 21 10 7 5 4)) 1行書き加える • 2. その後,DrScheme を使って,ステップ実行の様子を • 確認しなさい (Step ボタンを使用) • 理解しながら進むこと 32
(insert 20 (list 80 21 10 7 5 4))から (list 80 21 20 10 7 5 4))を得る過程の概略 (insert20 (list80 21 10 7 5 4)) → … → (cons80 (insert20 (rest (list80 21 10 7 5 4)))) → (cons80 (insert20 (list21 10 7 5 4))) → … → (cons80 (cons21 (insert20 (list10 7 5 4))) → … → (cons80 (cons21 (list20 10 7 5 4))) → (list80 21 20 10 7 5 4) 33
実習3. インサーションソート 実習1のinsertを補助関数として使い、数のリストを降順にソートする関数sortを作る 出力はソート済み の数のリスト 入力は数のリスト sort (list3 5 1 4) (list1 3 5 7 10 21 4 80) →(list5 4 3 1) →(list80 21 10 7 5 4 3 1)
実習3 • 「定義ウインドウ」で,次の関数定義を行いなさい • 入力した後に,Run ボタンを押す ;; sort: (listof number) -> (listof number)(sorted) ;; alon 内の数から,降順にソートされた数のリストを生成 ;; (sort empty) -> empty ;; (sort (list 2 5 1 3 4)) -> (list 5 4 3 2 1) (define (sort alon) (cond [(empty?alon) empty] [else (insert (firstalon) (sort (restalon)))])) ;; insert : number (listof number) (sorted) ;; -> (listof number) (sorted) ;; 数 n と,降順にソート済みの数のリスト alon から, ;; 降順にソート済みの数のリストを生成する ;; (insert 5 empty) -> (list 5) ;; (insert 2 (list 5 4 3 1)) -> (list 5 4 3 2 1) (define (insert n alon) (cond [(empty?alon) (consnempty)] [else (cond [(>=n (firstalon)) (consn alon)] [(<n (firstalon)) (cons (firstalon) (insert n (restalon)))])])) 実習1 と同じ
実習3 (続き) 2. その後,次の式を「対話ウインドウ」で評価させなさい (sort(list3 5 1 4)) (sort(list1 2 1 3 1 2)) (sortempty)
(sort (list 3 5 1 4)) から (list 5 4 3 1)) が得られる過程の概略 (1/2) (sort (list 3 5 1 4)) = … = (insert 3 (sort (rest (list 3 5 1 4)))) = (insert 3 (sort (list 5 1 4))) = … = (insert 3 (insert 5 (sort (rest (list 5 1 4))))) = (insert 3 (insert 5 (sort (list 1 4)))) = … = (insert 3 (insert 5 (insert 1 (sort (rest (list 1 4)))))) = (insert 3 (insert 5 (insert 1 (sort (list 4))))) = … = (insert 3 (insert 5 (insert 1 (insert 4 (sort (rest (list 4))))))) = (insert 3 (insert 5 (insert 1 (insert 4 (sort empty))))) = … = (insert 3 (insert 5 (insert 1 (insert 4 empty)))) 次ページへ
(sort (list 3 5 1 4)) から (list 5 4 3 1)) が得られる過程の概略 (1/2) (sort (list 3 5 1 4)) = … = (insert 3 (sort (rest (list 3 5 1 4)))) = (insert 3 (sort (list 5 1 4))) = … = (insert 3 (insert 5 (sort (rest (list 5 1 4))))) = (insert 3 (insert 5 (sort (list 1 4)))) = … = (insert 3 (insert 5 (insert 1 (sort (rest (list 1 4)))))) = (insert 3 (insert 5 (insert 1 (sort (list 4))))) = … = (insert 3 (insert 5 (insert 1 (insert 4 (sort (rest (list 4))))))) = (insert 3 (insert 5 (insert 1 (insert 4 (sort empty))))) = … = (insert 3 (insert 5 (insert 1 (insert 4 empty)))) これは, (define (sortalon) (cond [(empty? alon) empty] [else (insert (first alon) (sort (rest alon)))])) の alonを (list 3 5 1 4) で置き換えたもの 次ページへ
(sort (list 3 5 1 4)) から (list 5 4 3 1)) が得られる過程の概略 (2/2) = (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 (cons1 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. 並べ替え方の変更 実習1,2のinsert,sortを書き換えて,「数字の下2桁の昇順」で並び替えるようにする 出力はソート済み の数のリスト 入力は数のリスト sort (list101 205 301 403) →(list101 301 403 205)
実習4 (1/3) 変更箇所 • 「定義ウインドウ」の中身を次のように書き換えて,もう1度評価させなさい ;; sort: (listof number) -> (listof number)(sorted) ;; alon 内の数から,下2桁で昇順にソートされた数のリストを生成 ;; (sort empty) -> empty ;; (sort (list 101 205 301 403)) -> (list 101 301 403 205) (define (sort alon) (cond [(empty?alon) empty] [else (insert (firstalon) (sort (restalon)))])) 関数 sortの定義は変更しない(そのまま使う)
実習4 (2/3) 2. 「定義ウインドウ」の中身を次のように書き換えて,もう1度評価させなさい ;; insert : number (listof number) (sorted) ;; -> (listof number) (sorted) ;; 数 n と,下2桁で昇順にソート済みの数のリスト alon から, ;; 下2桁で昇順にソート済みの数のリストを生成する ;; (insert 5 empty) -> (list 5) ;; (insert 205 (list 301 404)) -> (list 301 403 205) (define (insert n alon) (cond [(empty?alon) (consnempty)] [else (cond [(<= (remaindern 100) (remainder (firstalon) 100)) (consn alon)] [else (cons (firstalon) (insert n (restalon)))])])) 変更箇所 43 ※remainderは、ある数をある数で割った余りを求める
実習4 (3/3) 3. その後,次の式を「対話ウインドウ」で評価させなさい (sort(list3 5 1 4)) (sort(list1 2 1 3 1 2)) (sort(list101 205 301 403)) (sortempty)
実習5.構造体のソート p3d構造体が次のように定義されている x フィールドの値で,昇順にソートするように,実習4の関数 sortと insertを書き換える (define-struct p3d(x y z)) (参考) ・p3d構造体のコンストラクタ (make-p3d ... ...) ・p3d構造体のセレクタ (p3d-x ...),(p3d-y ...),(p3d-z ...) ※p3d-x, p3d-y, p3d-zがセレクタ 46 46
実習5 (1/4) 1. 「定義ウインドウ」で,次の構造体定義と変数定義を行いなさい (define-structp3d (x y z)) (defineZ (list (make-p3d0 0 0) (make-p3d3 2 3) (make-p3d1 5 2) (make-p3d2 4 3))) 47
実習5 (2/4) 変更箇所 2. 「定義ウインドウ」で,次の関数定義を行いなさい ;; sort: (listof p3d) -> (listof p3d)(sorted by x) ;; p3d 構造体のリストを,xフィールドの値で昇順にソート ;; (sort empty) -> empty (define (sort alop) (cond [(empty?alop) empty] [else (insert (firstalop) (sort (restalop)))])) 変数名は alop (A List Of P3d) に変えている 48
実習5 (3/4) 3. 「定義ウインドウ」で,次の関数定義を行いなさい ;; insert : number (listof p3d) (sorted by x) ;; -> (listof p3d) (sorted by x) ;; 数 n と,xフィールドの値で昇順にソート済みの p3d リスト alop から, ;; xフィールドの値で昇順にソート済みの数のリストを生成する (define (insert p alop) (cond [(empty?alop) (conspempty)] [else (cond [(<= (p3d-xp) (p3d-x (firstalop))) (consp alop)] [else (cons (firstalop) (insert p (restalop)))])])) 変更箇所 49
実習5 (4/4) 4. その後,次の式を「対話ウインドウ」で評価させなさい (sortZ) (sortempty) 50