Как получить индекс элемента последовательности по индексу общих срезов?
Надумал написать итератор с возможностью изменения режима обхода непрерывной последовательности. То есть, я устанавливаю режим, передавая в соответствующий метод срез(std::gslicе, считаю так удобно) который принимает три аргумента: стартовый индекс и два объекта std::valarray<size_t>, представляющие размеры и шаги срезов соответственно. Для этого я написал базовый класс, который хранит некоторые данные, и, помимо других услуг, умеет выдать индекс элемента последовательности по индексу общих срезов(именно в реализации этого метода возникла трудность). Вот базовый класс с неправильной реализацией функции_члена get_slice_index:
class Base_matrix_iterator {
private:
const std::valarray<size_t> sizes, strides;
//...
protected:
size_t curr;
const size_t start;
//получить индекс элементa последовательности по индексу срезов
size_t get_slice_index(size_t n) {
//неправильный код
const size_t
sz = sizes.size(),
x = sizes[sz - 2],
y = sizes[sz - 1],
z = x * y;
size_t pos = start;
if (sz == 3 && n >= z) {
pos += strides[0] * (n / z);
n %= z;
}
if (n >= y) {
pos += strides[sz - 2] * n / y;
n %= y;
}
else
pos += n * strides[sz - 1];
return pos;
}
//...
public:
/*ожидается общий срез с двумерными или трехмерными размерами, предназначенные
для двухмерной и трехмерной матрицы соответственно
std::gslice(старт, {количество строк, столбцов }, {шаг строк, столбцов})
Если количество и шаги заданы как std::valarray<size_t>(3),
то первые аргументы это количество таких матриц и шаги соответственно*/
Base_matrix_iterator(const std::gslice& s)
: sizes(s.size()), strides(s.stride()),
start(s.start()), curr(0) {}
size_t get_index() { return get_slice_index(curr); }
//в дальнейшем конструктор и get_index будут
//в защищенной части, поскольку они нужны только итераторам
Base_matrix_iterator& operator++()
{
++curr;
return *this;
}
Base_matrix_iterator& operator +(const int off) {/*... */ }
//наследник будет реализовать Tp& operator *() { return array[get_slice_index(curr)]; } и т.д.
//...
};
Это означает, что:
Base_matrix_iterator It({ 0, {3, 2, 4}, {1, 10, 2} }),
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 4; ++j) {
cout << It.get_index() << ' ';
++It;
}
cout << endl;
}
должен выдать:
0 2 4 6 //старт==0 и 4 элемента (шаг == 2)
10 12 14 16 //вторая последовательность шаг == 10 от старта
1 3 5 7 //и таких (матриц) 3 штуки, ( шаг== 1)
11 13 15 17
2 4 6 8
12 14 16 18
а следующий код:
Base_matrix_iterator Iter2({ 1,{2, 3}, {7, 1} });
for (int i = 0; i < 2; ++i) {
cout << '\n';
for (int j = 0; j < 3; ++j) {
cout << Iter2.get_index() << ' ';
++Iter2;
}
}
должен выдать две последовательности по 3 элемента в каждой:
1 2 3 // старт== 1 и 3 элемента (шаг == 1)
8 9 10 //вторая последовательность шаг == 7 от старта
Мой код выдает:
0 2 4 6
10 12 15 17
1 3 5 7
11 13 16 18
2 4 6 8
12 14 17 19
и
1 2 3
8 10 12
соответственно.
У меня что то заклинило, и я не могу разобраться в чем ошибка в реализации метода get_slice_index.
И, необязательный вопрос: "Не изобретаю ли я велосипед?.."
P.S. чтобы было понятно, еще раз про std::gslice
Cрез std::gslice(start, {x, y}, {sx, sy}) описывает матрицу размером[x][y] элементов массива array:
- array[start ], array[start + sy], array[start + 2sy]... array[start + (y - 1) * sy]
- еще таких же
x - 1последовательностей, только стартом теперь являетсяstart + i * sx, где0 < i < xесть индекс строки, но .
Cрез std::gslice(start, {z, x, y}, {sz, sx, sy}) описывает z штук таких матриц(трехмерную матрицу), только start == start + j, где 0 <= j < z есть номер(индекс) матрицы. Словами, это z матриц шагом sz от начала матрицы, в каждой из них x строк, шагом sx от начала строк, в каждой строке sy элементов, шагом sy от начала строки
Я лишь хочу вычислить индекс элемента массива array по индексу полученной двумерной или трехмерной матрицы из его же элементов.
Ответы (1 шт):
Ошибка кроется в тонкостях:
- Когда выполняешь несколько действий в одном выражении, нужно не
забывать о необходимых скобках. В выражении
pos += strides[sz - 2] * n / y;я просто забыл скобки ставить вокругn / y, поскольку сначала нужно знать на сколько раз n > y, а потом умножить на шаг среза. - Дальше: поскольку, после взятия остатка
n %= y;,nчаще будет иметь не нулевое значение, и нужно выполнить увеличениеpos += n * strides[sz - 1];тоже, то нужноelseзаменить наif(n < y)или в телеif (n >= y)добавить в конце эту операцию.
В итоге правильным кодом будет:
size_t get_slice_index(size_t n) {
const size_t
sz = sizes.size(),
x = sizes[sz - 2],
y = sizes[sz - 1],
z = x * y;
size_t pos = start;
if (sz == 3 && n >= z) {
pos += strides[0] * (n / z);
n %= z;
}
if (n >= y) {
pos += strides[sz - 2] * (n / y);
n %= y;
}
if(n && n < y)
pos += n * strides[sz - 1];
return pos;
}