Skip to content
This repository was archived by the owner on Feb 18, 2023. It is now read-only.

StreamAPI

orisano edited this page Nov 21, 2016 · 6 revisions

Cranberries Stream API Library 爆誕

Cranberries Libraries の最新作ができました。 今回はストリームAPIライブラリででっす!

ストリームAPIとは

Java8のStream APIが一番近い。 C#のLINQも近い。 C++ではRange Adaptorが近い。

ストリームはイテレータのラップ。 レンジに対する処理を連続的に記述できる。

ストリームの構築とオペレーション

ストリームの利用には、3段階ある。 中間処理は遅延評価され、 終端処理が呼ばれたときに評価される。

  1. ストリームの構築
  2. 中間処理
  3. 終端処理

また、ストリームには無限ストリームと有限ストリームがある。 有限ストリームにはすべてのオペレーションが適用できるが、 無限ストリームは適用できるオペレーションが制限される。 これはオペレーションがレンジを要求することによる制限である。

ストリームのオペレーションはoperator >>によるメソッドチェインまたは、 メンバ関数のメソッドチェインによって行う。

ストリーム
  >> 中間処理1()
  >> 中間処理2()
  >> 中間処理3()
  >> 終端処理()
  ;

または、

ストリーム
  .中間処理1()
  .中間処理2()
  .中間処理3()
  .終端処理()
  ;

有限ストリームと無限ストリーム

  • 有限ストリーム

有限の要素を持つストリーム。 レンジを持つのでレンジ特有の操作(リバースやソート、シャッフルなど)を含めて、 すべての中間処理・終端処理が行える。

  • 無限ストリーム

無限の要素を持つストリーム。 Generate(生成関数による無限列)、 Iterate(漸化式による数列)、 Cyclic(範囲繰り返し数列)、 Random(乱数列)、 Distribution(分布乱数列) などがある。 レンジを要求する操作、 すべての終端処理が行えない。

ストリームの生成

ストリームの構築方法は主に3つある。

  1. 有限ストリームのコンストラクタによる構築
  2. make_streamクラスのstatic関数よる構築
  3. stream_builderによる構築

有限ストリームのコンストラクタによる構築

stream<int>{ 1,2,3,4 }; // initializer_listによるコンストラクタ
std::vector<int> v{1,2,3,4};
stream<int>{ v }; // rangeを引数に取るコンストラクタ
stream<int>{ v.begin(), v.end() }; // イテレータペアを引数に取るコンストラクタ

make_streamクラスのstatic関数よる生成

make_streamクラスのstatic関数をつかって、 色々なストリームを生成できる。

  • make_stream::of

有限ストリームの構築。

値のセットからストリームを作る。 イテレータペアを引数に取るものと、初期化子リストを引数に取るものがある。

template <
  typename Iterator,
  typename T = typename std::decay_t<Iterator>::value_type
>
static
stream<T>
of(Iteraotr&& first, Iterator&& last);

template < typename T >
static
stream<T>
of(std::initializer_list<T> il);

Example)

std::vector<int> vec{ 1, 2, 3 };

make_stream::of( vec.begin(), vec.end() );

make_stream::of( { 1, 2, 3 } );
  • make_stream::from

有限ストリームの構築。

レンジからストリームを生成する。 Rangeはbegin, endでイテレータを取れる必要がある。 また、stringsplitしてストリームを構築することができる。

template <
  typename Range,
  typename T = typename std::decay_t<Range>::value_type
>
static
stream<T>
from(Range&&);

Example)

std::vector<int> vec{ 1, 2, 3 };

make_stream::from( vec );

make_stream::from( "123:456:789" | streams::split(":") ); // { "123", "456", 789" }
  • make_stream::range

有限ストリームの構築。

初期値(第1引数)から始まり、 上界(第2引数)まで、 step(第3引数デフォルト値は1)ずつ増えていく有限数列を生成できる。

