sp-10. 構造体
1
金子邦彦
Scheme プログラミング)
URL: https://www.kkaneko.jp/pro/scheme/index.html
Schemeにおける構造体の定義方法と、コンストラク
タ・セレクタを用いたデータ操作の習得
学習内容の構成
1. 構造体の基本:複数のデータを1つにまとめ、新し
い型として名前を付ける仕組み
2. define-struct:構造体定義により自動生成されるコ
ンストラクタ(make-とセレクタ(属性取得)
3. 構造体の応用:構造体を引数とする関数の作成、構
造体のリスト処理、組み込み構造体(rectangular
による複素数計算
前提:Schemeの基本文法、リスト操作、関数定義
意義:複合データの抽象化によるプログラム設計力
向上
2
10-1 構造体,構造体の定義,構造体の
使用
3
複数のデータが集まって,1つ
のデータを構成
新しい型の名前が付いている
structure ともいう
構造体とは?
4
name
age
address
AddressNote
x
y
delta-x
delta-y
ball
例題1
例題2
例題3
例題4
型の名前
データの
集まり
構造体の例
5
6
define-struct ・・・ の機能
(define-struct ball (x y delta-x delta-y))
名前 属性の並び
上記のプログラムの実行によって
コンストラクタ: make-ball
セレクタ: ball-x, ball-y, ball-delta-x, ball-delta-y
が使えるようになる
7
コンストラクタとセレクタ
コンストラクタ
構造体の生成
(例) make-ball
セレクタ
属性値の取得
(例) ball-x, ball-y, ball-delta-x, ball-delta-y
10-2 パソコン演習
8
資料を見ながら,「例題」を行ってみる
各自,「課題」に挑戦する
自分のペースで先に進んで構いません
9
パソコン演習の進め方
DrScheme の起動
プログラム PLT Scheme → DrScheme
今日の演習では「Intermediate Student
に設定
Language
→ Choose Language
→ Intermediate Student
→ Execute ボタン
10
DrScheme の使用
ball 構造体(ball structure)を定義するプログラムを書く
ball は,x, y, delta-x, delta-y から構成する
define-struct 文を使用
位置
速度
x
y
delta-x
delta-y
11
例題1.ball 構造体
1. 次を「定義用ウインドウ」で,実行しなさい
入力した後に,Execute ボタンを押す
(define-struct ball
(x y delta-x delta-y))
次は,例題2に進んでください
(これは,例題2,3で使います)
12
「例題1.ball 構造体」の手順
ball 構造体を定義している
(実行結果は出ない)
13
(define-struct ball
(x y delta-x delta-y))
名前
属性の並び
(それぞれの属性にも名前がある)
14
ball 構造体
15
コンストラクタとセレクタ
(define-struct ball (x y delta-x delta-y))
名前 属性の並び
上記の定義によって
コンストラクタ: make-ball
セレクタ: ball-x, ball-y, ball-delta-x, ball-delta-y
が使えるようになる
例題2.ball の原点からの距離
ボールの座標値から,原点からの距離を求める関
distance-to-0 を作り,実行する
例題1の ball 構造体を使用
属性 x, y を取り出すために ball-x, ball-y (セレクタ)
を使う
位置
速度
x
y
delta-x
delta-y
16
1. 次を「定義用ウインドウ」で,実行しなさ
入力した後に,Execute ボタンを押す
(define-struct ball
(x y delta-x delta-y))
;; distance-to-0: ball -> number
;; to compute the distance of a ball to the origin
;; (distance-to-0 (make-ball 3 4 0 0)) = 5
(define (sqr x)
(* x x))
(define (distance-to-0 a-ball)
(sqrt
(+ (sqr (ball-x a-ball))
(sqr (ball-y a-ball)))))
2. その後,次を「実行用ウインドウ」で実行しなさい
次は,例題3に進んでください
(distance-to-0 (make-ball 3 4 0 0))
例題1と同じ
17
「例題2.ball の原点からの距離」の手順
まず,関数 sqr と関数
distance-to-0 を定義している
18
これは,
(distance-to-0 (make-ball 3 4 0 0))
と書いて,a-ball の値を
(make-ball 3 4 0 0) に設定しての実行
実行結果である「5」が
表示される
19
distance-to-0
5
入力
出力
a-ball の値:
(make-ball 3 4 0 0)
入力は ball 構造体
出力は数値
20
distance-to-0 の入力と出力
(define-struct ball (x y delta-x delta-y))
;; distance-to-0 : ball -> number
;; to compute the distance of a ball to the origin
;; (distance-to-0 (make-ball 3 4 0 0)) = 5
(define (sqr x)
(* x x))
(define (distance-to-0 a-ball)
(sqrt
(+ (sqr (ball-x a-ball))
(sqr (ball-y a-ball)))))
21
(define-struct ball (x y delta-x delta-y))
;; distance-to-0 : ball -> number
;; to compute the distance of a ball to the origin
;; (distance-to-0 (make-ball 3 4 0 0)) = 5
(define (sqr x)
(* x x))
(define (distance-to-0 a-ball)
(sqrt
(+ (sqr (ball-x a-ball))
(sqr (ball-y a-ball)))))
ball-x は,x の値の取得
ball-y は,x の値の取得
22
23
例題3.ステップ実行
関数 distance-to-0 (例題2)について,実行
結果に至る過程を見る
(distance-to-0 (make-ball 3 4 0 0)) から 5 に至る過
程を見る
DrScheme stepper を使用する
(distance-to-0 (make-ball 3 4 0 0))
= (sqrt (+ (sqr (ball-x (make-ball 3 4 0 0)))
(sqr (ball-y (make-ball 3 4 0 0)))))
= (sqrt (+ (sqr 3)
(sqr (ball-y (make-ball 3 4 0 0)))))
= ...
= (sqrt (+ 9
(sqr (ball-y (make-ball 3 4 0 0)))))
= (sqrt (+ 9
(sqr 4)))
= ...
= (sqrt (+ 9 16))
= (sqrt 25)
= 5
1. 次を「定義用ウインドウ」で,実行しなさい
Intermediate Student で実行すること
入力した後に,Execute ボタンを押す
(define-struct ball
(x y delta-x delta-y))
;; distance-to-0: ball -> number
;; to compute the distance of a ball to the origin
;; (distance-to-0 (make-ball 3 4 0 0)) = 5
(define (sqr x)
(* x x))
(define (distance-to-0 a-ball)
(sqrt
(+ (sqr (ball-x a-ball))
(sqr (ball-y a-ball)))))
(distance-to-0 (make-ball 3 4 0 0))
2. DrScheme を使って,ステップ実行の様子
確認しなさい Step ボタン,Next ボタンを使用)
理解しながら進むこと
次は,例題4に進んでください
例題2と同じ
24
「例題3.ステップ実行」の手
(distance-to-0 (make-ball 3 4 0 0)) から 5 に至る過程の概略
(distance-to-0 (make-ball 3 4 0 0))
= (sqrt (+ (sqr (ball-x (make-ball 3 4 0 0)))
(sqr (ball-y (make-ball 3 4 0 0)))))
= (sqrt (+ (sqr 3)
(sqr (ball-y (make-ball 3 4 0 0)))))
= ...
= (sqrt (+ 9
(sqr (ball-y (make-ball 3 4 0 0)))))
= (sqrt (+ 9
(sqr 4)))
= ...
= (sqrt (+ 9 16))
= (sqrt 25)
= 5
最初の式
実行結果
コンピュータ内部での計算
25
(distance-to-0 (make-ball 3 4 0 0)) から 5 に至る過程の概略
(distance-to-0 (make-ball 3 4 0 0))
= (sqrt (+ (sqr (ball-x (make-ball 3 4 0 0)))
(sqr (ball-y (make-ball 3 4 0 0)))))
= (sqrt (+ (sqr 3)
(sqr (ball-y (make-ball 3 4 0 0)))))
= ...
= (sqrt (+ 9
(sqr (ball-y (make-ball 3 4 0 0)))))
= (sqrt (+ 9
(sqr 4)))
= ...
= (sqrt (+ 9 16))
= (sqrt 25)
= 5
これは,
(define (distance-to-0 a-ball)
(sqrt
(+ (sqr (ball-x a-ball))
(sqr (ball-y a-ball)))))
a-ball (make-ball 3 4 0 0) で置き換えたもの
26
AddressNote 構造体を定義するプロ
グラムを書く
AddressNote は,name, age,
address から構成する
define-struct 文を使用
name
age
address
AddressNote
27
例題4.AddressNote 構造体
1. 次を「定義用ウインドウ」で,実行しなさい
入力した後に,Execute ボタンを押す
(define-struct AddressNote
(name age address))
次は,例題5に進んでください
(これは,例題5で使います)
28
「例題4.ball 構造体」の手順
AddressNote 構造体を
定義している
(実行結果は出ない)
29
(define-struct AddressNote
(name age address))
名前
属性の並び
(それぞれの属性にも名前がある)
30
AddressNote 構造体
31
コンストラクタとセレクタ
(define-struct AddressNote (name age address))
名前
属性の並び
上記のプログラムの実行によって
make-AddressNote
AddressNote-name, AddressNote-age,
AddressNote-address
が使えるようになる
AddressNote 構造体のリストから,名前
name)のリストを得る関数 select-name を作
り,実行する
例題3の AddressNote 構造体を使用
構造体1つ 1人分
構造体のリスト 複数人
属性 name を取り出すために AddressNote-name
(セレクタ)を使う
name
age
address
AddressNote
32
例題5.住所録
1. 次を「定義用ウインドウ」で,実行しなさ
入力した後に,Execute ボタンを押す
(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))
(select-name (rest a-list)))]))
2. その後,次を「実行用ウインドウ」で実行しなさい
次は,例題6に進んでください
(select-name (list
(make-AddressNote "Ken" 35 "Fukuoka")
(make-AddressNote "Bill" 30 "Saga")
(make-AddressNote "Mike" 28 "Nagasaki")))
例題4と同じ
33
「例題5.住所録」の手順
まず,関数 select-name
を定義している
34
ここでは,make-AddressNote
を使って,AddressNote 構造体のリスト
を作っている
実行結果である
(list "Bill" "Ken" "Mike)
が表示される
35
select-name
(list "Ken" "Bill" "Mike")
入力
出力
(list
(make-AddressNote "Ken" 35 "Fukuoka")
(make-AddressNote "Bill" 30 "Saga")
(make-AddressNote "Mike" 28 "Nagasaki"))
入力は AddressNote
構造体のリスト
出力はリスト
36
select-name の入力と出力
(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))
(select-name (rest a-list)))]))
37
(select-name (list
(make-AddressNote "Ken" 35 "Fukuoka")
(make-AddressNote "Bill" 30 "Saga")
(make-AddressNote "Mike" 28 "Nagasaki")))
select-name の入力は リスト
AddressNote 構造体のリストを作るために,
make-AddressNote(コンストラクタ) を並べている
38
select-name の実行では
DrScheme にすでに組み込み済みの構造体
rectangular を使って,複素数の計算を行ってみる
足し算 (1+2i) + (3+4i)
引き算 (5+7i) (3+4i)
かけ算 (1+2i) ×(3+4i)
割り算 (-5+10i) / (3+4i)
39
例題6.複素数の計算
DrScheme では make-rectangular を使用
実数部が x で,虚数部が y であるような複素数は,
(make-rectangular x y)
例: (make-rectangular 3 4)」は 3+4i
rectangular は,DrScheme に組み込み済みの
構造体
40
複素数の計算
よくある間違い
(+ (1+2i) (3+4i)) は,シンタックスエラー(文法エラー)
(+ (make-rectangular 1 2) (make-rectangular 3 4)) と書くべき
41
1. 次の式を「実行用ウインドウ」で,実行し
さい
(+ (make-rectangular 1 2)
(make-rectangular 3 4))
(- (make-rectangular 5 7)
(make-rectangular 3 4))
(* (make-rectangular 1 2)
(make-rectangular 3 4))
(/ (make-rectangular -5 10)
(make-rectangular 3 4))
次は,例題7に進んでください
42
「例題6.複素数の計算」の手順
43
例題7.複素数のべき乗
2つの値 theta n から (cosθ+ i sinθ)
n
を計算す
るプログラム myexp を作り,実行する
θはの単位はラジアン
(cosθ+ i sinθ)
n
の計算: expt を使用
expt は複素数にも使える
44
1. 次を「定義用ウインドウ」で,実行しなさい
入力した後に,Execute ボタンを押す
(define (myexp theta n)
(expt (make-rectangular (cos theta)
(sin theta))
n))
2. その後,次を「実行用ウインドウ」で実行しなさい
次は,課題に進んでください
(myexp 0.5236 1)
(myexp 0.5236 2)
(myexp 0.5236 3)
45
「例題7.複素数のべき乗」の手順
#iは「近似値という意味
46
「例題7.複素数のべき乗」の実行結果
10-3 課題
47
課題1
関数 distance-to-0 (授業の例題2)につい
ての問題
次の式を実行し,実行結果を報告せよ
1. (distance-to-0 (make-ball 1 2))
2. (distance-to-0 (make-ball (- 5 3) (- 4 6)))
3. (distance-to-0 (make-ball (* 2 3) (* 4 5)))
48
ball 構造体(授業の例題1)についての問題
ball のデータ a-ball x の値が 0 100 の間にある
ときに限り true を返し,範囲外 のときには false
返すような関数 in を作成し,実行結果を報告しなさ
但し,x = 0 あるいは x = 100 のときには false を返す
こと.
ヒント: 次の空欄を埋めなさい
(define-struct ball (x y delta-x delta-y))
(define (in a-ball)
(cond
[ ]
[ else ]))
49
課題2
AddressNote 構造体(授業の例題4)につい
ての問題
AddressNote のデータ a-person の氏名を取り出す
関数 get-name は,セレクタ AddressNote-name
を使って,次のように書ける.
(define (get-name a-person)
address-note-name a-person)
では,AddressNote のデータ a-person の年齢
(age)が20以上ならば「‘Adult」を,20未満な
ら「'Clild」を出力する関数を作成し,実行結果を
報告しなさい
50
課題3
AddressNote 構造体(授業の例題4)についての
問題
a-person の年齢が、ある年齢 an-age と一致していれ
ば名前(name)を、さもなければ,文字列 "none"
返す関数 check-by-age を作成し,実行結果を報告しな
さい
例えば
(check-by-age (make-address-note "Kunihiko
Kaneko" 35 "Hakozaki") 35) = "Kunihiko Kaneko"
(check-by-age (make-address-note "Kunihiko
Kaneko" 35 "Hakozaki") 30) = "none"
51
課題4
AddressNote 構造体(授業の例題4)につい
ての問題
AddressNote 構造体のリスト a-list と年齢 an-age
から,年齢が an-age に一致する人のリストを出
力する関数 selection-by-age を作成し,実行結果
について報告しなさい
例えば
(selection-by-age
(list (make-address-note Kunihiko Kaneko” 35 “Hakozaki”)
(make-address-note “Taro Tanaka” 34 “Kaizuka”)
(make-address-note Hanako Saito” 35 “Tenjin”)) 35)
= (list (make-address-note Kunihiko Kaneko” 35 “Hakozaki”)
(make-address-note “Hanako Saito” 35 “Tenjin”))
52
課題5
AddressNote 構造体のリストから,年齢(age)
平均を求める関数 avg-age を作り,実行結
果を報告しなさい
53
課題6
さらに勉強したい人への
補足説明資料
ドモアブルの定理
54
n = 1, 2, …, 20 について,(cosθ+ i sinθ)
n
cos
+ i sin を計算するプログラム f1, f2 を書
結果は,長さ20のリストとして得る
近似値を使った計算を繰り返すと,誤差が積み重な
ことを確かめる
(cosθ+ i sinθ)
n
は,(cosθ+ i sinθ)の値(これは
近似値)を使っての計算
55
ドモアブルの定理
1. 次を「定義用ウインドウ」で,実行しなさい
入力した後に,Execute ボタンを押す
(define (f1 theta n)
(cond
[(= n 0) empty]
[else (cons (expt (make-rectangular (cos theta)
(sin theta))
n)
(f1 theta (- n 1)))]))
(define (f2 theta n)
(cond
[(= n 0) empty]
[else (cons (make-rectangular (cos (* n theta))
(sin (* n theta)))
(f2 theta (- n 1)))]))
56
「ド・モアブルの定理」の手順 (1/2)
2. その後,次を「実行用ウインドウ」で実行しなさい
(f1 0.05 20)
(f2 0.05 20)
57
「ド・モアブルの定理」の手順 (2/2)
58
「ド・モアブルの定理」の実行結果
> (f1 0.05 20)
(list
#i0.5403023058681402+0.8414709848078972i
#i0.5816830894638839+0.8134155047893742i
#i0.6216099682706648+0.7833269096274839i
#i0.6599831458849826+0.7512804051402932i
#i0.6967067093471658+0.7173560908995231i
#i0.731688868873821+0.6816387600233345i
#i0.7648421872844886+0.6442176872376914i
#i0.796083798549056+0.6051864057360399i
#i0.8253356149096784+0.5646424733950356i
#i0.8525245220595062+0.5226872289306593i
#i0.8775825618903731+0.47942553860420317i
#i0.9004471023526772+0.43496553411123035i
#i0.9210609940028853+0.3894183423086506i
#i0.939372712847379+0.34289780745545145i
#i0.9553364891256061+0.29552020666133966i
#i0.968912421710645+0.24740395925452296i
#i0.9800665778412417+0.19866933079506124i
#i0.9887710779360424+0.14943813247359924i
#i0.9950041652780258+0.09983341664682816i
#i0.9987502603949663+0.04997916927067833i)
> (f2 0.05 20)
(list
#i0.5403023058681398+0.8414709848078965i
#i0.5816830894638836+0.8134155047893737i
#i0.6216099682706644+0.7833269096274834i
#i0.6599831458849822+0.7512804051402927i
#i0.6967067093471654+0.7173560908995228i
#i0.7316888688738209+0.6816387600233341i
#i0.7648421872844885+0.644217687237691i
#i0.7960837985490559+0.6051864057360396i
#i0.8253356149096783+0.5646424733950354i
#i0.8525245220595057+0.5226872289306592i
#i0.8775825618903728+0.479425538604203i
#i0.9004471023526769+0.43496553411123023i
#i0.9210609940028851+0.3894183423086505i
#i0.9393727128473789+0.34289780745545134i
#i0.955336489125606+0.29552020666133955i
#i0.9689124217106447+0.24740395925452294i
#i0.9800665778412416+0.19866933079506122i
#i0.9887710779360422+0.14943813247359922i
#i0.9950041652780258+0.09983341664682816i
#i0.9987502603949663+0.04997916927067833i)
(cosθ+ i sinθ)
n
cos nθ+ i sin nθ
値は、ほぼ一致しているが,わずかに食い違う
不一致
59
実行結果の比較
ド・モアブルの定理は:
なお,i は虚数単位
( )
n
i
sincos +
nin sincos +=
60
ド・モアブルの定理
(cosθ+ i sinθ)
2
= cos
2
θ- sin
2
θ+ 2i cosθsin θ
= cos2θ+ i sin2θ
(cosθ+ i sinθ)
3
= (cosθ+ i sinθ)
2
(cosθ+ i sinθ)
= (cos2θ+i sin2θ) (cosθ+ i sinθ)
= cos2θcosθ- sin2θsinθ
+ i (cos2θsinθ- sin2θcosθ)
= cos (2θ+θ) + i sin (2θ+θ)
= cos3θ+ i sin3θ
(以下同様に考える.数学的帰納法で証明できる)
61
(cosθ+ i sinθ)
n
= cos + i sin
z
1
= x
1
+ i y
1
z
2
= x
2
+ i y
2
のとき
z
1
z
2
= (x
1
+ i y
1
)
(x
2
+ i y
2
)
= x
1
x
2
+ x
1
i y
2
+ i y
1
x
2
+ i y
1
i y
2
= x
1
x
2
- y
1
y
2
+ i (x
1
y
2
+ y
1
x
2
)
実数部
虚数部
62
複素数の掛け算
本来なら「 (cosθ+ i sinθ)
n
= cos nθ+ i sin 」が
成り立つはず
しかし,ここで実行される計算は,あくまでも
似計算
sin, cos, log 等の計算結果は,近似値でしか得られない
例:「(sin 0.1)」を実行すると
#i0.09983341664682816
計算を繰り返す(つまり、計算結果を使った計
算)と,誤差が積み重なる
(cosθ+ i sinθ)
n
は,(cosθ+ i sinθ)の値(これは近似
値)を使っての計算
63
実際の計算では