Structured Programming with go to Statements
Donald E. Knuth
ACM computing surveys, 6(4), 1974
Grupo 9
Barbara Bellaver ,Virginia Oliveira
Apresentação
Duração : 25 minutos (8.30 – 8:55)
Técnica mas não complexa, baseada em trechos de código (ALGOL 60) , expositiva e não impositiva.
Partes :
Background Histórico
Eliminação dos Comandos go to
Introdução dos Comandos go to
Conclusões
Dúvidas
Background Histórico
Início na década de 60
1966 – D. V. Schorre
Improved organization for procedural languages
1968 - Edsger Dijkstra
Go to statement considered harmful
- convite a fazer bagunça dentro do código
- abolição do go to de todas as linguagens de “alto nível”
- criação de comandos / instruções alternativas
1974 - Donald E. Knuth Donald Knuth
Edsger Dijkstra
Eliminação dos Comandos go to
Motivação
Evitar o código espagueti !
Eliminação dos Comandos go to (cont)
Eliminação Sistemática de go to’s
1966 - G. Jacopini e C. Böhm provaram que qualquer
programa dado pode ser sistematicamente transformado em outro programa, o qual computa os mesmos resultados do original, usando apenas as três operações básicas de
composição, condicional e iteração, mais os possíveis
comandos de atribuição e testes sobre as variáveis auxiliares.
Portanto, a princípio, comandos go to, podem sempre ser removidos.
Mas como ?
Eliminação dos Comandos go to (cont)
Eliminação Sistemática de go to’s
Construção While + if’s + variável auxiliar p
p :=1;
while p>0 do
begin if p=1 then perform step1;
p := successor of step 1 fi;
if p=2 then perform step2;
p := successor of step 2 fi;
…
if p=n then perform step n;
p := successor of step n fi;
end.
A variável auxiliar p serve como um contador de programa representando em qual caixa do diagrama de fluxo se está, e o programa pára quando p se
torna zero. Todos os go to’s foram eliminados , mas na realidade toda a estrutura do programa foi perdida.
Eliminação dos Comandos go to (cont)
Eliminação Sistemática de go to’s
A construção original de Jacopini não era meramente a emulação trivial de um fluxograma. Seria possível aproveitar muito da
estrutura de um fluxograma dado se ele fosse razoavelmente bem comportado.
Ashcroft e Manna em 1972 criaram outra técnica para a eliminação de go to’s , tornando possível capturar ainda mais do fluxo natural de um programa. Porém com a nova técnica um programa poderia crescer exponencialmente em tamanho ...
Quando procedimentos de eliminação de go to são aplicados a programas mal estruturados, tem-se como resultado
programas igualmente mal esruturados.
Eliminação dos Comandos go to (cont)
Eliminação Sistemática de go to’s
Pior erro :
“Programação Estruturada” = Escrever programas da forma como sempre e então eliminar os go to’s.
O que se deseja é que programas sejam concebidos de tal forma que raramente sequer se pense sobre os comandos go to . A linguagem na qual expressamos nossas idéias tem forte
influência sobre nossos processos de pensamento. Por isso Dijkstra salienta a necessidade de novas instruções
(features) em linguagens de programação - estruturas que encorajam o pensamento claro - com o objetivo de evitar a tentação do go to.
Eliminação dos Comandos go to (cont)
Novas features
A) loop until event1 or … or eventn:
statement list0; repeat;
then event1 = > statement list1;
…
eventn = > statement listn; fi;
B) begin until event1 or . . . or eventn:
statement list0; end;
then event1 = > statement list1;
…
eventn = > statement listn; fi:
Novas features
Indicadores de Evento (C.T.Zahn) : Indicadores de evento NÃO são condições que são testadas continuamente. Os comandos
event são simplesmente transferências de controle.
A 1ª. linha só declara os nomes dos indicadores de evento.
Eliminação dos Comandos go to (cont)
Novas features
Exemplo : Pesquisa e inserção em árvore binária
compare:
if A[i] > x then if L[i] ≠ 0
then i := L[i]; go to compare;
else L[i] := j; go to insert fi;
else if R[i] ≠ 0
then i := R[i]; go to compare;
else R[i] := j; go to insert fi;
fi;
insert: A[j] := x;
L[j] := 0; R[j] := 0; j := j+1;
árvore binária de pesquisa é representada por três vetores : A[i] denota a informação armazenada no nodo número i, e L[i], R[i] são os
números de nodo para as raízes das subárvores esquerda e direita do nodo i.
Eliminação dos Comandos go to (cont)
Novas features
Exemplo : Pesquisa e inserção em árvore binária
compare:
if A[i] < x then if L[i] ≠ 0
then i := L[i]; go to compare;
else L[i] := j; go to insert fi;
else if R[i] ≠ 0
then i := R[i]; go to compare;
else R[i] := j; go to insert fi;
fi;
insert: A[j] := x;
L[j] := 0; R[j] := 0; j := j+l;
loop until left leaf hit or right leaf hit:
if A[i] > x
then if L[i] ≠ 0 then i := L[i];
else left leaf hit fi;
else if R[i] ≠ 0 then i := R[i];
else right leaf hit fi;
fi;
repeat;
then left leaf hit = > L[i] := j;
right leaf hit = > R[i] := j;
fi;
A[j] := x; L[j] := 0; R[j] := 0; j := j+1;
Eliminação dos Comandos go to (cont)
Iterações Simples
Iterações Simples :
Segundo Knuth os tipos de iteração que ele encontra mais freqüentemente em códigos têm a forma
A: S;
if B then go to Z fi;
T; go to A;
Z:
Onde S e T ambos representam longas seqüências de código. Se S é vazio, tem-se um laço while, e se T é vazio tem-se um laço repeat .
Exatamente uma condição sendo testada
Eliminação dos Comandos go to (cont)
Iterações Simples
Para evitar o uso de go to’s em tais laços pode-se :
1) duplicar o código para S escrevendo
S; while B do begin T; S end;
2) ou descobrir algum tipo de “inversa”para T de tal forma que “T-1;T” é equivalente a um comando nulo e escrever
T-1; repeat T; S until B;
3) ou duplicar o código para B e fazer um teste redundante, escrevendo repeat S; if B then T fi; until B;
Melhor proposta de sintaxe segundo Knuth (Johan Dalh) : loop: S; while B: T; repeat;
Introdução dos Comandos go to
A principal razão para introdução de comandos go to é a
eficiência.
Introdução dos Comandos go to
Eliminação da Recursão
Eliminação da Recursão
Surge quando se tenta otimizar programas bem estruturados que envolvem recursão explícita ou implícita.
procedure treeprint(t); integer t; value t;
if t ≠ 0
then treeprint (L[t]);
print (A[t]);
treeprint (R[t]);
fi;
Introdução dos Comandos go to (cont)
Eliminação da Recursão
Traz economias de espaço ou de tempo,
Tende a causar perda da legibilidade do programa.
Compiladores impõem um overhead às chamadas de procedimentos
Se simplificarmos à mão e não com um mecanismo genérico, podemos encontrar simplificações e adquirir um melhor
entendimento do algoritmo.
A regra para simplificação de chamadas de procedimentos é: “Se a última ação do procedimento p antes do seu retorno é chamar o procedimento q, simplesmente go to para o início do
procedimento q.”
Introdução dos Comandos go to (cont)
Eliminação da Recursão
Como resultado da aplicação da regra o exemplo anterior se torna
procedure treeprint(t); integer t; value t;
L: if t ≠ 0
then treeprint (L[t]);
print (A[t]);
t := (R[t]); go to L;
fi;
Introdução dos Comandos go to (cont)
Eliminação da Recursão
Do exemplo acima com iterações ao invés de recursão obtemos
procedure treeprint(t); integer t; value t;
begin integer stack S; S := empty;
L1: loop while t ≠ 0;
S < = t; t := L[t]; go to L1;
L2: t < = S;
print (A[t]);
t := (R[t]);
repeat;
if nonempty(S) then go to L2 fi;
end.
Go to em iteração: pecado mortal
Aceitável se bem documentado
Introdução dos Comandos go to (cont)
Eliminação da Recursão
Situação similar quando se quer provar que um programa está correto.
Para demonstrar Q, prova-se que um programa P mais simples porém menos eficiente é correto e então
prova-se que P pode ser transformado em Q por uma sequência de otimizações válidas.
Com base nisto, o autor propõe uma nova classe de software
o programador desenvolve um programa P bem
documentado e o otimiza para um programa Q que
Introdução dos Comandos go to (cont)
Sistemas de Manipulação de Programas
Sistemas de Manipulação de Programas
Um compilador para produzir código realmente eficiente necessitaria conhecer as propriedades dos dados.
Linguagem conveniente permite um sistema iterativo de manipulação de programas
Darlington e Burstall
Sistema que remove algumas recursões, faz conversões de estruturas de dados e reestruturação do programa por
combinação de loops similares em uma linguagem como Lisp.
O programador utilizando um sistema destes
Escreve o programa P e interativamente especifica as modificações para torná-lo eficiente.
O sistema fornece informações estatísticas sobre quais partes do programa devem ser otimizadas.
Introdução dos Comandos go to (cont)
Recursão vs. Iteração
Recursão vs. Iteração
É possível re-escrever o exemplo anterior sem a utilização de go to, utilizando apenas
iterações.
O autor não teve sucesso em encontrar um exemplo de remoção de recursão onde o uso de go to é estritamente necessário.
Iterativo: até duas vezes mais rápidos do que o
recursivo.
Introdução dos Comandos go to (cont)
Eliminação de Variáveis Boolenas
Eliminação de Variáveis Booleanas
Eliminação de variáveis Booleanas por duplicação de código
i := m; j := n;
v := A[ j ]; up := true;
loop:
if up
then if A[ i ] > v
then A[ j ] := A[ i ]; up := false fi;
else if v > A[ j ]
then A[ i ] := A[ j ]; up := true fi;
fi;
if up then i := i + 1 else j := j - 1 fi;
while i < j repeat;
A[ j ] := v;
i := m; j := n;
v := A[ j ];
loop: if A[ i ] > v
then A[ j ] := A[ i ]; go to upf fi;
upt : i := i + 1;
while i < j repeat; go to common;
loop: if v > A[ j ]
then A[ i ] := A[ j ]; go to upt fi;
upf: j := j - 1 while i < j repeat;
common: A[ j ] := v;
Exemplo 2: Mais desorganizado, 25% mais eficiente
Introdução dos Comandos go to (cont)
Redução da Complicação
Redução da Complicação
Caso de uso do go to para o qual o autor não encontrou um bom substituto.
Ocorre após um programa ter executado um (multiway branch) para um grande número de casos diferentes, porém relacionados.
Exemplo: Emulador microprogramado
Decodifica o endereço e executa a busca do operando na memória, executa um salto baseado no código da operação. As rotinas seriam:
Subtract operand := - operand; go to add;
Add accum := accum + operand;
tyme := tyme + 1;
go to no op;
Jump on Overflow if overflow
then overflow := false; go to jump;
else go to no op;
fi;
Conclusões
Há situações em que o uso de go to não é perigoso e é até mesmo desejável.
Novos tipos de sintaxe provêem bons
substitutos sem encorajar o programador a criar “espaguetes lógicos.”
Mas o que faz alguns go to’s ruins e outros
aceitáveis? A estrutura do programa.
Conclusões (cont)
Programação Estruturada
Programação Estruturada
Programação estruturada não é escrever um programa e depois eliminar os seus go to.
Dividir pra conquistar.
Cada nível corresponde à abstração dos detalhes referentes àquele nível.
Composição sequencial, iteração e condicionais apresentam estrutura sintática que os olhos facilmente assimilam, mas um go to não.
Estrutura visual como a dos fluxogramas.
Uma iteração é uma boa abstração se podemos atribuir uma invariante significativa.
Um comando if...then... else..fi, se pudermos estanciar um propósito geral para o comando como um todo.
Precisamos também de dados bem estruturados.
Conclusões (cont)
Com Comandos go to
Com Comandos go to
O go to pode ser uma abstração desde que se
tenha noção exata do que significa executar o go to para cada label.
É possível escrever programas bem estruturados com go to.
Dificilmente ele é a melhor alternativa uma vez que
estão surgindo novas linguagens.
Conclusões (cont)
Eficiência
Eficiência
Primeiro criar um programa legível e que funciona,
Depois pensar na sua eficência.
Geralmente só uma fração do código está envolvida e então, nesta parte, é desejável sacrificar a
legibilidade pela eficiência.
Conclusões (cont)
O Futuro
O Futuro
Novas linguagens facilitarão o desenvolvimento de programas estruturados.
Pequenos módulos identificados por seus nomes à medida em que são usados para construir módulos maiores.
Sistemas que manipulam programas.
Diferentes níveis de linguagem.
A linguagem final terá go to em seus níveis mais altos?
Aplicando o conceito de abstração, não haverá necessidade do go to.
Ele deveria ser abolido das linguagens de nível mais alto, ao menos para os programadores iniciantes.