Страницы

Структурное программирование не про запрет GOTO

Смотрит на вас как на GOTO.

Теорема Бёма — Якопини, которую часто называют теоремой про структурное программирование, на самом деле доказывает прямо противоположное тому, что ей приписывается. Она не доказывает достаточности структурного программирования, а наоборот, доказывает, что с помощью структурных операторов управления можно идеально воспроизводить любой неструктурный код, но сохраняя таким образом и все его отрицательные для человека свойства. Более подходящим названием для теоремы было бы «теорема про неструктурное программирование в структурных операторах».

Рассмотрим дикий бессмысленный пример неструктурного кода:

L0: Call0; IF Pred0 GOTO L2;
L1: Call1; IF Pred1 GOTO L3;
L2: Call2; IF Pred2 GOTO L3 ELSE GOTO L0;
L3: Call3; IF Pred3 GOTO L4 ELSE GOTO L1;

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

L := 0;
REPEAT 
  CASE L OF
    0: Call0; IF Pred0 THEN L := 2 ELSE L := 1 END
  | 1: Call1; IF Pred1 THEN L := 3 ELSE L := 2 END
  | 2: Call2; IF Pred2 THEN L := 3 ELSE L := 0 END
  | 3: Call3; IF Pred3 THEN L := 4 ELSE L := 1 END
  END
UNTIL L = 4;

Поэкспериментировать c кодом можно в песочнице.

В чуть более сложном примере может быть комбинация структурных и неструктурных выходов:

PROCEDURE Search(a: ARRAY OF INTEGER; val: INTEGER): INTEGER;
BEGIN
  i := 0;
  WHILE i < LEN(a) DO
    IF a[i] = val THEN
      RETURN i
    END;
    INC(i)
  END;
  RETURN -1
END Search;

В таком случае сначала лучше заменить циклы и ветвления на GOTO:

PROCEDURE Search(a: ARRAY OF INTEGER; val: INTEGER): INTEGER;
VAR ret: INTEGER;
BEGIN
  i := 0;
  Wh:IF ~(i < LEN(a))      GOTO EndWh;
    IF ~(a[i] = val)       GOTO EndIf;
      ret := i; GOTO Return
    EndIf:
    INC(i);
  GOTO Wh; EndWh:
  ret := -1; Return: RETURN ret
END Search;

А затем повторить преобразование в цикл с CASE:

PROCEDURE Step(): INTEGER; BEGIN INC(L) RETURN L - 1 END Step;
PROCEDURE Goto(l: INTEGER); BEGIN L := l END Goto;
PROCEDURE Wh(c: BOOLEAN):BOOLEAN; BEGIN DEC(L); RETURN ~c END Wh;

PROCEDURE Search(a: ARRAY OF INTEGER; val: INTEGER): INTEGER;
CONST Begin = 0; EndIf = 1; EndWh = 2; Return = 3;
VAR ret: INTEGER;
BEGIN L := 0;
  i := 0;
  REPEAT CASE Step() OF
| Begin:
  IF Wh(i < LEN(a))  THEN Goto(EndWh) ELSE
    IF ~(a[i] = val) THEN Goto(EndIf) ELSE
      ret := i; Goto(Return)          END END
|   EndIf:
    INC(i)
| EndWh:
  ret := -1;
  END UNTIL L = Return; RETURN ret
END Search;

Выглядит странновато, но этот код, используя только операторы с единственным выходом, тем не менее точно соответствует исходному примеру с выходом из середины функции. Структурное программирование, естественно, не про умение выполнять такие упражнения, а про аккуратное проектирование более простых потоков управления. И хотя потоки и ограничены композицией 3-х типов переходов, которые можно было бы назвать структурными, это нельзя назвать достаточным условием. Более того, оно не является и необходимым условием, потому что структурные операторы можно безупречно воспроизводить с помощью одних только условных и безусловных переходов, то есть, тех самых GOTO.

Структурный вариант поиска мог бы выглядеть так:

PROCEDURE Search(a: ARRAY OF INTEGER; val: INTEGER): INTEGER;
VAR i: INTEGER;
BEGIN
  i := 0;
  WHILE (i < LEN(a)) & (a[i] # val) DO
    INC(i)
  END;
  IF ~(i < LEN(a)) THEN
    i := -1
  END
  RETURN i
END Search;

Если бы в языке не было бы ни циклов, ни составных ветвлений, то структуру всё равно можно было бы сохранить:

PROCEDURE Search(a: ARRAY OF INTEGER; val: INTEGER): INTEGER;
VAR i: INTEGER;
BEGIN
  i := 0;
  Wh:IF ~((i < LEN(a)) & (a[i] # val)) GOTO EndWh;
    INC(i)
  GOTO Wh; EndWh:
  IF i < LEN(a) GOTO EndIf;
    i := -1
  EndIf:
  RETURN i
END Search;

Следовательно, сам GOTO не противоречит идее структурного программирования, он всего лишь ей не способствует. И это вторая причина того, что структурность не про запрет GOTO.

Всё дело в коварстве алгоритмической полноты — структурность легко воспроизводить с помощью «неструктурных» переходов, а неструктурность с помощью «структурных». Так как следить за соблюдением формалистических правил гораздо легче, чем следить за соблюдением более сложных понятий, когда они выражаются в других терминах, то и дальше понятие структурности будуть путать с запретом GOTO. Но мы-то этого делать не будем?

Комментариев нет:

Отправить комментарий