子数组通过索引:如何

subarray through index: how to?

本文关键字:如何 索引 数组      更新时间:2023-10-16

我试图实现一个简单的类似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根本没有改变,因为我们正在处理一个临时数组。我知道函数运算符">没有正确实现,但我不知道如何做到这一点。有人能给我点提示吗?由于

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

我认为对于共享数组,最好的解决方案是对原始(完整)矩阵和"视图"都具有单一类型。通过参数化元素访问,你可以让相同的泛型代码为两者工作,如果你在类中添加一个可选的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;
}

Andrea,谢谢你的提示。你说的大部分都很有道理,也很有帮助。我创建了另一个ind_array并保留了原始数组的地址,现在它工作了!唯一的变化是操作符[]现在返回ind_array。并且为ind_array定义了operator=来执行实际赋值。(为了简单起见,我现在删除了第二次元)

下面是修改后的代码:

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