Qual a diferença entre estes?
Na Wikipedia, há pouca informação e nenhum código claro explicando esses termos.
Quais são alguns exemplos muito simples que explicam esses termos?
Como a corecursão é o dual da recursão?
Existem algoritmos corecusivos clássicos?
algorithms
recursion
user167908
fonte
fonte
Respostas:
Existem várias maneiras boas de ver isso. O mais fácil para mim é pensar na relação entre "definições indutivas" e "definições coindutivas"
Uma definição indutiva de um conjunto é assim.
O conjunto "Nat" é definido como o menor conjunto, de modo que "Zero" esteja em Nat e, se n estiver em Nat, "Succ n" estará em Nat.
Que corresponde ao seguinte Ocaml
Uma coisa a notar sobre essa definição é que um número
NÃO é um membro deste conjunto. Por quê? Suponha que sim, agora considere o conjunto N que possui todos os mesmos elementos que Nat, exceto que ele não possui ômega. Claramente, Zero está em N e, se y está em N, Succ (y) está em N, mas N é menor que Nat, o que é uma contradição. Então, o ômega não está no Nat.
Ou talvez mais útil para um cientista da computação:
Dado um conjunto "a", o conjunto "Lista de a" é definido como o menor conjunto, de forma que "Nil" esteja na Lista de a, e que se xs estiver na Lista de aex esteja em "Contras x xs" está na lista de a.
O que corresponde a algo como
A palavra operativa aqui é "menor". Se não disséssemos "menor", não teríamos como saber se o conjunto Nat continha uma banana!
Novamente,
não é uma definição válida para uma lista de nats, assim como ômega não era um Nat válido.
Definir dados indutivamente como este nos permite definir funções que funcionam com recursão
podemos provar fatos sobre isso, como "mais um zero = a" usando indução (especificamente, indução estrutural)
Nossa prova procede por indução estrutural em a.
Para o caso base, seja zero.
plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)
então nós sabemosplus Zero Zero = Zero
. Sejaa
um nat. Assuma a hipótese indutiva de queplus a Zero = a
. Vamos agora mostrar queplus (Succ(a)) Zero = Succ(a)
isso é óbvio desdeplus (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)
Assim, por induçãoplus a Zero = a
para todosa
em natÉ claro que podemos provar coisas mais interessantes, mas essa é a ideia geral.
Até agora, lidamos com dados definidos indutivamente , obtendo-os por ser o conjunto "menor". Então, agora queremos trabalhar com coinductivly definidos CODATA qual obtemos por deixá-lo ser o maior conjunto.
assim
Seja um conjunto. O conjunto "Fluxo de a" é definido como o maior conjunto, de modo que, para cada x no fluxo de a, x consiste no par ordenado (cabeça, cauda), de modo que a cabeça está em a e a cauda está no fluxo de uma
Em Haskell, expressaríamos isso como
Na verdade, em Haskell, usamos normalmente as listas internas, que podem ser um par ordenado ou uma lista vazia.
Banana também não é um membro desse tipo, pois não é um par ordenado ou a lista vazia. Mas agora podemos dizer
e esta é uma definição perfeitamente válida. Além disso, podemos realizar co-recursão nesses co-dados. Na verdade, é possível que uma função seja co-recursiva e recursiva. Embora a recursão tenha sido definida pela função que possui um domínio que consiste em dados, a co-recursão significa apenas que ele possui um co-domínio (também chamado de intervalo) que é co-dados. Recursão primitiva significava sempre "chamar a si mesmo" em dados menores até alcançar alguns dados menores. A co-recursão primitiva sempre "se chama" em dados maiores ou iguais aos que você possuía antes.
é primitivamente co-recursivo. Enquanto a função
map
(como "foreach" em linguagens imperativas) é primitivamente recursiva (mais ou menos) e primitivamente co-recursiva.O mesmo vale para a função
zipWith
que pega uma função e um par de listas e os combina usando essa função.o exemplo clássico de linguagens funcionais é a sequência de Fibonacci
que é primitivamente recursivo, mas pode ser expresso de maneira mais elegante como uma lista infinita
Um exemplo interessante de indução / coindução está provando que essas duas definições calculam a mesma coisa. Isso é deixado como um exercício para o leitor.
fonte
Basicamente, a corecursão é do tipo acumulador de recursão , construindo seu resultado no caminho a partir do caso inicial, enquanto a recursão regular constrói seu resultado no caminho de volta do caso base.
(falando Haskell agora). É por isso que
foldr
(com uma função de combinação estrita) expressa recursão e (com uma combinaçãofoldl'
estrita de f.) /scanl
/until
/iterate
/unfoldr
/ Etc. expressa corecursão. Corecursão está em toda parte.foldr
com pente não estrito. f. expressa recursão da cauda módulo contras .E a recursão guardada de Haskell é como contras de módulo de recursão da cauda .
Isso é recursão:
(leia
$
como "de"). Isso é corecursão:Dobras: http://en.wikipedia.org/wiki/Fold_(higher-order_function)
fonte
Verifique isso no blog de Vitomir Kovanovic . Eu achei isso direto ao ponto:
fonte