C言語入門

1
C言語入門
第4週 補足資料
プログラミング言語Ⅰ(実習を含む。),
計算機言語Ⅰ・計算機言語演習Ⅰ,
情報処理言語Ⅰ(実習を含む。)
2
閏年の判定を例に
条件判定の簡略化
3
閏年(leap year)の判定
• 3重のif-else文による実装
• 条件1を実装
真
偽
4で割れる
if (year % 4 == 0) {
leap_year_flag = 1;
} else {
leap_year_flag = 0;
}
閏年
平年
4
閏年(leap year)の判定
• 3重のif-else文による実装
• 条件2を追加
真
偽
4で割れる
if (year % 4 == 0) {
if (year % 100 == 0) {
leap_year_flag = 0;
} else {
leap_year_flag = 1;
}
} else {
leap_year_flag = 0;
}
真
偽
100で割れる
平年
閏年
平年
閏年判定の整理
• 3重のif-else文による実装
• 条件3を追加して完成
is_leap_year_1_1.c
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
leap_year_flag = 1;
} else {
leap_year_flag = 0;
}
} else {
leap_year_flag = 1;
}
} else {
leap_year_flag = 0;
}
ごちゃごちゃしていて
分かり難い
もっと分かり易く
書けないか?
真
真
偽
100で割れる
真
偽
400で割れる
閏年
偽
4で割れる
平年
閏年
平年
5
閏年判定の整理
• 3重のif-else文による実装
平年
• 平年フラグを外に追い出してみる
leap_year_flag = 0;
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
leap_year_flag = 1;
} else {
leap_year_flag = 0;
}
} else {
leap_year_flag = 1;
}
} else {
leap_year_flag = 0;
}
真
偽
4で割れる
真
偽
100で割れる
真
偽
400で割れる
閏年
平年
閏年
平年
6
閏年判定の整理
• 3重のif-else文による実装
平年
• 平年フラグを外に追い出してみる
leap_year_flag = 0;
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
leap_year_flag = 1;
}
} else {
leap_year_flag = 1;
}
}
少し短くなったが
対称性が崩れていて
少し美しくない
真
偽
4で割れる
真
偽
100で割れる
真
偽
400で割れる
閏年
平年
閏年
平年
7
閏年判定の整理
• 3重のif-else文による実装
閏年
• 逆に閏年フラグを外に追い出してみる
leap_year_flag = 1;
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
leap_year_flag = 1;
} else {
leap_year_flag = 0;
}
} else {
leap_year_flag = 1;
}
} else {
leap_year_flag = 0;
}
真
偽
4で割れる
真
偽
100で割れる
真
偽
400で割れる
閏年
平年
閏年
平年
8
閏年判定の整理
• 3重のif-else文による実装
閏年
• 閏年フラグを外に追い出してみる
leap_year_flag = 1;
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
;
} else {
leap_year_flag = 0;
}
} else {
leap_year_flag = 0;
}
真
偽
4で割れる
真の場合の処理が
空になっているのも
格好が良くない
やはり
対称性が崩れていて
少し美しくない
真
偽
100で割れる
真
偽
400で割れる
閏年
平年
閏年
平年
9
閏年判定の整理
平年
真
• 3重のif-else文による実装
• 例外時に結果を随時上書きしてみる
leap_year_flag = 0;
if (year % 4 == 0) {
leap_year_flag = 1;
if (year % 100 == 0) {
leap_year_flag = 0;
if (year % 400 == 0) {
leap_year_flag = 1;
} else {
leap_year_flag = 0;
}
} else {
leap_year_flag = 1;
}
} else {
leap_year_flag = 0;
}
偽
4で割れる
閏年
真
偽
100で割れる
平年
真
平年
偽
400で割れる
閏年
閏年
平年
10
閏年判定の整理
平年
真
• 3重のif-else文による実装
• 例外時に結果を随時上書きしてみる
leap_year_ex2_1.c
偽
4で割れる
閏年
真
leap_year_flag = 0;
if (year % 4 == 0) {
leap_year_flag = 1;
if (year % 100 == 0) {
leap_year_flag = 0;
if (year % 400 == 0) {
leap_year_flag = 1;
}
}
}
短く対称性も取れていて
比較的美しい?
偽
100で割れる
平年
真
平年
偽
400で割れる
閏年
閏年
平年
11
12
西暦と閏年の集合
• 包含の関係
X
• 西暦⊃X⊃Y⊃Z
Y
Z
閏年判定の整理
• 分解してみると
100 or 400で
割れる場合
400で
割れる場合
1
真
2
偽
真
4で割れる
平年
閏年
3
偽
真
100で割れる
平年
偽
400で割れる
閏年
1
2
3
100 or 400で
割れない場合
400で
割れない場合
13
14
閏年判定の整理
• 展開したif文による実装
• 100 と 400 は 4 の倍数
400 は 100 の倍数
である点に着目すると
ネスト(入れ子)を展開可能
if文のロジック以外に
各条件間の数学的関係による
暗黙の前提が分からないと
読めないコードになるかも?
そういう意味では
良くないコード?
適切なコメント等が必要?
leap_year_ex2_1.c
leap_year_flag = 0;
if (year %
4 == 0) {
leap_year_flag = 1;
}
if (year % 100 == 0) {
leap_year_flag = 0;
}
if (year % 400 == 0) {
leap_year_flag = 1;
}
year %
4 != 0 および
year % 100 != 0 の場合には影響を与えない
year %
4 != 0 の場合には影響を与えない
15
閏年判定の整理
• 展開したif文による実装
• 前頁のコードから省略可能な { } を省略
leap_year_ex2_2.c
int leap_year_flag =
if (year %
4 == 0)
if (year % 100 == 0)
if (year % 400 == 0)
0;
leap_year_flag = 1;
leap_year_flag = 0;
leap_year_flag = 1;
実行する処理が
1つだけの場合は
省略出来る。
短くすっきりしていて見易い。
しかし、定義通りになっていないため、
100で割り切れた場合、4で割り切れなかった場合に影響を与えない
400で割り切れた場合、4,100で割り切れなかった場合に影響を与えない
事がわかっていないと混乱するかもしれない?
16
閏年判定の整理
• 3重の条件演算子による実装
• 条件1を実装
leap_year_flag = year % 4 == 0 ? 1 : 0;
• 条件2を追加
leap_year_flag = year % 4 == 0 ? (year % 100 == 0 ? 0 : 1) : 0;
• 条件3を追加して完成
leap_year_ex3_1.c
leap_year_flag = year % 4 == 0 ? (year % 100 == 0 ? (year % 400 == 0 ? 1 : 0) : 1) : 0;
条件演算子は便利だけど
下手に多用すると読み難くなるので注意
ネスト(入れ子)が深くなると
if-else文以上に読み難い
17
閏年判定の整理
• 3重の条件演算子による実装
• 条件1を実装
leap_year_flag = year % 4 == 0 ? 1 : 0;
leap_year_ex2_2.c と同じ方法で
入れ子の条件を展開する例
• 条件2を追加
leap_year_flag = year %
4 == 0 ? 1 : 0;
leap_year_flag = year % 100 == 0 ? 0 : leap_year_flag;
• 条件3を追加して完成
leap_year_ex3_2.c
leap_year_flag = year %
4 == 0 ? 1 : 0;
leap_year_flag = year % 100 == 0 ? 0 : leap_year_flag;
leap_year_flag = year % 400 == 0 ? 1 : leap_year_flag;
これなら leap_year_ex2_2.c の方が読み易いかも?
18
論理演算
• AND, OR, NOT
• 条件を論理演算する場合に使う
• AND:
• OR:
• NOT:
X && Y
X || Y
!X
X
Y
X && Y
X || Y
FALSE
FALSE
0
0
FALSE
TRUE
0
1
TRUE
FALSE
0
1
TRUE
TRUE
1
1
!X
1
0
19
閏年判定の整理
• 西暦全体の集合のうち
• X:
4で割り切れる集合(Y,Z を包含)
• Y: 100で割り切れる集合(Z を包含)
• Z: 400で割り切れる集合
X
Y
Z
!Y
Y
20
閏年判定の整理
• 論理演算で
考えると
!X || (Y && !Z)
X
Y
!X
(X && !Y) || Z
X && !Y
C言語の演算子の優先度的には
X && !Y || Z でも良いが
( ) を付けた方が理解が容易かつ
誤解の余地が生じない
Y && !Z
Z
Z
21
閏年判定の整理
• if-else文と論理演算による実装
leap_year_ex4_1.c
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
leap_year_flag = 1;
} else {
leap_year_flag = 0;
}
• 論理演算による実装
leap_year_ex4_2.c
leap_year_flag = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
22
閏年判定の整理
• 全ての条件が網羅出来ているか?
• 条件に漏れはないか?
year % 100 == 0
year % 4 == 0
year % 100 != 0
year % 100 == 0
year % 4 != 0
year % 100 != 0
× の個所は
条件を満たす
year が存在しない
year % 400 == 0
閏年
year % 400 != 0
平年
year % 400 == 0
×
year % 400 != 0
閏年
year % 400 == 0
×
year % 400 != 0
×
year % 400 == 0
×
year % 400 != 0
平年
2_1, 2_2, 3_2 の例で year % 100 != 0 や year % 400 != 0 が
考慮不要なことが確認出来る。
23
閏年判定の整理
• 全ての条件が網羅出来ているか?
今の場合前の条件への追加条件なので
• 条件に漏れはないか? 全ての組み合わせを考える必要はない
year % 100 == 0
year % 4 == 0
year % 100 != 0
year % 100 == 0
year % 4 != 0
year % 100 != 0
year % 400 == 0
閏年
year % 400 != 0
平年
year % 400 == 0
year % 400 != 0
閏年
year % 400 == 0
year % 400 != 0
year % 400 == 0
year % 400 != 0
平年
24
閏年判定の整理
• 全ての条件が網羅出来ているか?
• 条件に漏れはないか?
year % 100 == 0
year % 100 != 0
× の個所は
条件を満たす
year が存在しない
year % 4 == 0
year % 4 != 0
year % 400 == 0
閏年
×
year % 400 != 0
平年
×
year % 400 == 0
×
×
year % 400 != 0
閏年
平年
長くなる場合は横に展開しても良い
25
閏年判定の整理
• 全ての条件が網羅出来ているか?
• 条件に漏れはないか?
× の個所は
条件を満たす
year が存在しない
year % 4 == 0
year % 100 == 0
year % 400 == 0
閏年
year % 4 == 0
year % 100 == 0
year % 400 != 0
平年
year % 4 == 0
year % 100 != 0
year % 400 == 0
×
year % 4 == 0
year % 100 != 0
year % 400 != 0
閏年
year % 4 != 0
year % 100 == 0
year % 400 == 0
×
year % 4 != 0
year % 100 == 0
year % 400 != 0
×
year % 4 != 0
year % 100 != 0
year % 400 == 0
×
year % 4 != 0
year % 100 != 0
year % 400 != 0
平年
この書き方は Excel 等で sort して調べるのに向いている
26
閏年判定の整理
• 真偽値による文字列の切り替え
is_leap_year_template.c
if (leap_year_flag) {
printf("%d is leap year.\n", year);
} else {
printf("%d is not leap year.\n", year);
}
printf("%d is%s leap year.\n", year, leap_year_flag ? "" : " not");
leap_year_flag の値を見て直接
%s に埋め込む文字列を変更
27
同じ処理でも実装は様々
• 同じ処理でも複数の異なる実装が可能
• 効率や可読性等、何を重視するか?
• 読み易い、理解しやすいコードを推奨