template < typename T >
static
stream<T>
range(T first, T upper_bound, T step = T{1});
make_stream::range(1,10); // { 1, 2, ... , 9 }

make_stream::range(1,10,2); // { 1, 3, 5, 7, 9 }
  • make_stream::Iterate

無限順次ストリームの構築。

漸化式による列生成。 初期値(第1引数)を与え、 前項を引数に取る関数(第2引数)により次項を決定していく。

template <
  typename T,
  typename UnaryOperator Op
>
static
IterateStream<T,UnaryOperator>
iterate(T seed_, UnaryOperator operator_);

Example)

// フィボナッチ数列の生成
make_stream::iterate( 1,
[ prev1 = 0 , prev2 = 0]( auto&& a ) mutable {
  return prev2 = prev1, prev1 = a, prev1 + prev2;
}); // { 1, 1, 2, 3, 5, 8, 13, ... }
  • make_stream::counter

無限順次ストリームの構築。

初期値を指定して、インクリメントしていく。 ほぼiterateのラップであるが、初期値をインクリメントするのでiterateより速い可能性がある。

template < typename Iterable >
static
CountingStream<Iterable>
counter(Iterable seed);
  • make_stream::generate

無限順次ストリームの構築。

Supplier(引数)によって生成される値による無限列の生成。

template < typename Supplier >
static
GenerateStream<std::result_of_t<Supplier()>,Supplier>
generate(Supplier supplier);

Example)

// { { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, ... }
make_stream::generate(
    []{ return std::vector<int>{1,2,3}; }
  );
  • make_stream::repeat

無限順次ストリーム、または有限ストリームの構築。

特定の値を繰り返す無限列を生成する。 繰り返しに使用する値(第1引数)を指定する。 また、繰り返し回数(第2引数)を指定して有限ストリームを生成するオーバーロードもある。

template < typename T >
static
GenerateStream<T,Generator<T>>
repeat(T val);

template < typename T >
static
stream<T>
repeat(T val, std::size_t lim);

Example)

make_stream::repeat(1); // { 1, 1, 1, ... }

make_stream::repeat(1, 4); // { 1, 1, 1, 1 }
  • make_stream::cyclic

無限順次ストリームの構築。

範囲繰り返し列の生成。 繰り返す範囲を初期化子リスト、レンジ、イテレータペアのいずれかで指定する。

template < typename T >
static
CyclicStream<T>
cyclic(std::initializer_list<T> il);

template < typename Range >
static
CyclicStream<typename std::decay_t<Range>::value_type>
cyclic(Range&& range);

template < typename Iterator >
static
CyclicStream<typename std::decay_t<Iterator>::value_type>
cyclic(Iterator&& first, Iterator&& last);

Example)

make_stream::cyclic( { 1, 2, 3 } ); // { 1, 2, 3, 1, 2, 3, 1, ... }

std::vector<int> vec{ 1, 2, 3 };

make_stream::cyclic( vec );

make_stream::cyclic( vec.begin(), vec.end() );

###乱数列・分布乱数列の無限ストリーム

乱数列を生成するためには、乱数生成エンジンが必要である。 乱数生成エンジンを使うには初期シードが必要である。 したがって、乱数ストリーム・分布乱数ストリームにはこれらの情報を渡してやる必要がある。

乱数生成エンジンを指定するにはテンプレート引数を用いる。 指定しなければ、std::mt19937になる。

Example)

make_stream::random<std::default_random_engine>();

初期シードは引数の最後に指定する。 指定しなければ、std::random_deviceによって生成される。

Example)

make_stream::random( 0 );

分布乱数ストリームでは返り値の型を指定する事ができる。 第1テンプレート引数で指定できる。 この仕様のため、エンジンを指定する場合にはちょいとややこしい。 例えば、exponential_distributionをラップしたストリームの場合を考える。

exponential_distribution

