Como ensinar a superar dificuldades, enquanto ensina a escrever ciclos

Apesar de ser um dos tópicos básicos, este artigo foi escrito para profissionais experientes. O objetivo é mostrar quais são os equívocos comuns entre novatos em programação. Para desenvolvedores práticos, esses problemas já foram resolvidos há muito tempo, esquecidos ou simplesmente não notados. Este artigo pode ser útil caso você precise ajudar alguém com este tema. O conteúdo faz paralelos com materiais de vários livros de programação de autores como Schildt, Stroustrup e Okulov.

O tema dos loops foi escolhido porque é um ponto onde muitos se afastam ao aprender programação.

Esta metodologia é direcionada a estudantes com dificuldades. Normalmente, os alunos mais fortes não ficam travados nesse tópico e não é necessário criar metodologias especiais para eles. O objetivo secundário do artigo é transformar essa metodologia de "funciona para todos os alunos, mas apenas com um professor" para "funciona para todos os alunos e todos os professores". Não pretendo ser absolutamente original. Se você já aplica uma metodologia semelhante para ensinar este tema, por favor, compartilhe como sua abordagem difere. Se decidir aplicar, conte como foi a experiência. Se uma metodologia similar estiver descrita em algum livro, por favor, compartilhe o título.


Desenvolvi esta metodologia ao longo de 4 anos, trabalhando individualmente com estudantes de diferentes níveis de preparação. Foram cerca de cinquenta estudantes e duas mil horas de aulas. No início, os estudantes costumavam ficar presos neste tema e desistir. Após cada aluno, a metodologia e os materiais eram ajustados. No último ano, os alunos já não ficam presos nesse tópico, então decidi compartilhar as minhas descobertas.

Por que tanto texto? Loopings são tão simples!

Como já mencionei acima, para desenvolvedores práticos e alunos fortes, a complexidade do conceito de loops pode ser subestimada. Por exemplo, pode-se realizar uma longa palestra, ver cabeças acenando e olhos brilhantes. Mas, ao tentar resolver um problema, surgem bloqueios e problemas inexplicáveis. Após a palestra, os alunos certamente terão uma compreensão apenas parcial. A situação é agravada pelo fato de que os alunos não conseguem expressar o que exatamente os confunde.
Um dia, percebi que os alunos viam meus exemplos como hieróglifos. Ou seja, como partes indivisíveis de texto, nas quais precisavam adicionar uma 'letra mágica' e tudo funcionaria.
Às vezes, eu notava que os alunos achavam que para resolver uma tarefa específica era necessário alguma outra estrutura que eu simplesmente ainda não tinha mencionado. Embora para a solução fosse apenas necessário modificar um pouco o exemplo.

Portanto, cheguei à ideia de que o foco deve estar não na sintaxe das expressões, mas na ideia de refatorar o código repetitivo através de loops. Assim que os alunos dominam essa ideia, qualquer sintaxe é assimilada por meio de pequenos exercícios.

Para quem e por que eu ensino

Como não há exames de admissão, nas aulas pode haver tanto alunos fortes quanto muito fracos. Mais detalhes sobre meus alunos podem ser lidos no artigo Retrato dos ouvintes dos cursos noturnos
Eu buscava que todos que quisessem aprendessem programação.
Minhas aulas são individuais e os alunos pagam por cada uma delas. À primeira vista, os alunos iriam otimizar os custos e exigir o mínimo. No entanto, as pessoas participam das aulas presenciais com um professor não apenas pelos conhecimentos em si, mas pela confiança de que realmente compreenderam, pela sensação de progresso e pelo reconhecimento de um especialista (professor). Se os alunos não sentirem progresso em seu aprendizado, eles vão embora. De modo geral, é possível estruturar as aulas de forma que os alunos sintam progresso no aumento do número de construções conhecidas. Ou seja, primeiro estudamos detalhadamente o while, depois o for, em seguida o do while e então temos um curso de mil e uma noites, onde durante dois meses são estudados apenas loops, e no final o aluno escreveu a biblioteca padrão por ditado. No entanto, para resolver problemas práticos, é necessário não apenas o conhecimento do material, mas também a autonomia em sua aplicação e na busca de novas informações. Portanto, para os cursos presenciais, considero correto o princípio de ensinar o essencial e incentivar o aprendizado autônomo de nuances e tópicos correlatos. No tópico sobre loops, eu considero o mínimo a construção while. Com isso, é possível entender o princípio. Conhecendo o princípio, pode-se aprender o for e o do-while de forma autônoma.

