C++ 講座 演習2

注意: この記事は古い方針に基づいた内容であり、閲覧しないことを推奨します。学びたい人は [こちら](http://mikuroxina.github.io/cpp-book/) をどうぞ。

今回は複数の値の取り扱いと
それに必要な知識もやっていく

# 目標

* 2 通り以上の条件分岐
* 一時的な値の保存
* ループ
* 任意の長さの値の取り扱い

の習得

# インデント (字下げ)

括弧の中身が複数行にまたがるとき、
その始まりと終わりを明確にするために行う

```
{
  {
{
     {
}
  }
}}
```

上のものより
下のほうが見やすい (と思ってくれれば嬉しい)

```
{
  {
    {
      ;
    }
  }
  {
    ;
  }
}
```

これから波括弧などが増えてくるので書いておく

# `else`

`if` と一緒に使う構文
条件が `false` のときだけ実行される
分岐したお互いの文はどちらかしか実行されない

```cpp
if ( expr ) {
  ~ ; // when expr is true
} else {
  ~ ; // when expr is false
}
```

## `else if`

そして `else` の直後に `if` を付けられる
これで 2 パターン以上に対応できる

```cpp
if ( expr ) {
  ~ ; // when expr is true
} else if ( expr2 ) {
  ~ ; // when expr is false & expr2 is true
} else if ( expr3 ) {
  ~ ; // when expr2 is false & expr3 is true
} else {
  ~ ; // when expr3 is false
}
```

上の `if` から優先的に分岐する

# 変数

値を保存する場所をつくる仕組み
関数が受け取る引数も変数のうちの一つ

関数内において、
以下のように変数を作ることを「変数の定義」という
文献によっては「変数の宣言」ともいわれる

```cpp
// Variable definitions
int var = 12; // `var` stores 12
double rough_e = 2.7; // `rough_e` stores 2.7
bool completed = false; // `is_completed` stores false
```

## 変数にアクセス

変数の名前をそのまま値のように書くと、変数の値がコピーされる

```cpp
int var = 12;
int val = var; // `val` stores 12 (copied from `var`)
return val; // return 12 (copied from `val`)
```

## 命名規則

変数には使えない名前がいくつかある

* 数字で始まる
* `_` と大文字英字で始まる
* `__` が含まれている

ような名前は定義できない

```cpp
// You can't define:
int 2_to_3 = -1;
// You must not define:
int _X = 0;
int f_G = 1;
int a__ = 2;
int ___v__ = 3;
int __Q = 4;
```

## `auto`

`auto` はプレースホルダー型といわれるものの一つで、
実際の型は `=` の右側から推論される

```cpp
auto count = 0; // typed int
auto rough_pi = 3.; // typed double
auto done = true; // typed bool
```

# 代入

変数の値を書き換えること

代入演算子 `=` を使う
※変数の定義に使う `=` は同じ記号なだけで代入演算子ではない

```cpp
int ham = 1130;
ham = 2680; // Assignment is here
```

これを使うと、足し合わせることなどができる

```cpp
auto summing = 0;
summing = summing + 5;
summing = summing + 8;
summing = summing + 12;
summing = summing + 100;
```

引数も上書きできる

```cpp
int example(int what) {
  what = 2;
  return what;
}
```

## 複合代入

上の `v = v なんとか 数` というパターンが多いのでそのための構文

このパターンを `v なんとか= 数` と書ける
この `+=` `-=` などを複合代入演算子という

```cpp
auto summing = 0;
summing += 5;
summing += 8;
summing += 12;
summing += 100;
```

ただ対応する演算子が無いこともあるのでそのときは普通に書く

# インクリメント / デクリメント

変数の値を 1 だけ増やす / 減らす演算子

```cpp
auto count = 0;
++count; // increment
// count == 1
--count; // decrement
// count == 0
```

これらはそれぞれ `+= 1` / `-= 1` かのように使われる

C++ 使いは、**必ず絶対に変数の前** に書く (後に書く方もある)
技術的なアレコレがあるので理由は省く

# `while`

ループするための構文

```cpp
auto to_loop = true;
while (to_loop) { ~ }
// loop while `to_loop` is true

auto count = 3;
while (3 < count) {
  --count; // counting down
}
```

`{ ~ }` が繰り返され、これが終わる度に `while (~)` の式が評価される

## 実行順序

> 繰り返すかどうか → ループ内容 → 繰り返すかどうか → ループ内容 → ……

の順番で実行される
そのため条件式が最初から `false` だと中の処理は一度も実行されない

条件式が `true` のときにだけ繰り返されることに注意

ちなみに条件式に `true` と書くと (そのままでは) ループから出てこない

```cpp
while (true) { ; }
```

ある制御文と組み合わせて
これを使うこともあるが
今はまだ説明しない

# `for`

`while` の上位互換なので少し構文が複雑

```cpp
for (int i = 0; i < 11; ++i) { ~ }
// To explain ...
for ( // For
int i = 0; // i starts from 0
i < 11; // to 11
++i // increment by the loop
) { 
  ~ ; // is repeated 11 times
}

for (
~ ; // before the loop
~ ; // loop while the expression is true
~ // at the end of the loop
) { ~ }
```

`(ループ開始前; 条件式; ループ後処理)` のように式が記述できる
独特かつ伝統的な記法なので、何回も書いて覚えよう
繰り返しの条件が一箇所にまとまる、何回目のループか分かるのが長所

## 実行順序

> ループ開始前 → 条件式 → ループ内容　→ ループ後処理
>            → 条件式 → ループ内容　→ ループ後処理 → ……

と実行される

## よく使うパターン

* `0` から始まる `for (auto i = 0; i < 繰り返し回数; ++i) { ~ }`
* `1` から始まる `for (auto i = 1; i <= 繰り返し回数; ++i) { ~ }`

はよく使う
どうしてこうなるか考えてみよう

繰り返し回数を 2 とすると、

> `auto i = 0;`
> > `i < 2` は `true`

> ループ内容 1 回目
> > `++i` で `i` が `1` に
> > `i < 2` は `true`

> ループ内容 2 回目
> > `++i` で `i` が `2` に
> > `i < 2` は `false`

> ループ終了

1 で始まる方も同様

# 例題 1

```cpp
// void means return nothing
void fizz_buzz(int times) {
  // Write here
  print("Buzz");
}
```

[ここで関数を実装せよ](https://wandbox.org/permlink/zhZCsIG6eXybSfcT)
値 `times` が入力される
この数まで Fizz Buzz (下記) を行え

> 数字を数えていき、以下を出力する
> 
> * 3 と 5 の倍数なら "Fizz Buzz" を
> * 3 の倍数なら "Fizz" を
> * 5 の倍数なら "Buzz" を
> * それ以外は数字をそのまま

ここで出力する関数として `print` 関数を用意しておいた
`print(10);` とか `print("Fizz");` で出力してほしい

## 解説

変数 `count` で **1 から** 数え上げていき、
その値によって分岐する
`else` を使わないとおかしくなる

```cpp
void fizz_buzz(int times) {
  for (auto count = 1; count <= times; ++count) {
    if (count % 15 == 0) {
      print("FizzBuzz");
    } else if (count % 3 == 0) {
      print("Fizz");
    } else if (count % 5 == 0) {
      print("Buzz");
    } else {
      print(count);
    }
  }
}
```

# 関数呼び出し

さっき使ったけどここでちょっと解説

自分や他人が作った関数は、
`関数名(引数);`
とすると呼び出せる

# `std::string`

`std::string` はテキストを扱うためのクラス
先程のようにテキストの値を作るには `"` で囲う
これを「文字列」という

でもこの文字列 `"abcdefg"` は `std::string` ではない
`auto` を使ってしまうと違う型になる

```cpp
std::string empty;
std::string text = "abcdefg";
```

## `std::to_string`

`int` や `double` から数字の `std::string` にするには、`std::to_string` 関数を使う

```cpp
auto text = std::to_string(2); // == "2"
```

## `find`

`std::string` 型の値はオブジェクトという類のもので、
これらには関数が付いている
テキストにある文字が含まれているかどうかは、以下で判定できる

```cpp
std::string text = "timeleeeess!";
auto l_pos = text.find("l"); // returns position if found the text
auto x_pos = text.find("x"); // returns std::string::npos if not found
// so
auto contains_exclamation =
  text.find("!") != std::string::npos; // means `whether contains "!"`
```

他にも機能があるけど次でこれを使うから説明した

# `#include`

あ、これを使うより前に `#include <string>` と書いて
`string` ファイル (C++ に付属) を展開しておく必要がある

```cpp
#include <string>
```

こういう外部のプログラムファイル群をライブラリといい、
特に `<string>` のような C++ 標準のものを標準ライブラリという

他の標準ライブラリ内でも `#include <string>` されていることが多いので、
この `#include` が要らないこともある

# 問 1

```cpp
auto included_three(int n) {
  // Write here
  return false;
}
```

[ここで関数を実装せよ](https://wandbox.org/permlink/Kbstw6q06I1LNpcM)
値 `n` が入力される
これが 3 のつく数かどうか判定せよ

# `std::vector`

同じ型を大量に格納するためによく使われる
これは `<vector>` 標準ライブラリが必要

```cpp
std::vector<int> v; // contains ints
auto copied = v; // another v
```

今回はこれを自分で作ったり触ったりする必要はない
あらかじめ用意されたものを渡すので、
これをどう加工するかをプログラムする

## `size`

格納している個数を尋ねる

```cpp
auto elements_count = v.size();
```

## `at`

n 番目に格納しているものを取得する
この番号は 0 から始まり、`0` 以上 `size` 未満
また、格納しているものは「n 番目の要素」のように言う (他の格納するものでも同じ)
存在しない番号を渡すと `terminating ~ std::out_of_range: vector`
と出てプログラムが停止する

```cpp
auto first_element = v.at(0);
auto third_element = v.at(2);
v.at(1) = 0;
```

# イテレータ (反復子)

標準ライブラリには何かを格納するクラス (コンテナクラス) がいくつかあり、
それらはイテレーターを使って操作できる

## ランダムアクセスイテレータ

```cpp
std::vector<int> v;
auto head = v.begin();
auto tail = v.end();
```

によって得られるイテレータは、

* アクセス演算子 `*` を使って格納された値を読み/書きできる
* インクリメント演算子 `++` を使って次の要素に移動する
* デクリメント演算子 `--` を使って前の要素に移動する
* 比較演算子 `==` / `!=` を使って同じ要素を指すかどうか取得できる

といった特性を持っている

```cpp
void example(std::vector<int> v) {
  auto head = v.begin();
  auto first_element = *head; // Read
  *head = 2; // Write
  auto next = ++head; // Advance, then copy
  head == --next; // is true
}
```

これを利用して、要素に対する操作ができる

# 例題 2

```cpp
int sum(It head, It tail) {
  int current = 0;
  // Write here
  return current;
}
```

[ここで関数を実装せよ](https://wandbox.org/permlink/Z3NkdAXsZN13Ws2h)
イテレーター `head` `tail` が入力される
この範囲 (`head` ~ `tail`) の総和を計算せよ

## 解説

```cpp
int sum(It head, It tail) {
  int current = 0;
  for (auto it = head; it != tail; +it) {
    current += *it;
  }
  return current;
}
```

こうして実装できる

実は `<numeric>` 標準ライブラリに
`std::accumulate` という上位互換の関数がある

# 問 2

```cpp
void seq_interpolation(It begin, It end) {
  int back = 0;
  // Write here
}
```

[ここで関数を実装せよ](https://wandbox.org/permlink/xuTeeJ9QRw9MjwLW)
イテレータ `head` `tail` が入力される
途中に `-1` のデータがある
そのデータをそれより前の `-1` でないデータで置き換えよ
最初に `-1` がある場合は `0` にセットせよ

# 問 3

```cpp
void normalize(It head, It tail, double min, double max) {
  // Write here
}
```

[ここで関数を実装せよ](https://wandbox.org/permlink/oihwnV6WphA33YaS)
イテレータ `head` `tail` 値 `min` `max` が入力される
`head` ~ `tail` の要素を `min` ~ `max` の範囲で以下の値にせよ

* `min` 以下の値は `0`
* `max` 以上の値は `1`
* それ以外の値は `(要素の値 - min) / (max - min)`

これは、`min` ~ `max` の範囲で `0` ~ `1` の値に変換 (正規化) する処理になっている

# 多次元コンテナ

`std::vector` は任意の (実際は少し条件がある) 型を格納できる
つまり、

```cpp
std::vector<
  std::vector<
    ~
  >
>
```
としてもいい

```cpp
std::vector<std::vector<int>> square;
std::vector<std::vector<std::vector<int>>> cubic;
```

こういったコンテナのコンテナは「二次元」のコンテナと呼ばれ、
こんな感じで格納される

```
[
  [0, 0, 0, ...],
  [1, 2, 1, ...],
  [3, 6, 1, ...],
  :
  :
]
```

行 (上図での横) 方向に格納されたコンテナが
列 (上図での縦) 方向に格納されるため、
これらへのインデックスによるアクセスは逆順 (列→行) になる

```cpp
auto row = 0;
auto column = 0;
square.at(row).at(column);

auto depth = 0;
cubic.at(depth).at(row).at(column);
```

# インデックスによる要素ループ

インデックスを指定すれば要素にアクセスできるということは、
`for` 文で 0 からコンテナのサイズの範囲を数えているカウントを
インデックスに指定すると順々にアクセスできる
ちなみに、インデックスに使う値の型は `size_t` (これも整数) が正しい

```cpp
std::vector<int> v;
for (size_t i = 0; i < v.size(); ++i) {
  v.at(i) = 1;
}
```

二次元だとこう

```cpp
std::vector<std::vector<int>> m;
for (size_t y = 0; y < m.size(); ++y) {
  for (size_t x = 0; x < m[y].size(); ++x) {
    m.at(y).at(x);
  }
}
```

次回あたりからの数学的な問題を解くために
インデックスを用いた計算を実装する必要が多い

# 例題 3

```cpp
std::vector<std::vector<bool>> shift_field(
  std::vector<std::vector<bool>> field,
  int offset_x,
  int offset_y
) {
  auto processed = field;
  // Write here
  return processed;
}
```

[ここで関数を実装せよ](https://wandbox.org/permlink/BBFJOl0tnCvTo03y)
コンテナ `field` と整数 `offset_x` `offset_y` が入力される
`field` を行方向に `offset_x`、列方向に `offset_y` ずらしたものを返せ
範囲外からずらされてきた要素は `false` にせよ

## 解説

コピーして新しい配列を作り、
要素 `(x, y)` における元の配列との関係を
漸化式のように
`processed.at(y + offset_y).at(x + offset_x) = field.at(y).at(x);`
と記述する

また、アクセスしようとしている `x` のインデックスを
`src_x` として (名前は何でもいい)、
`0 <= src_x && src_x < field.at(y).size()`
というように範囲外でないかを
`y` においても同様に確認している

```cpp
std::vector<std::vector<bool>> shift_field(
  std::vector<std::vector<bool>> field,
  int offset_x,
  int offset_y
) {
  auto processed = field; // copy
  for (size_t y = 0; y < field.size(); ++y) {
    for (size_t x = 0; x < field.at(y).size(); ++x) {
      // index validation
      auto src_x = x - offset_x;
      auto src_y = y - offset_y;
      if (0 <= src_x && src_x < field.at(y).size()
        && 0 <= src_y && src_y < field.size()) {
        processed.at(y).at(x) = field.at(src_y).at(src_x);
      } else {
        processed.at(y).at(x) = false;
      }
    }
  }
  return processed;
}
```

# 問 4

```cpp
bool collides_field(
  std::vector<std::vector<bool>> field,
  std::vector<std::vector<bool>> to_join
) {
  // Write here
  return false;
}
```

[ここで関数を実装せよ](https://wandbox.org/permlink/Uq7URCO8xUS4MzKh)
同じサイズの二次元コンテナ `field` `to_join` が入力される
`field` と `to_join` において、
同じインデックスの2つの要素が `true` であれば、`true` を返せ
それ以外は `false` を返せ

# まとめ

お疲れ様！

次回はより複雑なデータ構造を扱うことになって、

「一つくらいの入力」→「データ構造を作成するシステム」→「データ構造を出力」

や

「データ構造」→「それを探索するシステム」→「一つくらいの出力」

を実装するよ

次回もよしなに