通过索引的子阵列:如何?

时间:2011-08-11 22:03:21

标签: c++

我正在尝试实现一个简单的类似matlab的数组(现在实际上只有一个维度),我尝试做的是实现以下matlab代码:

a=1:10;
ind=find(a>5);
a[ind]=5;

我知道std有valarray通过切片数组来做到这一点。我不太了解它的细节。代码是:

#include <iostream>
using namespace std;
template <typename T> class array
{
public:
    int m,n; //two dimensional at most
    T *pdata;
    //construct the array
    array(){m=n=0;pdata=NULL;} //default is empty matrix
    array(T a){m=n=1;pdata=new T[1];*pdata=a;} //array for scalar: array a=10;
    array(int m0,int n0=1) {m=m0;n=1;pdata=new T[m];}
    array(const array& a,int len=-1);
    //destructor
    ~array() {delete []pdata;}
    //operator overloading
    array<T>& operator+=(T s);
    T& operator[](int i) {return pdata[i];}
    array<T>& operator[](array<int> ind);
    array<T>& operator=(const array<T>& a);
    array<T>& operator=(T a) {for(int i=0;i<m;i++) pdata[i]=a;return *this;}
    array<bool> operator>(T a);
    array<bool> operator<(T a);
    array<bool> operator==(T a);
};

//copy a part of the other array
template <typename T> array<T>::array<T>(const array<T>& a,int len)
{
    if(len==-1) len=a.m*a.n;
    if(len==0) {m=0;n=0;pdata=NULL;}
    if(len>0)
    {
        m=len;n=1;
        pdata=new T[len];
        for(int i=0;i<len;i++) pdata[i]=a.pdata[i];
    }
}

template <typename T> array<T>& array<T>::operator +=(T s)
{
    for(int i=0;i<m*n;i++) pdata[i]+=s;
    return *this;
}

//this function does not meet the purpose, it returns a reference to a temp obj
template <typename T> array<T>& array<T>::operator[](array<int> ind)
{
    array<T> ret(ind.m,ind.n);
    for(int i=0;i<ind.m*ind.n;i++)
    {
        ret.pdata[i] = pdata[ind.pdata[i]];
    }
    return ret;
}

template <typename T> array<bool> array<T>::operator>(T a)
{
    array<bool> res(m*n);
    for(int i=0;i<m*n;i++) res.pdata[i]=pdata[i]>a;
    return res;
}

//helper function
array<int> find(array<bool> a)
{
    array<int> ret(a.m,a.n); //first use the same size space
    int len=0;
    for(int i=0;i<a.m*a.n;i++)
    {
        if(a.pdata[i]) {ret.pdata[len]=i;len++;}
    }
    return array<int>(ret,len);
}

/*ostream& operator<<(array<T>& a)
{
    ostream os;
    for(int i=0;i<a.m*a.n;i++) os>>a[i]>>'\t';
    return os;
}*/

int main()
{
    array<float> a(10);
    for(int i=0;i<10;i++) a[i]=i;
    for(i=0;i<10;i++) cout<<a[i]<<'\t';
    cout<<endl;
    array<int> ind=find(a>5);
    for(i=0;i<ind.m;i++) cout<<ind[i]<<'\t';
    cout<<endl;
    a[ind]=5;//this will not work on the original array
    //how do we support this????undefined
    for(i=0;i<10;i++) cout<<a[i]<<'\t';
    cout<<endl;

    return 0;
}

由于我们正在处理临时数组,因此最终的a根本没有改变。 我知道函数运算符“&gt;未正确实现,但我不知道如何执行此操作。任何人都可以给我一个提示?谢谢

3 个答案:

答案 0 :(得分:1)

我会创建一个ArraySlice类,并从operator []返回一个实例。此类将引用原始Array,并且需要将大多数成员重新实现为Array的前向调用。例如,ArraySlice::operator[]会使用适当的索引调用Array::operator[]

答案 1 :(得分:0)

我认为对于共享数组,最好的解决方案是为原始(完整)矩阵和“视图”提供单一类型。 通过参数化元素访问,您可以为两者使用相同的通用代码,如果在类中添加一个可选的std :: vector元素,它将包含原始完整矩阵的实际数据,那么内存处理将自动进行。