Para garantir a compreensão do material por alunos com dificuldades, descrever a sintaxe não é suficiente. É necessário oferecer uma variedade maior de tarefas simples e detalhar os exemplos de forma mais clara. No final, a velocidade de aprendizado é limitada pela capacidade do aluno de transformar expressões e buscar padrões. Para alunos mais inteligentes, a maioria das tarefas pode parecer monótona. Durante as aulas com eles, não é preciso insistir na resolução de 100% das tarefas. Meu material pode ser encontrado em meu GitHub. Entretanto, o repositório mais parece um grimório de um mago — ninguém além de mim entenderá onde está cada coisa, e se falhar a verificação, pode-se enlouquecer

A metodologia é orientada para a prática

A teoria é explicada através da resolução de um problema. Nas aulas de introdução à programação, onde se estudam ramificações e loops, não é possível realizar uma palestra útil sobre um único tópico durante uma hora. Quinze a vinte minutos são suficientes para explicar o conceito. As principais dificuldades surgem ao realizar tarefas práticas.
Professores iniciantes podem apresentar operadores, ramificações, loops e arrays em uma única aula. No entanto, os alunos enfrentarão dificuldades para assimilar essa informação.
É necessário não apenas apresentar o material, mas também garantir que os ouvintes o entenderam.

O fato de um tema ter sido compreendido é determinado pela forma como o aluno lida com o trabalho independente.
Se o aluno conseguiu resolver a tarefa sobre o tema sem a ajuda do professor, isso significa que o tema foi compreendido. Para garantir a verificação independente, cada tarefa é acompanhada por uma tabela com cenários de teste. As tarefas têm uma ordem bem definida. Não é recomendável pular tarefas. Se a tarefa atual for muito difícil, passar para a próxima é inútil. Ela será ainda mais complicada. Para que o aluno consiga lidar com a tarefa atual, são explicadas algumas técnicas usando o exemplo da primeira tarefa. De fato, todo o conteúdo do tema se resume às técnicas para superar dificuldades. Os loops são, na verdade, um efeito colateral.

A primeira tarefa é sempre um exemplo. A segunda difere ligeiramente e é executada ‘sozinha’ logo após a primeira sob a supervisão do professor. Todas as tarefas subsequentes visam chamar a atenção para diferentes detalhes que podem causar confusões.

A explicação do exemplo é um diálogo no qual o aluno precisa chamar a retropropagação e a validação cruzada para garantir a compreensão do material.

Serei banal e afirmarei que o primeiro exemplo sobre o tema é extremamente importante. Tendo material para um extenso trabalho independente, as falhas do primeiro exemplo podem ser corrigidas. Se não houver mais nada além do exemplo, o aluno provavelmente não dominará o tema.

While ou for?

Uma das questões polêmicas é a escolha da construção para o exemplo: while ou for. Uma vez, um amigo meu, um desenvolvedor praticante sem experiência em ensino, passou uma hora me convencendo de que o loop for é o mais fácil de entender. Seus argumentos eram baseados em "tudo é claro e organizado". No entanto, a verdadeira origem das dificuldades dos novatos está na própria ideia do loop, e não em sua implementação. Se a pessoa não entender essa ideia, terá dificuldades com a sintaxe. Assim que a ideia é compreendida, os problemas de formatação do código desaparecem.

Nos meus materiais, o tema dos loops segue o tema das ramificações. A semelhança externa entre if e while permite fazer uma analogia direta: "quando a condição no cabeçalho é verdadeira, o corpo é executado". A particularidade do loop é que o corpo é executado várias vezes.

Meu segundo argumento é que while exige menos formalidades do que for. Menos formalidades significam menos erros bobos com vírgulas e parênteses esquecidos. Novatos ainda não têm a atenção e o detalhismo necessários para evitar automaticamente erros de sintaxe.
O terceiro argumento é que em muitos bons livros, while é explicado primeiro.

Se o aluno consegue transformar expressões facilmente, pode-se mencionar for em passagem. O aluno escolherá o que prefere. Se, no entanto, as transformações causam dificuldades, é melhor não dispersar a atenção. O aluno deve primeiro resolver tudo usando while. Quando o tema dos loops for dominado, pode-se reescrever as soluções para praticar a transformação de while em for.
Loops com pós-condição são criaturas bastante raras. Não perco tempo com isso. Se o aluno dominou as ideias de identificação de padrões e transformação de expressões, conseguirá entender sem a minha ajuda.

