読者です 読者をやめる 読者になる 読者になる

Accelerated C++ 第2章

このあたりはざっと流して行きたいけど、課題を丁寧にやってると結構時間がかかるな。

ループの不変量(Loop Invariant)、不変な表明

「不変な表明」は、契約による設計への「不変条件」の意味か。
invariantそのものには不変量、不変式の意味があり、おぼろげな記憶では、「loop invariant」=「ループで値が代わらない式」で最適化の話に出てくる(気がする)。

for (init-statement condition; expression) statement

ini-statementがあれっと思ったけど、C++だと初期化ができるのでこちらになり、Cと違う点。
C言語でもそうだが、式(expression)と式に ; をつけた式文との区別が曖昧な記述が多いね。

プログラムの完成

1文字ずつ出力するなら cout << '*' とするほうが素直じゃないかなと思うし、"*"とする文字列リテラルの領域を確保して・・などと貧乏くさい考えが浮かんでくる。
一方 ++c を1つにするため(だけ?)に else の中に if を入れ子にして { } も省略としているが、ここはやはり

  • if のネストは避ける(多方向分岐はそのまま素直に書き下す)
  • if/else の本体が1行しかなくてもブロックにする({ }で囲む)

としたい。

カウント:ものを数えるときに1からではなく、0からである理由

不変な表明で上手く説明されているように思う。
ただforの条件式は < を使う方になじみがあるなぁ。コンテナクラスのイテレータへのつなぎを意図しているのだろうけど。

課題

2-0,2-1

やってみた。

2-2

pad を vpad(垂直、上下) と hpad(水平、左右)に分ける。

const int hpad = 4;
const int vpad = 2;
const int rows = vpad * 2 + 3;
const string::size_type cols = greeting.size() + hpad * 2 + 2;
    
cout << endl;
for (int r = 0; r != rows; ++r) {
    string::size_type c = 0;
    while (c != cols) {
        if (r == vpad + 1 && c == hpad + 1) {
2-3

pad を const でなくして、入力させるだけ。エラーチェックを考慮すると面倒。

2-4

greetingの前後の空白とかを考慮すると、空白だけ一気にというのは意外と面倒。*も含めて行単位で処理する方が判りやすい。
課題2-5へのつなぎ問題?

const string spaces(greeting.size(), ' ');
cout << endl;
for (int r = 0; r != rows; ++r) {
    string::size_type c = 0;
    while (c < cols) {
        if (r == pad + 1 && c == pad + 1) {
            cout << greeting;
            c += greeting.size();
        } else if (r == 0 || r == rows - 1 || c == 0 || c == cols - 1) {
            cout << '*';
            ++c;
        } else if (c == pad + 1) {
            cout << spaces;
            c += spaces.size();
        } else {
            cout << ' ';
            ++c;
        }
    }
2-5

stringオブジェクトの生成と出力を一気にやるのが意図か。
高さを指定して二等辺三角形を表示する。

void tri(const int h)
{
    for (int i = 0; i < h; i++) {
        cout << string(h - i - 1, ' ') << string(i * 2 + 1, '*') << endl;
    }
}
2-6

1から10まで表示。

2-7, 2-8, 2-9

つまらない例だけど、一応やってみた。

2-10

スコープに関連している。using で指定された name は、それが宣言されているスコープ内で有効となるため、最初の cout ではスコープ解決演算子は不要。ただこのusingはブロックの終了とともに無効となるので、ブロック外では明示的にスコープ解決演算子が必要。