template <class RealType = double> class exponential_distribution;

のように宣言されている。 RealTypeを省略するとdoubleになる。 ストリームもこれに習っている。

make_stream::exponential_distは次のように宣言されており、 Engineの指定をする場合、 RealTypeの指定を省略できない。

template <
  typename Engine = std::mt19937,
  typename RealType = double,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator<
    RealType,
    Init,
    std::exponential_distribution<RealType>,
    Engine,
    Seed
  >
>
exponential_dist
(
  Init exponent,
  Seed seed = std::random_device{}()
);

乱数列ストリームはGenerateStreamになる、 第2テンプレート引数はxxx_distributionをラップした GeneralizedDistributionGeneratorクラスが指定されます。 大抵の場合長いので以後、GeneralizedDistributionGeneratorの引数は省略します。

  • make_stream::random

無限乱数ストリームの構築。

std::randomのラップ。 必要な場合、 テンプレート第1引数でエンジンを指定し、 引数でシードを指定する。

template <
  typename Engine = std::mt19937,
  typename Seed = unsigned
>
GenerateStream<Seed, Engine>
make_stream::random
(
  Seed&& seed = std::random_device{}()
);
  • make_stream::uniform_dist

無限乱数ストリームの構築。

std::uniform_int_distribution, std::uniform_real_distributionのラップ。

ResultTypeintegral_typeの場合はstd::uniform_int_distributionに、 floating_pointの場合は std::uniform_real_distributionが自動的に選択されます。 範囲の指定がそのままResultTypeの推定になるので、テンプレート引数の指定を省略できます。

template <
  typename ResultType,
  typename Engine = std::mt19937,
  typename Seed = unsigned
>
static
GenerateStream<
  ResultType,
  UniformGen</* 省略 */>
>
uniform_dist
(
  ResultType min,
  ResultType max,
  Seed seed = std::random_device{}()
);

Example)

make_stream::uniform_dist( 1, 10 ); // uniform_int_distribution<int>(1, 10) による分布乱数列

make_stream::uniform_dist( 1ull, 10ull ); // uniform_int_distribution<unsigned long long>(1, 10) による分布乱数列

make_stream::uniform_dist( 1.0, 2.0 ); // uniform_real_distribution<double>(1.0, 2.0) による分布乱数列

make_stream::uniform_dist<long double>(0.0, 1.0); // uniform_real_distribution<long double>(0.0, 1.0) による分布乱数列
  • make_stream::bernoulli_dist

無限乱数ストリームの構築。

std::bernoulli_distributionのラップ。

第1引数で確率を指定、必要に応じて第2引数でシードを指定する。

bernoulli_distributionは生成される値はboolしかありえないので、 第1テンプレート引数でエンジンを指定できる。

template <
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  bool,
  GeneralizedDistributionGenerator</* 省略 */>
>
bernoulli_dist
(
  Init probability,
  Seed seed = std::random_device{}()
);

Example)

auto coin_flips = make_stream::bernoulli_dist(0.5);
  • make_stream::poisson_dist

無限乱数ストリームの構築。

poisson_distributionのラップ。 第1引数で平均値を指定、必要に応じて第2引数でシードを指定する。

template <
  typename IntType = int,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  IntType,
  GeneralizedDistributionGenerator</* 省略 */>
>
poisson_dist
(
  Init avarage,
  Seed seed = std::random_device{}()
);

Example)

make_stream::poisson_dist(1.0);

make_stream::poisson_dist<long>(1.0)
  • make_stream::exponential_dist

無限乱数ストリームの構築。

exponential_distributionのラップ。 第1引数で指数を指定、必要に応じて第2引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator<
    RealType,
    Init,
    std::exponential_distribution<RealType>,
    Engine,
    Seed
  >
>
exponential_dist
(
  Init exponent,
  Seed seed = std::random_device{}()
);

Example)

make_stream::exponential_dist(1.0);
  • make_stream::gamma_dist

