好的,我已经在谷歌上搜索了太久,我只是不确定如何称呼这种技术,所以我认为最好在 SO 上问一下。如果它有一个明显的名称和/或我忽略的解决方案,请指出我正确的方向。
对于外行来说:张量是矩阵的逻辑扩展,就像矩阵是向量的逻辑扩展一样。向量是 rank-1 张量(在编程术语中,一维数字数组),矩阵是 rank-2 张量(二维数字数组),然后 rank-N 张量只是 ND 数字数组.
现在,假设我有这样的张量类:
template<typename T = double> // possibly also with size parameters
class Tensor
{
private:
T *M; // Tensor data (C-array)
// alternatively, std::vector<T> *M
// or std::array<T> *M
// etc., or possibly their constant-sized versions
// using Tensor<>'s template parameters
public:
... // insert trivial fluffy stuff here
// read elements
const T & operator() (size_t a, size_t b) const {
... // error checks etc.
return M[a + rows*b];
}
// write elements
T & operator() (size_t a, size_t b) {
... // error checks etc.
return M[a + rows*b];
}
...
};
使用 的这些定义operator()(...)
,索引/分配单个元素然后具有相同的调用签名:
Tensor<> B(5,5);
double a = B(3,4); // operator() (size_t,size_t) used to both GET elements
B(3,4) = 5.5; // and SET elements
将其扩展到任意张量等级是相当简单的。但我希望能够实现的是一种更高级的索引/分配元素的方式:
Tensor<> B(5,5);
Tensor<> C = B( Slice(0,4,2), 2 ); // operator() (Slice(),size_t) used to GET elements
B( Slice(0,4,2), 2 ) = C; // and SET elements
// (C is another tensor of the correct dimensions)
我知道std::valarray
(以及许多其他人)已经做了非常相似的事情,但仅仅完成行为并不是我的目标;我的目标是学习如何优雅、高效、安全地将以下功能添加到我的Tensor<>
课程中:
// Indexing/assigning with Tensor<bool>
B( B>0 ) += 1.0;
// Indexing/assigning arbitrary amount of dimensions, each dimension indexed
// with either Tensor<bool>, size_t, Tensor<size_t>, or Slice()
B( Slice(0,2,FINAL), 3, Slice(0,3,FINAL), 4 ) = C;
// double indexing/assignment operation
B(3, Slice(0,4,FINAL))(mask) = C; // [mask] == Tensor<bool>
.. etc.
请注意,我打算operator[]
将operator()
. 或者,我将更多地坚持std::vector<>
使用.at()
检查版本的方法的方法operator[]
。无论如何,这是一个设计选择,除了现在的问题。
我想出了以下不完整的“解决方案”。此方法仅对向量/矩阵(rank-1 或 rank-2 张量)真正可管理,并且具有许多不良副作用:
// define a simple slice class
Slice ()
{
private:
size_t
start, stride, end;
public:
Slice(size_t s, size_t e) : start(s), stride(1), end(e) {}
Slice(size_t s, size_t S, size_t e) : start(s), stride(S), end(e) {}
...
};
template<typename T = double>
class Tensor
{
... // same as before
public:
// define two operators() for use with slices:
// version for retrieving data
const Tensor<T> & operator() (Slice r, size_t c) const {
// use slicing logic to construct return tensor
...
return M;
{
// version for assigning data
Sass operator() (Slice r, size_t c) {
// returns Sass object, defined below
return Sass(*this, r,c);
}
protected:
class Sass
{
friend class Tensor<T>;
private:
Tensor<T>& M;
const Slice &R;
const size_t c;
public:
Sass(Tensor<T> &M, const Slice &R, const size_t c)
: M(M)
, R(R)
, c(c)
{}
operator Tensor<T>() const { return M; }
Tensor<T> & operator= (const Tensor<T> &M2) {
// use R/c to copy contents of M2 into M using the same
// Slice-logic as in "Tensor<T>::operator()(...) const" above
...
return M;
}
};
但这只是感觉不对...
对于上面列出的每个索引/分配方法,我必须为每个这样的操作定义一个单独的Tensor<T>::Sass::Sass(...)
构造函数、一个 newTensor<T>::Sass::operator=(...)
和一个 new 。Tensor<T>::operator()(...)
此外,Tensor<T>::Sass::operators=(...)
将需要包含许多已经在对应的相同内容,并且使所有内容都Tensor<T>::operator()(...)
适合任意等级使这种方法变得非常丑陋,过于冗长,更重要的是,完全无法管理。Tensor<>
所以,我的印象是有一种更有效的方法来解决所有这些问题。
有什么建议么?