よくある数値計算のパターンとして以下のような感じのがあります(別に数値計算に限らないのですが).
while(アルゴリズムの停止判定){
数値の更新
}で,「繰り返し」->「繰り返しの英語は"iteration"」->「"iteration"を行うのは"iterator"」という意味不明な連想により,こういう数値計算のパターンをiteratorでやってみたら面白いんじゃないかという発想をだいぶ前から考えていたのですが・・・.
で,とりあえず例題としてexp(x)の数値計算をiteratorでやるとどうなるかをやってみます(expの数値計算にはもっと効率的な方法がありますが,とりあえずシンプルな形でやってます).
#include <iostream>
#include <iomanip>
#include <iterator>
#include <cmath>
#include <boost/iterator/iterator_facade.hpp>
class exp_iterator
: public boost::iterator_facade<
exp_iterator,
double,
boost::single_pass_traversal_tag,
double
>
{
friend class boost::iterator_core_access;
public:
// デフォルトコンストラクタはiterationを
// 停止するためのpass-the-endなiteratorを生成
// (std::istream_iteratorのデフォルトコンストラクタと同じ発想)
exp_iterator() : is_pte(true)
{ }
// exp(x)を計算するiteratorを生成
exp_iterator(double x)
: x(x), sum(1.0), term(1.0), n(1), is_pte(false)
{ }
exp_iterator(exp_iterator const &r)
: x(r.x), sum(r.sum), term(r.term), n(r.n), is_pte(r.is_pte)
{ }
private:
void increment()
{
// increment1回がアルゴリズムの繰り返し部分1回に対応
term *= x / n;
sum += term;
++n;
}
double dereference() const
{ return sum; }
bool equal(exp_iterator const &rhs) const
{
return
// 左辺が収束していて右辺がpass-the-endの場合
is_converged() && rhs.is_pte
// または左辺がpass-the-endで右辺が収束している場合
|| is_pte && rhs.is_converged()
// または両辺ともpass-the-endの場合
|| is_pte && rhs.is_pte;
}
bool is_converged() const
{
// アルゴリズムの停止条件
return std::abs(term / sum) < 1.0e-10; // 1.0e-10は許容相対誤差
}
double x, sum, term;
std::size_t n;
bool is_pte; // イテレータがpass-the-endかどうか
};
int main()
{
std::cout << std::setprecision(15);
std::cout << "級数計算によるexp(1.0)の数値の収束の様子" << std::endl;
std::copy(exp_iterator(1.0), exp_iterator(),
std::ostream_iterator<double>(std::cout, "\n"));
std::cout << std::endl;
for(exp_iterator it = exp_iterator(2.0); it != exp_iterator(); ++it);
std::cout << "exp(2.0) = " << *it << std::endl;
}うーん.本人的には結構(゜д゜)ウマーなんじゃないかと密かに思っていたりするんですが,こういうことやってるの他で見たことが無いので,多分実際に使ったら「読みにくい or 使いにくい」で一蹴されてしまいそうな悪寒(というか確信).一応この先の拡張の方向として,停止判定をコールバックで出来るようにするとか,アルゴリズムが停止しない状況に対処できるようにするとか色々あるにはあるんですが・・・.