無限乱数ストリームの構築。

gamma_distributionのラップ。 第1引数で形状母数を、 第2引数で尺度母数を、 必要に応じて第3引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator</* 省略 */>
>
gamma_dist
(
  Init shape_param,
  Init scale_param,
  Seed seed = std::random_device{}()
)

Example)

// 形状母数1.0, 尺度母数1.0 によるガンマ分布乱数列
make_stream::gamma_dist( 1.0, 1.0 );
  • make_stream::weibull_dist

無限乱数ストリームの構築。

weibull_distributionのラップ。 第1引数で形状母数を、 第2引数で尺度母数を、 必要に応じて第3引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator</* 省略 */>
>
weibull_dist
(
  Init shape_param,
  Init scale_param,
  Seed seed = std::random_device{}()
)

Example)

// 形状母数1.0, 尺度母数1.0 によるWeibull分布乱数列
make_stream::weibull_dist( 1.0, 2.0 );
  • make_stream::extreme_value_dist

無限乱数ストリームの構築。

extreme_value_distributionのラップ。 第1引数で位置変数を、 第2引数で尺度変数を、 必要に応じて第3引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator</* 省略 */>
>
extreme_value_dist
(
  Init location_param,
  Init scale_param,
  Seed seed = std::random_device{}()
)

Example)

// 位置変数 1.0, 尺度変数 2.0 の極値分布乱数列
make_stream::extreme_value_dist( 1.0, 2.0 );
  • make_stream::normal_dist

無限乱数ストリームの構築。

normal_distributionのラップ。 第1引数で平均値を、 第2引数で標準偏差を、 必要に応じて第3引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator</* 省略 */>
>
normal_dist
(
  Init average,
  Init standard_deviation,
  Seed seed = std::random_device{}()
)

Example)

// 平均値 5.0, 標準偏差 1.0 の正規分布乱数列
make_stream::normal_dist( 5.0, 1.0 );
  • make_stream::lognormal_dist

無限乱数ストリームの構築。

lognormal_distributionのラップ。 第1引数で平均値を、 第2引数で標準偏差を、 必要に応じて第3引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator</* 省略 */>
>
lognormal_dist
(
  Init average,
  Init standard_deviation,
  Seed seed = std::random_device{}()
)

Example)

// 平均値 3.14, 標準偏差 1.0 の対数正規分布乱数列
make_stream::lognormal_dist( 3.14, 1.0 );
  • make_stream::chi_squared_dist

無限乱数ストリームの構築。

chi_squared_distributionのラップ。

第1引数で自由度、 必要に応じて第2引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator<
    RealType,
    Init,
    std::chi_squared_distribution<RealType>,
    Engine,
    Seed
  >
>
chi_squared_dist
(
  Init degree_of_freedom,
  Seed seed = std::random_device{}()
)

Example)

// 自由席 1.0 による カイ2乗分布乱数列
make_stream::chi_squared_dist( 1.0 );
  • make_stream::cauchy_dist

無限乱数ストリームの構築。

cauchy_distributionのラップ。 第1引数で位置変数を、 第2引数で尺度変数を、 必要に応じて第3引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator</* 省略 */>
>
cauchy_dist
(
  Init location_param,
  Init scale_param,
  Seed seed = std::random_device{}()
)

Example)

// 位置変数 1.0, 尺度母数 1.0のコーシー分布による無限乱数列
make_stream::cauchy_dist( 1.0, 1.0 ) >> taken( 3 ) >> print_to();
  • make_stream::fisher_f_dist

無限乱数ストリームの構築。

fisher_f_distributionのラップ。 第1引数で一つ目の自由度、 第2引数で二つ目の自由度、 必要に応じて第3引数で初期シードを指定する。

template <
  typename RealType = double,
  typename Engine = std::mt19937,
  typename Init,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  GeneralizedDistributionGenerator</* 省略 */>