Ao demonstrar o primeiro exemplo para alunos avançados, enfatizo que, no primeiro exemplo, é importante registrar não apenas a solução, mas toda a cadeia de ações que levaram ao resultado. Alunos preguiçosos podem negligenciar a escrita e levar apenas o algoritmo final. É preciso convencê-los de que, um dia, irão se deparar com uma tarefa desafiadora. Para resolvê-la, será necessário seguir etapas como neste exemplo. É exatamente por isso que é importante registrar todas as fases. Nas próximas tarefas, podemos deixar apenas a versão final da solução.

A ideia principal da automação é que confiamos ao computador a execução do trabalho rotineiro em vez de uma pessoa. Um dos truques básicos é a escrita de loops. Isso é aplicado quando um programa executa várias ações repetitivas consecutivas.

Explícito é melhor do que implícito.

Pode parecer uma boa ideia, na primeira tarefa sobre loops, exibir na tela a mesma frase várias vezes. Por exemplo:

Hurra, funciona!
Hurra, funciona!
Hurra, funciona!
Hurra, funciona!
Hurra, funciona!
Hurra, funciona!
Hurra, funciona!
Hurra, funciona!

Essa opção é ruim porque na saída não é possível ver o valor do contador. Esse é um problema para iniciantes. Não devemos subestimá-lo. Inicialmente, esta tarefa era a primeira, e a tarefa sobre a exibição de uma sequência de números em ordem crescente era a segunda. Foi necessário introduzir termos adicionais como "loop N vezes" e "loop de A a B", que, na essência, significam a mesma coisa. Para não proliferar entidades desnecessárias, decidi mostrar apenas o exemplo de exibição de uma sequência de números. Poucos conseguem, sem preparação, aprender a manter um contador na cabeça e modelar o comportamento do programa mentalmente. Alguns alunos se deparam pela primeira vez com a modelagem "na mente" exatamente no tema de loops.
Após um certo período de prática, a tarefa de repetir um texto idêntico eu deixo para resolução independente. Se primeiro mostrarmos um contador visível e depois um invisível, os alunos enfrentam menos problemas. Às vezes, uma dica como "não escreva o contador na tela" é suficiente.

Como é explicado por outros?

Na maioria dos materiais didáticos disponíveis na internet, a sintaxe do loop é apresentada em forma de "palestra". Por exemplo, no developer.mozilla.org (atualmente), junto com o loop while, são descritas várias outras construções. No entanto, são fornecidas apenas as construções em forma de templates. O resultado de sua execução é descrito em palavras, e a ilustração está ausente. Na minha opinião, essa abordagem reduz a utilidade desses materiais a zero. O aluno pode escrever o código e executá-lo, mas ainda assim precisa de um padrão para comparação. Como saber se o exemplo foi reescrito corretamente se não há nada para comparar o resultado?
Quando apenas um template é fornecido, sem exemplo, o aluno encontra ainda mais dificuldades. Como entender se os fragmentos de código estão organizados corretamente no template? É possível tentar escrever de alguma forma, e depois executar. Mas se não há um padrão para comparação do resultado, a execução também não ajudará.

No curso de C++ na Intuit, a sintaxe do loop está enterrada na terceira página da aula 4 sobre o tema "operadores". Ao explicar a sintaxe dos loops, há um foco especial no termo "operador". O termo é apresentado como um conjunto de fatos, como "o símbolo; é um operador", "{} é um operador composto", "o corpo do loop deve ser um operador". Não gosto dessa abordagem porque esconde importantes inter-relações por trás de um único termo. A análise do código-fonte do programa em termos nesse nível é necessária para os desenvolvedores de compiladores para implementar a especificação da linguagem, mas não para os alunos em um primeiro momento. Iniciantes em programação raramente possuem a atenção necessária para lidar com os termos de forma tão minuciosa. Poucas pessoas se lembram e entendem novas palavras na primeira vez. Praticamente ninguém consegue aplicar corretamente um termo que acabou de aprender. Portanto, os alunos cometem muitos erros, como "escrevi while(a<7);{, e o programa não funciona".
Na minha opinião, é melhor apresentar a sintaxe da construção imediatamente com os parênteses no início. O formato sem parênteses deve ser explicado apenas se o aluno tiver uma pergunta específica sobre "por que aqui não há parênteses e funciona".

No livro de Okulov "Fundamentos da Programação" de 2012, a introdução aos loops começa com o modelo for, seguido por recomendações sobre seu uso, e logo depois há uma seção experimental da aula. Entendo que o livro foi escrito para aquela minoria de alunos muito capazes, que raramente aparecem nas minhas aulas.

Em livros populares, o resultado dos fragmentos de código sempre é apresentado. Por exemplo, no livro de Schildt "Java 8. Guia Completo", de 2015. Primeiro, é dado um modelo, depois um exemplo de programa e logo após, o resultado da execução.

