¿Cuál es la diferencia entre estos?
En Wikipedia, hay poca información y ningún código claro que explique estos términos.
¿Cuáles son algunos ejemplos muy simples que explican estos términos?
¿Cómo es corecursion el dual de recursión?
¿Hay algún algoritmo corecusive clásico?
algorithms
recursion
usuario167908
fuente
fuente
Respuestas:
Hay varias maneras buenas de ver esto. Lo más fácil para mí es pensar en la relación entre "Definiciones inductivas" y "Coinductivas"
Una definición inductiva de un conjunto es así.
El conjunto "Nat" se define como el conjunto más pequeño, de modo que "Zero" está en Nat, y si n está en Nat, "Succ n" está en Nat.
Que corresponde al siguiente Ocaml
Una cosa a tener en cuenta sobre esta definición es que un número
NO es miembro de este conjunto. ¿Por qué? Suponga que sí, ahora considere el conjunto N que tiene todos los mismos elementos que Nat, excepto que no tiene omega. Claramente, Zero está en N, y si y está en N, Succ (y) está en N, pero N es más pequeño que Nat, lo cual es una contradicción. Entonces, omega no está en Nat.
O, quizás más útil para un informático:
Dado un conjunto "a", el conjunto "Lista de a" se define como el conjunto más pequeño de modo que "Nil" está en la Lista de a, y que si xs está en la Lista de a y x está en un "Cons x xs" está en la lista de a.
Que corresponde a algo como
La palabra operativa aquí es "más pequeña". ¡Si no dijéramos "el más pequeño" no tendríamos ninguna forma de saber si el conjunto Nat contenía un plátano!
De nuevo,
no es una definición válida para una lista de nats, al igual que omega no era un Nat válido.
Definir datos inductivamente de esta manera nos permite definir funciones que funcionan en ellos utilizando la recursividad
entonces podemos probar hechos sobre esto, como "más un cero = a" usando inducción (específicamente, inducción estructural)
Nuestra prueba procede por inducción estructural en a.
Para el caso base, sea un cero.
plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)
por lo que sabemosplus Zero Zero = Zero
. Deja quea
sea un nat. Suponga la hipótesis inductiva de queplus a Zero = a
. Ahora mostramos queplus (Succ(a)) Zero = Succ(a)
esto es obvio ya queplus (Succ(a)) Zero = match a with |Zero -> Zero | Succ(a) -> let r = plus a Zero in Succ(r) = let r = a in Succ(r) = Succ(a)
, por inducción,plus a Zero = a
para todosa
en natPor supuesto, podemos probar cosas más interesantes, pero esta es la idea general.
Hasta ahora hemos tratado con datos inductivamente definidos que obtuvimos al dejar que sea el conjunto "más pequeño". Entonces, ahora queremos trabajar con codatos definidos de forma coductiva que obtenemos al dejar que sea el conjunto más grande.
Entonces
Deja que sea un conjunto. El conjunto "Flujo de a" se define como el conjunto más grande de tal manera que para cada x en el flujo de a, x consiste en el par ordenado (cabeza, cola) de modo que la cabeza está en ay la cola está en Flujo de un
En Haskell expresaríamos esto como
En realidad, en Haskell usamos las listas integradas normalmente, que pueden ser un par ordenado o una lista vacía.
Banana tampoco es miembro de este tipo, ya que no es un par ordenado o la lista vacía. Pero ahora podemos decir
y esta es una definición perfectamente válida. Además, podemos realizar la co-recursividad en estos datos conjuntos. En realidad, es posible que una función sea co-recursiva y recursiva. Si bien la recursión se definió por la función que tiene un dominio que consiste en datos, la co-recursión solo significa que tiene un co-dominio (también llamado rango) que es co-datos. La recursividad primitiva significaba siempre "llamarse a uno mismo" en datos más pequeños hasta alcanzar algunos datos más pequeños. La co-recursividad primitiva siempre "se llama a sí misma" en datos mayores o iguales a los que tenía antes.
es primitivamente co-recursivo. Mientras que la función
map
(algo así como "foreach" en lenguajes imperativos) es primitivamente recursiva (más o menos) y primitivamente co-recursiva.Lo mismo ocurre con la función
zipWith
que toma una función y un par de listas y las combina usando esa función.El ejemplo clásico de lenguajes funcionales es la secuencia de Fibonacci
que es primitivamente recursivo, pero puede expresarse más elegantemente como una lista infinita
Un ejemplo interesante de inducción / coinducción es demostrar que estas dos definiciones calculan lo mismo. Esto se deja como un ejercicio para el lector.
fuente
Básicamente, corecursion es un estilo de acumulador de recursión , construyendo su resultado en el camino hacia adelante desde el caso inicial, mientras que la recursión regular construye su resultado en el camino de regreso desde el caso base.
(Hablando Haskell ahora). Es por eso que
foldr
(con una estricta función de combinación) expresa la recursión yfoldl'
(con un estricto comb. F.) /scanl
/until
/iterate
/unfoldr
/ Etc.expresa corecursion. Corecursion está en todas partes.foldr
con peine no estricto. F. expresa recidiva de cola módulo contras .Y la recursión protegida de Haskell es como el módulo de recursión de cola .
Esto es recursividad:
(leído
$
como "de"). Esta es corecursion:Pliegues: http://en.wikipedia.org/wiki/Fold_(higher-order_function)
fuente
Mira esto en el blog de Vitomir Kovanovic . Lo encontré al punto:
fuente