>
fisher_f_dist
(
  Init degree_of_freedom1,
  Init degree_of_freedom2,
  Seed seed = std::random_device{}()
)

Example)

自由度 5, 10 のフィッシャーのf分布による無限乱数列
make_stream::fisher_f_dist( 5, 10 )
  • make_stream::student_t_dist

無限乱数ストリームの構築。

student_t_distributionのラップ。 第1引数で自由度、 必要に応じて第2引数で初期シードを指定する。

 template <
 typename RealType = double,
 typename Engine = std::mt19937,
 typename Init,
 typename Seed = unsigned
>
static
GenerateStream<
 RealType,
 GeneralizedDistributionGenerator</* 省略 */>
>
student_t_dist
(
 Init degree_of_freedom,
 Seed seed = std::random_device{}()
)

Example)

// 自由度 1.0 のスチューデントのt分布による無限乱数列
make_stream::student_t_dist( 1.0 );
  • make_stream::generate_canonical

無限乱数ストリームの構築。

generate_canonicalのラップ。 [ 0.0, 1.0 ) の区間に展開された一様分布乱数を生成する。

template <
  typename RealType = double,
  size_t Bits = std::numeric_limits<RealType>::digits,
  typename Engine = std::mt19937,
  typename Seed = unsigned
>
static
GenerateStream<
  RealType,
  CanonicalGen<RealType,Bits,Engine>
>
generate_canonical
(
  Seed seed = std::random_device{}()
)

stream_builder

stream_builder<T>クラステンプレートを用いて、 手動で逐次値を追加した後ストリームを構築することができます。

stream_builderクラスには値を追加するために、 addメンバ関数が用意されています。 または、operator<<を用いて値を追加する事ができます。

stream_builder<T>からstream<T>を構築するには、 buildメンバ関数を用います。 または、<< buildとすることもできます。

Example)

stream<int> x = stream_builder<int>{}
  .add(1)
  .add(2)
  .add(3)
  .build();

stream<int> y = stream_builder<int>{}
  << 1 << 2 << 3 << 4
  << build;

ストリームのオペレーション

オペレーションの種類

  • 中間処理
    • 非レンジ要求処理
    • レンジ要求処理
  • 終端処理

無限ストリームに適用できるのは、非レンジ要求の中間処理のみ。 有限ストリームにはすべてのオペレーションが適用できる。

中間処理は終端処理が呼ばれるまで評価が遅延される。

アダプター形式とメンバ関数形式の両方がある。 中間処理の場合は名前が異なる。

オペレーション形式 関数名が動詞 関数名が形容詞
アダプター形式 関数名は受動態 関数名は副詞形
メンバ関数形式 関数名は原型 関数名は形容詞

Example)

オペレーション形式 関数名が動詞 関数名が形容詞
アダプター形式 filtered uniquely
メンバ関数形式 filter unique

非レンジ要求処理

中間処理の中で非レンジ要求処理だけが無限ストリームに適用できる。

フィルター、写像、マージ、結合、重複削除など20種類。

  • filter(filtered)

条件に一致する要素を取り除く。

引数にストリーム要素型を受け取りboolを返す関数オブジェクトを渡す。

Example)

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  >> filtered( [](auto&& a){ return a%2 ==0; } ) // { 1,3,5,7 } 
  ;

make_stream::range(1,9)
  .filter( [](auto&& a){ return a%2 ==0; } )
  ;

make_stream::counter(1) // [ 1,2,3,.. )
  >> filtered( [](auto&& a){ return a%2 ==0; } ) // [ 1,3,5,... )
  ;
  • stride(strided)

要素をn刻みで選択する。

引数で刻み幅nを指定する。

Example)

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  >> strided( 2 ) // { 1,3,5,7 }
  ;

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  .stride( 3 ) // { 1,4,7 }
  ;

