Pull to refresh

Почему Go To Considered Harmful?

Reading time 3 min
Views 3.5K

Некоторое время назад мне понадобилось процитировать известное письмо Дейкстры 1968 года, и я воспользовался случаем, чтобы таки внимательно прочитать его. В наши дни "споры о goto" уже неактуальны, поскольку в большинстве современных языков команды goto либо нет вообще, либо используется она редко, и стало быть, обсуждать особо нечего. Однако мне была интересна аргументация. В нашей области масса "фольклорного знания", которое на поверку не всегда оказывается точным (что хорошо показано в книге Боссавита), так что оценить логику Дейкстры с позиции сегодняшнего дня не помешает. Надо сказать, что его формулировки не всегда легко понять, поэтому я решил изложить их несколько более простым языком, потратив немного больше места.

Письма в редакцию

Для начала отметим, что жанр "Go To Statement Considered Harmful" — это письмо в редакцию журнала Communications of the ACM. То есть перед нами не научная статья, и особых требований к строгости здесь не предъявляется. Большинство писем — это краткие сообщения об интересных находках (в том числе фрагменты кода), реакция на другие письма, сообщения об ошибках в статьях и тому подобное. Некоторые из них читать довольно забавно. Например, один автор в том же 1968 году выражает недовольство шестнадцатеричными цифрами. Дескать, если в числе нет символов A-F, то непонятно, десятичное это число или шестнадцатеричное. В качестве решения он предлагает свой собственный набор цифр, который выглядит так:

Думаю, я ещё полистаю секцию писем на досуге, но пока что давайте вернёмся к основной теме.

Вопросы пространства и времени

Первое наблюдение Дейкстры состоит в том, что программа существует в пространстве и во времени. Когда мы пишем код, мы видим его как текст, занимающий некое пространство экрана. В процессе же выполнения пространственная структура уходит на второй план: порядок выполнения инструкций лишь отчасти соответствует исходной организации текста. Далее, автор отмечает, что отслеживать последовательность выполнении инструкций во времени — задача нетривиальная, и хорошо бы стремиться к сокращению "концептуального расстояния" между мирами "кода в пространстве" и "кода во времени".

О трассировке

Теперь обсудим небольшой фрагмент кода, выполняющийся ровно в том порядке, в котором он изначально записан ("в пространстве"):

a = 5
b = 15
c = a + b # мы здесь
print(c)

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

Очевидно, что в данном конкретном случае достаточно поставить точку останова на третью строку, и после повторного запуска мы снова окажемся в том же самом состоянии. Иными словами, для воспроизведения состояния достаточно указать некоторую позицию в коде.

Ветвления никак не меняют эту картину. Чтобы дойти до вызова print() в следующем примере, я могу просто поставить точку останова и перезапустить отладчик:

a = 5
b = 15
c = a + b
if c == 20:
    print(c) # мы здесь
else:
    pass

Ситуация меняется, как только в программе появляются вызовы функций. Рассмотрим следующий фрагмент:

def f(a):
    c = 5 + a
    print(c) # мы здесь

f(5)
f(7)

Здесь уже недостаточно знать, что текущей инструкцией является print(). Нам нужно ещё понять, какой из двух вызовов f() сейчас выполняется. Таким образом, нам нужно располагать полным стеком вызовов.

Аналогичным образом, при работе с циклами нужно знать порядковый номер текущей итерации.

Почему использовать go to плохо

К этому моменту должно быть понятно, что перечисленные элементы (указатели текущей строки, стеки вызовов, счётчики итераций) необходимы для понимания того, что именно происходит в программе во время выполнения. Дейкстра подчёркивает, что их значения находятся за пределами нашего влияния. Скажем, мы можем написать функцию или не писать её, но если уж функция написана, она будет использоваться в самых различных контекстах. Таким образом, следует просто принять к сведению наличие указанных координат (именно это слово, а не "элементы", используется в исходной статье) и научиться отслеживать их значения.

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

Не сказал бы, что приведённый аргумент базируется на твёрдой научной основе, но, вероятно, в этом и нет необходимости. Даже если требуемый "набор координат" существует в теории, найти его непросто и, по всей видимости, непросто и использовать. Вывод самого Дейкстры состоит в том, что конструкция go to "попросту слишком примитивна": мы можем разумным образом перемещаться по чему-либо, имеющему структуру, а возможность мгновенно "телепортироваться" куда угодно эту структуру разрушает. Поэтому go to использовать не следует.

Tags:
Hubs:
+17
Comments 33
Comments Comments 33

Articles