这是这个想法的一个小实现...对于选择我使用了std::vector个整数对;分配给ArraySelection将使用元素访问运算符,因此它既适用于原始矩阵,也适用于视图。

主程序分配10x10矩阵,然后使用坐标为偶数/偶数,偶数/奇数,奇数/偶数和奇数/奇数的元素创建四个不同的5x5视图。 这些视图设置为4个不同的常量值。 然后在完整矩阵上进行选择,并对所选元素进行分配。最后打印出原始的完整矩阵。

#include <stdexcept>
#include <vector>
#include <functional>
#include <algorithm>

#include <stdio.h>

typedef std::vector< std::pair<int, int> > Index;

template<typename T>
struct Array;

template<typename T>
struct ArraySelection
{
    Array<T>& a;
    Index i;

    ArraySelection(Array<T>& a)
        : a(a)
    {
    }

    ArraySelection& operator=(const T& t)
    {
        for (int j=0,n=i.size(); j<n; j++)
            a(i[j].first, i[j].second) = t;
        return *this;
    }
};


template<typename T>
struct Array
{
    int rows, cols;
    std::vector<T*> rptr;
    int step;

    std::vector<T> data; // non-empty if data is owned

    T& operator()(int r, int c)
    {
        return rptr[r][c * step];
    }

    Array(int rows, int cols,
          Array *parent = NULL,
          int row0=0, int rowstep=1,
          int col0=0, int colstep=1)
        : rows(rows), cols(cols), rptr(rows)
    {
        if (parent == NULL)
        {
            // Owning matrix
            data.resize(rows*cols);
            for (int i=0; i<rows; i++)
                rptr[i] = &data[i*cols];
            step = 1;
        }
        else
        {
            // View of another matrix
            for (int i=0; i<rows; i++)
                rptr[i] = &((*parent)(row0 + i*rowstep, col0));
            step = colstep;
        }
    }

    template<typename F>
    ArraySelection<T> select(const F& f)
    {
        Index res;
        for (int i=0; i<rows; i++)
            for (int j=0; j<cols; j++)
                if (f((*this)(i, j)))
                    res.push_back(std::make_pair(i, j));
        ArraySelection<T> ar(*this);
        ar.i.swap(res);
        return ar;
    }

    // Copy construction of a full matrix makes a full matrix,
    // Copy construction of a view creates a view
    Array(const Array& other)
        : rows(other.rows), cols(other.cols), rptr(other.rptr), step(other.step)
    {
        if (other.data)
        {
            data = other.data;
            for (int i=0; i<rows; i++)
                rptr[i] = &data[i*cols];
        }
    }

    // Assignment is element-by-element optionally with conversion
    template<typename U>
    Array& operator=(const Array<U>& other)
    {
        if (other.rows != rows || other.cols != cols)
            throw std::runtime_error("Matrix size mismatch");
        for(int i=0; i<rows; i++)
            for (int j=0; j<cols; j++)
                (*this)(i, j) = other(i, j);
        return *this;
    }
};

int main()
{
    Array<double> a(10, 10);
    Array<double> a00(5, 5, &a, 0, 2, 0, 2);
    Array<double> a01(5, 5, &a, 0, 2, 1, 2);
    Array<double> a10(5, 5, &a, 1, 2, 0, 2);
    Array<double> a11(5, 5, &a, 1, 2, 1, 2);
    for (int i=0; i<5; i++)
        for (int j=0; j<5; j++)
        {
            a00(i, j) = 1.1;
            a01(i, j) = 2.2;
            a10(i, j) = 3.3;
            a11(i, j) = 4.4;
        }

    a.select(std::binder2nd< std::greater<double> >(std::greater<double>(), 3.5)) = 0;

    for (int i=0; i<10; i++)
    {
        for (int j=0; j<10; j++)
        {
            printf(" %0.3f", a(i, j));
        }
        printf("\n");
    }
    return 0;
}

答案 2 :(得分:0)

