Rokiのチラ裏

学生による学習のログ

Effective tuple algorithm

下書きに眠っていたので投稿。タプル生成に関するアレコレを書いた。主に個人的にBoost.fusionで納得のいかないものを実装。

分割

たまにやりたい時がある。一々記述するよりは書いてしまった方が良いだろう。

exp:

#include<tuple>
#include<srook/tuple/tuple_split.hpp>
#include<srook/algorithm/for_each.hpp>
#include<iostream>

int main()
{
    constexpr auto t=std::make_tuple(40,"hoge","foo","var",4.2f);  
    constexpr auto split=srook::tuple_split<std::tuple_size<std::decay_t<decltype(t)>>::value/2>(t);
    const auto disp=[](const auto& x){std::cout<<x<<" ";};

    srook::for_each(split.first,disp); std::cout<<std::endl;
    srook::for_each(split.second,disp); std::cout<<std::endl;
}

output:

40 hoge
foo var 4.2

削除とフィルター

まずは、インデックス指定された要素の削除から。これは、先ほどのtuple_splitを使えば簡単に実装できる。

exp:

#include<srook/tuple/tuple_erase.hpp>
#include<srook/algorithm/for_each.hpp>

int main()
{
    constexpr auto t=std::make_tuple(42,"hoge","foo",34);
    srook::for_each(srook::tuple_erase_at<1>(t),[](const auto& x){std::cout<<x<<std::endl;});
}

output:

42
foo
34

次はフィルターだ。既存のタプルから条件にマッチする型のみを残したタプルを生成する。

exp:

#include<iostream>
#include<srook/algorithm/for_each.hpp>
#include<srook/tuple/tuple_filter_type.hpp>
#include<boost/type_index.hpp>

struct W{};
struct X:W{};
struct Y:X{};
struct Z{};

template<class T>
using is_derived_from_W=std::is_base_of<W,T>; // 述語条件.

int main()
{
    constexpr auto tp=std::make_tuple(W(),X(),Y(),Z());
    constexpr auto result=srook::tuple_filter_type<is_derived_from_W>(tp); // Wを継承している型の値のみを残したタプルを生成.
    
    srook::for_each(std::move(result),[](const auto& x){std::cout<<boost::typeindex::type_id<decltype(x)>().pretty_name()<<std::endl;});
}

output:

W
X
Y

リバース

リバースはとても簡単だ。以下のように逆順のシーケンスを生成するメタ関数を作れば良い。

後は適用するだけだ。

exp:

#include<srook/tuple/tuple_reverse.hpp>
#include<srook/algorithm/for_each.hpp>

int main()
{
    constexpr auto t=std::make_tuple(42,"hoge","foo");
    constexpr auto result=srook::tuple_reverse(std::move(t));
    srook::for_each(result,[](const auto& x){std::cout<<x<<std::endl;});
}

output:

foo
hoge
42

BinaryPredicateを受け取るequal_range

std::tupleには標準でタプル同士が等しいか判定するop==があるが、その条件を変える事はできないので、書いておく。

少し複雑な再帰になってしまった。equalの基準は標準アルゴリズムstd::equalと同じになるようにしてある。比較する値を同じ型同士で行うために(変換可能な型同士での比較を基準としたければ、それを満たすpredを渡せば実現できる)、順次同じ型のタプルをコンパイル時に新たに生成する。機能として、通常のコンテナも受け付けるようにしたが、その場合は単に標準アルゴリズムに投げるだけだ。
exp:

#include<srook/algorithm/equal.hpp>
#include<iostream>
#include<string>
#include<vector>


int main()
{
    using namespace std::string_literals;

    const auto pred=[](const auto& x,const auto& y)noexcept{return x!=y;};

    auto t1=std::make_tuple("hoge"s,42,29,"hoge"s);
    auto t2=std::make_tuple("hoge"s,42,29,"foo"s);
    
    std::cout<<std::boolalpha<<srook::equal(t1,t2,pred)<<std::endl;

    std::vector<int> v1{1,2,3};
    const auto v2=v1;
    
    std::cout<<std::boolalpha<<srook::equal(v1,v2,pred)<<std::endl;
}

output:

false
false

rotate

rotateは…特にしたいと思った時は正直ないが、作れるぶんには作っておこう。

exp:

#include<srook/tuple/tuple_rotate.hpp>
#include<srook/algorithm/for_each.hpp>

int main()
{
    constexpr auto t=std::make_tuple(42,"hoge",12.3f,"foo");
    constexpr auto result=srook::tuple_rotate(t);

    srook::for_each(result,[](const auto& x){std::cout<<x<<std::endl;});
}

output:

foo
42
hoge
12.3

count

同じ型の同じ値をカウントする。これは簡単に実装できる。

exp:

#include<srook/algorithm/count.hpp>
#include<string>
#include<vector>
#include<iostream>

int main()
{
    using namespace std::string_literals;

    std::cout<< srook::count(std::make_tuple(42,"hoge"s,12.3f,"foo"s,42),42) <<std::endl;
    std::cout<< srook::count(std::vector<int>{1,2,3,1},1) <<std::endl;
}

output:

2
2

ベクトル演算

各要素の型がconvertibleで、指定された演算子が各要素の型で提供されている場合にベクトル演算を行う。

exp:

#include<srook/tuple/tuple_vector_operation.hpp>
#include<srook/algorithm/for_each.hpp>
#include<string>

int main()
{
    using namespace std::string_literals;

    const auto t1=std::make_tuple(42,"ho"s,"va"s);
    const auto t2=std::make_tuple(42,"ge"s,"r"s);
    const auto t3=std::make_tuple(42," hoge"s," var"s,"margin");

    const auto result1=srook::tuple_vector_operation<std::plus>(t1,t2);
    const auto result2=srook::tuple_vector_operation<std::plus>(t1,t2,t3);

    const auto print=[](const auto& x){std::cout<<x<<std::endl;};

    srook::for_each(result1,print);
    std::cout<<std::endl;
    srook::for_each(result2,print);
}

output:

84
hoge
var

126
hoge hoge
var var
margin

後は何か欲しい機能とかあるかなあ。欲しい機能思いつけば追記する。