Шрифт:
int s = a.size; // количество элементов
int d1 = a.dim1; // количество элементов в строке
int d2 = a.dim2; // количество элементов в столбце
int* p = a.data; // извлекаем данные с помощью указателя в стиле
// языка С
Мы можем запросить общее количество элементов и количество элементов в каждой размерности. Кроме того, можем получить указатель на элементы, размещенные в памяти в виде матрицы.
Мы можем использовать индексы.
a(i,j); // (i,j)-й элемент (в стиле языка Fortran) с проверкой
// диапазона
a[i]; // i-я строка (в стиле языка C) с проверкой диапазона
a[i][j]; // (i,j)-й элемент (в стиле языка C)
Matrix
индексирование с помощью конструкции [i]
создает одномерный объект класса Matrix
, представляющий собой i
– ю строку. Это значит, что мы можем извлекать строки и передавать их операторам и функциям, получающим в качестве аргументов одномерные объекты класса Matrix
и даже встроенные массивы (a[i].data)
. Обратите внимание на то, что индексирование вида a(i,j)
может оказаться быстрее, чем индексирование вида a[i][j]
, хотя это сильно зависит от компилятора и оптимизатора. Мы можем получить срезки.
a.slice(i); // строки от a[i] до последней
a.slice(i,n); // строки от a[i] до a[i+n–1]
Срезка двумерного объекта класса
Matrix
сама является двумерным объектом этого класса (возможно, с меньшим количеством строк). Распределенные операции над двумерными матрицами такие же, как и над одномерными. Этим операциям неважно, как именно хранятся элементы; они просто применяются ко всем элементам в порядке их следования в памяти.
Matrix<int,2> a2 = a; // копирующая инициализация
a = a2; // копирующее присваивание
a *= 7; // пересчет (и +=, –=, /= и т.д.)
a.apply(f); // a(i,j)=f(a(i,j)) для каждого элемента a(i,j)
a.apply(f,7); // a(i,j)=f(a(i,j),7) для каждого элемента a(i,j)
b=apply(f,a); // создаем новую матрицу с b(i,j)==f(a(i,j))
b=apply(f,a,7); // создаем новую матрицу с b(i,j)==f(a(i,j),7)
Оказывается, что перестановка строк также полезна, поэтому мы предусмотрим и ее.
a.swap_rows(1,2); // перестановка строк a[1] <–> a[2]
swap_columns
не существует. Если она вам потребуется, то вы сможете написать ее самостоятельно (см. упр. 11). Из-за построчной схемы хранения матриц в памяти строки и столбцы не совсем равноправны. Эта асимметрия проявляется также в том, что оператор [i]
возвращает только строку (а для столбцов аналогичный оператор не предусмотрен). Итак, в тройке (i,j)
первый индекс i
выбирает строку. Эта асимметрия имеет глубокие математические корни. Количество действий, которые можно было бы выполнить над двумерными матрицами, кажется бесконечным.
enum Piece { none, pawn, knight, queen, king, bishop, rook };
Matrix<Piece,2> board(8,8); // шахматная доска
const int white_start_row = 0;
const int black_start_row = 7;
Piece init_pos[] = {rook,knight,bishop, queen,king,bishop,knight,rook};
Matrix<Piece> start_row(init_pos); // инициализация элементов из
// init_pos
Matrix<Piece> clear_row(8); // 8 элементов со значениями
// по умолчанию
Инициализация объекта
clear_row
использует возможность задать условие none==0
и то, что эти элементы по умолчанию инициализируются нулем. Мы могли бы предпочесть другой код.
Matrix<Piece> start_row
= {rook,knight,bishop,queen,king,bishop,knight,rook};
Однако он не работает (по крайней мере, пока не появится новая версия языка C++ (C++0x)), поэтому пока приходится прибегать к трюкам с инициализацией массива (в данном случае
init_pos
) и использовать его для инициализации объектов класса Matrix
. Мы можем использовать объекты start_row
и clear_row
следующим образом:
board[white_start_row] = start_row; // расставить белые фигуры
for (int i = 1; i<7; ++i) board[i] = clear_row; // очистить середину