安德烈,谢谢你的提示。你所说的大部分都是有道理的,真的很有帮助。我创建另一个ind_array并保留原始数组的地址,现在它可以工作了! 唯一的变化是operator []现在返回ind_array。并且operator = for ind_array被定义为执行实际赋值。 (为了简单起见,我现在删除了第二个维度)

以下是修改后的代码:

#include <iostream>
using namespace std;
template <typename T> class ind_array;
template <typename T> class array
{
public:
    int len; //two dimensional at most
    T *pdata;
    //construct the array
    array(){len=0;pdata=NULL;} //default is empty matrix
    //array(T a){len=1;pdata=new T[1];*pdata=a;} //array for scalar: array a=10;
    array(int m0) {len=m0;pdata=new T[len];}
    array(const array& a,int len0=-1);
    //destructor
    ~array() {delete []pdata;}
    int size() const {return len;}
    //operator overloading
    array<T>& operator+=(T s);
    T& operator[](int i) {return pdata[i];}
    ind_array<T> operator[](const array<int>& ind);//{return (ind_array(ind,pdata));}
    array<T>& operator=(const array<T>& a);
    array<T>& operator=(T a) {for(int i=0;i<len;i++) pdata[i]=a;return *this;}
    array<bool> operator>(T a);
    array<bool> operator<(T a);
    array<bool> operator==(T a);
};

//Index array or similar indirect-array as in valarray
//this class shall keeps the array's address and the index
template <typename T> class ind_array
{
    array<int> ind; //an index array
    T* ptr; //a pointer to the original data
public:
    int size() const {return ind.size();} 
    void operator=(T a){for(int i=0;i<size();i++) ptr[ind[i]]=a;} //assignment a value to a subarray
    //how to construct the indx array then?
    //according to valarry, the default constructor shall be prohibited
    ind_array(const array<int>& indx,T* pt):ind(indx),ptr(pt){} //default constructor
};

//copy a part of the other array
template <typename T> array<T>::array<T>(const array<T>& a,int len0)
{
    if(len0==-1) len0=a.len;
    if(len0==0) {len=0;pdata=NULL;}
    if(len0>0)
    {
        len=len0;
        pdata=new T[len];
        for(int i=0;i<len;i++) pdata[i]=a.pdata[i];
    }
}

template <typename T> array<T>& array<T>::operator +=(T s)
{
    for(int i=0;i<len;i++) pdata[i]+=s;
    return *this;
}

//this function does not meet the purpose, it returns a reference to a temp obj
//now we change it to return a indx_array which stores the original array's address
template <typename T> ind_array<T> array<T>::operator[](const array<int>& ind)
{
    /*array<T> ret(ind.len);
    for(int i=0;i<ind.len;i++)
    {
        ret.pdata[i] = pdata[ind.pdata[i]];
    }
    return ret;*/
    return (ind_array<T>(ind,pdata)); //call the constructor
}

template <typename T> array<bool> array<T>::operator>(T a)
{
    array<bool> res(len);
    for(int i=0;i<len;i++) res.pdata[i]=pdata[i]>a;
    return res;
}

//helper function
array<int> find(array<bool> a)
{
    array<int> ret(a.len); //first use the same size space
    int len=0;
    for(int i=0;i<a.len;i++)
    {
        if(a.pdata[i]) {ret.pdata[len]=i;len++;}
    }
    return array<int>(ret,len);
}

/*ostream& operator<<(array<T>& a)
{
    ostream os;
    for(int i=0;i<a.m*a.n;i++) os>>a[i]>>'\t';
    return os;
}*/

int main()
{
    array<float> a(10);
    for(int i=0;i<10;i++) a[i]=i;
    for(i=0;i<10;i++) cout<<a[i]<<'\t';
    cout<<endl;
    array<int> ind=find(a>5);
    for(i=0;i<ind.len;i++) cout<<ind[i]<<'\t';
    cout<<endl;
    a[ind]=5;//this will not work on the original array
    //how do we support this????undefined
    for(i=0;i<10;i++) cout<<a[i]<<'\t';
    cout<<endl;

    return 0;
}