幾何ベクトルを表現する

物理シミュレーション、ゲーム、その他で幾何ベクトルの計算をしたいケースは多いと思います。

先の投稿でも三次元のベクトルを表現するクラスを定義したものがありました。

http://qiita.com/NatsukiLab/items/9148a48f05c5f8d45600

いくつか不満があったので、私なりに改良したものを作ってみました。 何番煎じだかわからないくらい似たようなものは色んなところで書かれていると思いますが。

以下にコードを示します。

```cpp

// -*- mode: c++ -*-

#ifndef EUCLIDEAN_VECTOR_HEADER__
#define EUCLIDEAN_VECTOR_HEADER__

#include <type_traits>
#include <iostream>
#include <tuple>
#include <functional>
#include <cmath>

template<int n, class T, template<class...> class S, class... U>
struct repeat_type {
  typedef typename repeat_type<n-1, T, S, U..., T>::type type;
};

template<class T, template<class...> class S, class... U>
struct repeat_type<0, T, S, U...> {
  typedef S<U...> type;
};

template<int degree, class T> class euclidean_vector;

template<class S, class... T>
euclidean_vector<sizeof...(T)+1, S> make_euclidean_vector(S arg, T... args) {
  return euclidean_vector<sizeof...(T)+1, S>(arg, args...);
}

template<int n, class U>
std::ostream&
operator<<(std::ostream&, const euclidean_vector<n, U>&);

template<int degree, class T=double>
class euclidean_vector :public repeat_type<degree, T, std::tuple>::type {
public:
  template<class... U>
  euclidean_vector(U... args) : repeat_type<degree, T, std::tuple>::type(args...) {}

  euclidean_vector(void) : repeat_type<degree, T, std::tuple>::type() {}

private:
  template<int n, class U>
  friend std::ostream&
  operator<<(std::ostream&, const euclidean_vector<n, U>&);

  typedef typename repeat_type<degree, T, std::tuple>::type type;

  template<int n>
  typename std::enable_if<n==degree && n!=0>::type
  print_impl(std::ostream& os) const {
    os << ")";
  }

  template<int n>
  typename std::enable_if<n!=degree && n!=0>::type
  print_impl(std::ostream& os) const {
    print_impl<n+1>(os << ", " << std::get<n>(*this));
  }

  template<int n>
  typename std::enable_if<n==0 && n!=degree>::type
  print_impl(std::ostream& os) const {
    print_impl<1>(os << "(" << std::get<0>(*this));
  }

  template<int n>
  typename std::enable_if<n==0 && n==degree>::type
  print_impl(std::ostream& os) const {
    os<<"()";
  }

  void print(std::ostream& os) const {
    print_impl<0>(os);
  }

  template<int n, class... U>
  typename std::enable_if<n==degree, euclidean_vector>::type
  transform_impl(const std::function<T(T)>&, U... args) const {
    return make_euclidean_vector(args...);
  }

  template<int n, class... U>
  typename std::enable_if<n!=degree, euclidean_vector>::type
  transform_impl(const std::function<T(T)>& func, U... args) const {
    return transform_impl<n+1>(func, args..., func(std::get<n>(*this)));
  }

  template<int n, class... U>
  typename std::enable_if<n==degree, euclidean_vector>::type
  zipwith_impl(const std::function<T(T,T)>& func, const euclidean_vector&, U... args) const {
    return make_euclidean_vector(args...);
  }

  template<int n, class... U>
  typename std::enable_if<n!=degree, euclidean_vector>::type
  zipwith_impl(const std::function<T(T, T)>& func, const euclidean_vector& x, U... args) const {
    return zipwith_impl<n+1>(func, x, args..., func(std::get<n>(*this), std::get<n>(x)));
  }

  template<int n>
  typename std::enable_if<n==degree, euclidean_vector>::type
  foreach_impl(const std::function<void(T)>&) const {
  }

  template<int n>
  typename std::enable_if<n!=degree, euclidean_vector>::type
  foreach_impl(const std::function<void(T)>& func) const {
    func(std::get<n>(*this));
    foreach_impl<n+1>(func);
  }

public:
  void foreach(const std::function<void(T)>& func) const {
    foreach_impl<0>(func);
  }

  euclidean_vector transform(std::function<T(T)>&& func) const {
    return transform_impl<0>(func);
  }

  euclidean_vector operator-(void) const {
    return transform(std::negate<T>());
  }

  euclidean_vector zipwith(std::function<T(T,T)>&& func, const euclidean_vector &x) const {
    return zipwith_impl<0>(func, x);
  }

  euclidean_vector operator+(const euclidean_vector& x) const {
    return zipwith(std::plus<T>(), x);
  }

  euclidean_vector operator+=(const euclidean_vector& x) {
    return *this=*this+x;
  }

  euclidean_vector operator-(const euclidean_vector& x) const {
    return zipwith(std::minus<T>(), x);
  }

  euclidean_vector operator-=(const euclidean_vector& x) {
    return *this=*this-x;
  }

  T operator*(const euclidean_vector& x) const {
    T acc=0;
    zipwith(std::multiplies<T>(), x).foreach([&acc](T e) -> void { acc+=e; });
    return acc;
  }

  euclidean_vector operator*(const T& x) const {
    return transform([x](T e) -> T { return e*x; });
  }

  euclidean_vector& operator*=(const euclidean_vector& x) {
    *this=*this*x;
    return *this;
  }

  euclidean_vector& operator*=(const T& x) {
    *this=*this*x;
    return *this;
  }

  euclidean_vector operator/(const T& x) const {
    return transform([x](T e) -> T { return e/x; });
  }

  euclidean_vector& operator/=(const T& x) {
    *this=*this/x;
    return *this;
  }
};

template<int n, class T>
T abs(const euclidean_vector<n, T> x) {
  T acc=0;
  x.foreach([&acc](T e)->void { acc+=e*e; });
  return std::sqrt(acc);
}

template<int n, class T>
euclidean_vector<n, T> operator*(const T& x, const euclidean_vector<n, T>& y) {
  return y*x;
}

template<int n, class T>
std::ostream&
operator<<(std::ostream& os, const euclidean_vector<n, T>& x) {
  x.print(os);
  return os;
}

#endif
```

