C++四則演算プログラムを作ったら割り算が面倒だったということ

## はじめに

暇だし脳トレ的に単純計算をやろうと思ったので問題作成プログラムつくることにしました。
まぁわかってはいましたが、割り算を算数の文法にすると表示が面倒なんですよね。

せっかくなので設計書っぽいものを書いて実装しました。

## 四則演算プログラム

### 仕様

・起動引数なし。
・四則演算を100個標準出力に表示する。
・どの演算をするかはランダムに決める。
・値は-19から+19までとする。
・割り算で割り切れない場合は仮分数で表示する。それ以外は整数で表示する。
・割り算は算数の方式にしたがう。
・表示は「数字 演算子 数字 = 数字」の形式にする。数字にはマイナス記号も含まれる。
・正の数の場合はプラス記号を付けない。

### 使用方法

・コマンドプロンプトから起動し、リダイレクトでファイルに出力する。
・テキストファイルを手修正し、問題のみのファイルと、問題と答えのファイルを作成する。

### 割り算の仕様について

約分が必要なのと、正負と、分母が１の場合と、分母０を弾く必要があるわけですね。また、分母にマイナスが入っているのは不自然なので編集する必要もあると思います。3 / -2 を -3 / 2にするということですね。

### 割り算の設計

すいませんが、markdownが面倒なのでエクセルで作成。で、画像はります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47053/f5bac134-ce0b-4d30-7f23-e5775bf4da55.png)

と、いう感じでやろうと思います。実際の処理はこのままではないですが、詳細設計としてはこのくらいかと思います。

### 処理

main関数：ランダムのシードを設定し、100回各処理を呼んでいます。

```C++
int main()
{
	auto count = 0;
	std::srand(static_cast<unsigned int>(std::time(nullptr)));

	while (count < 100)
	{
        auto const calc_operator = selectOperator();
		auto const number1 = selectNumber();
		auto const number2 = selectNumber2(calc_operator);
  
		auto const result = calc(calc_operator, number1, number2);

		std::cout << number1;
		std::cout << " " << calc_operator << " ";
		std::cout << number2;
		std::cout << " = ";
		std::cout << result << std::endl;

		count++;
	}


	return 0;
}
```

selectNumber関数：-19から+19までの数字を取得します。まず0から19を取得し、ランダムで負数にします。selectNumber2関数は割り算の割る数にあたるので、0を弾くようにしています。

```C++
auto selectNumber = []
{
	auto const rand = std::rand() % 39 - 19;
	return rand;
};

auto selectNumber2 = [](auto calc_operator)
{
	auto const rand = std::rand() % 39 - 19;
	auto ret = rand;

	if (calc_operator == "/")
	{
		while (ret == 0)
		{
			ret = std::rand() % 39 - 19;
		}
	}

	return ret;
};
```

selectOperator関数：演算方法を取得します。

```C++
auto selectOperator = []()
{
	auto const rand = std::rand() % 4;
	std::string ret{" "};

	switch (rand)
	{
	case 0:
		ret = "+";
		break;
	case 1:
		ret = "-";
		break;
	case 2:
		ret = "*";
		break;
	case 3:
		ret = "/";
		break;
	default:
		break;
	}

	return ret;
};

```

gcd関数:割り算時、約分するにあたって最大公約数が必要なのでそれを取得します。

```C++
auto gcd = [](auto number1, auto number2)
{
	auto ret = 0;
	while (number2 != 0)
	{
		ret = number1 % number2;
		number1 = number2;
		number2 = ret;
	}
	return number1;
};

```

calc関数:今回の主になる処理です。割り算以外はちょろですね。

