Document

3. 条件式、
データのバリエーション
プログラミング論I
今日の内容
• 条件式を使い,条件に応じた計算を行う
– 比較演算 (<,<=,>,>=,=)
– 条件式のキーワード: cond, else
– 値の性質の判定(奇数,偶数:odd?, even?)
– 論理演算 (and, or, not)
• データのバリエーションとその区別
– 数値、ブール値、文字列、シンボルおよびその判定
条件式
条件を判断するプログラム
• 異なる状況には,異なる方法で処理するプログラム
– ゲームプログラム:
• 物体の速度や位置はある範囲内か?
– エンジン制御プログラム
• どういう状況(温度,気圧など)であれば燃料の噴射量
を増減するか?
• ある条件の成立を判定して処理する道具立てが必要
– 真(true) または偽(false)の値を持つデータのクラス:
• ブール値(Boolean) (または真偽値、真理値)
– 条件を評価する式(ブール値を返す)
– ブール値(真偽)に依存して異なる値を計算する式
比較演算 - 比較演算子の例 値を比較する演算子: =
Scheme式 数式
(= x y)
x=y
(< x y)
x<y
(> x y)
x>y
(<= x y) x ≦ y
(>= x y) x ≧ y
<
>
<=
>=
意味
x は y と等しい
x は y より小さい
x は y より大きい
x は y 以下
x は y 以上
注意:Schemeでは「≦」,「≧」を使うことはできない
– キーボードのキーで入力できない
C言語の場合
値を比較する演算子: ==
Cの条件式
(x == y)
(x < y)
(x > y)
(x <= y)
(x >= y)
数式
x=y
x<y
x>y
x≦y
x≧y
<
>
<=
意味
x は y と等しい
x は y より小さい
x は y より大きい
x は y 以下
x は y 以上
>=
比較演算の実行例
比較演算の結果値は、条件が成り立てば true,
成り立たなければ false となる
比較演算の実行例
C言語の場合
printf(“%d”,(100==50));
0
printf(“%d”,(100==100));
1
printf(“%d”,(5<3));
0
printf(“%d”,(5<5));
0
printf(“%d”,(5<7));
1
比較演算の結果値は、条件が成り立てば 1,
成り立たなければ 0 となる
条件の判断 - 比較演算以外の例 例:奇数か(odd?)、偶数か(even?)
• odd? の意味:奇数ならば true (さもなければ false)
• even? の意味:偶数ならば true (さもなければ false)
※ 条件を判断する演算子や関数名の末尾は ? を使うことが多い
;; odd?: number -> T/F
(define (odd? n)
(= (remainder n 2) 1)
)
;; even?: number ->T/F
(define (even? n)
(= (remainder n 2) 0)
)
int odd(int n){ return n%2; }
int even(int n){ return (n%2==0); }
論理演算
単純な真偽判定を組合わせ、より複雑な論理的演算を行う.
• (and A B) A かつ B
A B AかつB AまたはB Aでない
例: (and (= x y) (< y z)) T T
• (or A B)
A または B
例: (or (= x y) (< x z))
• (not A)
Aでない
T
T
F
T F
F
T
F
F T
F
T
T
F F
F
F
T
例: (not (= x y))
計算例:
(and (= 5 5) (< 5 6)) =(and true true) = true
(and (= 5 5) (< 5 5)) =(and true false) = false
論理演算(C言語の場合)
単純な真偽判定を組合わせ、より複雑な論理的演算を行う.
• (and A B) A かつ B
C言語の場合
例: (and (= x y) (< y z))
• (or A B)
A または B
例: (or (= x y) (< x z))
• (not A)
Aでない
例: (not (= x y))
(x==y)&&(y<z)
(x==y)||(y<z)
!(x==y)
計算例:
(and (= 5 5) (< 5 6)) =(and true true) = true
(and (= 5 5) (< 5 5)) =(and true false) = false
条件によって結果を変える式
条件式(Conditional Expression):条件により結果を変える式
例:貯蓄口座~貯蓄残高が高いほど高い利率 (本の4.3節)
– $1,000以下の預金に対しては4%
– $5,000までの預金に対しては4.5%
(define
amount 3000) ;; が先に定義されてあると,
– $5,000より多額の預金に対しては5%を払う
;; cond式は,0.045を返す
例: ここで amount は預金額の変数
(cond [(<= amount 1000) 0.040]
[(<= amount 5000) 0.045]
[(> amount 5000) 0.050])
例
(define amount 3000)
;; amountには、3000という値が割り当てられる
(cond [(<= amount 1000) 0.040]
;; (<= 3000 1000) ? → false
[(<= amount 5000) 0.045]
;; (<= 3000 5000) ? → true 0.045が返る
[(> amount 5000) 0.050])
条件式
条件式の一般形
(cond
(cond
[question answer]
[question answer]
...
または
...
[question answer])
[else answer])
...部分:任意行数のcond行(cond節とも呼ばれる)
各cond行: 条件部(question)と結果部(answer)からなる
• 条件部は変数を含む条件述語である;
• 結果部は条件述語の成立時に結果を計算する式
条件式の例
(cond
[(<= amount 1000) 0.040]
①
[(<= amount 5000) 0.045]
②
[(> amount 5000) 0.050])
③
1つの
大きな式
amount の値によって結果値が変わる
複数の cond節の「条件部」は上から順に判定
• 上の例では①,②,③の順に判定が行われる
①が成り立てば,②,③は判定されない
②が成り立てば,③は判定されない
「条件」の並んだ順序に意味がある
条件の判定順序
(cond
一番上の行の条件
の判定から開始
[(<= amount 1000)
[(<= amount 5000)
[(>
amount 5000)
yes
no
yes
no
yes
0.040]
0.045]
0.050])
この式でこの判定が no になることはあり得ない→他の書き方もある
else の使用例
(cond
[(<= amount 1000) 0.040]
[(<= amount 5000) 0.045]
[else 0.050])
(cond
[(<= amount 1000) 0.040]
[(<= amount 5000) 0.045]
[(> amount 5000) 0.050])
この2つは同じ計算を行う
else:「前の条件がどれも成り立たなければ」という意味
(使うときは cond 節 の並びの最後)
★ else を使う方が分かりやすければそうする
C言語の場合
(cond
[(<= amount 1000) 0.040]
[(<= amount 5000) 0.045]
[else 0.050])
(cond
[(<= amount 1000) 0.040]
[(<= amount 5000) 0.045]
[(> amount 5000) 0.050])
この2つは同じ計算を行う
if (amount<=1000)
return 0.040;
else if (amount<=5000)
return 0.045;
else
return 0.050;
if (amount<=1000)
return 0.040;
else if (amount<=5000)
return 0.045;
else if (amount > 5000)
return 0.050;
条件の判定順序
(cond
一番上の行の条件
の判定から開始
[(<= amount 1000)
[(<= amount 5000)
[else
yes
no
yes
no
0.040]
0.045]
0.050])
else が評価されると必ずその結果部が選択される
条件式での「条件」として書けるもの
結果が true か false となるもの。例えば:
• 比較演算 (<,<=,>,>=,= ) の式
例: (<= amount 1000), (<= amount 5000),
(> amount 5000), (= (remainder year 400) 0)
• 奇数,偶数のような値の性質判定
例: (odd? k), (number? k) 後述
• trueまたはfalse を結果値とするような関数
例: (is-child? a)
• and, or, not を使った条件の組み合わせ
例:(or (= m 1) (= m 2)), (not (= a 0))
など
但し is-child?
が例えば次のよう
に定義されている
こと
(define (is-child? age)
(< age 12))
練習
• 福岡市営地下鉄の料
金は距離で決まる。距
離をあらわす変数が
distance だとして、
対応する料金を求め
る cond 式を書け。
(cond
[(<= distance 3) 200]
.... )
Scheme式の中では
単位は書かない
(暗黙に仮定)
距離 (distance)
料金
3km まで
200円
3km 超7kmまで
250円
7km 超11kmまで
290円
11km 超15kmまで 320円
15km 超19kmまで 340円
19km 超20kmまで 360円
条件式を使った条件関数
利率を求める関数:条件関数(Conditional Function)
先の例預金額を受け取りその金額に対する利率を返す。
→ 条件式を用い関数定義を定式化
引数の値によって結果が変わる ⇒ 条件関数と呼ばれる
(define (interest-rate
(cond
[(<= amount 1000)
[(<= amount 5000)
[(> amount 5000)
amount)
0.040]
0.045]
0.050]))
amountの
値は
引数として
与えられる
このような条件関数はどのようにして開発するか?
プログラム設計法:条件関数
条件関数の開発法は? 前述のプログラム設計法を拡張
•
Purpose:プログラムの目的を理解、機能の概要を記述
•
•
Contract, Header, Statement:
データ解析(Data Analysis)を導入:
– 問題が異なる状況を扱う場合→それらのすべてを識別する必要
•
Example:プログラムの振る舞いを「例」で記述.
•
•
状況ごとに最低でもひとつの関数例
Definition:プログラムの定義を具体化する
•
異なる状況の数だけの節をもったcond-式
– 各状況を特徴づける条件を定式化
– 適切な区別:あるcond節の条件がtrue →それ以前の条件はfalse
•
•
各cond-節に対して関数が何を生成すべきかを決定
Test:検査を通じた誤り(エラー)の発見
条件関数の開発
残高 amount から利率を求める関数 interest-rate
– 残高が $1000 以下ならば:利率 4%
– 残高が $5000 以下ならば:利率 4.5%
– 残高が $5000より多ければ:利率 5%
• Purpose
;; interest-rate : number -> number
;; 与えられた金額 amount に対する利率を求める
(define (interest-rate amount) ...)
• 複数の状況を扱う → 場合分けのためのデータ解析
データ解析
この例での良い方法:数直線上の区間への対応付
利率
0.05
0.045
0.04
0
1000
5000
残高
条件関数の開発:Example
残高 amount から利率を求める関数 interest-rate
– 残高が $1000 以下ならば: 利率 4%
– 残高が $5000 以下ならば: 利率 4.5%
– 残高が $5000より多ければ:利率 5%
Example: 状況ごとに少なくともひとつの関数例
(= (interest-rate 1000) .040)
(= (interest-rate 5000) .045)
(= (interest-rate 8000) .050)
注: 0.040 を .040 と書いても同じ
条件関数の開発:本体式の定義
3つの異なる状況を扱う
→ 3つの場合を区別するcond節を持つ cond 式
(define (interest-rate
(cond
[(<= amount 1000)
[(<= amount 5000)
[(> amount 5000)
amount)
...] amount ≦ 1000 のとき
...] 1000<amount≦5000のとき
...] )) 5000 >amount のとき
まず3つの条件節内の条件部を考える
条件関数の開発:本体式の定義
3つの異なる状況を扱う
→ 3つの場合を区別するcond節を持つ cond 式
(define (interest-rate amount)
(cond
[(<= amount 1000) .040] amount ≦ 1000 のとき
[(<= amount 5000) .045] 1000<amount≦5000のとき
[(> amount 5000) .050] )) 5000 >amount のとき
次に3つの条件節内の結果値を考える
(interest-rate 1500) から 0.045 が得られる過程
(interest-rate 1500) 最初の式
interest-rate の本体
= (cond
(cond
[(<= amount 1000) .040]
[(<= 1500 1000) .040]
[(<= amount 5000) .045]
[(<= 1500 5000) .045]
[(> amount 5000) .050])
[(> 1500 5000) .050]) にamount = 1500 の置き換え
= (cond
(<= 1500 1000) → false
[false .040]
[(<= 1500 5000) .045]
[(> 1500 5000) .050])
[false 0.040] → 消える
= (cond
[(<= 1500 5000) .045]
[(> 1500 5000) .050])
= (cond
(<= 1500 5000) → true
[true .045]
コンピュータ内部での計算
[(> 1500 5000) .050])
= 0.045 実行結果
条件関数の開発:Test
定義完成後は事前に考えた関数例でテストすること!
• Example として場合ごとに少なくともひとつ関数例を
考えた → 少なくとも以下の結果が true となるはず
例1. (= (interest-rate 1000) .040)
例2. (= (interest-rate 5000) .045)
例3. (= (interest-rate 8000) .050)
練習 月の日数
• 月 month を受け取り(数字)、その日数を
求める関数 easy-get-num-of-days を
作る。ただし閏年のことは考えない。
– まず月ごとに12通りに分けて考えてみよ。
– 日数は3通りしかない。3つのcond節を持つ cond 式
で考えよ。(比較演算と論理演算を組み合わせる)
記号情報
記号情報
Schemeの記号情報 (データは数値だけではない)
–シンボル: ' が前置したキーボード文字の並び
• 'the 'dog 'ate 'a 'chocolate 'cat! 'two^3 など
• 注:( ) " , などは使えない、' がなければ変数(値を持つ)。
• 象徴的に何かを表す不可分なデータ
–目的は名字や名前、職業的肩書、命令、通知などの記号的表現
–文字列: 2つの " で囲まれたキーボード文字の並び
• "the dog" "isn't" "made of" "chocolate" "two^3"など
• 文字のならび。シンボルと異なり分割できる複合データ
–画像
• DrRacketは画像をサポート:
–テキストの図7は惑星の写真を操作する関数の冒頭の例
種々のデータの属性判定、比較演算
• データの属性判定
number? boolean? symbol? string? など
• 数値同士の比較
< <= = >= > など
• シンボル同士の比較
symbol=?
• 文字列同士の比較
string<? string<=? string=? string>=? など
データの属性判定、比較演算の例
属性判定の例:
比較演算の例:
(number? 5)
= true
(boolean? false)
= true
(symbol? 'hello)
= true
(symbol? "hello")
= false
では以下はどうだろう?
(string? 'hello)
(string? "hello")
(< 3 4) = true
(= 3 4) = false
(symbol=? 'hello 'bye)
= false
(string=? "hello" "he")
= false
(string>? "hello" "bye")
= true
では以下は? (ヒント:辞書での順番)
(string<? "he" "hello")
(string<? "hello" "he")
練習問題:シンボルの条件関数
• 次の4種のシンボルに対応した「返答」を返すよ
うな関数 reply を作る。
入力シンボル
→ 答え
'GoodMorning
→ 'Hi
'HowAreYou
→ 'Fine
'GoodAfternoon → 'NeedANap
'GoodEvening
→ 'BoyAmITired
(これ以外の入力に対しては,実行エラー)
C言語の場合,シンボル型がない
const
const
const
const
int
int
int
int
GoodMorning=0;
HowAreYou=1;
GoodAfternoon=2;
GoodEvening=3;
などのように,最初に定義して,入力は,その定義した値にする
か,文字列として扱う.
#include <string.h>
char grt[12];
scanf_s(“%12s”, grt,12);//VisualStudio
scanf(“%s”, grt);
// 標準C言語処理系
if (strcmp(grt, “GoodMroning”)==0)
printf(“Hi\n”);
これまでの Scheme
Scheme も自然言語同様に語彙と文法を持つ
語彙
• 変数:関数と値の名前
• キーワード:句読点のようなもの
– define, cond, else (変数には使用不可)
• 定数
– 数: 5, -5, 0.5 など
– ブール値: true, false
– 記号的:シンボル、文字列
文法:語彙を用いてプログラム(式
と定義)を作る規則
定義
• 関数定義
– (define (関数名 変数並び) 式)
• 変数定義
– (define 変数名 式)
• プリミティブ演算:Scheme が最初
から提供している基本的な関数 式
– 四則演算子:+, -, *, /
• 変数
– 比較演算子:<, <=, >, >=, =
• 定数
– 奇数か偶数かの判定:odd?, even?
– 論理演算子:and, or, not
• プリミティブ演算の適用
– その他の演算子:remainder,
• 定義した関数の適用
quotient, max, min, abs, sqrt, expt,
log, sin, cos, tan, asin, acos, atanなど • 条件式
発展課題
課題1. うるう年の判定
• 西暦年 year から,うるう年であるかを求める関
数 is-leap? を作る
• うるう年の判定法
– 4の倍数の年 ならうるう年、
– 但し, 100の倍数の年で400の倍数でない年は うるう
年ではない (4の倍数だが例外)
• 2005年はうるう年でない(4の倍数でない)
• 2004年はうるう年(4の倍数)
• 2000年はうるう年(100の倍数で例外にも当てはまらない)
• 1900年はうるう年でない(4の倍数で100の倍数だが400の
倍数でないので例外に相当)
– 年がある数の倍数かを調べるため remainderを使う
課題2. 曜日を求める
• 西暦の年,月,日から曜日を求める公式である、
ツエラーの公式のプログラム zellar を作る。
– ツエラーの公式では「1年の起点 を3月とし、月は3
月から14月まである」と考えている。(うるう年がある
ので,1年の起点を3月とする方が計算が簡単)
– 1月,2月は,前年の13,14月と考え、月は3から14
– 計算された曜日は「数字」で次の意味になる.
0
1
2
3
4
5
6
日曜
月曜
火曜
水曜
木曜
金曜
土曜
ツエラーの公式(数式表現)
[(y+[y/4]+[y/400]-[y/100]+[(13m+8)/5]+d) mod 7]
ここで y: 年、 d: 日、m: 3月を起点とする月で値
は3から14(1月,2月は前年の13,14月と考える)
• この計算の結果値が0なら日曜,1なら月曜・・・
• 式中 [...] とあるのは,小数点以下切り捨て
– 切捨て計算には,quotientを使う
• modは剰余を計算。例えば 2003 mod 4 は 3
C言語では,y, m, dがint型の時,
(y+(y/4)+(y/400)-(y/100)+(13*m+8)/5+d) % 7