Найти тему
ZDG

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

Первая часть здесь.

На начальных этапах обучения программированию очень часто дают задачи с матрицами. Это могут быть математические матрицы, или матрицы из задач-упражнений (расставить ферзей на шахматной доске и т.п.) В программе матрица – это двумерный массив. Условный код:

a = Array(10, 10); // создать 2-мерный массив 10 * 10 элементов

Обращение к элементу такого массива, расположенному в столбце 1 и в строке 2, может выглядеть так: a[1, 2] или так: a[1][2].

В математике элементы матрицы обычно нумеруются по-другому: сначала идёт номер строки, потом номер столбца. Если же мы используем координатную систему, то первым идет X, а вторым Y (как здесь). Но это неважно, нумерацию можно построить как угодно. Проблема была в другом.
В математике элементы матрицы обычно нумеруются по-другому: сначала идёт номер строки, потом номер столбца. Если же мы используем координатную систему, то первым идет X, а вторым Y (как здесь). Но это неважно, нумерацию можно построить как угодно. Проблема была в другом.

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

-2

Когда учат циклам, обычно используют переменную i:

for (i = 0; i < 10; i++) ...

Но когда нужно в цикле, который использует i, устроить ещё один цикл, нельзя опять использовать букву i, она уже занята, значит нужна другая буква. И тогда обычно берут следующую по алфавиту букву, j. А если нужен будет ещё один цикл внутри этого цикла, то еще одну, k, и так далее.

Это чисто математический подход, перенятый из учебников математики. Сами по себе эти буквы воспринимаются учащимися как бессмысленные. И тогда получается нечто вроде:

С виду здесь всё правильно. Но обозначение a[i, j] – это в нашем случае столбец i и строка j. А так как цикл j выполняется внутри цикла i, получается, что мы обходим массив не перебирая столбцы внутри строк, а перебирая строки внутри столбцов (как если бы читали книгу поперёк).

-3

Что ж, массив можно обходить и так. Кроме того, матрица или массив могут быть заданы и таким образом, что a[1,2] это не столбец 1 и строка 2, а строка 1 и столбец 2. Но проблема не в этом. Проблема в том, что можно представлять в голове одно, а писать по факту совершенно другой код, который будет работать, но не так, как нужно, и можно очень долго биться с ним, не понимая, что там не так. А не так там всего лишь порядок букв i и j. Например, вышеуказанный код заполнит матрицу числами одинаково при любом порядке обхода и при любой системе нумерации. Это создаст впечатление, что всё работает правильно, и каждый раз, когда встречается задача про матрицу, можно просто запомнить и повторять этот код. Но стоит возникнуть задаче, где важен порядок обхода, как всё ранее работавшее вдруг начнет показывать не тот результат.

Несмотря на то, что код, казалось бы, предельно простой, новичку требуется большое умственное усилие, чтобы соотнести буквы i и j и внешний и внутренний циклы и обозначение a[i, j]. Каждый раз после написания таких циклов с матрицами у меня было ощущение, что я проделал тяжелую физическую работу.

Постоянно было большое желание написать циклы по порядку. Раз пишется a[i,j], то и циклы надо делать сначала i, потом j. И только через какое-то время наступило что-то вроде озарения, что нужно не просто писать какие-то буквы, а писать то, что тебе надо.

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

В заключение скажу, что когда я осуществляю перебор координат, то теперь пользуюсь буквами не i,j, а x,y – так нагляднее (а пусть теперь будет на Питоне):

Правда ведь?