Help us understand the problem. What is going on with this article?

【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 未満の要素を削除する。

# 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 オブジェクトを使ってこのようにも書けます。

# 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> から偶数を削除する。

# 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 に実装されています。

# 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 上にある点を削除する。

# 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 を返す) を削除する。

# 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 を返す関数で、自分自身は変更しません。

# 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}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away