Разработка простого итератора C ++ для Matrix - PullRequest
0 голосов
/ 29 июня 2019

Я думал о разработке простого итератора C ++ с операцией ++, которая ведет себя по-разному для прямой и обратной итераций, таких как STL.Чтобы к матрице A можно было получить доступ через строку и столбец, как показано ниже,

  A.row(3).begin()
  A.row(3).end()
  A.col(3).begin()
  A.col(3).end()
  A.col(3).rbegin()
  A.col(3).rend()

  ++ A.row(3).begin()
  ++ A.col(3).rbegin()

Мой класс матрицы будет выглядеть следующим образом:

class Matrix {
 public:
  Iter row(size_t rowID);
  Iter col(size_t colID);
 private:
  vector<int> data_{1, 2, 3, 4, 5, 6};
  size_t nRow{3};
  size_t nCol{2};
};

Есть ли какие-либо предложения о том, как яможно спроектировать мой Iter класс?

Ответы [ 2 ]

2 голосов
/ 29 июня 2019

Вы можете просто сохранить «шаг» внутри итератора, то есть, насколько далеко указатель перемещается при каждом увеличении или уменьшении. По одной оси шаг равен 1, а по другой - размер матрицы.

Если ваша матрица такая:

1 2 3 4
6 7 8 9

Тогда row (0) .begin () указывает на 1, а row (0) .end () указывает на 6 (с шагом 1), а column (2) .begin () указывает на 3 и column (3). ) .end () указывает на неиспользуемую ячейку ниже 8 (с шагом 4).

boost::make_strided_iterator() сделает это за вас.

1 голос
/ 30 июня 2019

Здесь решение C ++ без форсирования.Я также предоставил полный и протестированный пример исходного кода.

Исходный код скомпилирован и протестирован с MS Visual Studio 19.

Первый совет: я всегда буду использовать std::valarray для матричных вычислений.Пожалуйста, прочитайте об этом.

Объяснение этого решения:

Мы будем использовать вектор вектора int для представления матрицы.Ряды могут быть легко доступны.Они являются первым измерением матрицы данных.Если нам нужен итератор для строки, мы просто возвращаем стандартный итератор для вектора.При этом мы сразу получим полную функциональность.Просто.

К сожалению, столбцы разные.Это кусочки в непрерывной памяти данных.Итак, что мы будем реализовывать в качестве решения: создать вектор для каждого столбца со ссылками на данные в правильном положении.

Это звучит проще, чем есть, потому что мы не можем хранить ссылки в контейнерах в C ++,Так что, либо используйте std::reference_wrapper, либо создайте нашу собственную справочную оболочку.У меня были проблемы с присвоением значений разыменованным std::reference_wrapper и созданием собственного.Добавлен оператор присваивания.

Таким образом, мы можем вернуть итератор в столбцы на основе опорных векторов.

И у нас есть полная мощность итераторов для класса матрицы,с минимальными усилиями, просто повторно используя возможности std :: vector :: iterator.

Я поместил некоторый тестовый код в main

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <tuple>
#include <sstream>
#include <numeric>


// Unfortunately the std::reference_wrapper does not work as expected.
// So we will build our own one
class IntRef
{
    // Here we will store the reference
    std::tuple<int&> t;
public:
    // Constructor. Take reference and store it in tuple
    IntRef(int&& intV) : t(intV) {}     

    // Assignment to the referenced value
    int operator =(const int i) { std::get<0>(t) = i; return i; }      

    // Explicit type cast to int&
    operator int& () { return std::get<0>(t); }                        

    // And, return the reference
    decltype(&std::get<0>(t)) operator&() { return &std::get<0>(t); }  
};


// Some definitions to make reading easier
using IntRefV = std::vector<IntRef>;
using MatrixCIterator = std::vector<IntRef>::iterator;
using Columns = std::vector<int>;
using MatrixRIterator = Columns::iterator;


// The matrix
class Matrix
{
public:
    // Constructor defines the matrix size
    Matrix(size_t numberOfRows, size_t numberOfColumns);

    // Iterators for rows are simple, becuase we have vectors of columns. Use unterlying iterator
    MatrixRIterator rowIterBegin(size_t row) { return data[row].begin(); }
    MatrixRIterator rowIterEnd(size_t row) { return data[row].end(); }  

    // Column iterator is complicated. Retzurn iterator to vevtor of references to column values
    MatrixCIterator columnIterBegin(size_t column) { return columnReferences[column].begin(); }
    MatrixCIterator columnIterEnd(size_t column) { return columnReferences[column].end(); }

    // Access data of matrix
    std::vector<int>& operator [] (const size_t row) { return data[row]; }

    // And, for debug purposes. Output all data
    friend std::ostream& operator << (std::ostream& os, const Matrix& m) {
        std::for_each(m.data.begin(), m.data.end(), [&os](const Columns& columns) {std::copy(columns.begin(), columns.end(), std::ostream_iterator<int>(os, " ")); std::cout << '\n'; });
        return os;
    }
protected:
    //The matrix, vector of vector of int
    std::vector<Columns> data; 

    // The references to columns in data
    std::vector<IntRefV> columnReferences{};    
};

// Constructor. Build basic matrix and then store references to columns in data 
Matrix::Matrix(size_t numberOfRows, size_t numberOfColumns) : data(numberOfRows, std::vector<int>(numberOfColumns)), columnReferences(numberOfColumns)
{
    for (size_t column = 0; column < numberOfColumns; ++column) 
        for (size_t row = 0; row < numberOfRows; ++row)
            columnReferences[column].emplace_back(IntRef(std::move(data[row][column]))); // Std::move creates a rvalue reference (needed for constructor, nothing will be moved)
}



// Some test data for the istream_iterator
std::istringstream testData("1 2 10");



// Test the matrix
int main()
{
    // Define a matrix with 3 rows and 4 columns
    Matrix matrix(3, 4);
    // Test 1: Fill all values in column 2 with 42
    for (MatrixCIterator ci = matrix.columnIterBegin(2); ci != matrix.columnIterEnd(2); ++ci) {
        *ci = 42;
    }
    std::cout << matrix << "Column 2 filled with 42\n\n";

    // Test 2: Read input from istream and copy put that in column 1
    std::copy_n(std::istream_iterator<int>(testData), 3, matrix.columnIterBegin(1));
    std::cout << matrix << "Column 1 filled with testData '"<< testData.str() << "'\n\n";

    // Test 3: Copy column 2 to cout (Print column 2)
    std::copy(matrix.columnIterBegin(2), matrix.columnIterEnd(2), std::ostream_iterator<int>(std::cout, " "));
    std::cout << "This is column 2\n\n";

    // Test 4: Sum up the first 2 values of column 1 and show result
    std::cout << "\nSum of first 2 values of column 1:  " << std::accumulate(matrix.columnIterBegin(1), matrix.columnIterBegin(1)+2, 0) << "\n\n";

    // Test 5: Fill all values in row 0 with 33
    std::for_each(matrix.rowIterBegin(0), matrix.rowIterEnd(0), [](int& i) { i = 33; });
    std::cout << matrix << "Row 0 filled with 33\n\n";

    return 0;
}

Надеюсь, что это даст вам представление о том, как он мог быРабота .,.

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