A biblioteca StreamMemo para Coq ilustra como memorizar uma função f : nat -> A
sobre os números naturais. Em particular quando f (S n) = g (f n)
, imemo_make
compartilha o cálculo de chamadas recursivas.
Suponha que, em vez de números naturais, desejemos memorizar funções recursivas sobre árvores binárias:
Inductive binTree : Set :=
| Leaf : binTree
| Branch : binTree -> binTree -> binTree.
Suponha que tenhamos uma função f : binTree -> A
estruturalmente recursiva, significando que existe uma função g : A -> A -> A
como essa f (Branch x y) = g (f x) (f y)
. Como construímos uma tabela de memorando semelhante para f
no Coq, de modo que os cálculos recursivos sejam compartilhados?
Em Haskell, não é muito difícil criar uma tabela de notas (veja o MemoTrie, por exemplo) e amarrar o nó. Claramente, essas tabelas de notas são produtivas. Como podemos organizar as coisas para convencer uma linguagem tipicamente dependente a aceitar que esse nó é produtivo?
Embora eu tenha especificado o problema no Coq, eu ficaria feliz com uma resposta no Agda ou em qualquer outro idioma de tipo dependente.
fonte
go
valor é uma função de um parâmetro Size. Em geral, não há compartilhamento entre chamadas de função independentes com o mesmo valor. Provavelmente, isso pode ser corrigido adicionando uma instrução let na definição deh (Branch l r)
. Em segundo lugar, a definição estratificada deBT
significa que duas árvores, de outra forma idêntica, terão valores diferentes quando ocorrerem em níveis diferentes. Esses valores distintos não serão compartilhados no MemoTrie.Memo
inbranch
. O verificador de positividade da Coq parece rejeitar isso, tornando as coisas mais complicadas na Coq.Size
tipos acabam sendo apagados.Eu criei uma "solução" que memoriza recursivamente funções estruturalmente recursivas de árvores binárias no Coq. Minha essência está em https://gist.github.com/roconnor/286d0f21af36c2e97e74338f10a4315b .
Ele opera de maneira semelhante à solução da Saizan , estratificando árvores binárias com base em uma métrica de tamanho, no meu caso, contando o número de nós internos de árvores binárias e produzindo um fluxo lento de contêineres de todas as soluções para um tamanho específico. O compartilhamento ocorre devido a uma instrução let no gerador de fluxo que mantém a parte inicial do fluxo a ser usada nas partes posteriores do fluxo.
Os exemplos mostram que
vm_compute
, avaliar uma árvore binária perfeita com 8 níveis após ter avaliado uma árvore binária perfeita com 9 níveis é muito mais rápido do que apenas avaliar uma árvore binária perfeita com 8 níveis.No entanto, hesito em aceitar esta resposta porque a sobrecarga dessa solução em particular é ruim, pois ela tem um desempenho muito pior do que minha memorização sem recursão estrutural para meus exemplos de informações práticas. Naturalmente, quero uma solução que tenha um desempenho melhor com entradas razoáveis.
Tenho mais alguns comentários em " [Coq-Club] Como você pode criar uma tabela de memorização coindutiva para funções recursivas em árvores binárias? ".
fonte