📘 TypeScript

Двумерные массивы и матрицы в TypeScript 5 (Node.js)

Вот HTML-статья для начинающих программистов о двумерных массивах и матрицах в TypeScript 5 (Node.js). Она содержит теорию, примеры кода и практические задания. ```html Двумерные массивы и матрицы в TypeScript 5

Введение: зачем нужны двумерные массивы?

Представьте себе шахматную доску. У неё есть строки (горизонтали) и столбцы (вертикали). Чтобы описать такую структуру в коде, обычного одномерного массива недостаточно. Здесь на помощь приходят двумерные массивы — массивы, элементами которых являются другие массивы. В программировании их часто называют матрицами.

В этой статье мы разберёмся, как создавать, заполнять и обрабатывать двумерные массивы в TypeScript 5 (среда Node.js). Вы узнаете, как работать с таблицами данных, изображениями (пиксели — это тоже матрица), игровыми полями и многим другим.

Аналогия: Одномерный массив — это список покупок. Двумерный массив — это книжная полка: у каждой книги есть номер ряда (строка) и номер места на полке (столбец).

Основные концепции

Что такое двумерный массив?

Формально, двумерный массив — это массив, каждый элемент которого сам является массивом. Если мы говорим о матрице размером M x N, то у нас есть M строк и N столбцов. Доступ к элементу осуществляется по двум индексам: arr[строка][столбец].

В TypeScript (как и в JavaScript) двумерные массивы не являются встроенным типом данных — это просто массив массивов. Но благодаря строгой типизации мы можем описать их форму.

Типизация в TypeScript 5

Для объявления двумерного массива чисел используется синтаксис:

let matrix: number[][] = [];

Или более строгий вариант с указанием размера:

let grid: Array<Array<number>> = [];

Если вы хотите создать матрицу с заранее известными размерами, но пустую, можно сделать так:

const rows = 3;
const cols = 4;
// Создаём массив из rows элементов, каждый из которых — массив из cols нулей
const matrix: number[][] = Array.from({ length: rows }, () => new Array(cols).fill(0));
console.log(matrix);
// [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Важно: Метод fill() заполняет массив ссылками на один и тот же объект, если вы используете его для массивов или объектов. Для примитивов (числа, строки) это безопасно. Для матриц всегда создавайте новый подмассив через Array.from или цикл.

Создание и инициализация матриц

1. Статическая инициализация (литералы)

Самый простой способ — перечислить все элементы:

const identityMatrix: number[][] = [
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1]
];
console.log(identityMatrix[1][1]); // 1

2. Динамическое создание (циклы)

Часто размер матрицы становится известен только во время выполнения программы. Используем вложенные циклы:

function createMatrix(rows: number, cols: number, initialValue: number = 0): number[][] {
    const matrix: number[][] = [];
    for (let i = 0; i < rows; i++) {
        const row: number[] = [];
        for (let j = 0; j < cols; j++) {
            row.push(initialValue);
        }
        matrix.push(row);
    }
    return matrix;
}

const myMatrix = createMatrix(2, 3, 7);
console.log(myMatrix); // [[7, 7, 7], [7, 7, 7]]

3. Использование Array.from (современный способ)

const rows = 4;
const cols = 5;
const matrix: number[][] = Array.from({ length: rows }, () => Array(cols).fill(0));
// Более короткая запись, но помните про fill() для объектов!

Обход и работа с элементами

Обход по строкам и столбцам

Классический способ — два вложенных цикла for:

const matrix: number[][] = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
        console.log(`Элемент [${i}][${j}] = ${matrix[i][j]}`);
    }
}

Обход с помощью forEach

Более функциональный стиль:

matrix.forEach((row, rowIndex) => {
    row.forEach((value, colIndex) => {
        console.log(`[${rowIndex}][${colIndex}] = ${value}`);
    });
});

Изменение элемента

matrix[0][1] = 100; // Теперь первая строка: [1, 100, 3]

Примеры кода: полезные алгоритмы

1. Транспонирование матрицы

Транспонирование — это превращение строк в столбцы. Если матрица была размером M x N, после транспонирования она станет N x M.

function transpose(matrix: number[][]): number[][] {
    const rows = matrix.length;
    const cols = matrix[0].length;
    const result: number[][] = Array.from({ length: cols }, () => new Array(rows).fill(0));

    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            result[j][i] = matrix[i][j];
        }
    }
    return result;
}

const original = [
    [1, 2, 3],
    [4, 5, 6]
];
console.log(transpose(original));
// [[1, 4], [2, 5], [3, 6]]

2. Сумма элементов матрицы

function sumMatrix(matrix: number[][]): number {
    let sum = 0;
    for (const row of matrix) {
        for (const value of row) {
            sum += value;
        }
    }
    return sum;
}

const data = [[1, 2], [3, 4], [5, 6]];
console.log(sumMatrix(data)); // 21

3. Поиск максимального элемента

function findMax(matrix: number[][]): number {
    let max = -Infinity;
    for (const row of matrix) {
        for (const value of row) {
            if (value > max) max = value;
        }
    }
    return max;
}

const matrix = [[-5, 12], [0, 8], [3, -1]];
console.log(findMax(matrix)); // 12

Практика: задача "Морской бой" (упрощённая)

Давайте напишем небольшую программу, которая создаёт игровое поле 5x5 и случайным образом расставляет 3 однопалубных корабля (1 клетка). Затем программа проверяет, есть ли корабль по заданным координатам.

// Тип для клетки: 0 - пусто, 1 - корабль
type Cell = 0 | 1;
type BattleField = Cell[][];

function createField(size: number): BattleField {
    // Заполняем нулями
    const field: BattleField = Array.from({ length: size }, () => new Array(size).fill(0) as Cell[]);
    
    // Ставим 3 корабля случайным образом (без проверки на пересечение для простоты)
    let placed = 0;
    while (placed < 3) {
        const row = Math.floor(Math.random() * size);
        const col = Math.floor(Math.random() * size);
        if (field[row][col] === 0) {
            field[row][col] = 1;
            placed++;
        }
    }
    return field;
}

function checkShot(field: BattleField, row: number, col: number): string {
    if (row < 0 || row >= field.length || col < 0 || col >= field[0].length) {
        return "Мимо! (за пределами поля)";
    }
    return field[row][col] === 1 ? "Попадание!" : "Мимо!";
}

// Демонстрация
const field = createField(5);
console.log("Поле (0 - пусто, 1 - корабль):");
field.forEach(row => console.log(row.join(' ')));

const testRow = 2, testCol = 3;
console.log(`\nВыстрел по координатам [${testRow}][${testCol}]: ${checkShot(field, testRow, testCol)}`);
Как это работает: Мы создаём матрицу 5x5, заполняем её нулями. Затем в случайные позиции записываем единицы (корабли). Функция checkShot проверяет, не вышли ли мы за границы, и возвращает результат.

Работа с нерегулярными (зубчатыми) массивами

В TypeScript строки матрицы могут быть разной длины. Это называется зубчатый массив (jagged array). Например:

const jagged: number[][] = [
    [1, 2],
    [3, 4, 5, 6],
    [7]
];
console.log(jagged[1].length); // 4

При обходе таких массивов всегда используйте matrix[i].length во внутреннем цикле, а не фиксированное значение.

Производительность и best practices

  • Избегайте частого изменения размера — операции push в цикле могут быть медленными для больших матриц. Лучше заранее выделить память через new Array(size).
  • Типизируйте вложенные массивы — используйте number[][] или Array<Array<number>>. Для сложных структур создавайте отдельный тип.
  • Проверяйте границы — обращение по несуществующему индексу вернёт undefined, что может привести к ошибкам.
  • Для математических операций (умножение матриц, детерминант) используйте готовые библиотеки, например mathjs, но для обучения полезно писать свои реализации.

Итог

Мы разобрали, что такое двумерные массивы и матрицы в TypeScript 5, научились их создавать, заполнять и обрабатывать. Вы увидели, как работают вложенные циклы, как транспонировать матрицу и даже написали мини-игру "Морской бой".

Двумерные массивы — фундаментальная структура данных. Они лежат в основе работы с таблицами, изображениями, графами (матрицы смежности) и многими другими областями. Освоив их, вы сделаете большой шаг вперёд в изучении программирования.

Попробуйте самостоятельно модифицировать примеры: добавьте проверку на пересечение кораблей, реализуйте умножение матриц или напишите функцию, которая поворачивает матрицу на 90 градусов. Удачи!

```