【问题标题】:Apply same function over different dimensions of an multiple dimension vector在多维向量的不同维度上应用相同的函数
【发布时间】:2012-02-29 12:53:17
【问题描述】:

首先,对不起,我不太懂 c++,也许我的问题有点愚蠢。 我有一个多维向量 M。我希望能够沿行 i 的元素或沿列 j 的元素应用相同的函数。我不想两次编写相同的函数。可能以一种相当简单的方式做到这一点,比如一些重载或使用虚拟迭代器?谁能写一个简单的例子?谢谢。

【问题讨论】:

  • 你的多维向量是什么样的?

标签: c++ multidimensional-array iterator virtual


【解决方案1】:

您可以在列上定义自己的迭代器,以便您可以使用标准算法(如另一个答案中提到的for_eachtransform)将您的函数应用于数组的行或列,只需改变迭代器:

#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>

// Custom iterator to iterate over columns
//   to be adapted to the underlying storage
class ColIterator : public std::iterator<std::forward_iterator_tag, double>
{
public:
  typedef std::vector<std::vector<double> >    MDarray;

  ColIterator(MDarray & array, int i, int j) : array_(array), i_(i), j_(j) {}
  ColIterator(const ColIterator& it) : array_(it.array_), i_(it.i_), j_(it.j_) {}

  ColIterator& operator++() {
    ++i_;
    return *this;
  }

  ColIterator  operator++(int) {
    ColIterator tmp(*this);
    operator++();
    return tmp;
  }

  bool operator==(const ColIterator& rhs) { return &array_==&rhs.array_ && i_==rhs.i_ && j_==rhs.j_; }
  bool operator!=(const ColIterator& rhs) { return !operator==(rhs); }
  double& operator*() {return array_[i_][j_];}

private:
  MDarray & array_;
  int i_;
  int j_;
};


// a function
void mult2 (double & x) {
  x *= 2;
}



int main () {
  typedef std::vector<double>::iterator RowIterator;

  int nRows = 5;
  int nCols = 5;
  ColIterator::MDarray array (nRows, std::vector<double>(nCols, 1));


  // Apply function mult2 to column 3
  int col = 3;
  ColIterator beginCol (array, 0, col);
  ColIterator endCol   (array, nRows, col);

  std::for_each(beginCol, endCol, mult2);


  // Apply function mult2 to row 4
  int row = 4;
  RowIterator beginRow (array[row].begin());
  RowIterator endRow   (array[row].end());

  std::for_each(beginRow, endRow, mult2);


  // Check results
  for (int i=0 ; i<nRows ; ++i) {
    for (int j=0 ; j<nCols ; ++j) {
      std::cout << " " << array[i][j];
    }
    std::cout << std::endl;
  }

  return 0;
}