make_stream::counter(1) // [ 1,2,3,... )
  >> strided( 3 ) // [ 1,4,7,... )
  ;
  • drop(dropped)

先頭のn要素を取り除く。

引数で要素数nを指定する。

Example)

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  >> dropped( 4 ) // { 5,6,7,8 }
  ;

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  .drop( 3 ) // { 4,5,6,7,8 }
  ;

make_stream::counter(1) // [ 1,2,3,4,5... )
  >> dropped( 4 ) // [ 5,6,7,... ) 
  ;
  • drop_while(dropped_while)

条件を満たさなくなるまで先頭要素を取り除く。

引数でストリームの要素型を引数にとりboolを返す関数オブジェクトを渡す。

Example)

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  >> dropped_while( [](auto&& a){ return a < 5; } ) // { 5,6,7,8 }
  ;

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  .drop_while( [](auto&& a){ return a < 5; } ) // { 5,6,7,8 }
  ;

make_stream::cyclic({ 1,2,3 }) // [ 1,2,3,1,2,3,... )
  >> dropped_while( [](auto&& a){ return a < 3; } ) // [ 3,1,2,3,1,2,3,... )
  ;
  • take(taken)

先頭のn要素を選択する。

要素数nを引数で指定する。

このオペレーションを無限ストリームに適用すると有限ストリームに変換される。

Example)

make_stream(1,9) // { 1,2,3,4,5,6,7,8 }
  >> taken( 3 ) // { 1,2,3 }
  ;

make_stream::range(1,9)
  .take( 5 ) // { 1,2,3,4,5 }
  ;

make_stream::counter(1) // [ 1,2,3,... )
  >> taken( 4 ) // { 1,2,3,4 }
  ;
  • take_while(taken_while)

条件を満たさなくなるまで先頭要素を選択する。

引数でストリームの要素型を引数にとりboolを返す関数オブジェクトを渡す。

このオペレーションを無限ストリームに適用すると有限ストリームに変換される。

Example)

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  >> taken_while( [](auto&& a){ return a < 4; } ) // { 1,2,3 }
  ;

make_stream::range(1,9) // { 1,2,3,4,5,6,7,8 }
  .take_while( [](auto&& a){ return a < 4; } ) // { 1,2,3 }
  ;

make_stream::counter(1) // [ 1,2,3,... )
  >> taken_while( [](auto&& a){ return a < 4; } ) // { 1,2,3 }
  ;
  • slice(sliced)

m番目の要素からn番目の要素を選択する。

引数で位置n,mを指定する。

このオペレーションを無限ストリームに適用すると有限ストリームに変換される。

Example)

make_stream(1,9) // { 1,2,3,4,5,6,7,8 }
  >> sliced( 2, 5 ) // { 2,3,4,5 }
  ;

make_stream::range(1,9) //  // { 1,2,3,4,5,6,7,8 }
  .slice( 3,3 ) // { 3 }
  ;

make_stream::counter(1) // [ 1,2,3,... )
  >> sliced( 3, 5 ) // { 3,4,5 }
  ;
  • transform(transformed)

全単射。全要素に関数を適用し、返り値により要素を書き換える。

引数でストリームの要素型を引数にとり、何らかの値を返す関数オブジェクトを指定する。

Example)

make_stream::range(1,5) // { 1,2,3,4 }
  >> transformed( [](auto&& a){ return a*2; } ) // { 2,4,6,8 }
  ;

make_stream::range(1,5) // { 1,2,3,4 }
  .transform( [](auto&& a){ return a*2; } ) // { 2,4,6,8 }
  ;

make_stream::counter(1) // [ 1,2,3,... )
  >> transformed( [](auto&& a){ return a*2; } ) // [ 2,4,6,... )
  ;
  • transform_to(transformed_to)

要素をstatic_castにより型変換する。 std::stringが指定された場合はstd::to_stringを候補に加えたADLによってto_stringされる。

