Diferencia entre la recursividad de cola y la recursividad estructural
8
¿Hay alguna diferencia entre la recursividad estructural y la recursividad de cola o ambas son iguales? Veo que en estas dos recursiones, la función recursiva se llama en el subconjunto de los elementos originales.
¿Qué investigación hiciste? ¿Cuál es su comprensión de la recursividad de la cola? ¿Por qué crees que es similar a la recursión estructural?
Jonathan Cast
¿Alguna posibilidad de que pudieras agregar lo que viste que te hizo pensar que podrían ser iguales o muy similares? Podría ayudar a los instructores a aclararlo al enseñar los conceptos a las personas.
user541686
Respuestas:
28
Recurrencia estructural: las llamadas recursivas se realizan sobre argumentos estructuralmente más pequeños .
Recurrencia de la cola: la llamada recursiva es lo último que sucede.
No hay ningún requisito de que la recursión de cola se invoque en un argumento más pequeño. De hecho, con bastante frecuencia las funciones recursivas de cola están diseñadas para repetirse para siempre. Por ejemplo, aquí hay una recursión trivial de la cola (no muy útil, pero es una recursión de la cola):
def f(x):
return f(x+1)
De hecho, tenemos que ser un poco más cuidadosos. Puede haber varias llamadas recursivas en una función, y no todas deben ser recursivas de cola:
def g(x):
if x < 0:
return 42 # no recursive call
elif x < 20:
return 2 + g(x - 2) # not tail recursive (must add 2 after the call)
else:
return g(x - 3) # tail recursive
Se habla de llamadas recursivas de cola . Una función recursiva cuyas llamadas son todos recursiva de cola se llama entonces una función recursiva de cola.
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
DW
-1
La recursividad de cola es un caso muy simple de recursión estructural, donde la estructura en cuestión es una lista vinculada . En el lenguaje que probablemente esté utilizando principalmente, esta lista probablemente no esté literalmente en el código; más bien, es una "lista de llamadas a la función" conceptual, un concepto que puede no ser posible expresar como está escrito usando ese lenguaje. En Haskell (mi lenguaje), cualquier llamada de función recursiva de cola puede reemplazarse por acciones secuenciales en una lista literal cuyos elementos son literalmente "llamadas a una función", pero esto es probablemente una cuestión de lenguaje funcional.
La recursividad estructural es una forma de operar en un objeto definido como un compuesto de otros objetos (posiblemente compuestos). Por ejemplo, un árbol binario es un objeto que contiene referencias a dos árboles binarios, o está vacío (por lo tanto, es un objeto definido recursivamente ). Menos autorreferencialmente, un par (t1, t2) que contiene dos valores de algunos tipos t1 y t2 admite recursividad estructural, aunque t1 y t2 no necesitan ser también pares. Esta recursión toma la forma
acción en el par = combinación de los resultados de otras acciones en cada elemento
lo cual no suena muy profundo.
A menudo se da el caso de que una recursión estructural no puede ser recursiva de la cola, aunque cualquier tipo de recursión puede reescribirse como una recursión de cola (prueba: si ejecuta la recursión original, las acciones se completan en un cierto orden; por lo tanto, la recursividad es equivalente a realizar esa secuencia particular de acciones, que como mencioné anteriormente, es la recursión de cola).
El árbol binario o el ejemplo de par anterior lo demuestran: sin embargo, si organiza las llamadas recursivas en los subobjetos, solo uno de ellos puede ser la última acción; posiblemente ninguno lo es, si sus resultados se combinan de alguna manera (digamos, suma). Como Andrej Bauer dice en su respuesta, esto puede suceder incluso con una sola llamada recursiva, siempre que se modifique el resultado. En otras palabras, para cada tipo de objeto que no sean los que están efectivamente vinculados listas (solo un subobjeto hasta el final), la recursión estructural no es la recursividad de cola.
Es falso que la recursión de cola solo se trata de listas, imaginarias o reales. Es perfectamente posible tener una recursión de la cola sobre árboles binarios, por ejemplo. Puedo ver por qué alguien pensaría que es porque el resto de la lista es su "cola".
Andrej Bauer
@AndrejBauer Me sentiré adecuadamente avergonzado por esto cuando entiendo exactamente lo que está mal. Parece tautológico que la recursión de la cola de la forma f x = (stuff defining x'); f x'es la misma que la secuencia de nodos en una lista vinculada definida como l = modify f : l(en el estilo Haskell estado-mónada). No fue solo la similitud terminológica para mí. En cuanto a la recursión de la cola sobre los árboles binarios, ¿podría elaborar? Solo puedo pensar en el hecho de linealización de mi penúltimo párrafo.
Ryan Reich
No todas las llamadas recursivas de cola son de esa forma. Por ejemplo, puede haber varias llamadas recursivas de cola en diferentes ramas (de declaraciones de casos, o algo así). Es más natural pensar en aquellos que seleccionan un camino a través de un árbol . También tenga en cuenta que las llamadas son recursivas de cola (o no), por lo que podría tener f (f x)donde la llamada externa fes recursiva de cola. ¿Cómo encaja eso en la opinión de que se trata de listas? Aquí hay otro ejemplo: f : (Int -> Int) -> (Int -> Int)con f g 0 = g 42y f g (n + 1) = f (f . g) n. Las posibilidades son infinitas, y algunas son útiles.
Andrej Bauer
@AndrejBauer La pregunta era sobre la recursividad de cola en lugar de solo llamadas de cola , por lo que no consideraría lo f (f x)aplicable: en la evaluación de la f externa, la interna no es una llamada de cola (a menos que f sea la identidad). Sentencias if se pueden reescribir trivialmente no sucursal en la llamada de cola: if (c) then f a else f b == let x = if (c) then a else b in f x. El último ejemplo no es válido porque f . gno escribe checkcheck; aun así, todavía no sería una recursión de cola: f g = \n -> if n == 0 then g 42 else f (f . g) (n - 1)no es un llamado a f, sino una lambda genuinamente diferente. (siguiente)
Ryan Reich
De hecho, diría que ese ejemplo es de recursión mutua de cola , es decir f g = h where { h 0 = g 42; h n = f (f . g) (n - 1) }, pero si lo llevas a la discusión, cualquier función recursiva, cola o no, es admisible y el término deja de tener sentido.
Respuestas:
Recurrencia estructural: las llamadas recursivas se realizan sobre argumentos estructuralmente más pequeños .
Recurrencia de la cola: la llamada recursiva es lo último que sucede.
No hay ningún requisito de que la recursión de cola se invoque en un argumento más pequeño. De hecho, con bastante frecuencia las funciones recursivas de cola están diseñadas para repetirse para siempre. Por ejemplo, aquí hay una recursión trivial de la cola (no muy útil, pero es una recursión de la cola):
De hecho, tenemos que ser un poco más cuidadosos. Puede haber varias llamadas recursivas en una función, y no todas deben ser recursivas de cola:
Se habla de llamadas recursivas de cola . Una función recursiva cuyas llamadas son todos recursiva de cola se llama entonces una función recursiva de cola.
fuente
La recursividad de cola es un caso muy simple de recursión estructural, donde la estructura en cuestión es una lista vinculada . En el lenguaje que probablemente esté utilizando principalmente, esta lista probablemente no esté literalmente en el código; más bien, es una "lista de llamadas a la función" conceptual, un concepto que puede no ser posible expresar como está escrito usando ese lenguaje. En Haskell (mi lenguaje), cualquier llamada de función recursiva de cola puede reemplazarse por acciones secuenciales en una lista literal cuyos elementos son literalmente "llamadas a una función", pero esto es probablemente una cuestión de lenguaje funcional.
La recursividad estructural es una forma de operar en un objeto definido como un compuesto de otros objetos (posiblemente compuestos). Por ejemplo, un árbol binario es un objeto que contiene referencias a dos árboles binarios, o está vacío (por lo tanto, es un objeto definido recursivamente ). Menos autorreferencialmente, un par (t1, t2) que contiene dos valores de algunos tipos t1 y t2 admite recursividad estructural, aunque t1 y t2 no necesitan ser también pares. Esta recursión toma la forma
lo cual no suena muy profundo.
A menudo se da el caso de que una recursión estructural no puede ser recursiva de la cola, aunque cualquier tipo de recursión puede reescribirse como una recursión de cola (prueba: si ejecuta la recursión original, las acciones se completan en un cierto orden; por lo tanto, la recursividad es equivalente a realizar esa secuencia particular de acciones, que como mencioné anteriormente, es la recursión de cola).
El árbol binario o el ejemplo de par anterior lo demuestran: sin embargo, si organiza las llamadas recursivas en los subobjetos, solo uno de ellos puede ser la última acción; posiblemente ninguno lo es, si sus resultados se combinan de alguna manera (digamos, suma). Como Andrej Bauer dice en su respuesta, esto puede suceder incluso con una sola llamada recursiva, siempre que se modifique el resultado. En otras palabras, para cada tipo de objeto que no sean los que están efectivamente vinculados listas (solo un subobjeto hasta el final), la recursión estructural no es la recursividad de cola.
fuente
f x = (stuff defining x'); f x'
es la misma que la secuencia de nodos en una lista vinculada definida comol = modify f : l
(en el estilo Haskell estado-mónada). No fue solo la similitud terminológica para mí. En cuanto a la recursión de la cola sobre los árboles binarios, ¿podría elaborar? Solo puedo pensar en el hecho de linealización de mi penúltimo párrafo.f (f x)
donde la llamada externaf
es recursiva de cola. ¿Cómo encaja eso en la opinión de que se trata de listas? Aquí hay otro ejemplo:f : (Int -> Int) -> (Int -> Int)
conf g 0 = g 42
yf g (n + 1) = f (f . g) n
. Las posibilidades son infinitas, y algunas son útiles.f (f x)
aplicable: en la evaluación de la f externa, la interna no es una llamada de cola (a menos que f sea la identidad). Sentencias if se pueden reescribir trivialmente no sucursal en la llamada de cola:if (c) then f a else f b == let x = if (c) then a else b in f x
. El último ejemplo no es válido porquef . g
no escribe checkcheck; aun así, todavía no sería una recursión de cola:f g = \n -> if n == 0 then g 42 else f (f . g) (n - 1)
no es un llamado af
, sino una lambda genuinamente diferente. (siguiente)f g = h where { h 0 = g 42; h n = f (f . g) (n - 1) }
, pero si lo llevas a la discusión, cualquier función recursiva, cola o no, es admisible y el término deja de tener sentido.