【讨论】:

    【解决方案2】:

    解决此问题的一个好方法是使用std::transform。有关更多详细信息,请参阅此link。下面是如何对行执行此操作的简短示例。列部分有点棘手。


    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int nRowCnt = 3, nColCnt = 3;
    
    int RowFunc(int i) { return ++i; }
    int ColFunc(int i) { return --i; }
    
    void PrintArray(vector<vector<int>>& vecArray, int nRowCnt, int nColCnt)
    {
        for (int nOuter = 0; nOuter < nRowCnt; nOuter++)
        {
            for (int nInner = 0; nInner < nColCnt; nInner++)
            {
                cout<<vecArray[nOuter][nInner]<<" ";
            }
            cout<<endl;
        }
    }
    
    int main()
    {
        vector< vector<int> > vecVals(nRowCnt, vector<int>(nColCnt,0));    
        vector< int > rowOut(nColCnt*nRowCnt,0), colOut(nColCnt*nRowCnt,0);
        vector<int>::iterator itrOut;
    
        for (int nRow = 0; nRow < nRowCnt; nRow++)
        {
            for (int nCol = 0; nCol < nColCnt; nCol++)
            {
                vecVals[nRow][nCol] = nRow * (10+nCol) ;
            }
        }
    
    
        PrintArray(vecVals,nRowCnt,nColCnt);
    
        itrOut = rowOut.begin();
        for (int nOuter = 0; nOuter < nRowCnt; nOuter++)
        {
            std::transform(vecVals[nOuter].begin(),vecVals[nOuter].end(),itrOut,RowFunc);
            itrOut += nColCnt;
        }
    
        itrOut = colOut.begin();
        for (int nOuter = 0; nOuter < nRowCnt; nOuter++)
        {
            for (int nInner = 0; nInner < nColCnt; nInner++)
            {
                std::transform( vecVals[nInner].begin() + nOuter, vecVals[nInner].begin() + nOuter +1, itrOut,ColFunc);
                itrOut++;
            }
        }
    
        cout<<endl<<"Row Transformed"<<endl;
        for (itrOut = rowOut.begin(); itrOut != rowOut.end(); itrOut++)
            cout<<*itrOut<<" ";
    
        cout<<endl<<"Col Transformed"<<endl;
        for (itrOut = colOut.begin(); itrOut != colOut.end(); itrOut++)
            cout<<*itrOut<<" ";
    
        cout<<endl;
        return 0;
    }
    

    但有一个问题,列部分不适用于非方形二维数组(即,行数和列数必须相同)。我想这可以通过更多的思考来解决。

    【讨论】:

      【解决方案3】:

      如果您的多维向量是一个实际的多维向量,例如 std::vector&lt;std::vector&lt;int&gt;&gt;,不建议这样做,那么您将不得不编写自己的迭代器。这不是很复杂。 Boost.Iterator 有一些概念可以用来帮助实现它。

      如果您的多维向量是单个向量,其大小设置为维度的乘积(即宽度 * 高度),这是处理此问题的首选方法,那么它会容易得多。可以使用 Boost.Range 提供的实用程序来完成。

      这是一个使用 Boost.Range 的快速而肮脏的例子。使用decltype 可以让它更漂亮一点。如果您的编译器不支持 C++11(特别是 auto),我不建议使用它,因为代码变得非常难以阅读。

      template<typename T>
      boost::iterator_range<typename T::iterator> 
      GetRow(T& vec, typename T::size_type row, typename T::size_type w, 
      typename T::size_type h) {
          return boost::make_iterator_range(
            vec.begin() + (row * w), 
            vec.begin() + ((row + 1) * w)
          );
      }
      
      template<typename T>
      boost::strided_range<boost::iterator_range<typename T::iterator>> 
      GetColumn(T& vec, typename T::size_type col, typename T::size_type w, 
      typename T::size_type h) {
          boost::iterator_range<typename T::iterator> range = boost::make_iterator_range(
            vec.begin() + col, 
            vec.begin() + col + (h - 1) * w + 1
          );
          return boost::strided_range<boost::iterator_range<typename T::iterator>>(w, range);
      }
      

      然后使用这些函数非常容易,但如果你的编译器不支持auto,它会变得非常难看。

      const size_t WIDTH = 3;
      const size_t HEIGHT = 3;
      std::vector<int> vec(WIDTH * HEIGHT);
      
      // Fill the first row with 1.
      auto row =  GetRow(vec, 0, WIDTH, HEIGHT);
      for (auto it = row.begin(); it != row.end(); ++it) {
          (*it) = 1;
      }
      
      // Fill the second column with 2.
      auto col = GetColumn(vec, 1, WIDTH, HEIGHT);
      for (auto it = col.begin(); it != col.end(); ++it) {
          (*it) = 2;
      }
      
      // Contents of vec is:
      // 1 2 1
      // 0 2 0
      // 0 2 0
      

      您可能还想研究 Boost.MultiArray,这是一个用于此类事情的库。它提供了你想要的功能,但它绝对不是最友好的库。

      【讨论】:

        【解决方案4】:

        对于二维向量:

        行将很简单:

        const std::vector<int>& getRow( const std::vector<std::vector<int>>& input, int rowIdx )
        {
            return input.at( rowIdx );
        }
        

        列有点棘手:

        std::vector<int> getColumn( const std::vector<std::vector<int>>& input, int colIdx )
        {
            std::vector<int> output;
            for ( unsigned i = 0; i < input.size(); ++i )
                output.push_back( input.at( i ).at( colIdx ) );
            return output;
        }
        

        这些函数基本上采用二维整数向量并根据指定索引返回行/列向量。

        这是您尝试做的事情的基础(据我所知)。稍作修改将允许您有选择地将函数应用于行/列,而不是仅仅返回它们。

        【讨论】:

        • 在第一种情况下(行),返回const std::vector&lt;int&gt;&amp;不是更好吗?
        猜你喜欢
        • 1970-01-01
        • 2014-03-09
        • 1970-01-01
        • 2021-12-31
        • 1970-01-01
        • 2017-07-13
        • 2013-08-21
        • 2014-03-30
        • 2021-04-24
        相关资源
        最近更新 更多