Como exemplo, vamos considerar o loop while, que executa uma contagem regressiva,
começando a partir de 10, e imprime exatamente 10 linhas de "pulsos":

//Продемонстрировать применение оператора цикла while
class While {
    public static void main(String args []) {
        int n = 10;
        while (n > 0) {
            System.out.println("такт " + n);
            n--;
        }
    }
}

Ao ser executado, este programa imprime dez "pulsos" da seguinte forma:
takt 10
takt 9
takt 8
takt 7
takt 6
takt 5
takt 4
takt 3
takt 2
takt 1

A abordagem que descreve um modelo, um exemplo de programa e o resultado da execução é também utilizada no livro "JavaScript para Crianças" e no curso de js no w3schools.com. O formato da página web até permite tornar este exemplo interativo.

No livro de Stroustrup "Princípios e Prática com C++" de 2016, o autor foi ainda mais longe. Primeiro, é explicado qual deve ser o resultado, e só depois é apresentado o texto do programa. Além disso, como exemplo, é escolhida não apenas qualquer programa aleatória, mas é feita uma incursão na história. Isso ajuda a fazer com que se preste atenção ao afirmar: "Veja, isso não é apenas um texto sem valor. Você vê algo significativo."

Como exemplo de iteração, vamos considerar o primeiro programa executado em uma máquina com programa armazenado (EDSAC). Ele foi escrito por David Wheeler no laboratório de computação da Universidade de Cambridge, na Inglaterra, em 6 de maio de 1949. Este programa calcula e imprime uma lista simples de quadrados.
0 0
1 1
2 4
3 9
4 16
...
98 9604
99 9801

Aqui, em cada linha contém um número, seguido pelo sinal de tabulação (‘t’) e o quadrado desse número. A versão desse programa em C++ é assim:

//Вычисляем и распечатываем таблицу квадратов чисел 0-99
int main()
{
    int i = 0; // Начинаем с нуля
    while(i < 100){
        cout << i << 't' << square(i) << 'n';
        ++i;
    }
}

Curiosamente, o modelo de sintaxe neste livro não é descrito. Stroustrup, no guia do instrutor (tradução) enfatiza que respeita a inteligência de seus alunos. Talvez a habilidade de identificar um padrão em vários exemplos seja considerada uma manifestação dessa inteligência.

Como eu mesmo explico

A abordagem de Stroustrup: descrever o resultado, em seguida resolver o problema e depois a análise independente do aluno — parece ser a mais bem pensada. Portanto, decidi usar exatamente isso como base, mas contando um exemplo menos histórico — a tarefa de gerar um 'sumário'. Isso cria um ponto de referência reconhecível para depois poder dizer 'lembre-se da tarefa sobre o sumário' e os alunos se recordarem dela especificamente. No meu exemplo, procurei antecipar também dois dos equívocos mais comuns. A seguir, escreverei sobre eles em mais detalhes.

Nesta tarefa, nos familiarizamos com técnicas para resolver problemas complexos. A solução inicial deve ser feita de forma primitiva e simples. Depois disso, podemos pensar em como melhorar essa solução.
Introdução
Capítulo 1
Capítulo 2
Capítulo 3
Capítulo 4
Capítulo 5
Capítulo 6
Capítulo 7
Conclusão

De acordo com as minhas observações, a abordagem 'modelo-exemplo-resultado' em diferentes combinações ainda faz com que os alunos vejam o ciclo como um hieróglifo. Isso se manifestava na falta de compreensão sobre por que ter que escrever uma condição, como escolher entre i++ e i-- e outras coisas que parecem óbvias. Para evitar esses mal-entendidos, a abordagem para explicar ciclos deve enfatizar o sentido de repetir ações iguais e, somente depois, sua formatação através da construção. Portanto, antes de fornecer a sintaxe do ciclo, precisamos resolver a tarefa 'na cara'. A solução primitiva para a tarefa do sumário é a seguinte:

Console.WriteLine("Introdução");
Console.WriteLine("Capítulo 1");
Console.WriteLine("Capítulo 2");
Console.WriteLine("Capítulo 3");
Console.WriteLine("Capítulo 4");
Console.WriteLine("Capítulo 5");
Console.WriteLine("Capítulo 6");
Console.WriteLine("Capítulo 7");
Console.WriteLine("Conclusão");

