Применить одну и ту же функцию к разным измерениям многомерного вектора - PullRequest
1 голос
/ 29 февраля 2012

Во-первых, извините, я не знаю много с ++, может быть, мой вопрос довольно глупый.У меня есть многомерный вектор М. Я хочу иметь возможность применять одну и ту же функцию либо к элементам строки i, либо к элементам столбца j.Я не хочу писать одну и ту же функцию дважды.Возможно ли сделать это довольно простым способом, например, с некоторой перегрузкой или с виртуальными итераторами?Кто-нибудь может написать простой пример?спасибо.

Ответы [ 4 ]

1 голос
/ 29 февраля 2012

Вы можете определить свой собственный итератор для столбцов, чтобы вы могли использовать стандартные алгоритмы (например, for_each или transform, как упомянуто в другом ответе), чтобы применить вашу функцию к строкам или столбцам массива, простоизменение итераторов:

#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;
}
1 голос
/ 29 февраля 2012

Хороший способ сделать это - использовать std::transform. Обратитесь к этой ссылке для более подробной информации. Краткий пример того, как это сделать для строк, приведен ниже. Часть столбца немного хитрая.


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

Однако есть одна загвоздка: часть столбца не будет работать для неквадратных двумерных массивов (т. Е. Для того, чтобы количество строк и столбцов было одинаковым). Я думаю, что это можно обойти, немного подумав.

0 голосов
/ 29 февраля 2012

Если ваш многомерный вектор является фактическим многомерным вектором, например что-то вроде std::vector<std::vector<int>>, что не рекомендуется, то вам придется написать свой собственный итератор. Это не очень сложно. 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, библиотеку, предназначенную для таких вещей. Он предоставляет необходимую вам функциональность, но это определенно не самая дружелюбная библиотека.

0 голосов
/ 29 февраля 2012

Для 2D вектора:

Ряды будут просты:

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

Эти функции в основном принимают двумерный вектор целых чисел и возвращают вектор строки / столбца на основе указанного индекса.

Это основа того, что вы пытаетесь сделать (из того, что я могу собрать). Незначительные изменения позволят вам применять функцию к строкам / столбцам выборочно, а не просто возвращать их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...