【C++/OpenSiv3D】Array::remove_if() の使い方

`OpenSiv3D` では `std::vector` の代わりに `Array` を使います。`Array` にはさまざまな便利なメンバ関数が用意されていますが、その中でも知っていると便利な `Array::remove_if(f)` の使い方を紹介します。

`Array::remove_if(f)` は、`Array` の個々の要素 `x` に対して `f(x) == true` となる要素を `Array` から削除します。C++ 標準ライブラリの `v.erase(std::remove_if(v.begin(), v.end(), f), v.end())` と同じ働きです。

## 例 1
`Array<int32>` から、10 未満の要素を削除する。

```C++
# include <Siv3D.hpp>

void Main()
{
	Array<int32> v = { 3, 9, 10, 12, 8, 6, 11 };

	Print << v;

	v.remove_if([](int32 n){ return n < 10; });

	Print << v;

	while (System::Update())
	{

	}
}
```
```
{3, 9, 10, 12, 8, 6, 11}
{10, 12, 11}
```

▼ 上級者向けのテクニックですが、OpenSiv3D の `LessThan` オブジェクトを使ってこのようにも書けます。

```C++
# include <Siv3D.hpp>

void Main()
{
	Array<int32> v = { 3, 9, 10, 12, 8, 6, 11 };

	Print << v;

	v.remove_if(LessThan(10));

	Print << v;

	while (System::Update())
	{

	}
}
```
```
{3, 9, 10, 12, 8, 6, 11}
{10, 12, 11}
```


## 例 2
`Array<int32>` から偶数を削除する。

```C++
# include <Siv3D.hpp>

void Main()
{
	Array<int32> v = { 3, 9, 10, 12, 8, 6, 11 };

	Print << v;

	v.remove_if([](int32 n){ return n % 2 == 0; });

	Print << v;

	while (System::Update())
	{

	}
}
```
```
{3, 9, 10, 12, 8, 6, 11}
{3, 9, 11}
```

▼ 要素と同じ型の値を引数に取り `bool` を返すような関数があれば、それを使うこともできます。
`IsEven(x)` は整数 `x` が偶数なら `true`, そうでなければ `false` を返す関数で、 OpenSiv3D に実装されています。

```C++
# include <Siv3D.hpp>

void Main()
{
	Array<int32> v = { 3, 9, 10, 12, 8, 6, 11 };

	Print << v;

	v.remove_if(IsEven);

	Print << v;

	while (System::Update())
	{

	}
}
```
```
{3, 9, 10, 12, 8, 6, 11}
{3, 9, 11}
```

## 例 3
`Array<Vec2>` で表される点群から、指定した `Rect` 上にある点を削除する。

```C++
# include <Siv3D.hpp>

void Main()
{
	Array<Vec2> points;

	for (int32 i = 0; i < 300; ++i)
	{
		points << RandomVec2(Scene::Rect());
	}

	constexpr Rect rect(200, 200, 400, 200);

	// [rect] で rect をコピーキャプチャし、ラムダ式の中で使えるようにする
	points.remove_if([rect](const Vec2& point){ return point.intersects(rect); });

	while (System::Update())
	{
		rect.drawFrame(1, 0, ColorF(0.3));

		for (const auto& point : points)
		{
			Circle(point, 4).draw();
		}
	}
}
```

## 例 4
モンスターの配列 `Array<Monster>` (`Monster` はユーザが作ったクラス) から、残り寿命が尽きたモンスター (`Monster::isAlive()` が `false` を返す) を削除する。

```C++
# include <Siv3D.hpp>

class Monster
{
private:

	// 位置
	Vec2 m_pos;

	// 残り寿命（秒）
	double m_lifeTimeSec = 0.0;

public:

	Monster() = default;

	Monster(const Vec2& pos, double lifeTimeSec)
		: m_pos(pos)
		, m_lifeTimeSec(lifeTimeSec) {}

	// モンスターの更新（残り寿命を減らす）
	void update(double timeDeltaSec)
	{
		m_lifeTimeSec -= timeDeltaSec;
	}

	const Vec2& getPos() const
	{
		return m_pos;
	}

	// まだ残り寿命があれば true
	bool isAlive() const
	{
		return (m_lifeTimeSec > 0.0);
	}
};

void Main()
{
	// モンスター描画用の絵文字
	const Texture monsterTexture(Emoji(U"👾"));

	Array<Monster> monsters;

	for (int32 i = 0; i < 30; ++i)
	{
		// 画面上のランダムな場所にランダムな寿命のモンスターを作成
		monsters << Monster(RandomVec2(Scene::Rect()), Random(1.0, 8.0));
	}
	
	while (System::Update())
	{
		// 前のフレームからの経過時間
		const double timeDeltaSec = Scene::DeltaTime();

		for (auto& monster : monsters)
		{
			monster.update(timeDeltaSec);
		}

		// 寿命が尽きたモンスターを Array から削除
		monsters.remove_if([](const Monster& m){ return !m.isAlive(); });

		// モンスターを描画
		for (const auto& monster : monsters)
		{
			monsterTexture.scaled(0.5).drawAt(monster.getPos());
		}
	}
}
```


## 似た名前の関数に注意
`Array::removed_if(f)` という関数もありますが、これは削除した結果の新しい `Array` を返す関数で、自分自身は変更しません。

```C++
# include <Siv3D.hpp>

void Main()
{
	Array<int32> v = { 3, 9, 10, 12, 8, 6, 11 };

	Print << v.removed_if(IsEven);

	Print << v;

	while (System::Update())
	{

	}
}
```
```
{3, 9, 11}
{3, 9, 10, 12, 8, 6, 11}
```