Como podemos melhorá-lo?
Substituir ações repetitivas por um ciclo.
Quais ações se repetem aqui sem mudanças?
Neste fragmento, não há nenhuma. No entanto, os comandos para imprimir a palavra 'Capítulo' com o número são muito parecidos entre si.
Portanto, a próxima etapa é buscar a diferença entre os fragmentos. Isso só é óbvio nesta tarefa, depois as repetições não serão de comandos únicos, mas de blocos de código com 5 linhas ou mais. A busca terá que ser feita não apenas numa lista de comandos, mas em construções de ramificação ou de ciclo.
No exemplo, a diferença entre os comandos está no número após a palavra 'Capítulo'.
Assim que a diferença for encontrada, precisamos entender o padrão de mudança. O fragmento distinto é um número? Ele aumenta constantemente ou decresce? Como o valor do número muda entre dois comandos adjacentes?
No exemplo, o número após a palavra "Capítulo" aumenta em passos de 1. A diferença foi encontrada, o padrão foi identificado. Agora é possível substituir o fragmento que varia por uma variável.
É necessário declarar essa variável antes do primeiro dos fragmentos repetidos. Essa variável é geralmente chamada de i ou j, ou algo mais descritivo. Seu valor inicial deve ser igual ao primeiro valor exibido na tela. No exemplo, o primeiro valor é 1.
Qual valor inicial deve ser usado para exibir a sequência de números "100, 101, 102, 103, 104, 105"?
Neste conjunto, o primeiro número é 100.
Após cada comando de exibição, é necessário aumentar o valor dessa variável em 1. Essa unidade é o passo da mudança.
Qual será o passo na sequência de números "100, 102, 104, 106"?
Neste conjunto, o passo é 2.
Após substituir o fragmento variável, o código ficará assim:

Console.WriteLine("Introdução");
int i;
i = 0;
Console.WriteLine("Capítulo " + i);
i = i + 1;
Console.WriteLine("Capítulo " + i);
i = i + 1;
Console.WriteLine("Capítulo " + i);
i = i + 1;
Console.WriteLine("Capítulo " + i);
i = i + 1;
Console.WriteLine("Capítulo " + i);
i = i + 1;
Console.WriteLine("Capítulo " + i);
i = i + 1;
Console.WriteLine("Capítulo " + i);
i = i + 1;
Console.WriteLine("Conclusão");

Após aplicar a técnica "expressar o padrão como variável" no código, resultam várias grupos de ações idênticas que ocorrem em sequência. Agora, as ações repetitivas podem ser substituídas por um loop.

A sequência para resolver a tarefa, onde é necessário usar loops, consiste nas etapas:

  1. Resolver "de forma direta" com um conjunto de comandos separados
  2. Encontrar o padrão
  3. Expressar o padrão como variável
  4. Formatar como um loop

A seguir, novos termos são apresentados, para que o estudante não fique na situação de "tudo entendo, mas não consigo explicar":
— contador — sempre uma variável necessária para acompanhar a quantidade de passos do loop. Normalmente um número inteiro, que é comparado com um limite.
— passo do contador — descrição do padrão de mudança do contador.
— limite — número ou variável com a qual o contador é comparado, de modo que o algoritmo seja finito. O valor do contador é alterado para se aproximar do limite.
— corpo do loop — conjunto de comandos que serão repetidos. Quando se diz "comando escrito dentro do loop", refere-se especificamente ao corpo.
— iteração do loop — uma única execução do corpo do loop.
— condição do loop — a expressão lógica que determina se mais uma iteração será executada. (Aqui pode haver confusão com as estruturas de ramificação)
É preciso estar preparado para o fato de que, a princípio, os alunos usarão os termos de maneira inadequada. Isso se aplica tanto aos mais fortes quanto aos mais fracos. Estabelecer uma linguagem comum é uma verdadeira arte. Vou resumir: é necessário definir a tarefa "destacar o trecho de código com " e também usar esses termos corretamente na conversa.
Após a transformação com o loop, obtemos o trecho:

Console.WriteLine("Introdução");
int i = 0;
while (i < 7) {
    Console.WriteLine("Capítulo " + i);
    i = i + 1;
}
Console.WriteLine("Conclusão");

O principal equívoco

Um equívoco popular entre os alunos é que eles colocam dentro da estrutura do loop ações que precisam ser realizadas apenas uma vez. Por exemplo, assim:

;
int i = 0;
while (i < 7) {
    Console.WriteLine("Introdução")
    Console.WriteLine("Capítulo " + i);
    i = i + 1;
    Console.WriteLine("Conclusão");
}

Os alunos frequentemente se deparam com esse problema, tanto no início quanto em tarefas mais complexas.
A dica coringa neste caso:

Quantas vezes deve-se repetir o comando: uma vez ou muitas?

