Para explicar a recursão , uso uma combinação de explicações diferentes, geralmente para ambas tentar:
- explique o conceito,
- explique por que isso importa,
- explique como obtê-lo.
Para iniciantes, o Wolfram | Alpha o define em termos mais simples do que a Wikipedia :
Uma expressão tal que cada termo é gerado pela repetição de uma operação matemática específica.
Matemáticas
Se o seu aluno (ou a pessoa que você explica também, de agora em diante eu direi aluno) tem pelo menos algum conhecimento matemático, obviamente já encontrou recursão estudando séries e sua noção de recursividade e sua relação de recorrência .
Uma maneira muito boa de começar é demonstrar com uma série e dizer que é simplesmente do que se trata a recursão:
- uma função matemática ...
- ... que se chama para calcular um valor correspondente a um n-ésimo elemento ...
- ... e que define alguns limites.
Normalmente, você recebe um "huh huh, whatev '", na melhor das hipóteses, porque eles ainda não o usam, ou mais provavelmente apenas um ronco muito profundo.
Exemplos de codificação
De resto, na verdade, é uma versão detalhada do que apresentei no Adendo da minha resposta para a pergunta que você apontou sobre ponteiros (trocadilho).
Nesta fase, meus alunos geralmente sabem como imprimir algo na tela. Supondo que estamos usando C, eles sabem como imprimir um único caractere usando write
ou printf
. Eles também sabem sobre os loops de controle.
Eu costumo recorrer a alguns problemas de programação repetitivos e simples até que eles o entendam:
- a operação fatorial ,
- uma impressora de alfabeto,
- uma impressora de alfabeto invertida,
- a operação de exponenciação .
Fatorial
O fatorial é um conceito matemático muito simples de entender, e a implementação está muito próxima de sua representação matemática. No entanto, eles podem não conseguir no início.
Alfabetos
A versão em alfabeto é interessante para ensiná-los a pensar sobre a ordem de suas declarações recursivas. Como nos ponteiros, eles lançam linhas aleatoriamente para você. O objetivo é levá-los à conclusão de que um loop pode ser invertido modificando as condições OU apenas invertendo a ordem das instruções em sua função. É aí que a impressão do alfabeto ajuda, pois é algo visual para eles. Basta que eles escrevam uma função que imprima um caractere para cada chamada e se auto-recursivamente para escrever a próxima (ou anterior).
Fãs de FP, pule o fato de que imprimir coisas no fluxo de saída é um efeito colateral por enquanto ... Não vamos ficar muito irritantes na parte frontal do FP. (Mas se você usar um idioma com suporte à lista, sinta-se à vontade para concatenar uma lista a cada iteração e apenas imprimir o resultado final. Mas geralmente eu os inicio com C, que infelizmente não é o melhor para esse tipo de problemas e conceitos) .
Exponenciação
O problema da exponenciação é um pouco mais difícil ( nesta fase do aprendizado). Obviamente, o conceito é exatamente o mesmo que para um fatorial e não há complexidade adicional ... exceto que você possui vários parâmetros. E isso geralmente é suficiente para confundir as pessoas e jogá-las fora no começo.
Sua forma simples:
pode ser expresso assim pela recorrência:
Mais difíceis
Depois que esses problemas simples forem mostrados E reimplementados nos tutoriais, você poderá dar exercícios um pouco mais difíceis (mas muito clássicos):
- Os números de Fibonacci ,
- O Maior Divisor Comum ,
- O problema das 8 rainhas ,
- O jogo Torres de Hanói ,
- E se você possui um ambiente gráfico (ou pode fornecer stubs de código para ele ou para uma saída de terminal ou eles já podem gerenciar isso), coisas como:
- E para exemplos práticos, considere escrever:
- um algoritmo de passagem em árvore,
- um simples analisador de expressões matemáticas,
- um jogo de caça-minas.
Nota: Novamente, alguns deles realmente não são mais difíceis ... Eles apenas abordam o problema exatamente do mesmo ângulo, ou um pouco diferente. Mas a prática leva à perfeição.
Ajudantes
Uma referência
Algumas leituras nunca são demais. Bem, a princípio será, e eles se sentirão ainda mais perdidos. É o tipo de coisa que cresce em você e fica na parte de trás da sua cabeça, até que um dia você percebe que finalmente entendeu. E então você pensa nessas coisas que lê. A recursão , a recursão em Ciência da Computação e as páginas de relação de recorrência na Wikipedia serviriam por enquanto.
Nível / profundidade
Supondo que seus alunos não tenham muita experiência em codificação, forneça stubs de código. Após as primeiras tentativas, ofereça a eles uma função de impressão que possa exibir o nível de recursão. Imprimir o valor numérico do nível ajuda.
O diagrama de empilhar como gavetas
Recuar um resultado impresso (ou a saída do nível) também ajuda, pois fornece outra representação visual do que seu programa está fazendo, abrindo e fechando contextos de pilha, como gavetas ou pastas em um explorador de sistemas de arquivos.
Acrônimos Recursivos
Se seu aluno já é um pouco versado na cultura da computação, ele pode já usar alguns projetos / softwares com nomes usando siglas recursivas . Tem sido uma tradição que existe há algum tempo, especialmente em projetos GNU. Alguns exemplos incluem:
Recursivo:
- GNU - "GNU não é Unix"
- Nagios - "Nagios não vai insistir na santidade"
- PHP - "Pré-processador de hipertexto PHP" (e originall "Página inicial pessoal")
- Vinho - "O vinho não é um emulador"
- Zile - "O Zile é Emacs Perdido"
Mutuamente recursivo:
- HURD - "HIRD de Daemons que Substituem o Unix" (onde HIRD é "HURD de Interfaces representando Profundidade")
Faça com que eles tentem criar seus próprios.
Da mesma forma, existem muitas ocorrências de humor recursivo, como a correção de pesquisa recursiva do Google . Para mais informações sobre recursão, leia esta resposta .
Armadilhas e Aprendizagem Adicional
Alguns problemas com os quais as pessoas geralmente enfrentam e para os quais você precisa saber respostas.
Por que, oh Deus, por que ???
Por que você faria isso? Uma razão boa, mas não óbvia, é que muitas vezes é mais simples expressar um problema dessa maneira. Um motivo não tão bom, mas óbvio, é que muitas vezes é preciso digitar menos (não faça com que se sintam muuuuitas apenas por usar recursão ...).
Alguns problemas são definitivamente mais fáceis de resolver ao usar uma abordagem recursiva. Normalmente, qualquer problema que você possa resolver usando o paradigma Divide and Conquer se ajustará a um algoritmo de recursão com várias ramificações.
O que há de novo N ??
Por que meu n
ou (qualquer que seja o nome da sua variável) é diferente toda vez? Os iniciantes geralmente têm problemas para entender o que são variáveis e parâmetros e como as coisas nomeadas n
em seu programa podem ter valores diferentes. Então agora, se esse valor está no loop de controle ou na recursão, é ainda pior! Seja gentil e não use os mesmos nomes de variáveis em todos os lugares e deixe claro que os parâmetros são apenas variáveis .
Condições finais
Como determino minha condição final? Isso é fácil, basta que eles digam os passos em voz alta. Por exemplo, para o fatorial, comece de 5, depois 4, depois ... até 0.
O diabo está nos detalhes
Não fale com coisas anteriores, como otimização de chamada de cauda . Eu sei, sei, o TCO é bom, mas eles não se importam a princípio. Dê a eles algum tempo para entender o processo de uma maneira que funcione para eles. Sinta-se à vontade para destruir seu mundo novamente mais tarde, mas faça uma pausa.
Da mesma forma, não fale diretamente da primeira aula sobre a pilha de chamadas e seu consumo de memória e ... bem ... o estouro da pilha . Costumo dar aulas particulares a alunos que me mostram palestras onde eles têm 50 slides sobre tudo o que há para saber sobre recursão quando eles mal conseguem escrever um loop corretamente nesse estágio. Esse é um bom exemplo de como uma referência ajudará mais tarde, mas agora apenas o confunde profundamente.
Mas, por favor, no devido tempo, deixe claro que existem razões para seguir a rota iterativa ou recursiva .
Recursão mútua
Vimos que as funções podem ser recursivas e até podem ter vários pontos de chamada (8 rainhas, Hanói, Fibonacci ou até mesmo um algoritmo de exploração para um limpador de minas). Mas e as chamadas recursivas mutuamente ? Comece com a matemática aqui também. f(x) = g(x) + h(x)
onde g(x) = f(x) + l(x)
e h
e l
apenas fazer coisas.
Começando com apenas séries matemáticas, é mais fácil escrever e implementar, pois o contrato é claramente definido pelas expressões. Por exemplo, as sequências femininas e masculinas de Hofstadter :
No entanto, em termos de código, é de notar que a implementação de uma solução mutuamente recursiva muitas vezes leva a codificar a duplicação e deve, antes, ser simplificados em uma única forma recursiva (Veja Peter Norvig 's Resolver cada quebra-cabeça Sudoku .