テンプレート引数で変換先の型を指定する。

make_stream::range(1,4) // { 1,2,3 }
  >> transformed_to<std::string>() // { "1", "2", "3" }
  ;

make_stream::range(1,3) // { 1,2,3 }
  .transform_to<double>() // { 1.0, 2.0, 3.0 }
  ;

make_stream::counter(1) // [ 1,2,3,... )
  >> transformed_to<std::string>() // [ "1", "2", "3", ... )
  ;
  • flat_transform(flat_transformed)

一対多の写像。ひとつの要素から複数の要素に変換する。

引数でストリームの要素型を引数にとり、多値を返す関数オブジェクトを指定する。 関数オブジェクトが返すことが許可される型は

  1. 配列型
  2. レンジコンテナ
  3. tuple

の3つである。

レンジコンテナとはstd::begin(),std::end()でイテレータを取得できるコンテナを指す。 また、tupleの要素型はすべて一致していなければならない。

Example)

make_stream::range( 1, 4 ) // { 1,2,3 }
  >> flat_transformed( []( auto const& a ) { return std::make_tuple( a, a ); } ) // { 1,1,2,2,3,3 }
  ;
make_stream::range( 1, 4 ) // [ 1, 4 )
  >> flat_transformed( []( auto const& a ) { return std::vector<std::decay_t<decltype(a)>>( a, a ); } ) // { 1,2,2,3,3,3 }
  ;
make_stream::range( 1, 4 ) // [ 1, 4 )
  >> flat_transformed( []( auto const& a ) { return make_stream::range(0,a); } ) // { 0,0,1,0,1,2 }
  ;
make_stream::counter(1)
  >> flat_transformed([](auto const& a) { return std::make_tuple(a, a); }) // [ 1,1,2,2,3,3,... ]
  ;
  • flat(flatten) [無限ストリーム未実装]

ストリームの要素型がレンジである場合に、レンジの要素型のストリームにフラット化します。

Example)

make_stream::generate( []{ return std::vector<int>{1,2,3}; } ) // stream<std::vector<int>
  >> taken(3) // { { 1,2,3 }, { 1,2,3 }, { 1,2,3 } }
  >> flatten() // stream<int>
  ;            // { 1,2,3,1,2,3,1,2,3 }
  • all_flat(all_flatten) [無限ストリーム未実装]

ストリーム要素型の多重レンジコンテナを完全にフラット化します。

Example)

std::vector<std::vector<int>> aa = {
  {1,2,3},
  {1,2,3},
  {1,2,3}
};

make_stream::of( { aa, aa } ) // stream<std::vector<std::vector<int>>>
  >> all_faltten()  // stream<int>
  ;                 // { 1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3 }
  • merged

ソート済みストリームとソート済みストリーム(レンジ)をマージする。 ソート済みかどうかのチェックなど一切行わない。

無限ストリームと有限ストリーム(レンジ)をマージすることができる。 その場合、結果が無限ストリームになる。

Example)

// 有限ストリーム+有限ストリーム
make_stream::range( 1, 3 )
.merge(
  make_stream::range( 1, 3 )
)
;

// 有限ストリーム+無限ストリーム
make_stream::range( 1, 5 )
>> merged(
  make_stream::counter( 1 )
)
;

// 無限ストリーム+レンジ
make_stream::counter( 1 )
>> merged(
  std::vector<int>{1,2,3,4,5}
)
;
  • concat

ストリームの結合。

  • peeked

  • replaced

  • replaced_if

  • distinctly

  • uniquely

  • invoked

レンジ要求処理

レンジを前提とした処理で有限ストリームに適用できる。

シャッフル、集合演算、ソートなど、10種類。

終端処理

終端処理が呼ばれると、遅延されていたすべての中間処理が評価されたあと、 終端処理が評価される。

出力、グループ化、コンテナへの変換、集計、条件判定など23種類。