PPPUC++#6
"If it doesn't have to produce correct results, I can make it arbitrarily fast."
そりゃそうだ。コンピュータにこう言った良い「ズル」をさせる設計と言うのがプログラマ3大要素の「短気」の結果なのかな。
この章でswitch とかloopとかの制御構文や関数定義にようやく入ります。
4.1 Computation
- 全ては計算 : 入力→ →出力
- このってのは大抵グチャグチャのコードの塊とデータ
- 入力にはいろんなモノがあり得る。キーボードだったりマウスだったり他のプログラムだったり、、、事実上無限大
- 出力も同様。これがソフトウェアを面白いものにしているわけだ。
- プログラマにとって重要な入出力の相手は、「他のプログラム」と「プログラムの他の部分」
- 一番簡単な例では 引数と戻り値 がコードのI/Oということになる
- もともとは computerとは、会計士とか航海士などの計算する人のことだった。
4.2 Objective and tools
- プログラマのお仕事は、以下のように計算を表現すること
- 正しく
- シンプルに
- 効率的に
- 上ほど重要。どんなに効率的でも間違ってたら意味が無い
- 正しい事や効率的な事は当然。シンプルであることは、移植性や拡張性、修正の容易さなどにつながる
- 以上を考慮すると結局、コーディングとは他者のため。それを行う「義務」を受け入れることにつながる。
- すなわちプロとしての責務に目覚める、といったところ
- こうなってくると、とにかく動くように見えるコードを突っ込んでGO、では全く不十分で、コードの構造を熟考する必要が出てくる
- よく言われることだけれど、こう言った構造やコードの質に気を使うことは、逆に開発期間を短くすることが多い
- 結局一番時間がかかるのはデバッグだという話
- プログラムを組織化する方法、というのは思考方法と同じく、大きな問題を小さく小さく細分化していくこと。この手法には2種類ある。
- 一枚板は一定量を超えるとムリゲー
- 1000行コードは100行コードの何十倍ものエラーを含む傾向がある。100行以下のコードをたくさん書こうぜ、ということ。
- ツール重要。分割したり抽象化するために何が使えるのかはちゃんと調査するべし。
- 先に進みたくなる気持ちをぐっとこらえて。ここが重要。
- 構造化されていないコードは泥のレンガでできたもの、ってのが面白かった。「五階以上のものをつくろうとしたら崩れるよ。そのレンガは構造的に弱いから」って。
4.3 Expressions
- 式です。
- 一番簡単なのはリテラルです。
- 左辺値=箱(lvalue), 右辺値=中の値(rvalue)の区別など
- 演算順序(優先順位)など。カッコはとにかく「迷ったらつけろ」
- しかし読みにくくなるから付け過ぎには注意、、、とw
- 他人が読むものだから、という理由の他に、こんがらかった汚い式は直すのも面倒くさい。
4.3.1 Constant expressions
- piとか inch_to_cmとかプログラム中には定数がたくさん出てくる。
- const使って定数設定しよう。
- 突如 299792458って出てきてなんの数字かわかるっすか?*2
- ほら、わかんなかったでしょ。
- 同じことで他の定数も意味のある名前にしておいた方が良いです。
- というわけでこういうマジック定数やめましょう
- 後から書き換えることも考えるとさらに定数使っておいたほうが良いですな
- Avoid magic constants!
4.3.2 Operators
- 演算子:ほとんどはまあ見たまんま
- lvalの説明がleft-handedになっている. locater valueって教えなくていいのかな? 教えて偉い人!
- a
- 僕は #define _(X) (X)&&(X) したりして a < _(b) < cとかしているけど、悪いことなのかもしれない。
- ++a, a+=1, a=a+1 はどれがいいか? Mr.Sによると最初のが一番素直だから良い! とのこと
- そうかなあ????ラストはともかく 2番目でもいいじゃん。
- と、思ったんだけど、結局 a+=1 は a+=2の打ち間違いちゃうん?とかいうダウトをかけれるが、 ++a はまず間違いなくそのままだ、という意見。なるほど。深く納得した
- まあともかく、「なるべくアイディアをそもまま写像した表現を心がける」と良いコードを書ける、という話。まさにプログラミングとは文学だ。
- よく聞く話 ++インクリメントが効率が良い、とかいうのはほとんどの最新コンパイラではMYTHです。ここでの話はあくまでも読みやすさと正しさの基準
- 前置と後置については何も書いてない。うーん後で確かめてみようかな
- とおもっていろいろ試してみたけどやっぱりどれでも値を取り出さない限りは addl $1, %regにしかなりませぬ。for(;;)でも前置せよってのは都市伝説だと思うなあ
- 前置と後置については何も書いてない。うーん後で確かめてみようかな
4.3.3 Conversions
- 演算においてはオペランドのどっちかがdoubleなら double演算になるよーというはなし
- 演算後、代入とか初期化でまた再変換がかかることもある
- 左結合な演算子だと 例えば Fahrenheit -> Cersiusしようとして f = 9/5 * dc + 32; とすると9/5が先にint/int=int === 1 になってしまってハマるよ、とか
- なるべく double な演算があるところでは 9.0 / 5.0 とかしましょ。
4.4 Statements
- 式で個々の計算はできるのはわかった。
- これだけじゃたいしたことできないじゃん、(つ、つまらん技だと!? なら二つならどうだ!?)
- たくさんやるときは制御構文を使いましょう。
というわけで文です。この他にも, 入出力を行う場合とか、複数の選択子から条件に応じて選ぶだとか、などなど
- 式の評価自身も文(expression statement)。あと宣言文(declaration statement)もやった
- セミコロン重要。無いと文法が曖昧になる
- 副作用なしの文は何の役にも立たない。ってなるほど確かに。大抵はコンパイラが警告を出してくれるらしい
- てなわけで、典型的な式文は 代入、入出力、関数呼び出しのいずれか
- も1つ、empty statmentの話、
if (x == 5) ; { y = 3; }
- TYPOとして処理してほしい所だけれど、これも正規な文として扱われてしまう。これは見つけにくいバグ。
- empty自身は内部でbreakしたり、割り込み駆動の無限ループとかで使うことがあるのだよね。これも僕に教えてくれた人は #define nothing と言うのを用意しておいて、while(true) nothing; せいと教えてくれました。
4.4.1 Selection
- 候補の中から選ぶヤツ
- if or switch
4.4.1.1 if-statements
- 一番簡単な方法。候補数は2。条件合致 or not
- if ってのは小学校程度の論理だよなあ、とか言う話。If (the traffic light is green) go
- if - else だけだと条件を網羅できない場合がある。ので、プログラマは必ず不正な入力のチェックを行うべき
- else if という言語フィーチャーはないよーとかいう話(どっちでもいい気が・・・)
- if... else if ... else if ... とつないでいけばいくらでも複雑なプログラムをかけるけど、単純じゃないよね
- 理性を見せたいなら「複雑さ」を見せびらかすんじゃなく、「単純さ」を見せびらかすこと
というかそれ以前に強制的に線形探索になってしまうから効率もすっげー悪い気がするんだな。ここの例程度では、だけど。
単なるequality checkを越えたことをしたい場合は if 地下茎脈を使わざるを得ないですよねー
TRY THIS
そういえばこないだ通貨換算プログラム書いたなー。忘れたけど。
4.4.1.2 switch-statements
- 先ほどのような単純な定数チェックならswitch使いましょう
- 間違いない、って言いきれるならdefault抜いても良いけどね
- プログラミングが教えてくれる教訓の一つは、「100%確かなことなんて何も無い」、と言うことだ
すばらしい。
4.4.1.3 switch-technicalities
- switchでは,
- integer, char, 列挙型のみ。 得に、stringは使えないのでそのつもりで
- case labelは定数式じゃないとダメ。変数置いちゃダメということ
- 2度同じlabel書いちゃダメ(そりゃそうでしょ。。。)
- 各々のcase blockはbreakで終わるべし。忘れててもコンパイラは教えてくれない
- string 使いたければifかmapつかっとくれ
- switch はジャンプテーブルを作って最適化するので、得に選択肢が多い場合は効率が良い
- 複数の選択肢で同じ動作をさせるとかの場合、breakを抜いてfall through(本書ではdrop through)な書き方もできる(というかそのために合法になってるんだろう)
- でもそれが原因のバグは多い(そう思うならcase label に or 書けるようにして禁止してはどうかとおもうんだけど・・・)
- C互換性って大変だな
- ちなみにトリッキーな使い方をする(例えばある選択は他の選択での動作に+αのデコレーションをする必要)とかの場合、僕はコメント入れてます
case 'a': hoge(); /* FALLTHROUGH */ case 'b': moge(); break;
4.4.2 Iteration
- 一回ってことはないだろ。つ、つまらん技(もういいって)
- repetition とiterationって違うのか・・・(本文中では、得にデータ構造の要素列に繰り返し作用する場合がiterationとなっています)
4.4.2.1 while-statements
- 世界初のiterationを使った蓄積プログラムはケブンリッジ、、、じゃなかったケンブリッジのDavid Wheelerによって1949に書かれた、二乗を順にプリントするものだったそうな
0 0
1 1
2 4
3 9
4 16
:
- c++であの完動をもう一度、してるみたいだけど、これはwhileじゃなくforですべき
- 現実問題で結構問題になるのが、条件は完璧か? 全てのループ変数は初期化できたか? らしい
TRY THIS
main() { const char TAB = '\t'; char c = 'a'; while (c <= 'z') { cout << c << TAB << int(c) << endl; ++c; } }
Avoid magic numbers!! を実践してみた。ていうか'\t'ハードコードしちゃってダメじゃん Mr.S。
#ただそれが言いたかっただけ。。。
4.4.2.2 Blocks
- なんだこの節は。
- redundantだ
- と思ったけど #define nothing よりも while (true) {} の方がよろしいかな。
4.4.2.3 for-statements
- a〜bとかという反復は多いのでそれに特化したのが for
- 最初のwhileの例だとハミ子だった初期化i = 0と++iがfor構文の中に入るので、意図が直接的になった、という話。同感
- それなら foreach 言語仕様に入れればいいのに・・・・
- ループ内部でループ変数書き換えるなよー、という警告。読みにくい、と
- なんでもそうだけど、意図せずにやってしまう事は意図できないんだから防ぎようがないという身も蓋もない・・・
- で、意図してることは意図してやってるんだから余計なお世話だとか。でもこういうところは言語で記述しきれない「意図」だからコメント要なのかも
TRY THIS
main() { const char TAB = '\t'; for (char c = 'a'; c <= 'z'; ++c) cout << c << TAB << int(c) << endl; }
だから Avoi...(ry
4.5 Functions
- square()とかなんよ(ってかなりわざとらしい気が)
- 自分でも定義できるよーとか言う話
- 関数呼び出しの返り値は別に使わなくてもいいけど、必要な引数はちゃんと与えないとダメだよとか(しかし返り値を使わないのであれば何故関数呼び出すのだ→ 副作用、というながれかな)
- square(int) に "two"渡してもちゃんと反応してよ!
- いや、そのように書けば・・・
- プログラミング言語というのは単純な事を、単純に正確に、書けるように設計されたもの
Chapter 8を楽しみにしておこう。プレビューしてみたところ8章にてスコープとかnamespaceとか小銭で呼びつける(違w )とかABIとかやるっぽいです。
4.5.1 Why bother with functions?
関数にするメリット
- 論理的に計算を分離
- 上手い名前をつけることによってプログラムを読みやすくする
- 何度も出てくる計算を一箇所にまとめる
- テストの簡単化
- 実地で見せていくので体得するように!
- square(x)程度だったら x*xと書いてもいい、と思うかもだけど、sqrt(x)だと、抽象化できないと大変。
- しかし何でも関数化すればよいかというとそれも違う。
- ある抽象度に注目したとき、単一機能となるように区切るのが吉(というような話、つい最近別のところでもあったような・・・なんだったっけ・・・)。同一レイヤ上の複数の操作概念を突っ込むと見栄えが悪くなる。
- function_do_A_and_B_and_C() 見たいなのはダメ。A_and_B_and_Cが一言で言えるならOK。つまり下位レイヤで複雑作業はあり。
- ある抽象度に注目したとき、単一機能となるように区切るのが吉(というような話、つい最近別のところでもあったような・・・なんだったっけ・・・)。同一レイヤ上の複数の操作概念を突っ込むと見栄えが悪くなる。
TRY THIS
いやその・・・・
4.5.2 Function declarations
- 実は関数を呼び出すに当たって、名前と引数&戻り値が判っていれば充分なはず
- というわけで呼び出すだけなら関数宣言のみでいける。
- これはライブラリ関数などで使われている方法。ヘッダファイルにはこの関数宣言が書いてある。本体はライブラリファイルに(・・・ちと飽きてきた)
4.6 Vector
- 実用的なプログラムは、大抵データの集まりを扱う。
- いろんな方法がある。クラスとしてはコンテナクラスと呼ばれているものがある(20,21章)
- vectorはその中でも最も簡単なもの
- インデックスでアクセスできる、連続データ要素の集合
- もちろん0始まりです。
- しれっと「範囲外アクセスは実行時エラーになります」とか書いてるけど、SEGVよね
4.6.1 Growing a vector
4.6.2 A numeric example
- 今度こそ特記事項なし
- と、思ったけど、 cin >>
してる場合に文字列が来ると内部的にはエラーが起こって cin が false返す(正確にはcin自体がfalseになる)、ということを利用して'|'で終端文字入力ー!とかやっているみたいですが、、、 - なんつー強引な。別に'|'じゃなくてもどんな文字でもよいわけで。。。
- くそまじめにやろうとするとこんなかな
cin >> d; if (!cin) { char c; cin.clear(); cin.read(&c,1); if ( c == '|') break; else cerr << "error!" << endl; } else v.push_back(d);
まあ意味なし。
- メジアンのために sortとかに突っ込む。何気にalgorithmにまで艦首を突っ込んだ
- 奇数の場合は良いけど、という話になってる。偶数とでわけるよろしですね。
4.6.3 A text example
- 今度こそ特記事項なし
- と思ったけど agronomyって農業経営学とか栽培学とかって意味なんだー、ということを学んだ
- どんなところからでも何かしら得るものがあるものだ(失礼ナリ)
TRY THIS
「きょうぼくは<ピー>を食べようとしたら<ピー>が<ピー>の間からにょきにょきと<ピー>して・・・」みたいなプログラム。んーーーあんまり面白そうじゃないのでカット
4.7 Language features
- 前二つの例は本章で出てきた基本的な言語機能を使っている。
- selection, 基本演算、比較演算、関数、vector, cout, sort....... etc.etc
- こんな風にプログラムってのは基本的に非力な少数の概念を組み合わせて作られてるんだよー、というのがここで言いたかったことらしい
Drills
めんどくさー