```C++
auto calc = [](auto calc_operator, auto number1, auto number2)
{
	std::string ret;
	if (calc_operator == "+")
	{
		ret = std::to_string(number1 + number2);
	}
	else if (calc_operator == "-")
	{
		ret = std::to_string(number1 - number2);
	}
	else if (calc_operator == "*")
	{
		ret = std::to_string((number2 * number1));
	}
	else if (calc_operator == "/")
	{
		auto g = gcd(number1, number2);
		auto first = number1 / g;
		auto second = number2 / g;

		if (second < 0)
		{
			second = -second;
			first = -first;
		}

		ret = std::to_string(first);
		if (second > 1)
		{
			ret += "/" + std::to_string(second);
		}
	}
	else
	{
		ret = "0";
	}
	return ret;
};
```

### calc関数ちょっと解説のようななにか

以下で約分しています。ここから下の処理は約分結果を使っています。割り切れている場合は分母(second)が1になっています。

```C++
		auto g = gcd(number1, number2);
		auto first = number1 / g;
		auto second = number2 / g;
```

以下で分母が負数の場合は正の数にします。
```C++
		if (second < 0)
		{
			second = -second;
			first = -first;
		}
```

### 全文

と、いうわけで全文です。

```C++
#include <iostream>
#include <string>
#include <ctime>

auto selectOperator = []()
{
	auto const rand = std::rand() % 4;
	std::string ret{" "};

	switch (rand)
	{
	case 0:
		ret = "+";
		break;
	case 1:
		ret = "-";
		break;
	case 2:
		ret = "*";
		break;
	case 3:
		ret = "/";
		break;
	default:
		break;
	}

	return ret;
};


auto selectNumber = []
{
	auto const rand = std::rand() % 39 - 19;
	return rand;
};

auto selectNumber2 = [](auto calc_operator)
{
	auto const rand = std::rand() % 39 - 19;
	auto ret = rand;

	if (calc_operator == "/")
	{
		while (ret == 0)
		{
			ret = std::rand() % 39 - 19;
		}
	}

	return ret;
};

auto gcd = [](auto number1, auto number2)
{
	auto ret = 0;
	while (number2 != 0)
	{
		ret = number1 % number2;
		number1 = number2;
		number2 = ret;
	}
	return number1;
};

auto calc = [](auto calc_operator, auto number1, auto number2)
{
	std::string ret;
	if (calc_operator == "+")
	{
		ret = std::to_string(number1 + number2);
	}
	else if (calc_operator == "-")
	{
		ret = std::to_string(number1 - number2);
	}
	else if (calc_operator == "*")
	{
		ret = std::to_string((number2 * number1));
	}
	else if (calc_operator == "/")
	{
		auto g = gcd(number1, number2);
		auto first = number1 / g;
		auto second = number2 / g;

		if (second < 0)
		{
			second = -second;
			first = -first;
		}

		ret = std::to_string(first);
		if (second > 1)
		{
			ret += "/" + std::to_string(second);
		}
	}
	else
	{
		ret = "0";
	}
	return ret;
};

int main()
{
	auto count = 0;
	std::srand(static_cast<unsigned int>(std::time(nullptr)));

	while (count < 100)
	{
		auto const calc_operator = selectOperator();
		auto const number1 = selectNumber();
		auto const number2 = selectNumber2(calc_operator);

		auto const result = calc(calc_operator, number1, number2);

		std::cout << number1;
		std::cout << " " << calc_operator << " ";
		std::cout << number2;
		std::cout << " = ";
		std::cout << result << std::endl;

		count++;
	}


	return 0;
}
```

## 終わり

あとは
```
.\exec.exe > a.txt
```
のように使うだけですね。

```
4 - -17 = 21
-5 - -4 = -1
4 / 6 = 2/3
0 * -12 = 0
18 / -1 = -18
3 - -16 = 19
5 * -8 = -40
-1 * 0 = 0
2 + 2 = 4
14 * 10 = 140
0 / 2 = 0
0 / -16 = 0
6 / 2 = 3
-8 * 19 = -152
2 * 15 = 30
-12 + 0 = -12
6 - 18 = -12
11 * -10 = -110
-2 - -12 = 10
-2 + -3 = -5
0 + 9 = 9
0 * -19 = 0
```

<strong>これで脳トレできそうですね！</strong>

適当だからどこかバグってるかも、まぁ使えそうだからいいか。