コンピュータサイエンス概論 #3(3週目)
コンピュータサイエンスの学部で最初の学年に、基本的に最初に取る授業の内容の紹介の連載3回目です。プログラミングスクールのアフィリエイトで誘導したい人が「大学でCS専攻してもプログラム書けない」と言ってたりしますが、CSの最初の授業でどの程度の知識や実装力を最低限として習うか紹介してみようかな、という感じです。
また、プログラミングスクールよりはやや深めにコメントしていくので理解を確認して頂ければ。
振り返り
前回は
で条件文辺りまでいきました。その過程で型がどんなものか、数値の上限加減などの話がありました。
Reading課題
下記の6.1~6.3まで読んだ上で授業を受けるのが基本です。
http://greenteapress.com/thinkcpp/thinkCScpp.pdf
これ、無料公開してるんですね。
Lecture 6
論理演算子
AND(&, &&), OR(|, ||)
NOT(!)
AND, ORは2つのオペランドを必要とするbinary operator(2項演算子= a && bのように2つ対象が必要)でNOTはNAGATEとも言われ、unary operator(単項演算子= ex). !flagのように単独対象の前に付ける)と言われます。
言語によってあったりなかったりしますが、 &の場合は左側がtrueでも右側が評価されます。
if (do_something(var1) & do_else(var2))
とあった(&1つの)場合、do_somethingもdo_elseも実行されます。
対して
if (do_something(var1) && do_else(var2))
とある(&二つの)場合、左がfalseであれば全体がfalseに決まる(ANDは両方trueの場合のみtrue)ので、右は実行されません。
それを利用してRubyのような言語はANDやORを使って左だけ実行、右だけ実行を切り替えるように使うイディオムがあるとか。まぁ、副作用(関数内で呼び出し元にも反映される形=参照での引数を変更する)は良くないのですが。
switch文の話
switch(<expression>) {
case <const-expr>:
<statement>;
(break)
case <const-expr>:
<statement>;
(break)
default:
<statement>;
}
ない言語も少なくないですが、Switch文です。
これ、expressionの結果が一致するcaseにジャンプします。breakするとスコープを抜けますが、そこから下のbreakまで実行することができたり、caseだけを並べて共通の処理に落とすこともできます。例えば
switch e {
case e1:
case e2:
<statement>;
break;
case e3:
<statement>;
default:
<statement>;
}
とやると、最初の2つは同じ処理、e3は自分とdefaultの2つ、defaultは最後だけ、というような場合分けもできます。と言ってもbreak書かないとfall throughの注意がされたりします。defaultはbreak書いてもいいけど意味ないですね。
CSの学生の最初の方で離散数学とか離散構造を学んで集合や論理式をやったりしますが、分岐の評価式とかはその辺をしっかりやってれば迷わないかもですね。フィルタリングであったり評価式によってデータを同行するのはよくある話なので、集合とか論理式は理解してないとやばいです。理系ならド・モルガンの法則とか知ってるのが普通です。「おいらプログラミングできるから理系」みたいな謎ルールの人は覚えてないかもですが。ちゃんと学びましょう。
意外とこの辺できない人いて、バグ仕込んでくるんですよね。
繰り返し
forループ
whileループ
forループ
for (<variable> = {num}; <expression>; <operation>) {
<statement>;
....
}
variableの部分では変数の宣言もしくは代入を行います。何もしないこともできますね。
一般的にforは0からNまでのループで使われることが多いですが、Nから0までとかもよくあります。この時にunsignedにすると、0から1引くと最大値になって無限ループになるとか初心者にありがちな失敗ですね。また、不等号や等号をミスって終了条件がズレるのもありますね。その内、sqrt(N)までで切ったりとかもそのうち見るかも知れません。計算量とかはアルゴリズムの講義の話で出すと思いますが、for文の定義から計算量がわかった方が良いです。例えば for (int i = N; i > 0; i /= 2)とか何回実行されるかをパッと答えられると良いです。
Lecture 7
do-whileループ
do {
<statement>;
...
} while (<expression>);
必ず一回は実行するループですね。
for文やwhile文は繰り返しの開始時にチェックをします(forの変数の更新は終了時です)が、do-whileでは終了時に終了条件のチェックをします。それ故必ず1回は実行されるわけで、正規表現で言うと for/whileは0回以上の「*」でdo-whileは1回以上の「+」ですね。正規表現の方が習うの後だろ、というのはスルーします。
ループは言語によって式だったり文だったりします。文は処理が完結する塊(何も返さない)で、式は文の一部として使えて何かを返す可能性がある、という感じで、その意味はその内分かると思います。
break
return
exit(<status>)
どれも何かを終了する時に使われますが、break/returnはキーワード扱い(特殊な意味を持つ予約語)でexitは関数ですかね。
breakは基本的には現在のスコープを抜けるけど、抜けれるのはswitchとループですかね。Rustとか一部の言語ではループに名前を付けてbreakと組み合わせることで、例えば下記のように2重ループを一気に抜けれたりします。
'outer: for i in 0..100 {
for j in 0..100 {
if (<expression>) {
break 'outer;
}
}
}
returnは戻り値を返して関数を抜けますね。ループの途中でも分岐の途中でも関数呼び出しの評価が確定して呼び出し元の関数がreturnした値に置き換わる感じです。戻り値がない場合はreturnのみで関数を抜けますね。
exit()は言語による気がしますが、プログラム自体を終了します。
プログラムをコンソールから実行すると引数に渡した終了コードが下記で取れます。
$ echo $?
次回は文字列や入力周りに入って行きます。
#include <string>
string str;
std::cin >> str;
課題3(締切は日曜夜日付変わるまで)
この課題は2週に渡って段階的に提出する課題です。ゲーム作成を通じてループと条件分岐のプログラミングの練習を行います。
この週はDesign Documentの作成です。
問題
ブラックジャックのようなゲームを作成してください。
コンピューターによるディーラーと1~4人のプレイヤーで遊びます。全てのプレイヤーはゲーム開始時に指定する異なる量のお金を持ってゲームを開始します。ゲームのゴールはトランプに見立てたような1~11の数字をランダムに与えられて、21を超えないようにディーラーより21に近い数字にすることです。
まず、最初のプレイヤーのターンでディーラーに対して金額を指定してベットを行います。その後、最初の1~11の乱数が発行され、次の数字を受け取るか決めます。21を超えずに近づけるように任意の回数数字を受け取ったらディーラーのターンです。ディーラーも同様に数字を受け取りますが、戦略は自由にプログラミングしてください。最初のプレイヤーとの勝負が終わったら次のプレイヤーとの勝負に移ります。
ルール
プレイヤーのターンでバースト(22以上になる)したら即時負けになりディーラーのターンは不要
ディーラーがバーストしたら即時プレイヤーの勝ちとなりベットした額を受け取る
同数の場合は金銭の移動は起きない
ベットの金額が所持金を超える場合、再度額を入力させる
所持金が0の場合、そのプレイヤーはスキップされる
全てのユーザーの所持金が0になったらゲームは自動的に終了となる
全てのユーザーの勝負が終わったら全員でキャッシュアウトするか、次の周の勝負に入る
例
何人で遊びますか? (1-4): 2
プレイヤー1の所持金を入力してください: 20
プレイヤー2の所持金を入力してください: 50
プレイヤー1はいくらベットしますか?: 10
プレイヤー1は 10 をゲットしました。もう一度数字を取得しますか? (0-no/ 1-yes)?: 1
プレイヤー1は 8 をゲットしました。もう一度数字を取得しますか? (0-no/ 1-yes)?: 0
あなたの合計点は 18 です。
ディーラーは 11 をゲットしました。
ディーラーは 11 をゲットしました。
ディーラーはバーストしました!あなたの勝ちです!
プレイヤー1の所持金は 30 になりました。
プレイヤー2はいくらベットしますか?: 10
プレイヤー2は 8 をゲットしました。もう一度数字を取得しますか? (0-no/ 1-yes)?: 1
プレイヤー2は 9 をゲットしました。もう一度数字を取得しますか? (0-no/ 1-yes)?: 0
あなたの合計点は 17 です。
ディーラーは 8 をゲットしました。
ディーラーは 9 をゲットしました。
ディーラーの合計点は17です。引き分けました!
プレイヤー2の所持金は50になりました。
ゲームを続けますか? (0-no / 1-yes)?: 0
提出内容
実装は翌週になります。この週は下記のドキュメント作成が課題になります。
問題の理解と分析
- ユーザーの入力、前提、出力は?
- どんな仮定があるか
- この問題の全てのタスク、サブタスクは何かプログラムデザイン
- プログラムの大枠はどんな感じになるか(フローチャートや疑似コードなど)
- どんなデータを作る必要があるか/いつユーザーの入力を受けるか/ディーラーの戦略は?
- プログラムを書く上で必要な決定事項は?
- どのタスクが繰り返されるか
- 対処が必要と思われるどんな不正入力があるかフローチャートやステップのリストをなるべく具体的に明示的に書きましょう
テスト
テストの計画を立てましょう。下記をカバーする必要があります。
- 正常系
- 異常系
- 境界値
あとがき
このシリーズあまり需要ないっぽい(基本過ぎる)のでちゃちゃっと書いてもっと難しい話したいですなぁ。
この記事が気に入ったらサポートをしてみませんか?