Os comandos para exibir as palavras "Introdução" e "Conclusão", assim como a declaração e a inicialização da variável i, não se assemelham a outras ações repetitivas. Eles são executados apenas uma vez, portanto devem estar fora do corpo do loop.

O código deve manter todas as três etapas da solução, para que possam ser referenciadas em caso de dificuldades. Os dois primeiros exemplos podem ser apenas comentados para não interferirem.
A atenção do aluno deve ser direcionada aos seguintes fatos:
— Na condição do loop, geralmente se compara o contador e o limite. O contador pode mudar dentro do corpo do loop, enquanto o limite não. Para quebrar essa regra, é preciso apresentar razões válidas.
— Os comandos para exibir as palavras "Introdução" e "Conclusão" estão fora do corpo do loop. Precisamos executá-los uma vez. "Introdução" — antes da repetição das ações, "Conclusão" — após.
Durante a consolidação desse tópico, a aprendizagem dos seguintes e a análise de dificuldades, mesmo os alunos fortes, é útil fazer a pergunta: "E essa ação, quantas vezes precisa ser realizada? Uma ou muitas?".

Desenvolvimento de habilidades adicionais

Durante o estudo dos ciclos, os alunos ainda praticam a habilidade de diagnóstico e resolução de problemas. Para realizar o diagnóstico, o aluno precisa imaginar o resultado desejado e compará-lo com o resultado real. As ações para correção dependem da diferença entre eles.
Como os alunos nesta fase ainda têm uma noção fraca do que é um 'resultado desejado', eles podem se basear em dados de teste. Normalmente, ninguém nessa fase entende o que pode dar errado e como lidar com isso. Portanto, eu deixo anotado em um caderno a descrição de problemas típicos e várias formas de resolvê-los. A escolha da solução mais adequada é tarefa do próprio aluno.
As anotações são necessárias para questionar 'o que era esperado foi realmente alcançado?', 'qual dessas situações ocorreu agora?', 'a solução aplicada ajudou?'.

  1. O número de ações é 1 a menos ou a mais do que o esperado. Formas de resolver:
    — aumentar o valor inicial do contador em 1.
    — substituir o operador de comparação estrita (< ou >) por um não estrito (<= ou >=).
    — alterar o valor do limite em 1.
  2. As ações no ciclo são executadas sem parar, infinitamente. Formas de resolver:
    — adicionar um comando para alterar o contador, se este estiver faltando.
    — corrigir o comando para alterar o contador de modo que seu valor se aproxime do limite.
    — remover o comando que altera o limite, se ele estiver dentro do corpo do ciclo.
  3. O número de ações no ciclo é mais de 1 a menos ou a mais do que o esperado. A ação no ciclo não foi executada nenhuma vez. Primeiro, é necessário verificar os valores reais das variáveis imediatamente antes do início do ciclo. Formas de resolver:
    — alterar o valor inicial do limite.
    — alterar o valor inicial do contador.

Normalmente, o problema 3 está relacionado ao uso de uma variável incorreta ou à não inicialização do contador.

Após essa explicação, o aluno ainda pode ter várias concepções errôneas sobre o funcionamento dos ciclos.
Para dissipar as mais comuns, dou tarefas:

  1. Na qual o limite, o valor inicial do contador ou o passo do contador é inserido pelo usuário.
  2. Na qual o valor do contador precisa ser usado em alguma expressão aritmética. Preferencialmente com o contador em uma expressão aninhada ou no denominador, para que a diferença seja não linear.
  3. Em que o valor do contador não é exibido na tela durante a execução do ciclo. Por exemplo, mostrar a quantidade necessária de fragmentos de texto iguais ou desenhar uma figura com gráficos de tartaruga.
  4. Em que é necessário executar primeiro algumas ações repetitivas e depois outras.
  5. Em que é necessário realizar outras ações antes e depois das repetitivas.

Para cada tarefa, é necessário fornecer dados de teste e o resultado esperado.

Para entender quão rápido se pode avançar, é necessário deixar o aluno ler as condições dessas tarefas e perguntar: “Como elas diferem do exemplo?”, “O que precisa ser alterado no exemplo para resolvê-las?”. Se o estudante responder de forma reflexiva, então ele deve resolver pelo menos uma durante a aula, e as demais — em casa de forma independente. Se a solução for bem-sucedida, pode-se começar a explicação sobre as condições dentro dos ciclos.
Se houver dificuldades com a solução independente, todas as questões devem ser trabalhadas durante a aula. Para que a resolução do problema não se pareça com o desenho de uma coruja, recomendo primeiro resolver o problema de forma não universal. Ou seja, de maneira que a solução passe no primeiro teste e não utilize a estrutura do ciclo. Depois, pode-se aplicar transformações para alcançar a universalidade da solução.

