La recursividad es un tema complicado de entender y no creo que pueda hacerle justicia aquí. En su lugar, intentaré centrarme en la pieza de código particular que tienes aquí y trataré de describir tanto la intuición de por qué funciona la solución como la mecánica de cómo el código calcula su resultado.
El código que ha proporcionado aquí resuelve el siguiente problema: desea saber la suma de todos los números enteros desde a hasta b, inclusive. Para su ejemplo, desea la suma de los números del 2 al 5, inclusive, que es
2 + 3 + 4 + 5
Al intentar resolver un problema de forma recursiva, uno de los primeros pasos debería ser descubrir cómo dividir el problema en un problema más pequeño con la misma estructura. Así que suponga que desea sumar los números del 2 al 5, inclusive. Una forma de simplificar esto es notar que la suma anterior se puede reescribir como
2 + (3 + 4 + 5)
Aquí, (3 + 4 + 5) resulta ser la suma de todos los números enteros entre 3 y 5, inclusive. En otras palabras, si quieres saber la suma de todos los números enteros entre 2 y 5, comienza calculando la suma de todos los números enteros entre 3 y 5, luego suma 2.
Entonces, ¿cómo se calcula la suma de todos los números enteros entre 3 y 5, inclusive? Bueno, esa suma es
3 + 4 + 5
que se puede pensar en cambio como
3 + (4 + 5)
Aquí, (4 + 5) es la suma de todos los números enteros entre 4 y 5, inclusive. Entonces, si quisieras calcular la suma de todos los números entre 3 y 5, inclusive, calcularías la suma de todos los números enteros entre 4 y 5, luego sumarías 3.
¡Aquí hay un patrón! Si desea calcular la suma de los números enteros entre ayb, inclusive, puede hacer lo siguiente. Primero, calcule la suma de los números enteros entre a + 1 y b, inclusive. Luego, agregue a ese total. Notará que "calcular la suma de los números enteros entre a + 1 y b, inclusive" resulta ser más o menos el mismo tipo de problema que ya estamos tratando de resolver, pero con parámetros ligeramente diferentes. En lugar de calcular desde a hasta b, inclusive, estamos calculando desde a + 1 hasta b, inclusive. Ese es el paso recursivo: para resolver el problema más grande ("suma de a a b, inclusive"), reducimos el problema a una versión más pequeña de sí mismo ("suma de a + 1 a b, inclusive").
Si echas un vistazo al código que tienes arriba, notarás que hay este paso en él:
return a + sumInts(a + 1, b: b)
Este código es simplemente una traducción de la lógica anterior: si desea sumar de a a b, inclusive, comience sumando a + 1 a b, inclusive (esa es la llamada recursiva as sumInt
), luego agreguea
.
Por supuesto, este enfoque por sí solo no funcionará. Por ejemplo, ¿cómo calcularía la suma de todos los números enteros entre 5 y 5 inclusive? Bien, usando nuestra lógica actual, calcularías la suma de todos los enteros entre 6 y 5, inclusive, luego sumarías 5. Entonces, ¿cómo calculas la suma de todos los enteros entre 6 y 5, inclusive? Bueno, usando nuestra lógica actual, calcularías la suma de todos los enteros entre 7 y 5, inclusive, luego sumarías 6. Notarás un problema aquí - ¡esto sigue y sigue!
En la resolución recursiva de problemas, es necesario que haya alguna forma de dejar de simplificar el problema y, en su lugar, ir a resolverlo directamente. Por lo general, encontrará un caso simple en el que la respuesta se puede determinar de inmediato, luego estructurará su solución para resolver casos simples directamente cuando surjan. Esto se denomina típicamente un caso base o una base recursiva .
Entonces, ¿cuál es el caso base en este problema en particular? Cuando sumas números enteros de aab, inclusive, si a es mayor que b, entonces la respuesta es 0, ¡no hay números en el rango! Por lo tanto, estructuraremos nuestra solución de la siguiente manera:
- Si a> b, entonces la respuesta es 0.
- De lo contrario (a ≤ b), obtenga la respuesta de la siguiente manera:
- Calcule la suma de los números enteros entre a + 1 y b.
- Agregue un para obtener la respuesta.
Ahora, compare este pseudocódigo con su código real:
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a + 1, b: b)
}
}
Tenga en cuenta que existe casi exactamente un mapa uno a uno entre la solución descrita en pseudocódigo y este código real. El primer paso es el caso base: en el caso de que pida la suma de un rango vacío de números, obtendrá 0. De lo contrario, calcule la suma entre a + 1 yb, luego agregue a.
Hasta ahora, he dado solo una idea de alto nivel detrás del código. Pero tenías otras dos muy buenas preguntas. Primero, ¿por qué esto no siempre devuelve 0, dado que la función dice que devuelva 0 si a> b? En segundo lugar, ¿de dónde vienen realmente los 14? Veamos estos a su vez.
Probemos con un caso muy, muy simple. ¿Qué pasa si llamas sumInts(6, 5)
? En este caso, rastreando el código, verá que la función simplemente devuelve 0. Eso es lo correcto, no hay números en el rango. Ahora, intente algo más difícil. ¿Qué pasa cuando llamas sumInts(5, 5)
? Bueno, esto es lo que sucede:
- Usted llama
sumInts(5, 5)
. Caemos en elelse
Caemos rama, que devuelve el valor de `a + sumInts (6, 5).
- Para
sumInts(5, 5)
poder determinar qué sumInts(6, 5)
es, necesitamos pausar lo que estamos haciendo y hacer una llamada sumInts(6, 5)
.
sumInts(6, 5)
se llama. Entra en la if
sucursal y regresa 0
. Sin embargo, esta instancia de sumInts
fue llamada por sumInts(5, 5)
, por lo que el valor de retorno se comunica sumInts(5, 5)
a la persona que llama de nivel superior, no a ella.
sumInts(5, 5)
ahora puede calcular 5 + sumInts(6, 5)
para volver 5
. Luego lo devuelve a la persona que llama de nivel superior.
Observe cómo se formó el valor 5 aquí. Comenzamos con una llamada activa a sumInts
. Eso disparó otra llamada recursiva, y el valor devuelto por esa llamada le devolvió la información sumInts(5, 5)
. La llamada asumInts(5, 5)
entonces hizo algunos cálculos y devolvió un valor a la persona que llama.
Si prueba esto con sumInts(4, 5)
, esto es lo que sucederá:
sumInts(4, 5)
intenta volver 4 + sumInts(5, 5)
. Para hacer eso, llama sumInts(5, 5)
.
sumInts(5, 5)
intenta volver 5 + sumInts(6, 5)
. Para hacer eso, llama sumInts(6, 5)
.
sumInts(6, 5)
devuelve 0 a 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)
ahora tiene un valor para sumInts(5, 5)
, es decir, 5. Luego regresa 4 + 5 = 9
.
En otras palabras, el valor que se devuelve se forma sumando los valores uno a la vez, tomando cada vez un valor devuelto por una llamada recursiva particular ay sumInts
agregando el valor actual de a
. Cuando la recursividad toca fondo, la llamada más profunda devuelve 0. Sin embargo, ese valor no sale inmediatamente de la cadena de llamadas recursivas; en su lugar, simplemente devuelve el valor a la llamada recursiva una capa por encima. De esa manera, cada llamada recursiva simplemente agrega un número más y lo devuelve más arriba en la cadena, culminando con la suma general. Como ejercicio, intente rastrear esto sumInts(2, 5)
, que es con lo que quería comenzar.
¡Espero que esto ayude!