Quelle est la différence entre ceux-ci?
Sur Wikipedia, il y a peu d'informations et pas de code clair expliquant ces termes.
Quels sont quelques exemples très simples expliquant ces termes?
Comment la corecursion est-elle le double de la récursion?
Existe-t-il des algorithmes corecusifs classiques?
algorithms
recursion
utilisateur167908
la source
la source
Réponses:
Il y a un bon nombre de bonnes manières de regarder ceci. La chose la plus facile pour moi est de penser à la relation entre "définitions inductives" et "définitions coinductives"
Une définition inductive d'un ensemble va comme ceci.
L'ensemble "Nat" est défini comme le plus petit ensemble tel que "zéro" soit dans Nat, et si n est dans Nat, "Succ n" est dans Nat.
Ce qui correspond au Ocaml suivant
Une chose à noter à propos de cette définition est qu'un nombre
n'est pas un membre de cet ensemble. Pourquoi? Supposons que c’est le cas, considérons maintenant l’ensemble N qui a tous les mêmes éléments que Nat, sauf qu’il n’a pas d’oméga. Clairement, zéro est dans N, et si y est dans N, Succ (y) est dans N, mais N est plus petit que Nat, ce qui est une contradiction. Donc, oméga n'est pas dans Nat.
Ou peut-être plus utile pour un informaticien:
Pour un ensemble "a", l'ensemble "Liste d'un" est défini comme le plus petit ensemble tel que "Nil" figure dans la liste d'un, et que si xs se trouve dans la liste d'un et x se trouve dans un "Cons x xs" est dans la liste de a.
Ce qui correspond à quelque chose comme
Le mot clé ici est "le plus petit". Si nous ne disions pas "plus petit", nous n'aurions aucun moyen de savoir si le set Nat contenait une banane!
Encore,
n'est pas une définition valide pour une liste de noms, tout comme Omega n'était pas un Nat valide.
Définir les données de manière inductive de cette manière nous permet de définir des fonctions qui fonctionnent avec la récursion
nous pouvons alors prouver des faits à ce sujet, comme "plus un zéro = a" en utilisant l' induction (en particulier, l'induction structurelle)
Notre preuve procède par induction structurelle sur un.
Pour le cas de base, laissez-le être zéro.
plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)
alors nous savonsplus Zero Zero = Zero
. Soita
un nat. Supposons l'hypothèse inductive queplus a Zero = a
. Nous montrons maintenant queplus (Succ(a)) Zero = Succ(a)
cela est évident puisqueplus (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)
Ainsi, par inductionplus a Zero = a
pour tousa
dans natNous pouvons bien sûr prouver des choses plus intéressantes, mais c'est l'idée générale.
Jusqu'ici, nous avons traité des données définies par induction que nous avons obtenues en les laissant le "plus petit" ensemble. Nous voulons donc maintenant travailler avec des codata définies de manière coinductive que nous obtenons en les laissant être le plus grand ensemble.
Alors
Soit un ensemble. L'ensemble "Stream of a" est défini comme le plus grand ensemble tel que, pour chaque x du flux d'un, x se compose de la paire ordonnée (tête, queue) telle que la tête se trouve dans un et que la queue se trouve dans le flux d'un
En Haskell, nous dirions ceci comme
En fait, en Haskell, nous utilisons normalement les listes intégrées, qui peuvent être une paire ordonnée ou une liste vide.
Banana n'est pas un membre de ce type non plus, puisqu'il ne s'agit ni d'une paire ordonnée ni d'une liste vide. Mais maintenant on peut dire
et c'est une définition parfaitement valable. De plus, nous pouvons effectuer une co-récursion sur ces co-données. En fait, il est possible qu'une fonction soit à la fois co-récursive et récursive. Alors que la récursion était définie par le fait que la fonction avait un domaine composé de données, la co-récursivité signifiait simplement qu'elle avait un co-domaine (également appelé la plage) qui est la co-donnée. La récursion primitive signifiait toujours "s’appeler" sur des données plus petites jusqu’à atteindre des données plus petites. La co-récursivité primitive "s'appelle toujours" sur des données supérieures ou égales à ce que vous aviez auparavant.
est primitivement co-récursif. Alors que la fonction
map
(un peu comme "foreach" dans les langages impératifs) est à la fois primitivement récursive (en quelque sorte) et primitivement co-récursive.Il en va de même pour la fonction
zipWith
qui prend une fonction et une paire de listes et les combine en utilisant cette fonction.l'exemple classique des langages fonctionnels est la séquence de Fibonacci
qui est primitivement récursif, mais peut être exprimé plus élégamment comme une liste infinie
un exemple intéressant d'induction / coinduction prouve que ces deux définitions calculent la même chose. Ceci est laissé comme un exercice pour le lecteur.
la source
Fondamentalement, corecursion est de type accumulateur de récursivité , construisant son résultat sur la voie suivante du cas de départ, tandis que la récursion régulière construit son résultat sur le chemin du retour du cas de base.
(parle maintenant Haskell). C'est pourquoi
foldr
(avec une fonction de combinaison stricte) exprime la récursion, etfoldl'
(avec un peigne strict. F.) /scanl
/until
/iterate
/unfoldr
/ Etc. etc. corecursion. Corecursion est partout.foldr
avec peigne non strict. F. exprime la queue récursion modulo contre .Et la récursion gardée de Haskell est comme la contre-récursion de la queue, modulo .
C'est la récursion:
(lu
$
comme "de"). C'est corecursion:Plis: http://en.wikipedia.org/wiki/Fold_(higher-order_function)
la source
Vérifiez ceci sur le blog de Vitomir Kovanovic . Je l'ai trouvé au point:
la source