4. 合成データ:構造体 プログラミング論I 本日の内容 • 単純データと合成データ • 構造体(合成データの一種) – 構造体の定義:構造(構成要素など)の鋳型を準備 – 構造体の使用 • コンストラクタ:構造体の定義にあうデータを生成する • セレクタ:構造体の内容を参照する – 構造体データを使うプログラムの設計法 • 様々なデータとその区別 様々なデータ • 単純データ – 数値、ブール値、シンボル、文字列、… • 合成データ:複数要素で1つのデータを構成 – 構造体 (structure) :固定数の要素で1つのデータ • 2次元平面座標(x,y座標)、名簿の一人分の項目数、 … – リスト(後日説明):不定数の要素で1つのデータ • 窓口の行列の人数、 … 構造体の例 • 複数の要素から1つのデータを構成する例 2次元平面座標 名簿データ 例 x座標3, y座標4の点 例 名前:山田、年:19、住所:伊都 posn x y personal_info 構造体の名前 name age address 要素データ(フィー ルド)の並び 様々なフィールド(要素データ)を持つ構造データ → プログラマが定義可能 構造体データを扱う道具立て 様々な構造体を扱う道具立て – 各構造体の定義 – 個々の構造体データの実体を作る コンストラクタ – 構造体データから各フィールド値を選ぶ セレクタ 例えば二次元平面上の点を扱う場合 – 定義:二次元平面上の座標は何で構成される? • 座標はx 座標と y 座標で定まる.値は数値. (x, y) – コンストラクタ:定義にあう二次元平面上の点を作る • 各点の要素の具体的データ(例えば(1, 3) (5, 12) など)を与え 構造体データを生成 – セレクタ:ある点の座標データの値を調べる • どの点のどの要素(x座標か y座標)かを,指定して参照 (1, 3)のx座標の値 1. (1,3)のy座標の値 3 を取り出す. 構造体の定義(一般形) 様々な構造体があり得る→問題に合わせて定義 固定数のフィールド(属性値)を持つあらゆるオブジェクトを表現 構造体名 フィールド名の並び (define-struct c (s1 … sn) ) 上記の定義の実行によって – 個々の構造体データの実体を作るコンストラクタ: make-c (構造体名の前に make-をつける) – 構造体データから各フィールド値を選ぶセレクタ: c-s1 … c-sn (構造体名とフィールド名を - でつなぐ) が使えるようになる 構造体の定義例: posn 型構造体 2次元平面座標 posn は x, y 座標値から構成 構造体名 フィールドの並び (define-struct posn (x y) ) posn x y 座標データ フィールド • 上記の定義によって – コンストラクタ: make-posn – セレクタ: posn-x posn-y が使えるようになる (DrRacketではposn型定義は基本構造体として元から準備済) 例題1. posn構造体 • 以下のような点 の生成や参照 の例 y 4 (define-struct posn (x y)) ただし、 DrRacketでは posn は基本的な組込みの構造体として 最初から定義済なのであらためて定義しなくてよい 点a (3,4) 点 (3,4) を作る 点 (3,4) の x 座標値を選ぶ 3 x 点 (3,4) の y 座標値を選ぶ 点 (3,4) を作り a という名前にする 点a の x 座標値を選ぶ 点a の y 座標値を選ぶ C言語の場合 // (define-struct posn (x y)) typedef struct{ int x; int y; } posn; //(define a (make-posn 3 4)) posn a; // posn型の変数a a.x=3; // a.x == (posn-x a) a.y=4; // a.y == (posn-y a) C言語の場合 (define-struct personal_info (name age address)) (define a (make-personal_info “Matsuzaka” 33 “Fukuoka”)) (personal_info-name a) => “Matsuzaka” #define _CRT_SECURE_NO_WARNINGS typedef struct{ #define <string.h> personal_info a; char name[20]; strcpy(a.name,“Matsuzaka”); int age; a.age=33; char address[128]; strcpy(a.address,“Fukuoka”); } personal_info; 練習 (define b (make-posn 12 5)) (posn-x b) (posn-y b) (define-struct personal_info (name(12,5) age address)) • 二次元平面上の点b について (define SBF-1 (make-personal_info “Matsuzaka" 33 “Fukuoka")) – コンストラクタで点bの構造体データを生成せよ (personal_info-name SBF-1)x座標と (personal_info-age SBF-1) – セレクタで点bの y座標を参照せよ (personal_info-address SBF-1) • 個人情報がname(文字列), age(数字), address(文字 列) からなるとき、その構造体を personal_info という名前で定義せよ – そのコンストラクタは何か – そのセレクタは何か personal_info 型構造体 名簿データpersonal_info 型構造体を定義 – personal_info は name, age, address から構成 構造体名 フィールド名の並び (define-struct personal_info (name age address)) 上記の定義によって以下が使えるようになる – コンストラクタ make-personal_info personal_info – セレクタ name • personal_info-name フィールド • personal_info-age • personal_info-address age address 合成データの関数の プログラム設計法 プログラム設計法:合成データの関数 合成データを扱う関数向けに プログラム設計法を拡張 • データの解析と定義: • 処理対象がN個の特性を持つ → N個のフィールドの構造体を導入 • フィールドがどの種のデータを含むか仕様記述するデータ定義 • Purpose:プログラムの目的を理解、機能の概要を記述 • Contract, Header, Statement: • Example:プログラムの振る舞いを「例」で記述. • Template:ヘッダと可能な全セレクタ式を列挙した本体を構成 • 複合データを扱う関数は複合データの構成要素を使い結果を計算 • 各セレクタ式は適切なセレクタを構造データパラメータに適用 • Body Definition:プログラムの定義本体を具体化する • Test:検査を通じた誤り(エラー)の発見 例題:原点からの距離 二次元平面上の点の,原点までの距離を求める 関数 distance_to_0 を作る 点 (3, 4) のデータ 5 distance_to_0 入力 入力は点を表すデータ 出力 出力は数値 プログラム設計法:データの解析と定義 処理対象がN個の特性を持つならNフィールドの構造体を導入 → 今回,点は2つの座標値を持つので2つのフィールドの構造体 (define-struct posn (x y)) 可能な限り正確に posn構造体データ定義の仕様を記述: – – 後にデータ定義に対応した関数テンプレートを作成 フィールドがどのような種類のデータを含むか? 注:DrRacket では定義済 posn型の構造体を生成するにはコンストラクタを使う: (make-posn num1 num2) ここで num1 num2は数値 この構造体データの例: (make-posn 3 4) (make-posn 12 5) プログラム設計法: Contract, Header Contract の定式化のために使うことができるもの • numberや symbolのような不可分データのクラス名 • posn のようなデータ定義で導入した名前 ;; distance_to_0: posn -> number (define (distance_to_0 a_posn) … ) プログラム設計法: テンプレート 本体より先にまずテンプレートを設計 • ヘッダと,可能な全セレクタ式を列挙した本体を記述。 • 各セレクタ式は適切なセレクタを入力の構造体データ に適用したもの – (関数が入力の構造体データの要素を使う計算をすることを反映) 入力は posn 型構造体のデータ a_posn • セレクタは2つ: posn-x posn-y ;; distance_to_0: posn (define (distance_to_0 … (posn-x a_posn) … (posn-y a_posn) -> number a_posn) … … ) セレクタ posn-xでposn 型構造体の入力データ a_posn のx 座標フィールド値の取得、y座標フィールド値も同様 プログラム設計法: Example 二次元平面上の点の,原点までの距離を求める (distance_to_0 (make-posn 3 4)) ;; 期待される値 5 (distance_to_0 (make-posn 12 5)) ;; 期待される値 13 プログラム設計法: 本体定義 基本演算や他の関数を使い答を計算する式を定式化 – 利用可能なデータは入力、セレクタ式の結果 テンプレート: (define (distance_to_0 a_posn) … (posn-x a_posn) … … (posn-y a_posn) … ) x座標値 y座標値 セレクタで参照した値を二乗して 本体定義: 加算し平方根を求める (define (distance_to_0 a_posn) (sqrt (+ (sqr (posn-x a_posn)) (sqr (posn-y a_posn))))) distance_to_0 関数 x の2乗と y の2乗を加え平方根を求める ;; distance_to_0: posn -> number ;; compute the distance of a posn to the origin ;; (distance_to_0 (make-posn 3 4)) = 5 (define (distance_to_0 a_posn) (sqrt (+ (sqr (posn-x a_posn)) (sqr (posn-y a_posn))))) セレクタ posn-xでposn 型構造体の入力データ a_posn の x 座標フィールド値を取得、 y 座標フィールド値も同様 x 座標値の2乗と y座標値の2乗を加え平方根を求めている プログラム全体(例) (define-struct posn (x y)) (define a-posn (make-posn 3 4)) ;; distance_to_0: posn -> number (define (distance_to_0 a-posn) (sqrt (+ (sqr (posn-x a_posn)) (sqr (posn-y a_posn))))) ;; test > (distance_to_0 a-posn) 5 #include <stdio.h> C言語の場合 #include <math.h> typedef struct{ int x; int y; } posn; // sqr: double -> double double sqr(double x){ return x*x; } // distance_to_0: posn -> double double distance_to_0(posn a){ return sqrt(sqr(a.x)+sqr(a.y)); } // test void main(){ posn a; a.x=3; a.y=4; printf(“%f”, distance_to_0(a)); } 練習 前述の personal_info 構造体について – 構造体 personal_info 型のデータ a-person の年 齢(age)が20以上ならば true を,20未満なら false を出力する関数 check-age1 を作成せよ。 – 構造体 personal_info 型のデータ a-person の年 齢(age)が20以上ならば「‘Adult」を,20未満なら 「’Clild」を出力する関数 check-age2 を作成せよ。 様々なデータとその区別 データの種類として構造体が加わった • • • • 数:数値情報の表現; ブール値:真か偽であるか; 記号: 記号情報の表現; 構造体: 合成データの表現 様々なデータを処理する関数は • 数と構造体の双方を含む場合、 • 異なる複数種類の構造体を含む場合、 などデータを区別する必要がある 述語(Predicate) :データの特定の形式を判断する演算 • • • • • number? boolean? symbol? string? struct? 値が数なら true 違えば falseを返す 同様にブール値かどうか 同様にシンボルかどうか 同様に文字列かどうか 同様に構造体かどうか また構造体にもさまざまな定義があるのでその区別も必要 構造体 posn に対する posn? や personal_info の personal_info? これまでの Scheme Scheme も自然言語同様に語彙と文法を持つ言語 (構造体の追加) 語彙 文法:語彙を用いてプログラム(式 と定義)を作る規則 •変数:関数と値の名前 定義 •キーワード: (変数には使用不可) –define define-struct cond else • 関数定義 – (define (関数名 変数並び) 式) •定数 –単純値:数, ブール値, シンボルなど • 変数定義 –合成値:構造体(固定要素数) •プリミティブ演算: –四則演算子:+ - * / –比較演算子:< <= > >= = –データ特性の判定: even? symbol? – (define 変数名 式) • 構造体定義 (define-struct 構造体名 (フィールド並び)) 式 number? struct? … • 変数 –論理演算子:and or not • 定数 –構造体演算(コンストラクタ,セレクタ) • プリミティブ演算の適用 –その他の演算子: abs remainder quotient max min sqrt expt log • 定義した関数の適用 sin cos tan asin acos atan など • 条件式 課題 課題1 構造体 posn のデータ a_posn の x 座標の値が 0 と 10 の間にある場合に限り true を返し,範囲外 の場合には false を返すような関数 xwithin を作成せよ。 また点 a (3,4) と点 b (12, 5) に対する実行結果はどうな るか。 – 但し,x = 0 あるいは x = 10 のときには false を返すこと. – ヒント: 次の空欄を埋めなさい (define (xwithin a_posn) (cond [ [else ] ])) 解答例 (define-struct posn (x y)) ;; xwithin: posn -> true or false ;; (xwithin (make-posn 3 4)) => true ;; (xwithin (make-posn 12 5)) => false (define (xwithin a_posn) (cond [(and (< 0 (posn-x a-posn)) (< (posn-x a-posn) 10)) true] [else false])) ;; 以下と同じ (define (xwithin a_posn) (and (< 0 (posn-x a-posn)) (< (posn-x a-posn) 10))) 課題2 構造体 personal_info 型のデータ a-person の年齢(ageフィールド)が20以上ならば名前(name フィールドの文字列) を,20歳未満なら匿名を表 す文字列 "anonymous" を出力する関数 checkname1 を作成せよ。 入力は personal_info構造体 出力は文字列(string) check-name1 入力 出力 (make-personal_info "Ken" 18 "Ropponmatsu") ⇒ "anonymous" (make-personal_info "Bill" 20 "Hakozaki") ⇒ "Bill" 解答例 (define-struct personal_info (name age address) (define (check-name1 a-psn) (cond [(<= 20 (personal_info-age a-psn)) (personal_info-name a-psn)] [else “anonymous”])) ;; test (check-name1 (make-personal_info “Matsuzaka” 33 “Fukuoka”)) => “Matsuzaka” 課題3 構造体 personal_info 型のデータ a-person のageフィールドの値が,引数として与えられるあ る年齢の値と一致するならば,名前(name フィー ルドの値) を,一致しなければ“failed” を出力 する関数 check-name2 を定義せよ。 入力は personal_info構造体 のデータと年齢(数) 出力は文字列(string) check-name2 入力 出力 (make-personal_info "Ken" 18 "Ropponmatsu") と 20 ⇒ "failed" (make-personal_info "Bill" 20 "Hakozaki") と 20 ⇒ "Bill" 解答例 (define-struct personal_info (name age address) (define (check-name2 a-psn an-age) (cond [(= an-age (personal_info-age a-psn)) (personal_info-name a-psn)] [else “failed”])) ;; test (check-name1 (make-personal_info “Matsuzaka” 33 “Fukuoka”) 33) => “Matsuzaka” 課題4. 複素数の計算 • Scheme では複素数の計算もできる 足し算 (1+2i) + (3+4i) → 4+6i 引き算 (5+7i) – (3+4i) → 2+3i かけ算 (1+2i) ×(3+4i) → -5+10i 割り算 (-5+10i) / (3+4i) → 1+2i (分母の通分) (-5+10i)/(3+4i) =(-5+10i)*(3-4i) /((3+4i)*(3-4i)) =(25+50i)/25 =1+2i 課題4. 複素数の計算 複素数データの表現と演算を独自に作ってみる: 実数部を r ,虚数部を i とする複素数データ comp • 以下の ? を埋めてみる – データ定義は (define-?????? comp (? ?)) – コンストラクタは ????-comp – セレクタは comp-? と, comp-? • このcompを使い複素数の加減乗除comp+ compcomp* comp/ を定義し以下を実行してみる (comp+ (comp(comp* (comp/ (make-comp (make-comp (make-comp (make-comp 1 2) (make-comp 3 5 7) (make-comp 3 1 2) (make-comp 3 -5 10) (make-comp 4)) 4)) 4)) 3 4)) (+ 1+2i 3+4i) →4+6i (- 5+7i 3+4i) →2+3i (* 1+2i 3+4i) →-5+10i (/ -5+10i 3+4i)→ 1+2i と同等の計算 解答例 (define-struct comp (r i) (define (comp+ c1 c2) (make-comp (+ (comp-r c1) (comp-r c2)) (+ (comp-i c1) (comp-i c2)))) (define (comp- c1 c2) (make-comp (- (comp-r c1) (comp-r c2)) (- (comp-i c1) (comp-i c2)))) (define (comp* c1 c2) (make-comp (+ (* (comp-r c1) (comp-r c2)) (* -1 (comp-i c1) (comp-i c2))) (+ (
© Copyright 2024 ExpyDoc