Теорема Бёма — Якопини, которую часто называют теоремой про структурное программирование, на самом деле доказывает прямо противоположное тому, что ей приписывается. Она не доказывает достаточности структурного программирования, а наоборот, доказывает, что с помощью структурных операторов управления можно идеально воспроизводить любой неструктурный код, но сохраняя таким образом и все его отрицательные для человека свойства. Более подходящим названием для теоремы было бы «теорема про неструктурное программирование в структурных операторах».
Рассмотрим дикий бессмысленный пример неструктурного кода:
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. Но мы-то этого делать не будем?
Комментариев нет:
Отправить комментарий