potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20 Rangesプロジェクションの動作確認用コード

動作確認用に作成したコードのメモです。以下のコードではstd::ranges::range<T>コンセプトを満たす型をレンジあるいはrange<T>として表記することもあります。

プロジェクションとは?

プロジェクションはアルゴリズムがレンジ(Ranges)の各要素に変換や判定のような処理を加える前に呼び出すcallable-type(関数呼び出し演算子を適用できる型)です。クラスや構造体のフィールド、関数の実行と処理を分解して、それぞれを再利用しやすくすることを想定しているそうです。

range<optional<int>>の各要素の空判定を出力する。

range<optional<int>>はメンバー関数のポインタ&std::optional<int>::has_valueでプロジェクションできます。プロジェクション結果は空判定結果(bool)です。

#include <optional>

#include <algorithm>
#include <iostream>
#include <ranges>

int main()
{
    std::optional<int> values[]{ 0, 1, std::nullopt, 2, std::nullopt };

    auto print_boolalpha = [](bool f) {std::cout << std::boolalpha << f << std::endl; };
    std::ranges::for_each(values, print_boolalpha, &std::optional<int>::has_value);
    // 出力:true, true, false, true, false

    return 0;
}

range<optional<int>>の無効値を-1に変換して各要素を出力する。

range<optional<int>>に対するプロジェクションとしてラムダ式を適用する例です。ここではrangeの要素を引数としたラムダ式value_or_minus_1の結果が引数を出力する処理関数printへ渡されます。

#include <optional>

#include <algorithm>
#include <iostream>
#include <ranges>

int main()
{
    std::optional<int> values[]{ 0, 1, std::nullopt, 2, std::nullopt };

    auto print = [](const int& value) {std::cout << value << std::endl; };
    auto value_or_minus_1 = [](const std::optional<int>& value) { return value.value_or(-1); };
    std::ranges::for_each(values, print, value_or_minus_1);
    // 出力:
    // 0, 1, -1, 2, -1

    return 0;
}

range<tuple>からstringを抜き出す。

std::identity{}でプロジェクション結果をそのまま返せます。この場合は素直に抜き出したほうが良いかもしれません。

#include <string>
#include <tuple>
#include <vector>

#include <algorithm>
#include <iostream>
#include <ranges>
#include <utility>

int main()
{
    std::tuple<int, std::string> tuples[]{ {0, "0"}, {1, "1"}, {2, ""}, {3, "3"}, {4, ""} };

    std::vector<std::string> v1;
    auto get_string = [](const auto& value) { return std::get<std::string>(value); };
    std::ranges::transform(tuples, std::back_inserter(v1), std::identity{}, get_string);

    std::ranges::for_each(v1, [](auto&& value) { std::cout << value << std::endl; });
    // 出力:"0", "1", "", "3", ""

    return 0;
}

range<tuple>からタプル内のstring型要素が空のタプルだけコピーしたvector<tuple>を作成する。

std::ranges::copy_ifは判別関数にプロジェクションで取得した値を渡しますが、コピーされる値は渡したタプルそのものであることに注意してください。

#include <string>
#include <tuple>
#include <vector>

#include <algorithm>
#include <iostream>
#include <ranges>

int main()
{
    std::tuple<int, std::string> tuples[]{ {0, "0"}, {1, "1"}, {2, ""}, {3, "3"}, {4, ""} };

    std::vector<std::tuple<int, std::string>> v1;
    auto get_string = [](const auto& value) { return std::get<std::string>(value); };
    auto is_string_empty = [](const std::string& s) { return s.empty(); };
    std::ranges::copy_if(tuples, std::back_inserter(v1), is_string_empty, get_string);

    std::ranges::for_each(v1,
        [](const auto& tuple) { std::cout << "{" << std::get<int>(tuple) << ", \"" << std::get<std::string>(tuple) << "\"}" << std::endl; });
    // 出力:{2, ""}, {4, ""}

    return 0;
}