Ciclos e ramificações

Na minha opinião, é útil dar a temática 'ciclos dentro das ramificações' separadamente. Assim, será possível ver a diferença entre a verificação múltipla de uma condição e a verificação única.
As tarefas para fixação serão sobre a exibição de números de A a B, que são inseridos pelo usuário:
— sempre em ordem crescente.
— em ordem crescente ou decrescente dependendo dos valores de A e B.

A temática 'ramificações dentro dos ciclos' deve ser abordada apenas após o aluno ter dominado as técnicas: 'substituição de padrões por variáveis' e 'substituição de ações repetitivas por ciclos'.
A principal razão para aplicar ramificações dentro de ciclos é a anomalia nos padrões. No meio, ela é interrompida dependendo dos dados de entrada.
Para os alunos que conseguem buscar soluções combinando técnicas simples, é suficiente dizer que 'ramificações podem ser escritas dentro dos ciclos' e dar a tarefa 'como exemplo' completamente para solução independente.
Tarefa de exemplo:

O usuário insere o número X. Exibir em coluna os números de 0 a 9 e colocar o sinal ‘+’ ao lado do número que é igual a X.

Se 0 foi inserido0+
1
2
3
4
5
6
7
8
9

Se foi inserido 60
1
2
3
4
5
6+
7
8
9

Se foi inserido 90
1
2
3
4
5
6
7
8
9+

Se foi inserido 7770
1
2
3
4
5
6
7
8
9

Se a explicação breve não for suficiente para escrever um ciclo, então é necessário alcançar uma solução universal para essa mesma tarefa sem um ciclo.
Sairá uma das duas opções:
Desejado

string temp;
temp = Console.ReadLine();
int x;
x = int.Parse(temp);
if (x==0) {
    Console.WriteLine(0 + "+");
} else {
    Console.WriteLine(0);
}
if (x==1) {
    Console.WriteLine(1 + "+");
} else {
    Console.WriteLine(1);
}
if (x==2) {
    Console.WriteLine(2 + "+");
} else {
    Console.WriteLine(2);
}
if (x==3) {
    Console.WriteLine(3 + "+");
} else {
    Console.WriteLine(3);
}
if (x==4) {
    Console.WriteLine(4 + "+");
} else {
    Console.WriteLine(4);
}
if (x==5) {
    Console.WriteLine(5 + "+");
} else {
    Console.WriteLine(5);
}
if (x==6) {
    Console.WriteLine(6 + "+");
} else {
    Console.WriteLine(6);
}
if (x==7) {
    Console.WriteLine(7 + "+");
} else {
    Console.WriteLine(7);
}
if (x==8) {
    Console.WriteLine(8 + "+");
} else {
    Console.WriteLine(8);
}
if (x==9) {
    Console.WriteLine(9 + "+");
} else {
    Console.WriteLine(9);
}

Possível

string temp;
temp = Console.ReadLine();
int x;
x = int.Parse(temp);
if (x==0) {
    Console.WriteLine("0+n1n2n3n4n5n6n7n8n9");
}
if (x==1) {
    Console.WriteLine("0n1+n2n3n4n5n6n7n8n9");
}
if (x==2) {
    Console.WriteLine("0n1n2+n3n4n5n6n7n8n9");
}
if (x==3) {
    Console.WriteLine("0n1n2n3+n4n5n6n7n8n9");
}
if (x==4) {
    Console.WriteLine("0n1n2n3n4+n5n6n7n8n9");
}
if (x==5) {
    Console.WriteLine("0n1n2n3n4n5+n6n7n8n9");
}
if (x==6) {
    Console.WriteLine("0n1n2n3n4n5n6+n7n8n9");
}
if (x==7) {
    Console.WriteLine("0n1n2n3n4n5n6n7+n8n9");
}
if (x==8) {
    Console.WriteLine("0n1n2n3n4n5n6n7n8+n9");
}
if (x==9) {
    Console.WriteLine("0n1n2n3n4n5n6n7n8n9+");
}

Eu dou uma tarefa semelhante antecipadamente, durante o estudo do tema sobre ramificações.
Se o aluno obteve a opção "possível", deve-se explicar que há várias soluções para o mesmo problema. No entanto, elas diferem em sua resistência às mudanças nos requisitos. Pergunte: "Quantos locais no código precisarão ser corrigidos se tivermos que adicionar mais um número?" Na opção "possível", será necessário adicionar mais uma ramificação e escrever o novo número em 10 outros lugares. Na opção "desejada", basta adicionar apenas uma ramificação.
Defina a tarefa de reproduzir a opção "desejada", depois encontre um padrão no código, realize a substituição da variável e escreva um ciclo.
Se você tiver uma ideia de como resolver essa tarefa sem um ciclo de outra forma, por favor, escreva nos comentários.

