Saltar para o conteúdo
Topos Uranos

Resumo

Esta aula encerra a quarta unidade do curso, dedicada ao controle de fluxo. Reconstrói a cadeia de conceitos que vai da condição aos algoritmos numéricos, passando pela decisão e pela análise por casos, pelo laço e suas três formas, pelos padrões de laço, pelo invariante e pela variante; separa o que disso vale em qualquer linguagem do que é próprio de C++; reúne o comportamento indefinido da unidade, que se concentra nos contadores, nos acumuladores e nas condições que estouram, com um programa que o mostra e sua correção; repete em sua avaliação de saída as habilidades da avaliação de entrada; e termina com seis desafios que combinam a unidade inteira, desde o algoritmo de Euclides estendido até o calendário de um mês.

Objetivos de aprendizagem

  1. Projetar decisões cujos casos sejam disjuntos e exaustivos, e testá-las com uma entrada por caso e por borda.
  2. Projetar laços corretos que terminam, escolhendo sua forma e seus padrões a partir da especificação.
  3. Justificar a correção de um laço com um invariante e sua terminação com uma variante, também nos algoritmos numéricos da unidade.
  4. Reconhecer o comportamento indefinido da unidade (o contador, o acumulador e a condição que estouram, a divisão que a condição devia impedir) e reescrever o laço sem ele.
  5. Separar, no que se aprendeu na unidade, o que vale em qualquer linguagem do que é próprio de C++.

O que a unidade alcançou

A unidade se propôs ensinar a governar a ordem de execução com decisões e laços, e a raciocinar sobre sua correção: que cada caso seja atendido, que cada laço faça o que deve e que termine. O propósito foi cumprido apoiando-se somente no que a introdução declarou como sabido: as condições da unidade 3, os tipos da unidade 2 e o rastreamento e a variante da aula 1.1. O programa que calculava o máximo divisor comum com três passos fixos, e que, conforme os dados, respondia errado ou dividia por zero, foi substituído por um laço de três linhas, e agora se pode demonstrar que esse laço é correto para todos os dados não negativos.

Decisões

A aula Decisões ensinou a instrução if, sempre com chaves. Mostrou que em uma cadeia de else if se executa o primeiro ramo cuja condição é verdadeira, de modo que o caso real de cada ramo é sua condição junto com a negação de todas as anteriores; daí que a ordem importe e que um ramo possa ficar inalcançável sem aviso. Estabeleceu o critério de correção de toda decisão: seus casos devem ser disjuntos (são-no pela forma da cadeia) e exaustivos (são-no se ela termina em else), e devem coincidir com os da especificação, o que se testa no interior de cada caso e de cada lado de cada borda. Apresentou o operador condicional, para escolher entre dois valores breves sem uma decisão inteira. Encerrou com enum class e switch, cujo caso esquecido o compilador adverte.

Laços

A aula Laços apresentou while, do e for como três formas de um mesmo mecanismo: o while avalia sua condição antes de cada iteração e pode não fazer nenhuma; o do faz pelo menos uma; o for reúne em seu cabeçalho a inicialização, a condição e o incremento, e sua variável de controle vive somente no laço. Ensinou suas equivalências, a tabela de rastreamento, com k+1k + 1 avaliações da condição para kk iterações de um while que termina por sua condição (e kk avaliações em um do), e a contagem das iterações pelo cabeçalho, que previne o erro de um a mais. Acrescentou break, continue e os laços aninhados, cujo corpo interno se executa uma soma de vezes: m⋅pm \cdot p com limites fixos, n(n+1)/2n(n + 1)/2 com o triângulo.

Projetar laços corretos

A aula Projetar laços corretos fez do laço um objeto de projeto. Reuniu os padrões (contador, acumulador, máximo e mínimo, sentinela e flag), cada um definido pelo que sua variável significa depois de cada iteração. Definiu o invariante, uma afirmação que vale cada vez que se avalia a condição, com suas três obrigações: inicialização, manutenção e conclusão, que junto com a condição falsa dá a pós-condição; e o reconheceu como uma indução sobre o número de iterações. Ensinou a projetar ao contrário, da pós-condição ao invariante e do invariante ao corpo. Justificou a terminação com a variante, e mostrou três laços em que a aritmética de C++ a invalida: o real que salta o 1.0, o contador sem sinal que dá a volta e o contador com sinal que estoura. Deu, por último, o primeiro contato com o depurador.

Algoritmos numéricos

A aula Algoritmos numéricos aplicou o anterior a quatro algoritmos clássicos, escritos dentro de main e demonstrados. A primalidade por divisões por tentativa, com a proposição de que basta chegar a n\sqrt{n} e com a condição d <= n / d, que não estoura onde d * d <= n estoura. O algoritmo de Euclides, com o invariante mdc⁡(a,b)=mdc⁡(a0,b0)\operatorname{mdc}(a, b) = \operatorname{mdc}(a_0, b_0) e a variante bb, e seus dois perigos em C++: o resultado negativo e a divisão por zero se a condição é avaliada depois do corpo. A mudança de base por divisões sucessivas e a regra de Horner, que avalia um polinômio com uma multiplicação por coeficiente. E a bissecção, garantida pelo teorema do valor intermediário e governada por um contador inteiro cujo número de iterações se calcula de antemão.