STL (標準テンプレートライブラリ) の std::vector とか liboctave の ColumnVector,あるいは普通の配列に対して共通に使えるテンプレート関数の作り方を説明する.
ポイントは
- テンプレートを使うことと,
- ColumnVector の fortran_vec() メンバ関数を使うこと
の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<T>::iterator などが該当する.
この関数を使うには,
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();
}
のような関数群を用意しておくとよい.
ちなみに STL の numeric には accumulate という関数があり,これは上で作った sum と同じ機能をもちます.
http://www.cplusplus.com/reference/std/numeric/accumulate.html このへんを参照.