数学的には vector という名前を使いたいところですが、 C++ のライブラリには既に vector があるので区別のために euclidean_vector と名付けました。 冗長だと感じるならば、利用の際には適当な別名を付けて下さい。

# 改良点

では、どんな点を改良したのか説明していきます。

## 次数を指定できる

ここで euclidean_vector はふたつのテンプレートパラメタを受け取る型です。 そのふたつは次数と要素の型です。 三次元以外のベクトルを定義することが出来ます。

例えば要素が double 型の四次元ベクトルの宣言は以下のようにします。

```cpp
euclidean_vector<4, double> foo(1, 2, 3, 4);
```

## ストリームに出力できる

差込み演算子でストリームに出力することが出来ます。

例えば、

```cpp
std::cout << euclidean_vector<3, int>(1, 2, 3) <<std::endl;
```

とすると

```
(1, 2, 3)
```

のように表示されます。

## ヘルパ関数

幾何ベクトルオブジェクトを生成するヘルパ関数を用意しました。

型を明記せずとも自動で推論してくれる (もちろん明記してもよいですが) ので書くのが楽になることもあるでしょう。

以下のような要領で使います。

```cpp
std::cout << make_euclidean_vector(1, 2, 3) <<std::endl;
```

## タプルからの派生

euclidean_vector は tuple から派生する形で定義しています。 つまり、 tuple に対してできる操作は euclidean_vector に対しても出来るということです。

例えば、 get などのアクセサも適用することが可能です。

```cpp
euclidean_vector<4, double> foo(1, 2, 3, 4);
std::cout << std::get<0>(foo) <<std::endl;
```

ですがこれは失敗だったかもしれないと思います。 要素の型が全て同じという前提であればタプルにする意義は何もありません。 実際に使ってみるとイテレータなどが使えないことがずいぶんと手間に思えました。

## その他

あとは元ネタ同様に各種演算が可能です。

```cpp
auto x = make_euclidean_vector(1.0, 2.0, 3.0);
auto y = make_euclidean_vector(1.0, 2.0, 3.0);

std::cout << x+y    << std::endl; // ベクトル同士の足し算
std::cout << x-y    << std::endl; // ベクトル同士の引き算
std::cout << x*y    << std::endl; // ベクトルの内積
std::cout << x*2.0  << std::endl; // ベクトルのスカラ倍
std::cout << 2.0*x  << std::endl; // ベクトルのスカラ倍、順序を変えても OK
std::cout << x/2    << std::endl; // ベクトルをスカラ値で割る
std::cout << abs(x) << std::endl; // 絶対値の計算。 cmath の abs をオーバーロードしてある
```