std::vector, liboctave の ColumnVector, 配列に共通の関数
STL (標準テンプレートライブラリ) の std::vector とか liboctave の ColumnVector,あるいは普通の配列に対して共通に使えるテンプレート関数の作り方を説明する.
ポイントは
の2点.テンプレートについては,知っていることを前提とします(or 適当な書籍を参照).
ColumnVector の fortran_vec() は
const double* ColumnVector::fortran_vec (void) const;
という関数で*1, ColumnVector のデータが保存されているメモリ領域へのポインタを返す.つまり, ColumnVector::length() 個の要素を持つ double 型配列の先頭ポインタと同等のものを返す.
よって,例えばある「ベクタ」の和を取るテンプレート関数は,次のように作ればよい:
template <typename T, typename ForwardIterator> inline T sum (ForwardIterator first, ForwardIterator last, const T &zero=0.0l) { T res(zero); if (first==last) return res; for (; first!=last; ++first) res += *first; return res; }
ここで T は double など,ベクタの要素の型を表す. ForwardIterator は,インクリメント演算子 (++) によって順番に要素に順番に要素にアクセスでき,間接演算子 (*) によって値を参照できる型であれば何でもよい.例えばポインタや, std::vector
この関数を使うには,
ColumnVector x(10); vector<double> y(10); double z[10];
のそれぞれに対し,
cout<<"sum(x)= "<< sum<double>(x.fortran_vec(), x.fortran_vec()+x.length()) <<endl; cout<<"sum(y)= "<< sum<double>(y.begin(), y.end()) <<endl; cout<<"sum(z)= "<< sum<double>(z, z+sizeof(z)/sizeof(z[0])) <<endl;
のように使う. ForwardIterator の型は自動的に判定されるから明示していない.
サンプルプログラム (g++ でテスト):
#include <iostream> #include <vector> #include <cstdlib> #include <octave/config.h> #include <octave/Matrix.h> inline double u_rand (const double &max) { return (max)*static_cast<double>(rand()) / static_cast<double>(RAND_MAX); } inline double u_rand (const double &min, const double &max) { return u_rand(max - min) + min; } template <typename T, typename ForwardIterator> inline T sum (ForwardIterator first, ForwardIterator last, const T &zero=0.0l) { T res(zero); if (first==last) return res; for (; first!=last; ++first) res += *first; return res; } template <typename T> struct vec2 { T x,y; vec2(void) : x(0),y(0) {}; vec2(T _x, T _y) : x(_x), y(_y) {}; const vec2& operator+=(const vec2 &rhs) {x+=rhs.x; y+=rhs.y; return *this;}; }; template <typename T> inline std::ostream& operator<< (std::ostream& lhs, const vec2<T> &x) { lhs<<"("<<x.x<<", "<<x.y<<")"; return lhs; } using namespace std; int main(int argc, char**argv) { srand((unsigned)time(NULL)); ColumnVector x(10); vector<double> y(10); double z[10]; vec2<double> w[10]; for(int r(0);r<x.length();++r) x(r)=y[r]=z[r]=w[r].x=w[r].y= u_rand(-5.0,5.0); cout<<"x= "<<x.transpose()<<endl; cout<<"sum(x)= "<<sum<double>(x.fortran_vec(),x.fortran_vec()+x.length())<<endl; cout<<"sum(y)= "<<sum<double>(y.begin(),y.end())<<endl; cout<<"sum(z)= "<<sum<double>(z,z+sizeof(z)/sizeof(z[0]))<<endl; cout<<"sum(w)= "<<sum(w,w+sizeof(w)/sizeof(w[0]),vec2<double>(0.0,0.0))<<endl; return 0; }
サンプルには,(x,y) の二つの要素を持つテンプレートクラス vec2 の和の例も盛り込んである.この場合は sum 関数の第3引数 zero を明示する必要がある一方, sum の T 型は zero から判断できるから省略してもいい.
追記 (2008/11/10)
手軽に利用するために,
inline double* CVbegin (ColumnVector &vec) { return vec.fortran_vec(); } inline const double* CVbegin (const ColumnVector &vec) { return vec.fortran_vec(); } inline double* CVend (ColumnVector &vec) { return vec.fortran_vec()+vec.length(); } inline const double* CVend (const ColumnVector &vec) { return vec.fortran_vec()+vec.length(); }
のような関数群を用意しておくとよい.
*1:実は Array