A recursão é um tópico complicado de entender e não acho que posso fazer justiça aqui. Em vez disso, tentarei me concentrar na parte específica do código que você tem aqui e tentarei descrever a intuição de por que a solução funciona e a mecânica de como o código calcula seu resultado.
O código que você forneceu aqui resolve o seguinte problema: você deseja saber a soma de todos os inteiros de a a b, inclusive. Para o seu exemplo, você quer a soma dos números de 2 a 5, inclusive, que é
2 + 3 + 4 + 5
Ao tentar resolver um problema recursivamente, uma das primeiras etapas deve ser descobrir como dividir o problema em um problema menor com a mesma estrutura. Suponha que você queira somar os números de 2 a 5, inclusive. Uma maneira de simplificar isso é observar que a soma acima pode ser reescrita como
2 + (3 + 4 + 5)
Aqui, (3 + 4 + 5) passa a ser a soma de todos os inteiros entre 3 e 5, inclusive. Em outras palavras, se você quiser saber a soma de todos os inteiros entre 2 e 5, comece calculando a soma de todos os inteiros entre 3 e 5 e, em seguida, some 2.
Então, como você calcula a soma de todos os inteiros entre 3 e 5, inclusive? Bem, essa soma é
3 + 4 + 5
que pode ser pensado em vez de
3 + (4 + 5)
Aqui, (4 + 5) é a soma de todos os inteiros entre 4 e 5, inclusive. Portanto, se você quisesse calcular a soma de todos os números entre 3 e 5, inclusive, calcularia a soma de todos os inteiros entre 4 e 5 e, em seguida, adicionaria 3.
Existe um padrão aqui! Se você deseja calcular a soma dos inteiros entre a e b, inclusive, você pode fazer o seguinte. Primeiro, calcule a soma dos inteiros entre a + 1 e b, inclusive. Em seguida, adicione a a esse total. Você notará que "calcular a soma dos inteiros entre a + 1 e b, inclusive" é praticamente o mesmo tipo de problema que já estamos tentando resolver, mas com parâmetros ligeiramente diferentes. Em vez de calcular de a a b, inclusive, estamos computando de a + 1 a b, inclusive. Essa é a etapa recursiva - para resolver o problema maior ("soma de a para b, inclusive"), reduzimos o problema a uma versão menor dele mesmo ("soma de a + 1 para b, inclusive.").
Se você der uma olhada no código acima, perceberá que há esta etapa nele:
return a + sumInts(a + 1, b: b)
Este código é simplesmente uma tradução da lógica acima - se você quiser somar de a a b, inclusive, comece somando a + 1 a b, inclusive (essa é a chamada recursiva para sumInt
s) e, em seguida, adicionea
.
Claro, essa abordagem por si só não funcionará. Por exemplo, como você calcularia a soma de todos os inteiros entre 5 e 5, inclusive? Bem, usando nossa lógica atual, você calcularia a soma de todos os inteiros entre 6 e 5, inclusive, e depois adicionaria 5. Então, como você calcula a soma de todos os inteiros entre 6 e 5, inclusive? Bem, usando nossa lógica atual, você calcularia a soma de todos os inteiros entre 7 e 5, inclusive, e depois adicionaria 6. Você notará um problema aqui - isso continua e continua!
Na resolução recursiva de problemas, deve haver uma maneira de parar de simplificar o problema e, em vez disso, ir resolvê-lo diretamente. Normalmente, você encontrará um caso simples onde a resposta pode ser determinada imediatamente e, em seguida, estruture sua solução para resolver casos simples diretamente quando eles surgirem. Isso é normalmente chamado de caso base ou base recursiva .
Então, qual é o caso básico neste problema específico? Quando você está somando inteiros de a a b, inclusive, se a for maior que b, então a resposta é 0 - não há números no intervalo! Portanto, estruturaremos nossa solução da seguinte maneira:
- Se a> b, a resposta é 0.
- Caso contrário (a ≤ b), obtenha a resposta da seguinte forma:
- Calcule a soma dos inteiros entre a + 1 e b.
- Adicione um para obter a resposta.
Agora, compare este pseudocódigo com o seu código real:
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a + 1, b: b)
}
}
Observe que há quase exatamente um mapa um para um entre a solução delineada no pseudocódigo e este código real. O primeiro passo é o caso básico - no caso de você pedir a soma de um intervalo vazio de números, você obtém 0. Caso contrário, calcule a soma entre a + 1 e b e, em seguida, adicione a.
Até agora, dei apenas uma ideia de alto nível por trás do código. Mas você tinha duas outras perguntas muito boas. Primeiro, por que isso nem sempre retorna 0, visto que a função diz para retornar 0 se a> b? Em segundo lugar, de onde vêm os 14? Vamos dar uma olhada nisso.
Vamos tentar um caso muito, muito simples. O que acontece se você ligar sumInts(6, 5)
? Nesse caso, rastreando o código, você vê que a função retorna apenas 0. Essa é a coisa certa a se fazer, para - não há nenhum número no intervalo. Agora, tente algo mais difícil. O que acontece quando você liga sumInts(5, 5)
? Bem, aqui está o que acontece:
- Você liga
sumInts(5, 5)
. Nós caímos noelse
ramo, que retorna o valor de `a + sumInts (6, 5).
- Para
sumInts(5, 5)
determinar o que sumInts(6, 5)
é, precisamos pausar o que estamos fazendo e ligar para sumInts(6, 5)
.
sumInts(6, 5)
é chamado. Ele entra na if
agência e retorna 0
. No entanto, essa instância de sumInts
foi chamada por sumInts(5, 5)
, portanto, o valor de retorno é comunicado de volta sumInts(5, 5)
, não para o chamador de nível superior.
sumInts(5, 5)
agora pode calcular 5 + sumInts(6, 5)
para voltar 5
. Em seguida, ele o retorna para o chamador de nível superior.
Observe como o valor 5 foi formado aqui. Começamos com uma chamada ativa para sumInts
. Isso disparou outra chamada recursiva, e o valor retornado por essa chamada comunicou a informação de volta para sumInts(5, 5)
. A chamada para sumInts(5, 5)
then, por sua vez, fez alguns cálculos e retornou um valor ao chamador.
Se você tentar fazer isso sumInts(4, 5)
, eis o que acontecerá:
sumInts(4, 5)
tenta voltar 4 + sumInts(5, 5)
. Para fazer isso, ele chamasumInts(5, 5)
.
sumInts(5, 5)
tenta voltar 5 + sumInts(6, 5)
. Para fazer isso, ele chamasumInts(6, 5)
.
sumInts(6, 5)
retorna 0 de volta para sumInts(5, 5).</li>
<li>
sumInts (5, 5) now has a value for
sumInts (6, 5) , namely 0. It then returns
5 + 0 = 5`.
sumInts(4, 5)
agora tem um valor para sumInts(5, 5)
, a saber, 5. Ele então retorna 4 + 5 = 9
.
Em outras palavras, o valor retornado é formado pela soma dos valores um de cada vez, cada vez pegando um valor retornado por uma chamada recursiva específica sumInts
e adicionando o valor atual de a
. Quando a recursão chega ao fundo, a chamada mais profunda retorna 0. No entanto, esse valor não sai imediatamente da cadeia de chamadas recursivas; em vez disso, ele apenas devolve o valor para a chamada recursiva uma camada acima dela. Dessa forma, cada chamada recursiva apenas adiciona mais um número e o retorna mais acima na cadeia, culminando com a soma geral. Como exercício, tente rastrear isso sumInts(2, 5)
, que é o que você queria para começar.
Espero que isto ajude!