Ciclos dentro de ciclos

Neste tema, é importante observar que:
— os contadores para o ciclo interno e externo devem ser variáveis diferentes.
— o contador para o ciclo interno precisa ser reiniciado várias vezes (ou seja, no corpo do ciclo externo).
— nas tarefas de saída de texto, não se pode escrever uma letra em várias linhas e, em seguida, a segunda. É necessário primeiro imprimir todas as letras da primeira linha, depois todas da segunda e assim por diante.

A explicação do tema sobre ciclos aninhados deve começar pela importância de reiniciar o contador.
Tarefa de exemplo:

O usuário insere dois números: R e T. Imprima duas linhas de caracteres "#". Na primeira linha deve haver R caracteres. Na segunda linha, T caracteres. Se algum número for negativo, exiba uma mensagem de erro.

R=5, T=11#####
###########

R=20, T=3####################
###

R=-1, T=6O valor de R deve ser não negativo

R=6, T=-2O valor de T deve ser não negativo

É evidente que esta tarefa também tem pelo menos duas opções de solução.
Desejado

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
int i = 0;
while (i < R)
{
    Console.Write("#");
    i = i + 1;
}
Console.WriteLine();
i = 0;
while (i < T)
{
    Console.Write("#");
    i = i + 1;
}

Possível №1

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
int i = 0;
while (i < R)
{
    Console.Write("#");
    i = i + 1;
}
Console.WriteLine();
int j = 0;
j = 0;
while (j < T)
{
    Console.Write("#");
    j = j + 1;
}

A diferença é que na solução "possível" para a impressão da segunda linha, foi utilizada uma segunda variável. É necessário insistir no uso da mesma variável para ambos os ciclos. Este limite pode ser argumentado pelo fato de que a solução com um contador para dois ciclos ilustrará o termo "reinicialização do contador". A compreensão deste termo é necessária para resolver as próximas tarefas. Como compromisso, ambas as soluções da tarefa podem ser mantidas.

Um problema típico ao usar uma única variável contador para dois ciclos se manifesta assim:
R=5, T=11#####
######

A quantidade de caracteres na segunda linha não corresponde ao valor de T. Se precisar de ajuda com este problema, deve-se "apontar" para as anotações sobre problemas típicos com ciclos. Este é o sintoma №3. É diagnosticado se adicionar a saída do valor do contador diretamente antes do segundo ciclo. Corrige-se reinicializando. Mas é melhor não contar isso imediatamente. O estudante deve tentar formular pelo menos uma hipótese.

Claro, há também essa opção de solução. Mas eu nunca a vi nos estudantes. Na fase de estudo dos ciclos, falar sobre isso dispersará a atenção. Pode-se voltar a isso mais tarde, ao estudar funções de manipulação de strings.
Possível №2

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
Console.WriteLine(new String('#', R));
Console.WriteLine(new String('#', T));

A próxima tarefa obrigatória:

Exiba na tela os números de 0 a 9. Cada número deve estar em sua própria linha. O número de dígitos em cada linha (W) é inserido pelo teclado.

W=10
1
2
3
4
5
6
7
8
9

W=100000000000
1111111111
2222222222
3333333333
4444444444
5555555555
6666666666
7777777777
8888888888
9999999999

Se o estudante dominou o conceito de substituição de variável, ele conseguirá fazer isso rapidamente. O problema possível pode ser novamente a redefinição da variável. Se ele não conseguir fazer a conversão, significa que você se apressou e deve resolver problemas mais simples.

Obrigado pela atenção. Curta e se inscreva no canal.

P.S. Se você encontrou erros de digitação ou erros no texto, por favor, me avise. Você pode fazer isso destacando uma parte do texto e pressionando no Mac «⌘ + Enter» ou em teclados clássicos «Ctrl / Enter», ou através de mensagens diretas. Se essas opções não estiverem disponíveis, escreva sobre os erros nos comentários. Obrigado!

Apenas usuários registrados podem participar da pesquisa. Entre, por favor.

Pesquisa para leitores sem karma

  • 20,0%Dou aulas profissionalmente, +12

  • 10,0%Dou aulas profissionalmente, -11

  • 70,0%Não dou aulas, +17

  • 0,0%Não dou aulas, -10

  • 0,0%Outro0

10 usuários votaram. 5 usuários se abstiveram.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster