开发者

subarray through index: how to?

I am trying to implement a simple matlab-like array (now only one dimension actually), what I tried to do is to implement the following matlab codes:

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

I know that the std has valarray to do this through a slice array. I do not know much details on it. The code is:

#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;
}

The final a is not changed at all since we are working on a temp array. I know the function operator"> is not properly implemented, but I do not know how to do this. Anyone can give me a hint? Thanks


I would create an ArraySlice class, and return an instance of that from operator []. This class would have a reference to the original Array, and would need to re-implement most members as forward calls to the Array. For instance, ArraySlice::operator[] would call Array::operator[] with the appropriate index.


I think that for shared arrays the best solution is to have a single type for both the original (full) matrix and the "views". By parametrizing the element access you can have the same generic code work for both, also if you add an optional std::vector element inside the class that will contain the actual data for the original full matrix then memory handling becomes automatic.

This is a small implementation of this idea... for the selection I've used an std::vector of pairs of integers; assigning to ArraySelection will use the element access operator so it will work both for the original matrix or for views.

The main program allocates a 10x10 matrix, and then creates four different 5x5 views with elements with coordinates that are even/even, even/odd, odd/even and odd/odd. Those views are set to 4 different constant values. Then a selection is done on the full matrix and an assignment is done on the selected elements. Finally the original full matrix is printed.

#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;
}


Andrea, Thank you for the hint. Mostly what you said makes sense and really helps. I create another ind_array and keep the original array's address and now it works! The only change is the operator[] now returns the ind_array. and the operator= for ind_array is defined to do the real assignment. (to make it simple, I now removed the second dimension)

Here is the modified code:

#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;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